Improve command-line argument parsing
This commit is contained in:
parent
ac4e00c7c6
commit
b8f8e76899
|
@ -1,21 +1,21 @@
|
||||||
use clap::{App, ArgMatches, SubCommand};
|
use clap::{App, ArgMatches, SubCommand};
|
||||||
use mdbook::errors::Result;
|
use mdbook::errors::Result;
|
||||||
use mdbook::MDBook;
|
use mdbook::MDBook;
|
||||||
use std::path::PathBuf;
|
|
||||||
use {get_book_dir, open};
|
use {get_book_dir, open};
|
||||||
|
|
||||||
// Create clap subcommand arguments
|
// Create clap subcommand arguments
|
||||||
pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
|
pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
|
||||||
SubCommand::with_name("build")
|
SubCommand::with_name("build")
|
||||||
.about("Build the book from the markdown files")
|
.about("Builds a book from its markdown files")
|
||||||
.arg_from_usage("-o, --open 'Open the compiled book in a web browser'")
|
|
||||||
.arg_from_usage(
|
.arg_from_usage(
|
||||||
"-d, --dest-dir=[dest-dir] 'The output directory for your book{n}(Defaults to ./book \
|
"-d, --dest-dir=[dest-dir] 'Output directory for the book{n}\
|
||||||
when omitted)'",
|
(If omitted, uses build.build-dir from book.toml or defaults to ./book)'",
|
||||||
)
|
)
|
||||||
.arg_from_usage(
|
.arg_from_usage(
|
||||||
"[dir] 'A directory for your book{n}(Defaults to Current Directory when omitted)'",
|
"[dir] 'Root directory for the book{n}\
|
||||||
|
(Defaults to the Current Directory when omitted)'",
|
||||||
)
|
)
|
||||||
|
.arg_from_usage("-o, --open 'Opens the compiled book in a web browser'")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build command implementation
|
// Build command implementation
|
||||||
|
@ -24,7 +24,7 @@ pub fn execute(args: &ArgMatches) -> Result<()> {
|
||||||
let mut book = MDBook::load(&book_dir)?;
|
let mut book = MDBook::load(&book_dir)?;
|
||||||
|
|
||||||
if let Some(dest_dir) = args.value_of("dest-dir") {
|
if let Some(dest_dir) = args.value_of("dest-dir") {
|
||||||
book.config.build.build_dir = PathBuf::from(dest_dir);
|
book.config.build.build_dir = dest_dir.into();
|
||||||
}
|
}
|
||||||
|
|
||||||
book.build()?;
|
book.build()?;
|
||||||
|
|
|
@ -3,15 +3,18 @@ use get_book_dir;
|
||||||
use mdbook::errors::*;
|
use mdbook::errors::*;
|
||||||
use mdbook::MDBook;
|
use mdbook::MDBook;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
// Create clap subcommand arguments
|
// Create clap subcommand arguments
|
||||||
pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
|
pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
|
||||||
SubCommand::with_name("clean")
|
SubCommand::with_name("clean")
|
||||||
.about("Delete built book")
|
.about("Deletes a built book")
|
||||||
.arg_from_usage(
|
.arg_from_usage(
|
||||||
"-d, --dest-dir=[dest-dir] 'The directory of built book{n}(Defaults to ./book when \
|
"-d, --dest-dir=[dest-dir] 'Output directory for the book{n}\
|
||||||
omitted)'",
|
(If omitted, uses build.build-dir from book.toml or defaults to ./book)'",
|
||||||
|
)
|
||||||
|
.arg_from_usage(
|
||||||
|
"[dir] 'Root directory for the book{n}\
|
||||||
|
(Defaults to the Current Directory when omitted)'",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +24,7 @@ pub fn execute(args: &ArgMatches) -> ::mdbook::errors::Result<()> {
|
||||||
let book = MDBook::load(&book_dir)?;
|
let book = MDBook::load(&book_dir)?;
|
||||||
|
|
||||||
let dir_to_remove = match args.value_of("dest-dir") {
|
let dir_to_remove = match args.value_of("dest-dir") {
|
||||||
Some(dest_dir) => PathBuf::from(dest_dir),
|
Some(dest_dir) => dest_dir.into(),
|
||||||
None => book.root.join(&book.config.build.build_dir),
|
None => book.root.join(&book.config.build.build_dir),
|
||||||
};
|
};
|
||||||
fs::remove_dir_all(&dir_to_remove).chain_err(|| "Unable to remove the build directory")?;
|
fs::remove_dir_all(&dir_to_remove).chain_err(|| "Unable to remove the build directory")?;
|
||||||
|
|
|
@ -10,12 +10,12 @@ use std::process::Command;
|
||||||
// Create clap subcommand arguments
|
// Create clap subcommand arguments
|
||||||
pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
|
pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
|
||||||
SubCommand::with_name("init")
|
SubCommand::with_name("init")
|
||||||
.about("Create boilerplate structure and files in the directory")
|
.about("Creates the boilerplate structure and files for a new book")
|
||||||
// the {n} denotes a newline which will properly aligned in all help messages
|
// 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 \
|
.arg_from_usage("[dir] 'Directory to create the book in{n}\
|
||||||
when omitted)'")
|
(Defaults to the Current Directory when omitted)'")
|
||||||
.arg_from_usage("--theme 'Copies the default theme into your source folder'")
|
.arg_from_usage("--theme 'Copies the default theme into your source folder'")
|
||||||
.arg_from_usage("--force 'skip confirmation prompts'")
|
.arg_from_usage("--force 'Skips confirmation prompts'")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init command implementation
|
// Init command implementation
|
||||||
|
|
|
@ -7,7 +7,7 @@ use self::iron::{
|
||||||
};
|
};
|
||||||
#[cfg(feature = "watch")]
|
#[cfg(feature = "watch")]
|
||||||
use super::watch;
|
use super::watch;
|
||||||
use clap::{App, ArgMatches, SubCommand};
|
use clap::{App, Arg, ArgMatches, SubCommand};
|
||||||
use mdbook::errors::*;
|
use mdbook::errors::*;
|
||||||
use mdbook::utils;
|
use mdbook::utils;
|
||||||
use mdbook::MDBook;
|
use mdbook::MDBook;
|
||||||
|
@ -19,23 +19,52 @@ struct ErrorRecover;
|
||||||
// Create clap subcommand arguments
|
// Create clap subcommand arguments
|
||||||
pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
|
pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
|
||||||
SubCommand::with_name("serve")
|
SubCommand::with_name("serve")
|
||||||
.about("Serve the book at http://localhost:3000. Rebuild and reload on change.")
|
.about("Serves a book at http://localhost:3000, and rebuilds it on changes")
|
||||||
.arg_from_usage(
|
.arg_from_usage(
|
||||||
"[dir] 'A directory for your book{n}(Defaults to Current Directory when omitted)'",
|
"-d, --dest-dir=[dest-dir] 'Output directory for the book{n}\
|
||||||
)
|
(If omitted, uses build.build-dir from book.toml or defaults to ./book)'",
|
||||||
.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(
|
.arg_from_usage(
|
||||||
"-i, --interface=[interface] 'Interface to listen on{n}(Defaults to localhost)'",
|
"[dir] 'Root directory for the book{n}\
|
||||||
|
(Defaults to the Current Directory when omitted)'",
|
||||||
)
|
)
|
||||||
.arg_from_usage(
|
.arg(
|
||||||
"-a, --address=[address] 'Address that the browser can reach the websocket server \
|
Arg::with_name("hostname")
|
||||||
from{n}(Defaults to the interface address)'",
|
.short("n")
|
||||||
|
.long("hostname")
|
||||||
|
.takes_value(true)
|
||||||
|
.default_value("localhost")
|
||||||
|
.empty_values(false)
|
||||||
|
.help("Hostname to listen on for HTTP connections"),
|
||||||
)
|
)
|
||||||
.arg_from_usage("-o, --open 'Open the book server in a web browser'")
|
.arg(
|
||||||
|
Arg::with_name("port")
|
||||||
|
.short("p")
|
||||||
|
.long("port")
|
||||||
|
.takes_value(true)
|
||||||
|
.default_value("3000")
|
||||||
|
.empty_values(false)
|
||||||
|
.help("Port to use for HTTP connections"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("websocket-hostname")
|
||||||
|
.long("websocket-hostname")
|
||||||
|
.takes_value(true)
|
||||||
|
.empty_values(false)
|
||||||
|
.help(
|
||||||
|
"Hostname to connect to for WebSockets connections (Defaults to the HTTP hostname)",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("websocket-port")
|
||||||
|
.short("w")
|
||||||
|
.long("websocket-port")
|
||||||
|
.takes_value(true)
|
||||||
|
.default_value("3001")
|
||||||
|
.empty_values(false)
|
||||||
|
.help("Port to use for WebSockets livereload connections"),
|
||||||
|
)
|
||||||
|
.arg_from_usage("-o, --open 'Opens the book server in a web browser'")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Watch command implementation
|
// Watch command implementation
|
||||||
|
@ -43,19 +72,23 @@ pub fn execute(args: &ArgMatches) -> Result<()> {
|
||||||
let book_dir = get_book_dir(args);
|
let book_dir = get_book_dir(args);
|
||||||
let mut book = MDBook::load(&book_dir)?;
|
let mut book = MDBook::load(&book_dir)?;
|
||||||
|
|
||||||
let port = args.value_of("port").unwrap_or("3000");
|
let port = args.value_of("port").unwrap();
|
||||||
let ws_port = args.value_of("websocket-port").unwrap_or("3001");
|
let ws_port = args.value_of("websocket-port").unwrap();
|
||||||
let interface = args.value_of("interface").unwrap_or("localhost");
|
let hostname = args.value_of("hostname").unwrap();
|
||||||
let public_address = args.value_of("address").unwrap_or(interface);
|
let public_address = args.value_of("websocket-address").unwrap_or(hostname);
|
||||||
let open_browser = args.is_present("open");
|
let open_browser = args.is_present("open");
|
||||||
|
|
||||||
let address = format!("{}:{}", interface, port);
|
let address = format!("{}:{}", hostname, port);
|
||||||
let ws_address = format!("{}:{}", interface, ws_port);
|
let ws_address = format!("{}:{}", hostname, ws_port);
|
||||||
|
|
||||||
let livereload_url = format!("ws://{}:{}", public_address, ws_port);
|
let livereload_url = format!("ws://{}:{}", public_address, ws_port);
|
||||||
book.config
|
book.config
|
||||||
.set("output.html.livereload-url", &livereload_url)?;
|
.set("output.html.livereload-url", &livereload_url)?;
|
||||||
|
|
||||||
|
if let Some(dest_dir) = args.value_of("dest-dir") {
|
||||||
|
book.config.build.build_dir = dest_dir.into();
|
||||||
|
}
|
||||||
|
|
||||||
book.build()?;
|
book.build()?;
|
||||||
|
|
||||||
let mut chain = Chain::new(staticfile::Static::new(book.build_dir_for("html")));
|
let mut chain = Chain::new(staticfile::Static::new(book.build_dir_for("html")));
|
||||||
|
@ -87,10 +120,8 @@ pub fn execute(args: &ArgMatches) -> Result<()> {
|
||||||
|
|
||||||
// FIXME: This area is really ugly because we need to re-set livereload :(
|
// FIXME: This area is really ugly because we need to re-set livereload :(
|
||||||
|
|
||||||
let livereload_url = livereload_url.clone();
|
|
||||||
|
|
||||||
let result = MDBook::load(&book_dir)
|
let result = MDBook::load(&book_dir)
|
||||||
.and_then(move |mut b| {
|
.and_then(|mut b| {
|
||||||
b.config.set("output.html.livereload-url", &livereload_url)?;
|
b.config.set("output.html.livereload-url", &livereload_url)?;
|
||||||
Ok(b)
|
Ok(b)
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use clap::{App, ArgMatches, SubCommand};
|
use clap::{App, Arg, ArgMatches, SubCommand};
|
||||||
use get_book_dir;
|
use get_book_dir;
|
||||||
use mdbook::errors::Result;
|
use mdbook::errors::Result;
|
||||||
use mdbook::MDBook;
|
use mdbook::MDBook;
|
||||||
|
@ -6,11 +6,24 @@ use mdbook::MDBook;
|
||||||
// Create clap subcommand arguments
|
// Create clap subcommand arguments
|
||||||
pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
|
pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
|
||||||
SubCommand::with_name("test")
|
SubCommand::with_name("test")
|
||||||
.about("Test that code samples compile")
|
.about("Tests that a book's Rust code samples compile")
|
||||||
.arg_from_usage("-L, --library-path [DIR]... 'directories to add to crate search path'")
|
|
||||||
.arg_from_usage(
|
.arg_from_usage(
|
||||||
"[dir] 'A directory for your book{n}(Defaults to Current Directory when omitted)'",
|
"-d, --dest-dir=[dest-dir] 'Output directory for the book{n}\
|
||||||
|
(If omitted, uses build.build-dir from book.toml or defaults to ./book)'",
|
||||||
)
|
)
|
||||||
|
.arg_from_usage(
|
||||||
|
"[dir] 'Root directory for the book{n}\
|
||||||
|
(Defaults to the Current Directory when omitted)'",
|
||||||
|
)
|
||||||
|
.arg(Arg::with_name("library-path")
|
||||||
|
.short("L")
|
||||||
|
.long("library-path")
|
||||||
|
.value_name("dir")
|
||||||
|
.takes_value(true)
|
||||||
|
.require_delimiter(true)
|
||||||
|
.multiple(true)
|
||||||
|
.empty_values(false)
|
||||||
|
.help("A comma-separated list of directories to add to {n}the crate search path when building tests"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// test command implementation
|
// test command implementation
|
||||||
|
@ -21,6 +34,10 @@ pub fn execute(args: &ArgMatches) -> Result<()> {
|
||||||
let book_dir = get_book_dir(args);
|
let book_dir = get_book_dir(args);
|
||||||
let mut book = MDBook::load(&book_dir)?;
|
let mut book = MDBook::load(&book_dir)?;
|
||||||
|
|
||||||
|
if let Some(dest_dir) = args.value_of("dest-dir") {
|
||||||
|
book.config.build.build_dir = dest_dir.into();
|
||||||
|
}
|
||||||
|
|
||||||
book.test(library_paths)?;
|
book.test(library_paths)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -13,11 +13,16 @@ use {get_book_dir, open};
|
||||||
// Create clap subcommand arguments
|
// Create clap subcommand arguments
|
||||||
pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
|
pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
|
||||||
SubCommand::with_name("watch")
|
SubCommand::with_name("watch")
|
||||||
.about("Watch the files for changes")
|
.about("Watches a book's files and rebuilds it on changes")
|
||||||
.arg_from_usage("-o, --open 'Open the compiled book in a web browser'")
|
|
||||||
.arg_from_usage(
|
.arg_from_usage(
|
||||||
"[dir] 'A directory for your book{n}(Defaults to Current Directory when omitted)'",
|
"-d, --dest-dir=[dest-dir] 'Output directory for the book{n}\
|
||||||
|
(If omitted, uses build.build-dir from book.toml or defaults to ./book)'",
|
||||||
)
|
)
|
||||||
|
.arg_from_usage(
|
||||||
|
"[dir] 'Root directory for the book{n}\
|
||||||
|
(Defaults to the Current Directory when omitted)'",
|
||||||
|
)
|
||||||
|
.arg_from_usage("-o, --open 'Open the compiled book in a web browser'")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Watch command implementation
|
// Watch command implementation
|
||||||
|
|
17
src/main.rs
17
src/main.rs
|
@ -20,22 +20,23 @@ use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
mod cmd;
|
mod cmd;
|
||||||
|
|
||||||
const NAME: &'static str = "mdbook";
|
const NAME: &'static str = "mdBook";
|
||||||
|
const VERSION: &'static str = concat!("v", crate_version!());
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
init_logger();
|
init_logger();
|
||||||
|
|
||||||
// Create a list of valid arguments and sub-commands
|
// Create a list of valid arguments and sub-commands
|
||||||
let app = App::new(NAME)
|
let app = App::new(NAME)
|
||||||
.about("Create a book in form of a static website from markdown files")
|
.about("Creates a book from markdown files")
|
||||||
.author("Mathieu David <mathieudavid@mathieudavid.org>")
|
.author("Mathieu David <mathieudavid@mathieudavid.org>")
|
||||||
// Get the version from our Cargo.toml using clap's crate_version!() macro
|
.version(VERSION)
|
||||||
.version(concat!("v",crate_version!()))
|
.setting(AppSettings::GlobalVersion)
|
||||||
.setting(AppSettings::ArgRequiredElseHelp)
|
.setting(AppSettings::ArgRequiredElseHelp)
|
||||||
.after_help("For more information about a specific command, \
|
.after_help(
|
||||||
try `mdbook <command> --help`\n\
|
"For more information about a specific command, try `mdbook <command> --help`\n\
|
||||||
Source code for mdbook available \
|
The source code for mdBook is available at: https://github.com/rust-lang-nursery/mdBook",
|
||||||
at: https://github.com/rust-lang-nursery/mdBook")
|
)
|
||||||
.subcommand(cmd::init::make_subcommand())
|
.subcommand(cmd::init::make_subcommand())
|
||||||
.subcommand(cmd::build::make_subcommand())
|
.subcommand(cmd::build::make_subcommand())
|
||||||
.subcommand(cmd::test::make_subcommand())
|
.subcommand(cmd::test::make_subcommand())
|
||||||
|
|
Loading…
Reference in New Issue