From 396426662de5ff6a9f12b8892ef89eba17b3bba2 Mon Sep 17 00:00:00 2001 From: Michael Bryan Date: Wed, 13 Dec 2017 12:04:24 +1100 Subject: [PATCH] Made `mdbook watch` and `mdbook serve` rebuild the book again (#508) Made `mdbook watch` and `mdbook serve` rebuild the book again --- src/bin/serve.rs | 41 +++++++++++------------------ src/bin/watch.rs | 68 +++++++++++++++--------------------------------- 2 files changed, 36 insertions(+), 73 deletions(-) diff --git a/src/bin/serve.rs b/src/bin/serve.rs index 2768e466..ada545fa 100644 --- a/src/bin/serve.rs +++ b/src/bin/serve.rs @@ -3,12 +3,11 @@ extern crate staticfile; extern crate ws; use std; -use std::path::PathBuf; use self::iron::{status, AfterMiddleware, Chain, Iron, IronError, IronResult, Request, Response, Set}; use clap::{App, ArgMatches, SubCommand}; use mdbook::MDBook; -use mdbook::errors::Result; +use mdbook::errors::*; use {get_book_dir, open}; #[cfg(feature = "watch")] use watch; @@ -18,28 +17,21 @@ struct ErrorRecover; // Create clap subcommand arguments pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> { SubCommand::with_name("serve") - .about( - "Serve the book at http://localhost:3000. Rebuild and reload on change.", - ) + .about("Serve the book at http://localhost:3000. Rebuild and reload on change.") .arg_from_usage( - "[dir] 'A directory for your book{n}(Defaults to \ - Current Directory when omitted)'", - ) - .arg_from_usage( - "-d, --dest-dir=[dest-dir] 'The output directory for \ - your book{n}(Defaults to ./book when omitted)'", + "[dir] 'A directory for your book{n}(Defaults to Current Directory when omitted)'", ) .arg_from_usage("-p, --port=[port] 'Use another port{n}(Defaults to 3000)'") .arg_from_usage( - "-w, --websocket-port=[ws-port] 'Use another port for the \ - websocket connection (livereload){n}(Defaults to 3001)'", + "-w, --websocket-port=[ws-port] 'Use another port for the websocket connection \ + (livereload){n}(Defaults to 3001)'", ) .arg_from_usage( "-i, --interface=[interface] 'Interface to listen on{n}(Defaults to localhost)'", ) .arg_from_usage( - "-a, --address=[address] 'Address that the browser can reach the \ - websocket server from{n}(Defaults to the interface address)'", + "-a, --address=[address] 'Address that the browser can reach the websocket server \ + from{n}(Defaults to the interface address)'", ) .arg_from_usage("-o, --open 'Open the book server in a web browser'") } @@ -51,10 +43,6 @@ pub fn execute(args: &ArgMatches) -> Result<()> { let book_dir = get_book_dir(args); let mut book = MDBook::load(&book_dir)?; - if let Some(dest_dir) = args.value_of("dest-dir") { - book.config.build.build_dir = PathBuf::from(dest_dir); - } - let port = args.value_of("port").unwrap_or("3000"); let ws_port = args.value_of("websocket-port").unwrap_or("3001"); let interface = args.value_of("interface").unwrap_or("localhost"); @@ -80,18 +68,19 @@ pub fn execute(args: &ArgMatches) -> Result<()> { }} "#, - public_address, - ws_port, - RELOAD_COMMAND + public_address, ws_port, RELOAD_COMMAND )); book.build()?; let mut chain = Chain::new(staticfile::Static::new(book.get_destination())); chain.link_after(ErrorRecover); - let _iron = Iron::new(chain).http(&*address).unwrap(); + let _iron = Iron::new(chain) + .http(&*address) + .chain_err(|| "Unable to launch the server")?; - let ws_server = ws::WebSocket::new(|_| |_| Ok(())).unwrap(); + let ws_server = + ws::WebSocket::new(|_| |_| Ok(())).chain_err(|| "Unable to start the websocket")?; let broadcaster = ws_server.broadcaster(); @@ -107,9 +96,9 @@ pub fn execute(args: &ArgMatches) -> Result<()> { } #[cfg(feature = "watch")] - watch::trigger_on_change(&mut book, move |path, book| { + watch::trigger_on_change(&mut book, move |path, book_dir| { println!("File changed: {:?}\nBuilding book...\n", path); - match book.build() { + match MDBook::load(&book_dir).and_then(|mut b| b.build()) { Err(e) => println!("Error while building: {:?}", e), _ => broadcaster.send(RELOAD_COMMAND).unwrap(), } diff --git a/src/bin/watch.rs b/src/bin/watch.rs index eb9c5b62..8f3eb4e6 100644 --- a/src/bin/watch.rs +++ b/src/bin/watch.rs @@ -1,6 +1,6 @@ extern crate notify; -use std::path::{Path, PathBuf}; +use std::path::Path; use self::notify::Watcher; use std::time::Duration; use std::sync::mpsc::channel; @@ -15,12 +15,7 @@ pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> { .about("Watch the files for changes") .arg_from_usage("-o, --open 'Open the compiled book in a web browser'") .arg_from_usage( - "-d, --dest-dir=[dest-dir] 'The output directory for \ - your book{n}(Defaults to ./book when omitted)'", - ) - .arg_from_usage( - "[dir] 'A directory for your book{n}(Defaults to \ - Current Directory when omitted)'", + "[dir] 'A directory for your book{n}(Defaults to Current Directory when omitted)'", ) } @@ -29,30 +24,28 @@ pub fn execute(args: &ArgMatches) -> Result<()> { let book_dir = get_book_dir(args); let mut book = MDBook::load(&book_dir)?; - if let Some(dest_dir) = args.value_of("dest-dir") { - book.config.build.build_dir = PathBuf::from(dest_dir); - } - if args.is_present("open") { book.build()?; open(book.get_destination().join("index.html")); } - trigger_on_change(&mut book, |path, book| { + trigger_on_change(&book, |path, book_dir| { println!("File changed: {:?}\nBuilding book...\n", path); - if let Err(e) = book.build() { - println!("Error while building: {:?}", e); + let result = MDBook::load(&book_dir).and_then(|mut b| b.build()); + + if let Err(e) = result { + println!("Error while building: {}", e); } - println!(""); + println!(); }); Ok(()) } -// Calls the closure when a book source file is changed. This is blocking! -pub fn trigger_on_change(book: &mut MDBook, closure: F) -> () +/// Calls the closure when a book source file is changed, blocking indefinitely. +pub fn trigger_on_change(book: &MDBook, closure: F) where - F: Fn(&Path, &mut MDBook) -> (), + F: Fn(&Path, &Path), { use self::notify::RecursiveMode::*; use self::notify::DebouncedEvent::*; @@ -64,48 +57,29 @@ where Ok(w) => w, Err(e) => { println!("Error while trying to watch the files:\n\n\t{:?}", e); - ::std::process::exit(0) + ::std::process::exit(1) } }; // Add the source directory to the watcher if let Err(e) = watcher.watch(book.source_dir(), Recursive) { println!("Error while watching {:?}:\n {:?}", book.source_dir(), e); - ::std::process::exit(0); + ::std::process::exit(1); }; - // Add the theme directory to the watcher - watcher.watch(book.theme_dir(), Recursive) - .unwrap_or_default(); + let _ = watcher.watch(book.theme_dir(), Recursive); - // Add the book.{json,toml} file to the watcher if it exists, because it's not - // located in the source directory - if watcher.watch(book.root.join("book.json"), NonRecursive) - .is_err() - { - // do nothing if book.json is not found - } - if watcher.watch(book.root.join("book.toml"), NonRecursive) - .is_err() - { - // do nothing if book.toml is not found - } + // Add the book.toml file to the watcher if it exists + let _ = watcher.watch(book.root.join("book.toml"), NonRecursive); println!("\nListening for changes...\n"); - loop { - match rx.recv() { - Ok(event) => { - match event { - Create(path) | Write(path) | Remove(path) | Rename(_, path) => { - closure(&path, book); - } - _ => {} - } - } - Err(e) => { - println!("An error occured: {:?}", e); + for event in rx.recv() { + match event { + Create(path) | Write(path) | Remove(path) | Rename(_, path) => { + closure(&path, &book.root); } + _ => {} } } }