Merge pull request #1731 from epage/clap3

Port mdBook to clap3
This commit is contained in:
Eric Huss 2022-03-28 12:34:17 -07:00 committed by GitHub
commit 7c37dd5e85
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 177 additions and 128 deletions

View File

@ -31,7 +31,8 @@ jobs:
rust: stable
- build: msrv
os: ubuntu-latest
rust: 1.46.0
# sync MSRV with docs: guide/src/guide/installation.md
rust: 1.54.0
steps:
- uses: actions/checkout@master
- name: Install Rust

53
Cargo.lock generated
View File

@ -185,17 +185,27 @@ dependencies = [
[[package]]
name = "clap"
version = "2.33.3"
version = "3.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
checksum = "7a30c3bf9ff12dfe5dae53f0a96e0febcd18420d1c0e7fad77796d9d5c4b5375"
dependencies = [
"ansi_term",
"atty",
"bitflags",
"indexmap",
"lazy_static",
"os_str_bytes",
"strsim",
"termcolor",
"textwrap",
"unicode-width",
"vec_map",
]
[[package]]
name = "clap_complete"
version = "3.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d044e9db8cd0f68191becdeb5246b7462e4cf0c069b19ae00d1bf3fa9889498d"
dependencies = [
"clap",
]
[[package]]
@ -837,6 +847,7 @@ dependencies = [
"assert_cmd",
"chrono",
"clap",
"clap_complete",
"elasticlunr-rs",
"env_logger",
"futures-util",
@ -1053,6 +1064,15 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "os_str_bytes"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
dependencies = [
"memchr",
]
[[package]]
name = "output_vt100"
version = "0.1.2"
@ -1578,9 +1598,9 @@ dependencies = [
[[package]]
name = "strsim"
version = "0.8.0"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "strum"
@ -1647,12 +1667,9 @@ dependencies = [
[[package]]
name = "textwrap"
version = "0.11.0"
version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
"unicode-width",
]
checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80"
[[package]]
name = "time"
@ -1860,12 +1877,6 @@ version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
[[package]]
name = "unicode-width"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
[[package]]
name = "unicode-xid"
version = "0.2.2"
@ -1890,12 +1901,6 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "version_check"
version = "0.9.3"

View File

@ -18,7 +18,8 @@ description = "Creates a book from markdown files"
[dependencies]
anyhow = "1.0.28"
chrono = "0.4"
clap = "2.24"
clap = { version = "3.0", features = ["cargo"] }
clap_complete = "3.0"
env_logger = "0.7.1"
handlebars = "4.0"
lazy_static = "1.0"

View File

@ -1,5 +1,5 @@
use crate::nop_lib::Nop;
use clap::{App, Arg, ArgMatches, SubCommand};
use clap::{App, Arg, ArgMatches};
use mdbook::book::Book;
use mdbook::errors::Error;
use mdbook::preprocess::{CmdPreprocessor, Preprocessor, PreprocessorContext};
@ -7,12 +7,12 @@ use semver::{Version, VersionReq};
use std::io;
use std::process;
pub fn make_app() -> App<'static, 'static> {
pub fn make_app() -> App<'static> {
App::new("nop-preprocessor")
.about("A mdbook preprocessor which does precisely nothing")
.subcommand(
SubCommand::with_name("supports")
.arg(Arg::with_name("renderer").required(true))
App::new("supports")
.arg(Arg::new("renderer").required(true))
.about("Check whether a renderer is supported by this preprocessor"),
)
}

View File

@ -20,7 +20,7 @@ To make it easier to run, put the path to the binary into your `PATH`.
To build the `mdbook` executable from source, you will first need to install Rust and Cargo.
Follow the instructions on the [Rust installation page].
mdBook currently requires at least Rust version 1.46.
mdBook currently requires at least Rust version 1.54.
Once you have installed Rust, the following command can be used to build and install mdBook:

View File

@ -1,22 +1,28 @@
use crate::{get_book_dir, open};
use clap::{App, ArgMatches, SubCommand};
use clap::{arg, App, Arg, ArgMatches};
use mdbook::errors::Result;
use mdbook::MDBook;
// Create clap subcommand arguments
pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
SubCommand::with_name("build")
pub fn make_subcommand<'help>() -> App<'help> {
App::new("build")
.about("Builds a book from its markdown files")
.arg_from_usage(
"-d, --dest-dir=[dest-dir] '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("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_from_usage(
"[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'")
.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

View File

@ -1,23 +1,28 @@
use crate::get_book_dir;
use anyhow::Context;
use clap::{App, ArgMatches, SubCommand};
use clap::{arg, App, Arg, ArgMatches};
use mdbook::MDBook;
use std::fs;
// Create clap subcommand arguments
pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
SubCommand::with_name("clean")
pub fn make_subcommand<'help>() -> App<'help> {
App::new("clean")
.about("Deletes a built book")
.arg_from_usage(
"-d, --dest-dir=[dest-dir] 'Output directory for the book{n}\
Relative paths are interpreted relative to the book's root directory.{n}\
Running this command deletes this directory.{n}\
If omitted, mdBook 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::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!([dir]
"Root directory for the book{n}\
(Defaults to the Current Directory when omitted)"
))
}
// Clean command implementation

View File

@ -1,5 +1,5 @@
use crate::get_book_dir;
use clap::{App, Arg, ArgMatches, SubCommand};
use clap::{arg, App, Arg, ArgMatches};
use mdbook::config;
use mdbook::errors::Result;
use mdbook::MDBook;
@ -8,25 +8,25 @@ use std::io::Write;
use std::process::Command;
// Create clap subcommand arguments
pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
SubCommand::with_name("init")
pub fn make_subcommand<'help>() -> App<'help> {
App::new("init")
.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_from_usage(
"[dir] 'Directory to create the book in{n}\
(Defaults to the Current Directory when omitted)'",
)
.arg_from_usage("--theme 'Copies the default theme into your source folder'")
.arg_from_usage("--force 'Skips confirmation prompts'")
.arg(arg!([dir]
"Directory to create the book in{n}\
(Defaults to the Current Directory when omitted)"
))
.arg(arg!(--theme "Copies the default theme into your source folder"))
.arg(arg!(--force "Skips confirmation prompts"))
.arg(
Arg::with_name("title")
Arg::new("title")
.long("title")
.takes_value(true)
.help("Sets the book title")
.required(false),
)
.arg(
Arg::with_name("ignore")
Arg::new("ignore")
.long("ignore")
.takes_value(true)
.possible_values(&["none", "git"])

View File

@ -1,7 +1,7 @@
#[cfg(feature = "watch")]
use super::watch;
use crate::{get_book_dir, open};
use clap::{App, Arg, ArgMatches, SubCommand};
use clap::{arg, App, Arg, ArgMatches};
use futures_util::sink::SinkExt;
use futures_util::StreamExt;
use mdbook::errors::*;
@ -18,37 +18,43 @@ use warp::Filter;
const LIVE_RELOAD_ENDPOINT: &str = "__livereload";
// Create clap subcommand arguments
pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
SubCommand::with_name("serve")
pub fn make_subcommand<'help>() -> App<'help> {
App::new("serve")
.about("Serves a book at http://localhost:3000, and rebuilds it on changes")
.arg_from_usage(
"-d, --dest-dir=[dest-dir] '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_from_usage(
"[dir] 'Root directory for the book{n}\
(Defaults to the Current Directory when omitted)'",
)
.arg(
Arg::with_name("hostname")
.short("n")
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!([dir]
"Root directory for the book{n}\
(Defaults to the Current Directory when omitted)"
))
.arg(
Arg::new("hostname")
.short('n')
.long("hostname")
.takes_value(true)
.default_value("localhost")
.empty_values(false)
.forbid_empty_values(true)
.help("Hostname to listen on for HTTP connections"),
)
.arg(
Arg::with_name("port")
.short("p")
Arg::new("port")
.short('p')
.long("port")
.takes_value(true)
.default_value("3000")
.empty_values(false)
.forbid_empty_values(true)
.help("Port to use for HTTP connections"),
)
.arg_from_usage("-o, --open 'Opens the book server in a web browser'")
.arg(arg!(-o --open "Opens the compiled book in a web browser"))
}
// Serve command implementation

View File

@ -1,29 +1,37 @@
use crate::get_book_dir;
use clap::{App, Arg, ArgMatches, SubCommand};
use clap::{arg, App, Arg, ArgMatches};
use mdbook::errors::Result;
use mdbook::MDBook;
// Create clap subcommand arguments
pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
SubCommand::with_name("test")
pub fn make_subcommand<'help>() -> App<'help> {
App::new("test")
.about("Tests that a book's Rust code samples compile")
.arg_from_usage(
"-d, --dest-dir=[dest-dir] '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("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_from_usage(
"[dir] 'Root directory for the book{n}\
(Defaults to the Current Directory when omitted)'",
)
.arg(Arg::with_name("library-path")
.short("L")
.arg(arg!([dir]
"Root directory for the book{n}\
(Defaults to the Current Directory when omitted)"
))
.arg(Arg::new("library-path")
.short('L')
.long("library-path")
.value_name("dir")
.takes_value(true)
.use_delimiter(true)
.require_delimiter(true)
.multiple(true)
.empty_values(false)
.multiple_values(true)
.multiple_occurrences(true)
.forbid_empty_values(true)
.help("A comma-separated list of directories to add to {n}the crate search path when building tests"))
}

View File

@ -1,5 +1,5 @@
use crate::{get_book_dir, open};
use clap::{App, ArgMatches, SubCommand};
use clap::{arg, App, Arg, ArgMatches};
use mdbook::errors::Result;
use mdbook::utils;
use mdbook::MDBook;
@ -10,19 +10,25 @@ use std::thread::sleep;
use std::time::Duration;
// Create clap subcommand arguments
pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
SubCommand::with_name("watch")
pub fn make_subcommand<'help>() -> App<'help> {
App::new("watch")
.about("Watches a book's files and rebuilds it on changes")
.arg_from_usage(
"-d, --dest-dir=[dest-dir] '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("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_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'")
.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

View File

@ -5,7 +5,8 @@ extern crate log;
use anyhow::anyhow;
use chrono::Local;
use clap::{App, AppSettings, Arg, ArgMatches, Shell, SubCommand};
use clap::{App, AppSettings, Arg, ArgMatches};
use clap_complete::Shell;
use env_logger::Builder;
use log::LevelFilter;
use mdbook::utils;
@ -25,25 +26,31 @@ fn main() {
// Check which subcomamnd the user ran...
let res = match app.get_matches().subcommand() {
("init", Some(sub_matches)) => cmd::init::execute(sub_matches),
("build", Some(sub_matches)) => cmd::build::execute(sub_matches),
("clean", Some(sub_matches)) => cmd::clean::execute(sub_matches),
Some(("init", sub_matches)) => cmd::init::execute(sub_matches),
Some(("build", sub_matches)) => cmd::build::execute(sub_matches),
Some(("clean", sub_matches)) => cmd::clean::execute(sub_matches),
#[cfg(feature = "watch")]
("watch", Some(sub_matches)) => cmd::watch::execute(sub_matches),
Some(("watch", sub_matches)) => cmd::watch::execute(sub_matches),
#[cfg(feature = "serve")]
("serve", Some(sub_matches)) => cmd::serve::execute(sub_matches),
("test", Some(sub_matches)) => cmd::test::execute(sub_matches),
("completions", Some(sub_matches)) => (|| {
Some(("serve", sub_matches)) => cmd::serve::execute(sub_matches),
Some(("test", sub_matches)) => cmd::test::execute(sub_matches),
Some(("completions", sub_matches)) => (|| {
let shell: Shell = sub_matches
.value_of("shell")
.ok_or_else(|| anyhow!("Shell name missing."))?
.parse()
.map_err(|s| anyhow!("Invalid shell: {}", s))?;
create_clap_app().gen_completions_to("mdbook", shell, &mut std::io::stdout().lock());
let mut complete_app = create_clap_app();
clap_complete::generate(
shell,
&mut complete_app,
"mdbook",
&mut std::io::stdout().lock(),
);
Ok(())
})(),
(_, _) => unreachable!(),
_ => unreachable!(),
};
if let Err(e) = res {
@ -54,14 +61,13 @@ fn main() {
}
/// Create a list of valid arguments and sub-commands
fn create_clap_app<'a, 'b>() -> App<'a, 'b> {
fn create_clap_app() -> App<'static> {
let app = App::new(crate_name!())
.about(crate_description!())
.author("Mathieu David <mathieudavid@mathieudavid.org>")
.version(VERSION)
.setting(AppSettings::GlobalVersion)
.setting(AppSettings::PropagateVersion)
.setting(AppSettings::ArgRequiredElseHelp)
.setting(AppSettings::ColoredHelp)
.after_help(
"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",
@ -71,12 +77,12 @@ fn create_clap_app<'a, 'b>() -> App<'a, 'b> {
.subcommand(cmd::test::make_subcommand())
.subcommand(cmd::clean::make_subcommand())
.subcommand(
SubCommand::with_name("completions")
App::new("completions")
.about("Generate shell completions for your shell to stdout")
.arg(
Arg::with_name("shell")
Arg::new("shell")
.takes_value(true)
.possible_values(&Shell::variants())
.possible_values(Shell::possible_values())
.help("the shell to generate completions for")
.value_name("SHELL")
.required(true),
@ -137,3 +143,8 @@ fn open<P: AsRef<OsStr>>(path: P) {
error!("Error opening web browser: {}", e);
}
}
#[test]
fn verify_app() {
create_clap_app().debug_assert();
}