mirror of
https://github.com/rust-lang/mdBook
synced 2024-12-13 22:32:35 +00:00
commit
68a75dae48
13 changed files with 181 additions and 210 deletions
44
Cargo.lock
generated
44
Cargo.lock
generated
|
@ -53,17 +53,6 @@ dependencies = [
|
||||||
"wait-timeout",
|
"wait-timeout",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "atty"
|
|
||||||
version = "0.2.14"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
|
||||||
dependencies = [
|
|
||||||
"hermit-abi 0.1.19",
|
|
||||||
"libc",
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
@ -176,34 +165,33 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "3.2.23"
|
version = "4.0.29"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5"
|
checksum = "4d63b9e9c07271b9957ad22c173bae2a4d9a81127680962039296abcd2f8251d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atty",
|
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"clap_lex",
|
"clap_lex",
|
||||||
"indexmap",
|
"is-terminal",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"strsim",
|
"strsim",
|
||||||
"termcolor",
|
"termcolor",
|
||||||
"textwrap",
|
"terminal_size",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_complete"
|
name = "clap_complete"
|
||||||
version = "3.2.5"
|
version = "4.0.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3f7a2e0a962c45ce25afce14220bc24f9dade0a1787f185cecf96bfba7847cd8"
|
checksum = "b7b3c9eae0de7bf8e3f904a5e40612b21fb2e2e566456d177809a48b892d24da"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_lex"
|
name = "clap_lex"
|
||||||
version = "0.2.4"
|
version = "0.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
|
checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"os_str_bytes",
|
"os_str_bytes",
|
||||||
]
|
]
|
||||||
|
@ -1654,18 +1642,22 @@ dependencies = [
|
||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "terminal_size"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cb20089a8ba2b69debd491f8d2d023761cbf196e999218c591fa1e7e15a21907"
|
||||||
|
dependencies = [
|
||||||
|
"rustix",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "termtree"
|
name = "termtree"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "95059e91184749cb66be6dc994f67f182b6d897cb3df74a5bf66b5e709295fd8"
|
checksum = "95059e91184749cb66be6dc994f67f182b6d897cb3df74a5bf66b5e709295fd8"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "textwrap"
|
|
||||||
version = "0.16.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.37"
|
version = "1.0.37"
|
||||||
|
|
|
@ -19,8 +19,8 @@ rust-version = "1.60"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.28"
|
anyhow = "1.0.28"
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
clap = { version = "3.0", features = ["cargo"] }
|
clap = { version = "4.0.29", features = ["cargo", "wrap_help"] }
|
||||||
clap_complete = "3.0"
|
clap_complete = "4.0.6"
|
||||||
once_cell = "1"
|
once_cell = "1"
|
||||||
env_logger = "0.10.0"
|
env_logger = "0.10.0"
|
||||||
handlebars = "4.0"
|
handlebars = "4.0"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::nop_lib::Nop;
|
use crate::nop_lib::Nop;
|
||||||
use clap::{App, Arg, ArgMatches};
|
use clap::{Arg, ArgMatches, Command};
|
||||||
use mdbook::book::Book;
|
use mdbook::book::Book;
|
||||||
use mdbook::errors::Error;
|
use mdbook::errors::Error;
|
||||||
use mdbook::preprocess::{CmdPreprocessor, Preprocessor, PreprocessorContext};
|
use mdbook::preprocess::{CmdPreprocessor, Preprocessor, PreprocessorContext};
|
||||||
|
@ -7,11 +7,11 @@ use semver::{Version, VersionReq};
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::process;
|
use std::process;
|
||||||
|
|
||||||
pub fn make_app() -> App<'static> {
|
pub fn make_app() -> Command {
|
||||||
App::new("nop-preprocessor")
|
Command::new("nop-preprocessor")
|
||||||
.about("A mdbook preprocessor which does precisely nothing")
|
.about("A mdbook preprocessor which does precisely nothing")
|
||||||
.subcommand(
|
.subcommand(
|
||||||
App::new("supports")
|
Command::new("supports")
|
||||||
.arg(Arg::new("renderer").required(true))
|
.arg(Arg::new("renderer").required(true))
|
||||||
.about("Check whether a renderer is supported by this preprocessor"),
|
.about("Check whether a renderer is supported by this preprocessor"),
|
||||||
)
|
)
|
||||||
|
@ -54,7 +54,9 @@ fn handle_preprocessing(pre: &dyn Preprocessor) -> Result<(), Error> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_supports(pre: &dyn Preprocessor, sub_args: &ArgMatches) -> ! {
|
fn handle_supports(pre: &dyn Preprocessor, sub_args: &ArgMatches) -> ! {
|
||||||
let renderer = sub_args.value_of("renderer").expect("Required argument");
|
let renderer = sub_args
|
||||||
|
.get_one::<String>("renderer")
|
||||||
|
.expect("Required argument");
|
||||||
let supported = pre.supports_renderer(renderer);
|
let supported = pre.supports_renderer(renderer);
|
||||||
|
|
||||||
// Signal whether the renderer is supported by exiting with 1 or 0.
|
// Signal whether the renderer is supported by exiting with 1 or 0.
|
||||||
|
|
|
@ -313,6 +313,7 @@ impl MDBook {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debug!("running {:?}", cmd);
|
||||||
let output = cmd.output()?;
|
let output = cmd.output()?;
|
||||||
|
|
||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
|
|
|
@ -1,28 +1,16 @@
|
||||||
|
use super::command_prelude::*;
|
||||||
use crate::{get_book_dir, open};
|
use crate::{get_book_dir, open};
|
||||||
use clap::{arg, App, Arg, ArgMatches};
|
|
||||||
use mdbook::errors::Result;
|
use mdbook::errors::Result;
|
||||||
use mdbook::MDBook;
|
use mdbook::MDBook;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
// Create clap subcommand arguments
|
// Create clap subcommand arguments
|
||||||
pub fn make_subcommand<'help>() -> App<'help> {
|
pub fn make_subcommand() -> Command {
|
||||||
App::new("build")
|
Command::new("build")
|
||||||
.about("Builds a book from its markdown files")
|
.about("Builds a book from its markdown files")
|
||||||
.arg(
|
.arg_dest_dir()
|
||||||
Arg::new("dest-dir")
|
.arg_root_dir()
|
||||||
.short('d')
|
.arg_open()
|
||||||
.long("dest-dir")
|
|
||||||
.value_name("dest-dir")
|
|
||||||
.help(
|
|
||||||
"Output directory for the book{n}\
|
|
||||||
Relative paths are interpreted relative to the book's root directory.{n}\
|
|
||||||
If omitted, mdBook uses build.build-dir from book.toml or defaults to `./book`.",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.arg(arg!([dir]
|
|
||||||
"Root directory for the book{n}\
|
|
||||||
(Defaults to the Current Directory when omitted)"
|
|
||||||
))
|
|
||||||
.arg(arg!(-o --open "Opens the compiled book in a web browser"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build command implementation
|
// Build command implementation
|
||||||
|
@ -30,13 +18,13 @@ 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") {
|
if let Some(dest_dir) = args.get_one::<PathBuf>("dest-dir") {
|
||||||
book.config.build.build_dir = dest_dir.into();
|
book.config.build.build_dir = dest_dir.into();
|
||||||
}
|
}
|
||||||
|
|
||||||
book.build()?;
|
book.build()?;
|
||||||
|
|
||||||
if args.is_present("open") {
|
if args.get_flag("open") {
|
||||||
// FIXME: What's the right behaviour if we don't use the HTML renderer?
|
// FIXME: What's the right behaviour if we don't use the HTML renderer?
|
||||||
let path = book.build_dir_for("html").join("index.html");
|
let path = book.build_dir_for("html").join("index.html");
|
||||||
if !path.exists() {
|
if !path.exists() {
|
||||||
|
|
|
@ -1,28 +1,16 @@
|
||||||
|
use super::command_prelude::*;
|
||||||
use crate::get_book_dir;
|
use crate::get_book_dir;
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use clap::{arg, App, Arg, ArgMatches};
|
|
||||||
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<'help>() -> App<'help> {
|
pub fn make_subcommand() -> Command {
|
||||||
App::new("clean")
|
Command::new("clean")
|
||||||
.about("Deletes a built book")
|
.about("Deletes a built book")
|
||||||
.arg(
|
.arg_dest_dir()
|
||||||
Arg::new("dest-dir")
|
.arg_root_dir()
|
||||||
.short('d')
|
|
||||||
.long("dest-dir")
|
|
||||||
.value_name("dest-dir")
|
|
||||||
.help(
|
|
||||||
"Output directory for the book{n}\
|
|
||||||
Relative paths are interpreted relative to the book's root directory.{n}\
|
|
||||||
If omitted, mdBook uses build.build-dir from book.toml or defaults to `./book`.",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.arg(arg!([dir]
|
|
||||||
"Root directory for the book{n}\
|
|
||||||
(Defaults to the Current Directory when omitted)"
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean command implementation
|
// Clean command implementation
|
||||||
|
@ -30,7 +18,7 @@ pub fn execute(args: &ArgMatches) -> mdbook::errors::Result<()> {
|
||||||
let book_dir = get_book_dir(args);
|
let book_dir = get_book_dir(args);
|
||||||
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.get_one::<PathBuf>("dest-dir") {
|
||||||
Some(dest_dir) => dest_dir.into(),
|
Some(dest_dir) => dest_dir.into(),
|
||||||
None => book.root.join(&book.config.build.build_dir),
|
None => book.root.join(&book.config.build.build_dir),
|
||||||
};
|
};
|
||||||
|
|
45
src/cmd/command_prelude.rs
Normal file
45
src/cmd/command_prelude.rs
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
//! Helpers for building the command-line arguments for commands.
|
||||||
|
|
||||||
|
pub use clap::{arg, Arg, ArgMatches, Command};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
pub trait CommandExt: Sized {
|
||||||
|
fn _arg(self, arg: Arg) -> Self;
|
||||||
|
|
||||||
|
fn arg_dest_dir(self) -> Self {
|
||||||
|
self._arg(
|
||||||
|
Arg::new("dest-dir")
|
||||||
|
.short('d')
|
||||||
|
.long("dest-dir")
|
||||||
|
.value_name("dest-dir")
|
||||||
|
.value_parser(clap::value_parser!(PathBuf))
|
||||||
|
.help(
|
||||||
|
"Output directory for the book\n\
|
||||||
|
Relative paths are interpreted relative to the book's root directory.\n\
|
||||||
|
If omitted, mdBook uses build.build-dir from book.toml \
|
||||||
|
or defaults to `./book`.",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn arg_root_dir(self) -> Self {
|
||||||
|
self._arg(
|
||||||
|
Arg::new("dir")
|
||||||
|
.help(
|
||||||
|
"Root directory for the book\n\
|
||||||
|
(Defaults to the current directory when omitted)",
|
||||||
|
)
|
||||||
|
.value_parser(clap::value_parser!(PathBuf)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn arg_open(self) -> Self {
|
||||||
|
self._arg(arg!(-o --open "Opens the compiled book in a web browser"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CommandExt for Command {
|
||||||
|
fn _arg(self, arg: Arg) -> Self {
|
||||||
|
self.arg(arg)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::get_book_dir;
|
use crate::get_book_dir;
|
||||||
use clap::{arg, App, Arg, ArgMatches};
|
use clap::{arg, ArgMatches, Command as ClapCommand};
|
||||||
use mdbook::config;
|
use mdbook::config;
|
||||||
use mdbook::errors::Result;
|
use mdbook::errors::Result;
|
||||||
use mdbook::MDBook;
|
use mdbook::MDBook;
|
||||||
|
@ -8,30 +8,22 @@ use std::io::Write;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
// Create clap subcommand arguments
|
// Create clap subcommand arguments
|
||||||
pub fn make_subcommand<'help>() -> App<'help> {
|
pub fn make_subcommand() -> ClapCommand {
|
||||||
App::new("init")
|
ClapCommand::new("init")
|
||||||
.about("Creates the boilerplate structure and files for a new book")
|
.about("Creates the boilerplate structure and files for a new book")
|
||||||
// the {n} denotes a newline which will properly aligned in all help messages
|
.arg(
|
||||||
.arg(arg!([dir]
|
arg!([dir]
|
||||||
"Directory to create the book in{n}\
|
"Directory to create the book in\n\
|
||||||
(Defaults to the Current Directory when omitted)"
|
(Defaults to the current directory when omitted)"
|
||||||
))
|
)
|
||||||
|
.value_parser(clap::value_parser!(std::path::PathBuf)),
|
||||||
|
)
|
||||||
.arg(arg!(--theme "Copies the default theme into your source folder"))
|
.arg(arg!(--theme "Copies the default theme into your source folder"))
|
||||||
.arg(arg!(--force "Skips confirmation prompts"))
|
.arg(arg!(--force "Skips confirmation prompts"))
|
||||||
|
.arg(arg!(--title <title> "Sets the book title"))
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("title")
|
arg!(--ignore <ignore> "Creates a VCS ignore file (i.e. .gitignore)")
|
||||||
.long("title")
|
.value_parser(["none", "git"]),
|
||||||
.takes_value(true)
|
|
||||||
.help("Sets the book title")
|
|
||||||
.required(false),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::new("ignore")
|
|
||||||
.long("ignore")
|
|
||||||
.takes_value(true)
|
|
||||||
.possible_values(&["none", "git"])
|
|
||||||
.help("Creates a VCS ignore file (i.e. .gitignore)")
|
|
||||||
.required(false),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,12 +33,12 @@ pub fn execute(args: &ArgMatches) -> Result<()> {
|
||||||
let mut builder = MDBook::init(&book_dir);
|
let mut builder = MDBook::init(&book_dir);
|
||||||
let mut config = config::Config::default();
|
let mut config = config::Config::default();
|
||||||
// If flag `--theme` is present, copy theme to src
|
// If flag `--theme` is present, copy theme to src
|
||||||
if args.is_present("theme") {
|
if args.get_flag("theme") {
|
||||||
let theme_dir = book_dir.join("theme");
|
let theme_dir = book_dir.join("theme");
|
||||||
println!();
|
println!();
|
||||||
println!("Copying the default theme to {}", theme_dir.display());
|
println!("Copying the default theme to {}", theme_dir.display());
|
||||||
// Skip this if `--force` is present
|
// Skip this if `--force` is present
|
||||||
if !args.is_present("force") && theme_dir.exists() {
|
if !args.get_flag("force") && theme_dir.exists() {
|
||||||
println!("This could potentially overwrite files already present in that directory.");
|
println!("This could potentially overwrite files already present in that directory.");
|
||||||
print!("\nAre you sure you want to continue? (y/n) ");
|
print!("\nAre you sure you want to continue? (y/n) ");
|
||||||
|
|
||||||
|
@ -59,7 +51,7 @@ pub fn execute(args: &ArgMatches) -> Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ignore) = args.value_of("ignore") {
|
if let Some(ignore) = args.get_one::<String>("ignore").map(|s| s.as_str()) {
|
||||||
match ignore {
|
match ignore {
|
||||||
"git" => builder.create_gitignore(true),
|
"git" => builder.create_gitignore(true),
|
||||||
_ => builder.create_gitignore(false),
|
_ => builder.create_gitignore(false),
|
||||||
|
@ -71,8 +63,8 @@ pub fn execute(args: &ArgMatches) -> Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
config.book.title = if args.is_present("title") {
|
config.book.title = if args.contains_id("title") {
|
||||||
args.value_of("title").map(String::from)
|
args.get_one::<String>("title").map(String::from)
|
||||||
} else {
|
} else {
|
||||||
request_book_title()
|
request_book_title()
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
pub mod build;
|
pub mod build;
|
||||||
pub mod clean;
|
pub mod clean;
|
||||||
|
pub mod command_prelude;
|
||||||
pub mod init;
|
pub mod init;
|
||||||
#[cfg(feature = "serve")]
|
#[cfg(feature = "serve")]
|
||||||
pub mod serve;
|
pub mod serve;
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
|
use super::command_prelude::*;
|
||||||
#[cfg(feature = "watch")]
|
#[cfg(feature = "watch")]
|
||||||
use super::watch;
|
use super::watch;
|
||||||
use crate::{get_book_dir, open};
|
use crate::{get_book_dir, open};
|
||||||
use clap::{arg, App, Arg, ArgMatches};
|
use clap::builder::NonEmptyStringValueParser;
|
||||||
use futures_util::sink::SinkExt;
|
use futures_util::sink::SinkExt;
|
||||||
use futures_util::StreamExt;
|
use futures_util::StreamExt;
|
||||||
use mdbook::errors::*;
|
use mdbook::errors::*;
|
||||||
|
@ -18,43 +19,30 @@ use warp::Filter;
|
||||||
const LIVE_RELOAD_ENDPOINT: &str = "__livereload";
|
const LIVE_RELOAD_ENDPOINT: &str = "__livereload";
|
||||||
|
|
||||||
// Create clap subcommand arguments
|
// Create clap subcommand arguments
|
||||||
pub fn make_subcommand<'help>() -> App<'help> {
|
pub fn make_subcommand() -> Command {
|
||||||
App::new("serve")
|
Command::new("serve")
|
||||||
.about("Serves a book at http://localhost:3000, and rebuilds it on changes")
|
.about("Serves a book at http://localhost:3000, and rebuilds it on changes")
|
||||||
.arg(
|
.arg_dest_dir()
|
||||||
Arg::new("dest-dir")
|
.arg_root_dir()
|
||||||
.short('d')
|
|
||||||
.long("dest-dir")
|
|
||||||
.value_name("dest-dir")
|
|
||||||
.help(
|
|
||||||
"Output directory for the book{n}\
|
|
||||||
Relative paths are interpreted relative to the book's root directory.{n}\
|
|
||||||
If omitted, mdBook uses build.build-dir from book.toml or defaults to `./book`.",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.arg(arg!([dir]
|
|
||||||
"Root directory for the book{n}\
|
|
||||||
(Defaults to the Current Directory when omitted)"
|
|
||||||
))
|
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("hostname")
|
Arg::new("hostname")
|
||||||
.short('n')
|
.short('n')
|
||||||
.long("hostname")
|
.long("hostname")
|
||||||
.takes_value(true)
|
.num_args(1)
|
||||||
.default_value("localhost")
|
.default_value("localhost")
|
||||||
.forbid_empty_values(true)
|
.value_parser(NonEmptyStringValueParser::new())
|
||||||
.help("Hostname to listen on for HTTP connections"),
|
.help("Hostname to listen on for HTTP connections"),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("port")
|
Arg::new("port")
|
||||||
.short('p')
|
.short('p')
|
||||||
.long("port")
|
.long("port")
|
||||||
.takes_value(true)
|
.num_args(1)
|
||||||
.default_value("3000")
|
.default_value("3000")
|
||||||
.forbid_empty_values(true)
|
.value_parser(NonEmptyStringValueParser::new())
|
||||||
.help("Port to use for HTTP connections"),
|
.help("Port to use for HTTP connections"),
|
||||||
)
|
)
|
||||||
.arg(arg!(-o --open "Opens the compiled book in a web browser"))
|
.arg_open()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serve command implementation
|
// Serve command implementation
|
||||||
|
@ -62,17 +50,17 @@ 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();
|
let port = args.get_one::<String>("port").unwrap();
|
||||||
let hostname = args.value_of("hostname").unwrap();
|
let hostname = args.get_one::<String>("hostname").unwrap();
|
||||||
let open_browser = args.is_present("open");
|
let open_browser = args.get_flag("open");
|
||||||
|
|
||||||
let address = format!("{}:{}", hostname, port);
|
let address = format!("{}:{}", hostname, port);
|
||||||
|
|
||||||
let update_config = |book: &mut MDBook| {
|
let update_config = |book: &mut MDBook| {
|
||||||
book.config
|
book.config
|
||||||
.set("output.html.live-reload-endpoint", &LIVE_RELOAD_ENDPOINT)
|
.set("output.html.live-reload-endpoint", LIVE_RELOAD_ENDPOINT)
|
||||||
.expect("live-reload-endpoint update failed");
|
.expect("live-reload-endpoint update failed");
|
||||||
if let Some(dest_dir) = args.value_of("dest-dir") {
|
if let Some(dest_dir) = args.get_one::<PathBuf>("dest-dir") {
|
||||||
book.config.build.build_dir = dest_dir.into();
|
book.config.build.build_dir = dest_dir.into();
|
||||||
}
|
}
|
||||||
// Override site-url for local serving of the 404 file
|
// Override site-url for local serving of the 404 file
|
||||||
|
|
|
@ -1,63 +1,54 @@
|
||||||
|
use super::command_prelude::*;
|
||||||
use crate::get_book_dir;
|
use crate::get_book_dir;
|
||||||
use clap::{arg, App, Arg, ArgMatches};
|
use clap::builder::NonEmptyStringValueParser;
|
||||||
|
use clap::{Arg, ArgAction, ArgMatches, Command};
|
||||||
use mdbook::errors::Result;
|
use mdbook::errors::Result;
|
||||||
use mdbook::MDBook;
|
use mdbook::MDBook;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
// Create clap subcommand arguments
|
// Create clap subcommand arguments
|
||||||
pub fn make_subcommand<'help>() -> App<'help> {
|
pub fn make_subcommand() -> Command {
|
||||||
App::new("test")
|
Command::new("test")
|
||||||
.about("Tests that a book's Rust code samples compile")
|
.about("Tests that a book's Rust code samples compile")
|
||||||
|
// FIXME: --dest-dir is unused by the test command, it should be removed
|
||||||
|
.arg_dest_dir()
|
||||||
|
.arg_root_dir()
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("dest-dir")
|
|
||||||
.short('d')
|
|
||||||
.long("dest-dir")
|
|
||||||
.value_name("dest-dir")
|
|
||||||
.help(
|
|
||||||
"Output directory for the book{n}\
|
|
||||||
Relative paths are interpreted relative to the book's root directory.{n}\
|
|
||||||
If omitted, mdBook uses build.build-dir from book.toml or defaults to `./book`.",
|
|
||||||
),
|
|
||||||
).arg(
|
|
||||||
Arg::new("chapter")
|
Arg::new("chapter")
|
||||||
.short('c')
|
.short('c')
|
||||||
.long("chapter")
|
.long("chapter")
|
||||||
.value_name("chapter")
|
.value_name("chapter"),
|
||||||
.help(
|
|
||||||
"Only test the specified chapter{n}\
|
|
||||||
Where the name of the chapter is defined in the SUMMARY.md file.{n}\
|
|
||||||
Use the special name \"?\" to the list of chapter names."
|
|
||||||
)
|
)
|
||||||
)
|
.arg(
|
||||||
.arg(arg!([dir]
|
Arg::new("library-path")
|
||||||
"Root directory for the book{n}\
|
|
||||||
(Defaults to the Current Directory when omitted)"
|
|
||||||
))
|
|
||||||
.arg(Arg::new("library-path")
|
|
||||||
.short('L')
|
.short('L')
|
||||||
.long("library-path")
|
.long("library-path")
|
||||||
.value_name("dir")
|
.value_name("dir")
|
||||||
.takes_value(true)
|
.value_delimiter(',')
|
||||||
.use_delimiter(true)
|
.num_args(1..)
|
||||||
.require_delimiter(true)
|
.value_parser(NonEmptyStringValueParser::new())
|
||||||
.multiple_values(true)
|
.action(ArgAction::Append)
|
||||||
.multiple_occurrences(true)
|
.help(
|
||||||
.forbid_empty_values(true)
|
"A comma-separated list of directories to add to the crate \
|
||||||
.help("A comma-separated list of directories to add to {n}the crate search path when building tests"))
|
search path when building tests",
|
||||||
|
),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// test command implementation
|
// test command implementation
|
||||||
pub fn execute(args: &ArgMatches) -> Result<()> {
|
pub fn execute(args: &ArgMatches) -> Result<()> {
|
||||||
let library_paths: Vec<&str> = args
|
let library_paths: Vec<&str> = args
|
||||||
.values_of("library-path")
|
.get_many("library-path")
|
||||||
.map(std::iter::Iterator::collect)
|
.map(|it| it.map(String::as_str).collect())
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let chapter: Option<&str> = args.value_of("chapter");
|
|
||||||
|
let chapter: Option<&str> = args.get_one::<String>("chapter").map(|s| s.as_str());
|
||||||
|
|
||||||
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") {
|
if let Some(dest_dir) = args.get_one::<PathBuf>("dest-dir") {
|
||||||
book.config.build.build_dir = dest_dir.into();
|
book.config.build.build_dir = dest_dir.to_path_buf();
|
||||||
}
|
}
|
||||||
match chapter {
|
match chapter {
|
||||||
Some(_) => book.test_chapter(library_paths, chapter),
|
Some(_) => book.test_chapter(library_paths, chapter),
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
use super::command_prelude::*;
|
||||||
use crate::{get_book_dir, open};
|
use crate::{get_book_dir, open};
|
||||||
use clap::{arg, App, Arg, ArgMatches};
|
|
||||||
use mdbook::errors::Result;
|
use mdbook::errors::Result;
|
||||||
use mdbook::utils;
|
use mdbook::utils;
|
||||||
use mdbook::MDBook;
|
use mdbook::MDBook;
|
||||||
|
@ -9,25 +9,12 @@ use std::thread::sleep;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
// Create clap subcommand arguments
|
// Create clap subcommand arguments
|
||||||
pub fn make_subcommand<'help>() -> App<'help> {
|
pub fn make_subcommand() -> Command {
|
||||||
App::new("watch")
|
Command::new("watch")
|
||||||
.about("Watches a book's files and rebuilds it on changes")
|
.about("Watches a book's files and rebuilds it on changes")
|
||||||
.arg(
|
.arg_dest_dir()
|
||||||
Arg::new("dest-dir")
|
.arg_root_dir()
|
||||||
.short('d')
|
.arg_open()
|
||||||
.long("dest-dir")
|
|
||||||
.value_name("dest-dir")
|
|
||||||
.help(
|
|
||||||
"Output directory for the book{n}\
|
|
||||||
Relative paths are interpreted relative to the book's root directory.{n}\
|
|
||||||
If omitted, mdBook uses build.build-dir from book.toml or defaults to `./book`.",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.arg(arg!([dir]
|
|
||||||
"Root directory for the book{n}\
|
|
||||||
(Defaults to the Current Directory when omitted)"
|
|
||||||
))
|
|
||||||
.arg(arg!(-o --open "Opens the compiled book in a web browser"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Watch command implementation
|
// Watch command implementation
|
||||||
|
@ -36,13 +23,13 @@ pub fn execute(args: &ArgMatches) -> Result<()> {
|
||||||
let mut book = MDBook::load(&book_dir)?;
|
let mut book = MDBook::load(&book_dir)?;
|
||||||
|
|
||||||
let update_config = |book: &mut MDBook| {
|
let update_config = |book: &mut MDBook| {
|
||||||
if let Some(dest_dir) = args.value_of("dest-dir") {
|
if let Some(dest_dir) = args.get_one::<PathBuf>("dest-dir") {
|
||||||
book.config.build.build_dir = dest_dir.into();
|
book.config.build.build_dir = dest_dir.into();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
update_config(&mut book);
|
update_config(&mut book);
|
||||||
|
|
||||||
if args.is_present("open") {
|
if args.get_flag("open") {
|
||||||
book.build()?;
|
book.build()?;
|
||||||
let path = book.build_dir_for("html").join("index.html");
|
let path = book.build_dir_for("html").join("index.html");
|
||||||
if !path.exists() {
|
if !path.exists() {
|
||||||
|
|
42
src/main.rs
42
src/main.rs
|
@ -5,7 +5,7 @@ extern crate log;
|
||||||
|
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use chrono::Local;
|
use chrono::Local;
|
||||||
use clap::{App, AppSettings, Arg, ArgMatches};
|
use clap::{Arg, ArgMatches, Command};
|
||||||
use clap_complete::Shell;
|
use clap_complete::Shell;
|
||||||
use env_logger::Builder;
|
use env_logger::Builder;
|
||||||
use log::LevelFilter;
|
use log::LevelFilter;
|
||||||
|
@ -13,7 +13,7 @@ use mdbook::utils;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::PathBuf;
|
||||||
|
|
||||||
mod cmd;
|
mod cmd;
|
||||||
|
|
||||||
|
@ -22,10 +22,10 @@ const VERSION: &str = concat!("v", crate_version!());
|
||||||
fn main() {
|
fn main() {
|
||||||
init_logger();
|
init_logger();
|
||||||
|
|
||||||
let app = create_clap_app();
|
let command = create_clap_command();
|
||||||
|
|
||||||
// Check which subcomamnd the user ran...
|
// Check which subcommand the user ran...
|
||||||
let res = match app.get_matches().subcommand() {
|
let res = match command.get_matches().subcommand() {
|
||||||
Some(("init", sub_matches)) => cmd::init::execute(sub_matches),
|
Some(("init", sub_matches)) => cmd::init::execute(sub_matches),
|
||||||
Some(("build", sub_matches)) => cmd::build::execute(sub_matches),
|
Some(("build", sub_matches)) => cmd::build::execute(sub_matches),
|
||||||
Some(("clean", sub_matches)) => cmd::clean::execute(sub_matches),
|
Some(("clean", sub_matches)) => cmd::clean::execute(sub_matches),
|
||||||
|
@ -35,15 +35,13 @@ fn main() {
|
||||||
Some(("serve", sub_matches)) => cmd::serve::execute(sub_matches),
|
Some(("serve", sub_matches)) => cmd::serve::execute(sub_matches),
|
||||||
Some(("test", sub_matches)) => cmd::test::execute(sub_matches),
|
Some(("test", sub_matches)) => cmd::test::execute(sub_matches),
|
||||||
Some(("completions", sub_matches)) => (|| {
|
Some(("completions", sub_matches)) => (|| {
|
||||||
let shell: Shell = sub_matches
|
let shell = sub_matches
|
||||||
.value_of("shell")
|
.get_one::<Shell>("shell")
|
||||||
.ok_or_else(|| anyhow!("Shell name missing."))?
|
.ok_or_else(|| anyhow!("Shell name missing."))?;
|
||||||
.parse()
|
|
||||||
.map_err(|s| anyhow!("Invalid shell: {}", s))?;
|
|
||||||
|
|
||||||
let mut complete_app = create_clap_app();
|
let mut complete_app = create_clap_command();
|
||||||
clap_complete::generate(
|
clap_complete::generate(
|
||||||
shell,
|
*shell,
|
||||||
&mut complete_app,
|
&mut complete_app,
|
||||||
"mdbook",
|
"mdbook",
|
||||||
&mut std::io::stdout().lock(),
|
&mut std::io::stdout().lock(),
|
||||||
|
@ -61,13 +59,13 @@ fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a list of valid arguments and sub-commands
|
/// Create a list of valid arguments and sub-commands
|
||||||
fn create_clap_app() -> App<'static> {
|
fn create_clap_command() -> Command {
|
||||||
let app = App::new(crate_name!())
|
let app = Command::new(crate_name!())
|
||||||
.about(crate_description!())
|
.about(crate_description!())
|
||||||
.author("Mathieu David <mathieudavid@mathieudavid.org>")
|
.author("Mathieu David <mathieudavid@mathieudavid.org>")
|
||||||
.version(VERSION)
|
.version(VERSION)
|
||||||
.setting(AppSettings::PropagateVersion)
|
.propagate_version(true)
|
||||||
.setting(AppSettings::ArgRequiredElseHelp)
|
.arg_required_else_help(true)
|
||||||
.after_help(
|
.after_help(
|
||||||
"For more information about a specific command, try `mdbook <command> --help`\n\
|
"For more information about a specific command, try `mdbook <command> --help`\n\
|
||||||
The source code for mdBook is available at: https://github.com/rust-lang/mdBook",
|
The source code for mdBook is available at: https://github.com/rust-lang/mdBook",
|
||||||
|
@ -77,12 +75,11 @@ fn create_clap_app() -> App<'static> {
|
||||||
.subcommand(cmd::test::make_subcommand())
|
.subcommand(cmd::test::make_subcommand())
|
||||||
.subcommand(cmd::clean::make_subcommand())
|
.subcommand(cmd::clean::make_subcommand())
|
||||||
.subcommand(
|
.subcommand(
|
||||||
App::new("completions")
|
Command::new("completions")
|
||||||
.about("Generate shell completions for your shell to stdout")
|
.about("Generate shell completions for your shell to stdout")
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("shell")
|
Arg::new("shell")
|
||||||
.takes_value(true)
|
.value_parser(clap::value_parser!(Shell))
|
||||||
.possible_values(Shell::possible_values())
|
|
||||||
.help("the shell to generate completions for")
|
.help("the shell to generate completions for")
|
||||||
.value_name("SHELL")
|
.value_name("SHELL")
|
||||||
.required(true),
|
.required(true),
|
||||||
|
@ -124,11 +121,10 @@ fn init_logger() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_book_dir(args: &ArgMatches) -> PathBuf {
|
fn get_book_dir(args: &ArgMatches) -> PathBuf {
|
||||||
if let Some(dir) = args.value_of("dir") {
|
if let Some(p) = args.get_one::<PathBuf>("dir") {
|
||||||
// Check if path is relative from current dir, or absolute...
|
// Check if path is relative from current dir, or absolute...
|
||||||
let p = Path::new(dir);
|
|
||||||
if p.is_relative() {
|
if p.is_relative() {
|
||||||
env::current_dir().unwrap().join(dir)
|
env::current_dir().unwrap().join(p)
|
||||||
} else {
|
} else {
|
||||||
p.to_path_buf()
|
p.to_path_buf()
|
||||||
}
|
}
|
||||||
|
@ -146,5 +142,5 @@ fn open<P: AsRef<OsStr>>(path: P) {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn verify_app() {
|
fn verify_app() {
|
||||||
create_clap_app().debug_assert();
|
create_clap_command().debug_assert();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue