From 218caa410aa38c29984be31a5229b9fa717560ee Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:19:13 +0200 Subject: Merging upstream version 1.68.2+dfsg1. Signed-off-by: Daniel Baumann --- vendor/mdbook/src/cmd/build.rs | 30 ++++-------- vendor/mdbook/src/cmd/clean.rs | 26 +++------- vendor/mdbook/src/cmd/command_prelude.rs | 45 ++++++++++++++++++ vendor/mdbook/src/cmd/init.rs | 44 +++++++---------- vendor/mdbook/src/cmd/mod.rs | 1 + vendor/mdbook/src/cmd/serve.rs | 47 +++++++----------- vendor/mdbook/src/cmd/test.rs | 66 ++++++++++++++------------ vendor/mdbook/src/cmd/watch.rs | 81 +++++++++++++++++--------------- 8 files changed, 175 insertions(+), 165 deletions(-) create mode 100644 vendor/mdbook/src/cmd/command_prelude.rs (limited to 'vendor/mdbook/src/cmd') diff --git a/vendor/mdbook/src/cmd/build.rs b/vendor/mdbook/src/cmd/build.rs index 5fe73236c..14a9fec6e 100644 --- a/vendor/mdbook/src/cmd/build.rs +++ b/vendor/mdbook/src/cmd/build.rs @@ -1,28 +1,16 @@ +use super::command_prelude::*; use crate::{get_book_dir, open}; -use clap::{arg, App, Arg, ArgMatches}; use mdbook::errors::Result; use mdbook::MDBook; +use std::path::PathBuf; // Create clap subcommand arguments -pub fn make_subcommand<'help>() -> App<'help> { - App::new("build") +pub fn make_subcommand() -> Command { + Command::new("build") .about("Builds a book from its markdown files") - .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)" - )) - .arg(arg!(-o --open "Opens the compiled book in a web browser")) + .arg_dest_dir() + .arg_root_dir() + .arg_open() } // Build command implementation @@ -30,13 +18,13 @@ 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") { + if let Some(dest_dir) = args.get_one::("dest-dir") { book.config.build.build_dir = dest_dir.into(); } 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? let path = book.build_dir_for("html").join("index.html"); if !path.exists() { diff --git a/vendor/mdbook/src/cmd/clean.rs b/vendor/mdbook/src/cmd/clean.rs index 0569726e1..3ec605fea 100644 --- a/vendor/mdbook/src/cmd/clean.rs +++ b/vendor/mdbook/src/cmd/clean.rs @@ -1,28 +1,16 @@ +use super::command_prelude::*; use crate::get_book_dir; use anyhow::Context; -use clap::{arg, App, Arg, ArgMatches}; use mdbook::MDBook; use std::fs; +use std::path::PathBuf; // Create clap subcommand arguments -pub fn make_subcommand<'help>() -> App<'help> { - App::new("clean") +pub fn make_subcommand() -> Command { + Command::new("clean") .about("Deletes a built 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(arg!([dir] - "Root directory for the book{n}\ - (Defaults to the Current Directory when omitted)" - )) + .arg_dest_dir() + .arg_root_dir() } // Clean command implementation @@ -30,7 +18,7 @@ pub fn execute(args: &ArgMatches) -> mdbook::errors::Result<()> { let book_dir = get_book_dir(args); let book = MDBook::load(&book_dir)?; - let dir_to_remove = match args.value_of("dest-dir") { + let dir_to_remove = match args.get_one::("dest-dir") { Some(dest_dir) => dest_dir.into(), None => book.root.join(&book.config.build.build_dir), }; diff --git a/vendor/mdbook/src/cmd/command_prelude.rs b/vendor/mdbook/src/cmd/command_prelude.rs new file mode 100644 index 000000000..b6362e603 --- /dev/null +++ b/vendor/mdbook/src/cmd/command_prelude.rs @@ -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) + } +} diff --git a/vendor/mdbook/src/cmd/init.rs b/vendor/mdbook/src/cmd/init.rs index c964dcc13..d8ce93d16 100644 --- a/vendor/mdbook/src/cmd/init.rs +++ b/vendor/mdbook/src/cmd/init.rs @@ -1,5 +1,5 @@ use crate::get_book_dir; -use clap::{arg, App, Arg, ArgMatches}; +use clap::{arg, ArgMatches, Command as ClapCommand}; use mdbook::config; use mdbook::errors::Result; use mdbook::MDBook; @@ -8,30 +8,22 @@ use std::io::Write; use std::process::Command; // Create clap subcommand arguments -pub fn make_subcommand<'help>() -> App<'help> { - App::new("init") +pub fn make_subcommand() -> ClapCommand { + ClapCommand::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(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::new("title") - .long("title") - .takes_value(true) - .help("Sets the book title") - .required(false), + arg!([dir] + "Directory to create the book in\n\ + (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!(--force "Skips confirmation prompts")) + .arg(arg!(--title "Sets the book title")) .arg( - Arg::new("ignore") - .long("ignore") - .takes_value(true) - .possible_values(&["none", "git"]) - .help("Creates a VCS ignore file (i.e. .gitignore)") - .required(false), + arg!(--ignore <ignore> "Creates a VCS ignore file (i.e. .gitignore)") + .value_parser(["none", "git"]), ) } @@ -41,12 +33,12 @@ pub fn execute(args: &ArgMatches) -> Result<()> { 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.get_flag("theme") { let theme_dir = book_dir.join("theme"); println!(); println!("Copying the default theme to {}", theme_dir.display()); // 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."); 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 { "git" => builder.create_gitignore(true), _ => builder.create_gitignore(false), @@ -71,8 +63,8 @@ pub fn execute(args: &ArgMatches) -> Result<()> { } } - config.book.title = if args.is_present("title") { - args.value_of("title").map(String::from) + config.book.title = if args.contains_id("title") { + args.get_one::<String>("title").map(String::from) } else { request_book_title() }; diff --git a/vendor/mdbook/src/cmd/mod.rs b/vendor/mdbook/src/cmd/mod.rs index c5b6730f1..b21979b27 100644 --- a/vendor/mdbook/src/cmd/mod.rs +++ b/vendor/mdbook/src/cmd/mod.rs @@ -2,6 +2,7 @@ pub mod build; pub mod clean; +pub mod command_prelude; pub mod init; #[cfg(feature = "serve")] pub mod serve; diff --git a/vendor/mdbook/src/cmd/serve.rs b/vendor/mdbook/src/cmd/serve.rs index bafbfd52e..88898567e 100644 --- a/vendor/mdbook/src/cmd/serve.rs +++ b/vendor/mdbook/src/cmd/serve.rs @@ -1,7 +1,8 @@ +use super::command_prelude::*; #[cfg(feature = "watch")] use super::watch; use crate::{get_book_dir, open}; -use clap::{arg, App, Arg, ArgMatches}; +use clap::builder::NonEmptyStringValueParser; use futures_util::sink::SinkExt; use futures_util::StreamExt; use mdbook::errors::*; @@ -18,43 +19,30 @@ use warp::Filter; const LIVE_RELOAD_ENDPOINT: &str = "__livereload"; // Create clap subcommand arguments -pub fn make_subcommand<'help>() -> App<'help> { - App::new("serve") +pub fn make_subcommand() -> Command { + Command::new("serve") .about("Serves a book at http://localhost:3000, and rebuilds it on changes") - .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)" - )) + .arg_dest_dir() + .arg_root_dir() .arg( Arg::new("hostname") .short('n') .long("hostname") - .takes_value(true) + .num_args(1) .default_value("localhost") - .forbid_empty_values(true) + .value_parser(NonEmptyStringValueParser::new()) .help("Hostname to listen on for HTTP connections"), ) .arg( Arg::new("port") .short('p') .long("port") - .takes_value(true) + .num_args(1) .default_value("3000") - .forbid_empty_values(true) + .value_parser(NonEmptyStringValueParser::new()) .help("Port to use for HTTP connections"), ) - .arg(arg!(-o --open "Opens the compiled book in a web browser")) + .arg_open() } // Serve command implementation @@ -62,17 +50,17 @@ 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(); - let hostname = args.value_of("hostname").unwrap(); - let open_browser = args.is_present("open"); + let port = args.get_one::<String>("port").unwrap(); + let hostname = args.get_one::<String>("hostname").unwrap(); + let open_browser = args.get_flag("open"); let address = format!("{}:{}", hostname, port); let update_config = |book: &mut MDBook| { 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"); - 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(); } // Override site-url for local serving of the 404 file @@ -89,8 +77,7 @@ pub fn execute(args: &ArgMatches) -> Result<()> { let input_404 = book .config .get("output.html.input-404") - .map(toml::Value::as_str) - .and_then(std::convert::identity) // flatten + .and_then(toml::Value::as_str) .map(ToString::to_string); let file_404 = get_404_output_file(&input_404); diff --git a/vendor/mdbook/src/cmd/test.rs b/vendor/mdbook/src/cmd/test.rs index 02f982a49..3efe130b1 100644 --- a/vendor/mdbook/src/cmd/test.rs +++ b/vendor/mdbook/src/cmd/test.rs @@ -1,54 +1,58 @@ +use super::command_prelude::*; 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::MDBook; +use std::path::PathBuf; // Create clap subcommand arguments -pub fn make_subcommand<'help>() -> App<'help> { - App::new("test") +pub fn make_subcommand() -> Command { + Command::new("test") .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::new("dest-dir") - .short('d') - .long("dest-dir") - .value_name("dest-dir") + Arg::new("chapter") + .short('c') + .long("chapter") + .value_name("chapter"), + ) + .arg( + Arg::new("library-path") + .short('L') + .long("library-path") + .value_name("dir") + .value_delimiter(',') + .value_parser(NonEmptyStringValueParser::new()) + .action(ArgAction::Append) .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`.", + "A comma-separated list of directories to add to the crate \ + search path when building tests", ), ) - .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_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")) } // test command implementation pub fn execute(args: &ArgMatches) -> Result<()> { let library_paths: Vec<&str> = args - .values_of("library-path") - .map(std::iter::Iterator::collect) + .get_many("library-path") + .map(|it| it.map(String::as_str).collect()) .unwrap_or_default(); + + let chapter: Option<&str> = args.get_one::<String>("chapter").map(|s| s.as_str()); + 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 = dest_dir.into(); + if let Some(dest_dir) = args.get_one::<PathBuf>("dest-dir") { + book.config.build.build_dir = dest_dir.to_path_buf(); } - - book.test(library_paths)?; + match chapter { + Some(_) => book.test_chapter(library_paths, chapter), + None => book.test(library_paths), + }?; Ok(()) } diff --git a/vendor/mdbook/src/cmd/watch.rs b/vendor/mdbook/src/cmd/watch.rs index 9336af779..bbc6bde71 100644 --- a/vendor/mdbook/src/cmd/watch.rs +++ b/vendor/mdbook/src/cmd/watch.rs @@ -1,34 +1,20 @@ +use super::command_prelude::*; use crate::{get_book_dir, open}; -use clap::{arg, App, Arg, ArgMatches}; use mdbook::errors::Result; use mdbook::utils; use mdbook::MDBook; -use notify::Watcher; use std::path::{Path, PathBuf}; use std::sync::mpsc::channel; use std::thread::sleep; use std::time::Duration; // Create clap subcommand arguments -pub fn make_subcommand<'help>() -> App<'help> { - App::new("watch") +pub fn make_subcommand() -> Command { + Command::new("watch") .about("Watches a book's files and rebuilds it on changes") - .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)" - )) - .arg(arg!(-o --open "Opens the compiled book in a web browser")) + .arg_dest_dir() + .arg_root_dir() + .arg_open() } // Watch command implementation @@ -37,13 +23,13 @@ pub fn execute(args: &ArgMatches) -> Result<()> { let mut book = MDBook::load(&book_dir)?; 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(); } }; update_config(&mut book); - if args.is_present("open") { + if args.get_flag("open") { book.build()?; let path = book.build_dir_for("html").join("index.html"); if !path.exists() { @@ -121,30 +107,42 @@ pub fn trigger_on_change<F>(book: &MDBook, closure: F) where F: Fn(Vec<PathBuf>, &Path), { - use notify::DebouncedEvent::*; use notify::RecursiveMode::*; // Create a channel to receive the events. let (tx, rx) = channel(); - let mut watcher = match notify::watcher(tx, Duration::from_secs(1)) { - Ok(w) => w, + let mut debouncer = match notify_debouncer_mini::new_debouncer(Duration::from_secs(1), None, tx) + { + Ok(d) => d, Err(e) => { error!("Error while trying to watch the files:\n\n\t{:?}", e); std::process::exit(1) } }; + let watcher = debouncer.watcher(); // Add the source directory to the watcher - if let Err(e) = watcher.watch(book.source_dir(), Recursive) { + if let Err(e) = watcher.watch(&book.source_dir(), Recursive) { error!("Error while watching {:?}:\n {:?}", book.source_dir(), e); std::process::exit(1); }; - let _ = watcher.watch(book.theme_dir(), Recursive); + let _ = watcher.watch(&book.theme_dir(), Recursive); // Add the book.toml file to the watcher if it exists - let _ = watcher.watch(book.root.join("book.toml"), NonRecursive); + let _ = watcher.watch(&book.root.join("book.toml"), NonRecursive); + + for dir in &book.config.build.extra_watch_dirs { + let path = dir.canonicalize().unwrap(); + if let Err(e) = watcher.watch(&path, Recursive) { + error!( + "Error while watching extra directory {:?}:\n {:?}", + path, e + ); + std::process::exit(1); + } + } info!("Listening for changes..."); @@ -155,18 +153,25 @@ where let all_events = std::iter::once(first_event).chain(other_events); - let paths = all_events - .filter_map(|event| { - debug!("Received filesystem event: {:?}", event); - - match event { - Create(path) | Write(path) | Remove(path) | Rename(_, path) => Some(path), - _ => None, + let paths: Vec<_> = all_events + .filter_map(|event| match event { + Ok(events) => Some(events), + Err(errors) => { + for error in errors { + log::warn!("error while watching for changes: {error}"); + } + None } }) - .collect::<Vec<_>>(); - - let paths = remove_ignored_files(&book.root, &paths[..]); + .flatten() + .map(|event| event.path) + .collect(); + + // If we are watching files outside the current repository (via extra-watch-dirs), then they are definitionally + // ignored by gitignore. So we handle this case by including such files into the watched paths list. + let any_external_paths = paths.iter().filter(|p| !p.starts_with(&book.root)).cloned(); + let mut paths = remove_ignored_files(&book.root, &paths[..]); + paths.extend(any_external_paths); if !paths.is_empty() { closure(paths, &book.root); -- cgit v1.2.3