extract serving code to a separate module
This commit is contained in:
parent
f889eb3d12
commit
79cdcb46de
|
@ -13,17 +13,6 @@ extern crate time;
|
||||||
#[cfg(feature = "watch")]
|
#[cfg(feature = "watch")]
|
||||||
extern crate crossbeam;
|
extern crate crossbeam;
|
||||||
|
|
||||||
// Dependencies for the Serve feature
|
|
||||||
#[cfg(feature = "serve")]
|
|
||||||
extern crate iron;
|
|
||||||
#[cfg(feature = "serve")]
|
|
||||||
extern crate staticfile;
|
|
||||||
#[cfg(feature = "serve")]
|
|
||||||
extern crate ws;
|
|
||||||
|
|
||||||
#[cfg(feature = "serve")]
|
|
||||||
use iron::{Iron, AfterMiddleware, IronResult, IronError, Request, Response, status, Set, Chain};
|
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
|
@ -96,7 +85,7 @@ fn main() {
|
||||||
#[cfg(feature = "watch")]
|
#[cfg(feature = "watch")]
|
||||||
("watch", Some(sub_matches)) => watch(sub_matches),
|
("watch", Some(sub_matches)) => watch(sub_matches),
|
||||||
#[cfg(feature = "serve")]
|
#[cfg(feature = "serve")]
|
||||||
("serve", Some(sub_matches)) => serve(sub_matches),
|
("serve", Some(sub_matches)) => serve::serve(sub_matches),
|
||||||
("test", Some(sub_matches)) => test(sub_matches),
|
("test", Some(sub_matches)) => test(sub_matches),
|
||||||
(_, _) => unreachable!(),
|
(_, _) => unreachable!(),
|
||||||
};
|
};
|
||||||
|
@ -237,50 +226,63 @@ fn watch(args: &ArgMatches) -> Result<(), Box<Error>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serve")]
|
#[cfg(feature = "serve")]
|
||||||
struct ErrorRecover;
|
mod serve {
|
||||||
|
extern crate iron;
|
||||||
|
extern crate staticfile;
|
||||||
|
extern crate ws;
|
||||||
|
|
||||||
#[cfg(feature = "serve")]
|
use std;
|
||||||
impl AfterMiddleware for ErrorRecover {
|
use std::path::Path;
|
||||||
fn catch(&self, _: &mut Request, err: IronError) -> IronResult<Response> {
|
use std::error::Error;
|
||||||
match err.response.status {
|
use self::iron::{Iron, AfterMiddleware, IronResult, IronError, Request, Response, status, Set, Chain};
|
||||||
Some(_) => Ok(err.response.set(status::NotFound)),
|
use clap::ArgMatches;
|
||||||
_ => Err(err)
|
use mdbook::MDBook;
|
||||||
|
|
||||||
|
use {get_book_dir, open, trigger_on_change};
|
||||||
|
|
||||||
|
struct ErrorRecover;
|
||||||
|
|
||||||
|
impl AfterMiddleware for ErrorRecover {
|
||||||
|
fn catch(&self, _: &mut Request, err: IronError) -> IronResult<Response> {
|
||||||
|
match err.response.status {
|
||||||
|
// each error will result in 404 response
|
||||||
|
Some(_) => Ok(err.response.set(status::NotFound)),
|
||||||
|
_ => Err(err),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Watch command implementation
|
// Watch command implementation
|
||||||
#[cfg(feature = "serve")]
|
pub fn serve(args: &ArgMatches) -> Result<(), Box<Error>> {
|
||||||
fn serve(args: &ArgMatches) -> Result<(), Box<Error>> {
|
const RELOAD_COMMAND: &'static str = "reload";
|
||||||
const RELOAD_COMMAND: &'static str = "reload";
|
|
||||||
|
|
||||||
let book_dir = get_book_dir(args);
|
let book_dir = get_book_dir(args);
|
||||||
let book = MDBook::new(&book_dir).read_config()?;
|
let book = MDBook::new(&book_dir).read_config()?;
|
||||||
|
|
||||||
let mut book = match args.value_of("dest-dir") {
|
let mut book = match args.value_of("dest-dir") {
|
||||||
Some(dest_dir) => book.with_destination(dest_dir),
|
Some(dest_dir) => book.with_destination(Path::new(dest_dir)),
|
||||||
None => book,
|
None => book,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let None = book.get_destination() {
|
if let None = book.get_destination() {
|
||||||
println!("The HTML renderer is not set up, impossible to serve the files.");
|
println!("The HTML renderer is not set up, impossible to serve the files.");
|
||||||
std::process::exit(2);
|
std::process::exit(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if args.is_present("curly-quotes") {
|
if args.is_present("curly-quotes") {
|
||||||
book = book.with_curly_quotes(true);
|
book = book.with_curly_quotes(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
let port = args.value_of("port").unwrap_or("3000");
|
let port = args.value_of("port").unwrap_or("3000");
|
||||||
let ws_port = args.value_of("websocket-port").unwrap_or("3001");
|
let ws_port = args.value_of("websocket-port").unwrap_or("3001");
|
||||||
let interface = args.value_of("interface").unwrap_or("localhost");
|
let interface = args.value_of("interface").unwrap_or("localhost");
|
||||||
let public_address = args.value_of("address").unwrap_or(interface);
|
let public_address = args.value_of("address").unwrap_or(interface);
|
||||||
let open_browser = args.is_present("open");
|
let open_browser = args.is_present("open");
|
||||||
|
|
||||||
let address = format!("{}:{}", interface, port);
|
let address = format!("{}:{}", interface, port);
|
||||||
let ws_address = format!("{}:{}", interface, ws_port);
|
let ws_address = format!("{}:{}", interface, ws_port);
|
||||||
|
|
||||||
book.set_livereload(format!(r#"
|
book.set_livereload(format!(r#"
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var socket = new WebSocket("ws://{}:{}");
|
var socket = new WebSocket("ws://{}:{}");
|
||||||
socket.onmessage = function (event) {{
|
socket.onmessage = function (event) {{
|
||||||
|
@ -295,42 +297,43 @@ fn serve(args: &ArgMatches) -> Result<(), Box<Error>> {
|
||||||
}}
|
}}
|
||||||
</script>
|
</script>
|
||||||
"#,
|
"#,
|
||||||
public_address,
|
public_address,
|
||||||
ws_port,
|
ws_port,
|
||||||
RELOAD_COMMAND));
|
RELOAD_COMMAND));
|
||||||
|
|
||||||
book.build()?;
|
book.build()?;
|
||||||
|
|
||||||
let mut chain = Chain::new(staticfile::Static::new(book.get_destination().expect("destination is present, checked before")));
|
let mut chain = Chain::new(staticfile::Static::new(book.get_destination()
|
||||||
chain.link_after(ErrorRecover);
|
.expect("destination is present, checked before")));
|
||||||
let _iron = Iron::new(chain).http(&*address).unwrap();
|
chain.link_after(ErrorRecover);
|
||||||
|
let _iron = Iron::new(chain).http(&*address).unwrap();
|
||||||
|
|
||||||
let ws_server = ws::WebSocket::new(|_| |_| Ok(())).unwrap();
|
let ws_server = ws::WebSocket::new(|_| |_| Ok(())).unwrap();
|
||||||
|
|
||||||
let broadcaster = ws_server.broadcaster();
|
let broadcaster = ws_server.broadcaster();
|
||||||
|
|
||||||
std::thread::spawn(move || { ws_server.listen(&*ws_address).unwrap(); });
|
std::thread::spawn(move || { ws_server.listen(&*ws_address).unwrap(); });
|
||||||
|
|
||||||
let serving_url = format!("http://{}", address);
|
let serving_url = format!("http://{}", address);
|
||||||
println!("\nServing on: {}", serving_url);
|
println!("\nServing on: {}", serving_url);
|
||||||
|
|
||||||
if open_browser {
|
if open_browser {
|
||||||
open(serving_url);
|
open(serving_url);
|
||||||
}
|
|
||||||
|
|
||||||
trigger_on_change(&mut book, move |path, book| {
|
|
||||||
println!("File changed: {:?}\nBuilding book...\n", path);
|
|
||||||
match book.build() {
|
|
||||||
Err(e) => println!("Error while building: {:?}", e),
|
|
||||||
_ => broadcaster.send(RELOAD_COMMAND).unwrap(),
|
|
||||||
}
|
}
|
||||||
println!("");
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(())
|
trigger_on_change(&mut book, move |path, book| {
|
||||||
|
println!("File changed: {:?}\nBuilding book...\n", path);
|
||||||
|
match book.build() {
|
||||||
|
Err(e) => println!("Error while building: {:?}", e),
|
||||||
|
_ => broadcaster.send(RELOAD_COMMAND).unwrap(),
|
||||||
|
}
|
||||||
|
println!("");
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn test(args: &ArgMatches) -> Result<(), Box<Error>> {
|
fn test(args: &ArgMatches) -> Result<(), Box<Error>> {
|
||||||
let book_dir = get_book_dir(args);
|
let book_dir = get_book_dir(args);
|
||||||
let mut book = MDBook::new(&book_dir).read_config()?;
|
let mut book = MDBook::new(&book_dir).read_config()?;
|
||||||
|
|
Loading…
Reference in New Issue