diff --git a/Cargo.lock b/Cargo.lock index 3474961176..72afa90058 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -524,6 +524,7 @@ dependencies = [ "serde_json 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", "shlex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "staticfile 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "structopt 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -776,6 +777,14 @@ dependencies = [ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "proc-macro2" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "pulldown-cmark" version = "0.1.2" @@ -811,6 +820,14 @@ dependencies = [ "proc-macro2 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "quote" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rand" version = "0.3.22" @@ -1055,6 +1072,25 @@ name = "strsim" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "structopt" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)", + "structopt-derive 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "structopt-derive" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.14.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "strum" version = "0.9.0" @@ -1089,6 +1125,16 @@ dependencies = [ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "syn" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "synom" version = "0.11.3" @@ -1472,11 +1518,13 @@ dependencies = [ "checksum precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" "checksum pretty_assertions 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a029430f0d744bc3d15dd474d591bed2402b645d024583082b9f63bb936dac6" "checksum proc-macro2 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "49b6a521dc81b643e9a51e0d1cf05df46d5a2f3c0280ea72bcb68276ba64a118" +"checksum proc-macro2 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "effdb53b25cdad54f8f48843d67398f7ef2e14f12c1b4cb4effc549a6462a4d6" "checksum pulldown-cmark 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d6fdf85cda6cadfae5428a54661d431330b312bc767ddbc57adbedc24da66e32" "checksum pulldown-cmark-to-cmark 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "57efca5f52f63336ee3a49bceee1a1169f18ef01c75aa7e71949441b49bbe7e4" "checksum quick-error 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eda5fe9b71976e62bc81b781206aaa076401769b2143379d3eb2118388babac4" "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" "checksum quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9949cfe66888ffe1d53e6ec9d9f3b70714083854be20fd5e271b232a017401e8" +"checksum quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e44651a0dc4cdd99f71c83b561e221f714912d11af1a4dff0631f923d53af035" "checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1" "checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5" "checksum redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "0d92eecebad22b767915e4d529f89f28ee96dbbf5a4810d2b844373f136417fd" @@ -1508,10 +1556,13 @@ dependencies = [ "checksum string_cache_codegen 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "35293b05cf1494e8ddd042a7df6756bf18d07f42d234f32e71dce8a7aabb0191" "checksum string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b1884d1bc09741d466d9b14e6d37ac89d6909cbcac41dd9ae982d4d063bbedfc" "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" +"checksum structopt 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d8e9ad6a11096cbecdcca0cc6aa403fdfdbaeda2fb3323a39c98e6a166a1e45a" +"checksum structopt-derive 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4cbce8ccdc62166bd594c14396a3242bf94c337a51dbfa9be1076dd74b3db2af" "checksum strum 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "099e21b5dd6dd07b5adcf8c4b723a7c0b7efd7a9359bf963d58c0caae8532545" "checksum strum_macros 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0dd9bd569e88028750e3ae5c25616b8278ac16a8e61aba4339195c72396d49e1" "checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" "checksum syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "91b52877572087400e83d24b9178488541e3d535259e04ff17a63df1e5ceff59" +"checksum syn 0.14.4 (registry+https://github.com/rust-lang/crates.io-index)" = "2beff8ebc3658f07512a413866875adddd20f4fd47b2a4e6c9da65cd281baaea" "checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" "checksum tempfile 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "47776f63b85777d984a50ce49d6b9e58826b6a3766a449fc95bc66cd5663c15b" "checksum tendril 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1b72f8e2f5b73b65c315b1a70c730f24b9d7a25f39e98de8acbe2bb795caea" diff --git a/Cargo.toml b/Cargo.toml index d7fc6567d4..f1884b41f4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,7 @@ tempfile = "3.0" itertools = "0.7" shlex = "0.1" toml-query = "0.6" +structopt = "0.2" # Watch feature notify = { version = "4.0", optional = true } diff --git a/src/bin/build.rs b/src/bin/build.rs index 24d9a7fdb1..d705153403 100644 --- a/src/bin/build.rs +++ b/src/bin/build.rs @@ -1,35 +1,32 @@ use std::path::PathBuf; -use clap::{App, ArgMatches, SubCommand}; use mdbook::MDBook; use mdbook::errors::Result; use {get_book_dir, open}; -// Create clap subcommand arguments -pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> { - SubCommand::with_name("build") - .about("Build the book from the markdown files") - .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)'", - ) +#[derive(StructOpt)] +pub struct BuildArgs { + #[structopt(long = "open", short = "o", help = "Open the compiled book in a web browser")] + open: bool, + #[structopt(help = "A directory for your book{n}(Defaults to Current Directory when omitted)")] + dir: Option, + #[structopt(long = "dest-dir", short = "d", + help = "The output directory for your book{n}(Defaults to ./book when omitted)", + parse(from_os_str))] + dest_dir: Option, } // Build command implementation -pub fn execute(args: &ArgMatches) -> Result<()> { - let book_dir = get_book_dir(args); +pub fn execute(args: BuildArgs) -> Result<()> { + let book_dir = get_book_dir(args.dir); 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 let Some(dest_dir) = args.dest_dir { + book.config.build.build_dir = dest_dir; } book.build()?; - if args.is_present("open") { + if args.open { // FIXME: What's the right behaviour if we don't use the HTML renderer? open(book.build_dir_for("html").join("index.html")); } diff --git a/src/bin/clean.rs b/src/bin/clean.rs index 0984519334..ca06071776 100644 --- a/src/bin/clean.rs +++ b/src/bin/clean.rs @@ -1,26 +1,23 @@ use std::fs; use std::path::PathBuf; -use clap::{App, ArgMatches, SubCommand}; use mdbook::MDBook; use mdbook::errors::*; use get_book_dir; -// Create clap subcommand arguments -pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> { - SubCommand::with_name("clean") - .about("Delete built book") - .arg_from_usage( - "-d, --dest-dir=[dest-dir] 'The directory of built book{n}(Defaults to ./book when \ - omitted)'", - ) +#[derive(StructOpt)] +pub struct CleanArgs { + #[structopt(long = "dest-dir", short = "d", + help = "The output directory for your book{n}(Defaults to ./book when omitted)", + parse(from_os_str))] + dest_dir: Option, } // Clean command implementation -pub fn execute(args: &ArgMatches) -> ::mdbook::errors::Result<()> { - let book_dir = get_book_dir(args); +pub fn execute(args: CleanArgs) -> ::mdbook::errors::Result<()> { + let book_dir = get_book_dir(None); let book = MDBook::load(&book_dir)?; - let dir_to_remove = match args.value_of("dest-dir") { + let dir_to_remove = match args.dest_dir { Some(dest_dir) => PathBuf::from(dest_dir), None => book.root.join(&book.config.build.build_dir), }; diff --git a/src/bin/init.rs b/src/bin/init.rs index 0f66a647e2..c66aa62211 100644 --- a/src/bin/init.rs +++ b/src/bin/init.rs @@ -1,34 +1,31 @@ use std::io; -use std::io::Write; use std::process::Command; -use clap::{App, ArgMatches, SubCommand}; +use std::io::Write; use mdbook::MDBook; use mdbook::errors::Result; use mdbook::config; use get_book_dir; -// Create clap subcommand arguments -pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> { - SubCommand::with_name("init") - .about("Create boilerplate structure and files in the directory") - // the {n} denotes a newline which will properly aligned in all help messages - .arg_from_usage("[dir] 'A directory for your book{n}(Defaults to Current Directory \ - when omitted)'") - .arg_from_usage("--theme 'Copies the default theme into your source folder'") - .arg_from_usage("--force 'skip confirmation prompts'") +#[derive(StructOpt)] +pub struct InitArgs { + #[structopt(long = "theme", help = "Copies the default theme into your source folder")] + pub theme: bool, + #[structopt(long = "force", help = "Skip confirmation prompts")] pub force: bool, + #[structopt(help = "A directory for your book{n}(Defaults to Current Directory when omitted)")] + pub dir: Option, } // Init command implementation -pub fn execute(args: &ArgMatches) -> Result<()> { - let book_dir = get_book_dir(args); +pub fn execute(args: InitArgs) -> Result<()> { + let book_dir = get_book_dir(args.dir); let mut builder = MDBook::init(&book_dir); let mut config = config::Config::default(); // If flag `--theme` is present, copy theme to src - if args.is_present("theme") { + if args.theme { config.set("output.html.theme", "src/theme")?; // Skip this if `--force` is present - if !args.is_present("force") { + if !args.force { // Print warning println!(); println!( diff --git a/src/bin/mdbook.rs b/src/bin/mdbook.rs index 7f619bb6f1..5a5115a0e3 100644 --- a/src/bin/mdbook.rs +++ b/src/bin/mdbook.rs @@ -1,5 +1,4 @@ extern crate chrono; -#[macro_use] extern crate clap; extern crate env_logger; extern crate error_chain; @@ -7,16 +6,18 @@ extern crate error_chain; extern crate log; extern crate mdbook; extern crate open; +#[macro_use] +extern crate structopt; use std::env; use std::ffi::OsStr; use std::path::{Path, PathBuf}; use std::io::Write; -use clap::{App, AppSettings, ArgMatches}; use chrono::Local; use log::LevelFilter; use env_logger::Builder; use mdbook::utils; +use structopt::StructOpt; pub mod build; pub mod clean; @@ -27,43 +28,20 @@ pub mod serve; #[cfg(feature = "watch")] pub mod watch; -const NAME: &'static str = "mdbook"; - fn main() { init_logger(); - // Create a list of valid arguments and sub-commands - let app = App::new(NAME) - .about("Create a book in form of a static website from markdown files") - .author("Mathieu David ") - // Get the version from our Cargo.toml using clap's crate_version!() macro - .version(concat!("v",crate_version!())) - .setting(AppSettings::ArgRequiredElseHelp) - .after_help("For more information about a specific command, \ - try `mdbook --help`\n\ - Source code for mdbook available \ - at: https://github.com/rust-lang-nursery/mdBook") - .subcommand(init::make_subcommand()) - .subcommand(build::make_subcommand()) - .subcommand(test::make_subcommand()) - .subcommand(clean::make_subcommand()); - - #[cfg(feature = "watch")] - let app = app.subcommand(watch::make_subcommand()); - #[cfg(feature = "serve")] - let app = app.subcommand(serve::make_subcommand()); + let opt = Opts::from_args(); - // Check which subcomamnd the user ran... - let res = match app.get_matches().subcommand() { - ("init", Some(sub_matches)) => init::execute(sub_matches), - ("build", Some(sub_matches)) => build::execute(sub_matches), - ("clean", Some(sub_matches)) => clean::execute(sub_matches), + let res = match opt { + Opts::Init(x) => init::execute(x), + Opts::Build(x) => build::execute(x), + Opts::Clean(x) => clean::execute(x), #[cfg(feature = "watch")] - ("watch", Some(sub_matches)) => watch::execute(sub_matches), + Opts::Watch(x) => watch::execute(x), #[cfg(feature = "serve")] - ("serve", Some(sub_matches)) => serve::execute(sub_matches), - ("test", Some(sub_matches)) => test::execute(sub_matches), - (_, _) => unreachable!(), + Opts::Serve(x) => serve::execute(x), + Opts::Test(x) => test::execute(x), }; if let Err(e) = res { @@ -73,6 +51,30 @@ fn main() { } } +/// Subcommands and their respective parameters. +#[derive(StructOpt)] +#[structopt(about = "Create a book in form of a static website from markdown files", + after_help = "For more information about a specific command, \ + try `mdbook --help`\n\ + Source code for mdbook available \ + at: https://github.com/rust-lang-nursery/mdBook", + author = "Mathieu David ")] +enum Opts { + #[structopt(name = "init", about = "Create boilerplate structure and files in the directory")] + Init(init::InitArgs), + #[structopt(name = "build", about = "Build the book from the markdown files")] + Build(build::BuildArgs), + #[structopt(name = "clean", about = "Delete built book")] Clean(clean::CleanArgs), + #[cfg(feature = "watch")] + #[structopt(name = "watch", about = "Watch the files for changes")] + Watch(watch::WatchArgs), + #[cfg(feature = "serve")] + #[structopt(name = "serve", + about = "Serve the book at http://localhost:3000. Rebuild and reload on change.")] + Serve(serve::ServeArgs), + #[structopt(name = "test", about = "Test that code samples compile")] Test(test::TestArgs), +} + fn init_logger() { let mut builder = Builder::new(); @@ -99,8 +101,8 @@ fn init_logger() { builder.init(); } -fn get_book_dir(args: &ArgMatches) -> PathBuf { - if let Some(dir) = args.value_of("dir") { +fn get_book_dir(book_dir: Option) -> PathBuf { + if let Some(ref dir) = book_dir { // Check if path is relative from current dir, or absolute... let p = Path::new(dir); if p.is_relative() { diff --git a/src/bin/serve.rs b/src/bin/serve.rs index 783282342e..376ab434a4 100644 --- a/src/bin/serve.rs +++ b/src/bin/serve.rs @@ -5,7 +5,6 @@ extern crate ws; use std; use self::iron::{status, AfterMiddleware, Chain, Iron, IronError, IronResult, Request, Response, Set}; -use clap::{App, ArgMatches, SubCommand}; use mdbook::MDBook; use mdbook::utils; use mdbook::errors::*; @@ -15,46 +14,34 @@ use watch; 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.") - .arg_from_usage( - "[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)'", - ) - .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)'", - ) - .arg_from_usage("-o, --open 'Open the book server in a web browser'") +#[derive(StructOpt)] +pub struct ServeArgs { + #[structopt(help = "A directory for your book{n}(Defaults to Current Directory when omitted)")] + dir: Option, + #[structopt(short = "p", long = "port", help = "Use another port{n}", default_value = "3000")] + port: String, + #[structopt(short = "w", long = "websocket-port", help = "Use another port for the websocket connection (livereload){n}", default_value = "3001")] + ws_port: String, + #[structopt(short = "i", long = "interface", help = "Interface to listen on{n}", default_value = "localhost")] + interface: String, + #[structopt(short = "a", long = "address", help = "Address that the browser can reach the websocket server from{n}(Defaults to the interface address)")] + address: Option, + #[structopt(short = "o", long = "open", help = "Open the compiled book in a web browser")] + open : bool, } -// Watch command implementation -pub fn execute(args: &ArgMatches) -> Result<()> { - let book_dir = get_book_dir(args); - let mut book = MDBook::load(&book_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"); - let public_address = args.value_of("address").unwrap_or(interface); - let open_browser = args.is_present("open"); - - let address = format!("{}:{}", interface, port); - let ws_address = format!("{}:{}", interface, ws_port); - - let livereload_url = format!("ws://{}:{}", public_address, ws_port); - book.config - .set("output.html.livereload-url", &livereload_url)?; +/// Serve command implementation +pub fn execute(args: ServeArgs) -> Result<()> { + let book_dir = get_book_dir(args.dir); + let address = format!("{}:{}", args.interface, args.port); + let ws_address = format!("{}:{}", args.interface, args.ws_port); + let livereload_url = format!( + "ws://{}:{}", + args.address.unwrap_or(args.interface), + args.ws_port + ); + let mut book = MDBook::load(&book_dir)?; book.build()?; let mut chain = Chain::new(staticfile::Static::new(book.build_dir_for("html"))); @@ -75,10 +62,9 @@ pub fn execute(args: &ArgMatches) -> Result<()> { let serving_url = format!("http://{}", address); info!("Serving on: {}", serving_url); - if open_browser { + if args.open { open(serving_url); } - #[cfg(feature = "watch")] watch::trigger_on_change(&mut book, move |path, book_dir| { info!("File changed: {:?}", path); diff --git a/src/bin/test.rs b/src/bin/test.rs index c0ec67315e..5dce6266e5 100644 --- a/src/bin/test.rs +++ b/src/bin/test.rs @@ -1,26 +1,18 @@ -use clap::{App, ArgMatches, SubCommand}; use mdbook::MDBook; use mdbook::errors::Result; use get_book_dir; -// Create clap subcommand arguments -pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> { - SubCommand::with_name("test") - .about("Test that code samples compile") - .arg_from_usage( - "-L, --library-path [DIR]... 'directory to add to crate search path'", - ) +#[derive(StructOpt)] +pub struct TestArgs { + #[structopt(help = " Directories to add to crate search path")] library_paths: Vec, } // test command implementation -pub fn execute(args: &ArgMatches) -> Result<()> { - let library_paths: Vec<&str> = args.values_of("library-path") - .map(|v| v.collect()) - .unwrap_or_default(); - let book_dir = get_book_dir(args); +pub fn execute(args: TestArgs) -> Result<()> { + let book_dir = get_book_dir(None); let mut book = MDBook::load(&book_dir)?; - book.test(library_paths)?; + book.test(args.library_paths.iter().map(AsRef::as_ref).collect())?; Ok(()) } diff --git a/src/bin/watch.rs b/src/bin/watch.rs index 5d39e71508..8b64af706e 100644 --- a/src/bin/watch.rs +++ b/src/bin/watch.rs @@ -4,28 +4,25 @@ use std::path::Path; use self::notify::Watcher; use std::time::Duration; use std::sync::mpsc::channel; -use clap::{App, ArgMatches, SubCommand}; use mdbook::MDBook; use mdbook::utils; use mdbook::errors::Result; use {get_book_dir, open}; -// Create clap subcommand arguments -pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> { - SubCommand::with_name("watch") - .about("Watch the files for changes") - .arg_from_usage("-o, --open 'Open the compiled book in a web browser'") - .arg_from_usage( - "[dir] 'A directory for your book{n}(Defaults to Current Directory when omitted)'", - ) +#[derive(StructOpt)] +pub struct WatchArgs { + #[structopt(help = "A directory for your book{n}(Defaults to Current Directory when omitted)")] + dir: Option, + #[structopt(long = "open", short = "o", help = "Open the compiled book in a web browser")] + open: bool, } // Watch command implementation -pub fn execute(args: &ArgMatches) -> Result<()> { - let book_dir = get_book_dir(args); +pub fn execute(args: WatchArgs) -> Result<()> { + let book_dir = get_book_dir(args.dir); let book = MDBook::load(&book_dir)?; - if args.is_present("open") { + if args.open { book.build()?; open(book.build_dir_for("html").join("index.html")); }