From 231134095c4a3b56cbb16e2d368fd413d3f0794a Mon Sep 17 00:00:00 2001 From: Marcin Puc Date: Mon, 3 Jan 2022 19:43:42 +0100 Subject: [PATCH 1/4] Update arg parsing to clap v3 --- Cargo.lock | 93 ++++++++++++++++++++++++-- Cargo.toml | 5 +- src/cli.rs | 186 ++++++++++++++++++++++++---------------------------- src/main.rs | 122 +++++++++++++--------------------- 4 files changed, 224 insertions(+), 182 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 563757100..1c456874d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -291,12 +291,51 @@ dependencies = [ "ansi_term", "atty", "bitflags", - "strsim", - "textwrap", + "strsim 0.8.0", + "textwrap 0.11.0", "unicode-width", "vec_map", ] +[[package]] +name = "clap" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d17bf219fcd37199b9a29e00ba65dfb8cd5b2688b7297ec14ff829c40ac50ca9" +dependencies = [ + "atty", + "bitflags", + "clap_derive", + "indexmap", + "lazy_static", + "os_str_bytes", + "strsim 0.10.0", + "termcolor", + "textwrap 0.14.2", +] + +[[package]] +name = "clap_complete" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e3cb395a9ef3c4ddabee9d6a0d0419b107cccf3e431fc0015df90f24849386c" +dependencies = [ + "clap 3.0.0", +] + +[[package]] +name = "clap_derive" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1b9752c030a14235a0bd5ef3ad60a1dcac8468c30921327fc8af36b20c790b9" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "color_quant" version = "1.1.0" @@ -1350,7 +1389,7 @@ dependencies = [ "anyhow", "bincode", "byteorder", - "clap", + "clap 2.34.0", "encoding", "glob", "lindera-core", @@ -1892,6 +1931,15 @@ dependencies = [ "vcpkg", ] +[[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 = "parking_lot" version = "0.11.2" @@ -2120,6 +2168,30 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" version = "1.0.36" @@ -2754,6 +2826,12 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "strum" version = "0.21.0" @@ -2933,6 +3011,12 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "textwrap" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80" + [[package]] name = "thiserror" version = "1.0.30" @@ -3590,7 +3674,8 @@ version = "0.16.0" dependencies = [ "atty", "chrono", - "clap", + "clap 3.0.0", + "clap_complete", "ctrlc", "errors", "front_matter", diff --git a/Cargo.toml b/Cargo.toml index ef2048d5e..4809e42d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,14 +13,15 @@ keywords = ["static", "site", "generator", "blog"] include = ["src/**/*", "LICENSE", "README.md"] [build-dependencies] -clap = "2" +clap = "3" +clap_complete = "3" [[bin]] name = "zola" [dependencies] atty = "0.2.11" -clap = { version = "2", default-features = false } +clap = { version = "3", features = ["derive"] } chrono = "0.4" lazy_static = "1.1" termcolor = "1.0.4" diff --git a/src/cli.rs b/src/cli.rs index e6d6332ff..4f78f8b4c 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,102 +1,86 @@ -use clap::{crate_authors, crate_description, crate_version, App, AppSettings, Arg, SubCommand}; - -pub fn build_cli() -> App<'static, 'static> { - App::new("zola") - .version(crate_version!()) - .author(crate_authors!()) - .about(crate_description!()) - .setting(AppSettings::SubcommandRequiredElseHelp) - .arg( - Arg::with_name("root") - .short("r") - .long("root") - .takes_value(true) - .default_value(".") - .help("Directory to use as root of project") - ) - .arg( - Arg::with_name("config") - .short("c") - .long("config") - .takes_value(true) - .help("Path to a config file other than config.toml in the root of project") - ) - .subcommands(vec![ - SubCommand::with_name("init") - .about("Create a new Zola project") - .args(&[ - Arg::with_name("name") - .default_value(".") - .help("Name of the project. Will create a new directory with that name in the current directory"), - Arg::with_name("force") - .short("f") - .long("force") - .takes_value(false) - .help("Force creation of project even if directory is non-empty") - ]), - SubCommand::with_name("build") - .about("Deletes the output directory if there is one and builds the site") - .args(&[ - Arg::with_name("base_url") - .short("u") - .long("base-url") - .takes_value(true) - .help("Force the base URL to be that value (default to the one in config.toml)"), - Arg::with_name("output_dir") - .short("o") - .long("output-dir") - .takes_value(true) - .help("Outputs the generated site in the given path (by default 'public' dir in project root)"), - Arg::with_name("drafts") - .long("drafts") - .takes_value(false) - .help("Include drafts when loading the site"), - ]), - SubCommand::with_name("serve") - .about("Serve the site. Rebuild and reload on change automatically") - .args(&[ - Arg::with_name("interface") - .short("i") - .long("interface") - .takes_value(true) - .help("Interface to bind on (default: 127.0.0.1)"), - Arg::with_name("port") - .short("p") - .long("port") - .takes_value(true) - .help("Which port to use (default: 1111)"), - Arg::with_name("output_dir") - .short("o") - .long("output-dir") - .takes_value(true) - .help("Outputs assets of the generated site in the given path (by default 'public' dir in project root). HTML/XML will be stored in memory."), - Arg::with_name("base_url") - .short("u") - .long("base-url") - .takes_value(true) - .help("Changes the base_url (default: 127.0.0.1)"), - Arg::with_name("drafts") - .long("drafts") - .takes_value(false) - .help("Include drafts when loading the site"), - Arg::with_name("open") - .short("O") - .long("open") - .takes_value(false) - .help("Open site in the default browser"), - Arg::with_name("fast") - .short("f") - .long("fast") - .takes_value(false) - .help("Only rebuild the minimum on change - useful when working on a specific page/section"), - ]), - SubCommand::with_name("check") - .about("Try building the project without rendering it. Checks links") - .args(&[ - Arg::with_name("drafts") - .long("drafts") - .takes_value(false) - .help("Include drafts when loading the site"), - ]) - ]) +use std::path::PathBuf; + +use clap::{Parser, Subcommand}; + +#[derive(Parser)] +#[clap(version, author, about)] +pub struct Cli { + /// Directory to use as root of project + #[clap(short, long, default_value = ".")] + pub root: PathBuf, + + /// Path to a config file other than config.toml in the root of project + #[clap(short, long)] + pub config: Option, + + #[clap(subcommand)] + pub command: Command, +} + +#[derive(Subcommand)] +pub enum Command { + /// Create a new Zola project + Init { + /// Name of the project. Will create a new directory with that name in the current directory + #[clap(default_value = ".")] + name: String, + + /// Force creation of project even if directory is non-empty + #[clap(short, long)] + force: bool, + }, + + /// Deletes the output directory if there is one and builds the site + Build { + /// Force the base URL to be that value (defaults to the one in config.toml) + #[clap(short = 'u', long)] + base_url: Option, + + /// Outputs the generated site in the given path (by default 'public' dir in project root) + #[clap(short, long)] + output_dir: Option, + + /// Include drafts when loading the site + #[clap(long)] + drafts: bool, + }, + + /// Serve the site. Rebuild and reload on change automatically + Serve { + /// Interface to bind on + #[clap(short, long, default_value = "127.0.0.1")] + interface: String, + + /// Which port to use + #[clap(short, long, default_value = "1111")] + port: u16, + + /// Outputs assets of the generated site in the given path (by default 'public' dir in project root). + /// HTML/XML will be stored in memory. + #[clap(short, long)] + output_dir: Option, + + /// Changes the base_url + #[clap(short = 'u', long, default_value = "127.0.0.1")] + base_url: String, + + /// Include drafts when loading the site + #[clap(long)] + drafts: bool, + + /// Open site in the default browser + #[clap(short = 'O', long)] + open: bool, + + /// Only rebuild the minimum on change - useful when working on a specific page/section + #[clap(short, long)] + fast: bool, + }, + + /// Try to build the project without rendering it. Checks links + Check { + /// Include drafts when loading the site + #[clap(long)] + drafts: bool, + }, } diff --git a/src/main.rs b/src/main.rs index 00991967c..a7ecb7332 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,124 +1,96 @@ -use std::env; -use std::path::{Path, PathBuf}; use std::time::Instant; +use cli::{Cli, Command}; use utils::net::{get_available_port, port_is_available}; +use clap::Parser; + mod cli; mod cmd; mod console; mod prompt; fn main() { - let matches = cli::build_cli().get_matches(); - - let root_dir = match matches.value_of("root").unwrap() { - "." => env::current_dir().unwrap(), - path => PathBuf::from(path) - .canonicalize() - .unwrap_or_else(|_| panic!("Cannot find root directory: {}", path)), - }; - let config_file = match matches.value_of("config") { - Some(path) => PathBuf::from(path) - .canonicalize() - .unwrap_or_else(|_| panic!("Cannot find config file: {}", path)), - None => root_dir.join("config.toml"), - }; + let cli = Cli::parse(); + let root_dir = cli + .root + .canonicalize() + .unwrap_or_else(|_| panic!("Cannot find root directory: {}", cli.root.display())); + let config_file = cli + .config + .map(|path| { + path.canonicalize() + .unwrap_or_else(|_| panic!("Cannot find config file: {}", path.display())) + }) + .unwrap_or_else(|| root_dir.join("config.toml")); - match matches.subcommand() { - ("init", Some(matches)) => { - let force = matches.is_present("force"); - match cmd::create_new_project(matches.value_of("name").unwrap(), force) { - Ok(()) => (), - Err(e) => { - console::unravel_errors("Failed to create the project", &e); - ::std::process::exit(1); - } - }; - } - ("build", Some(matches)) => { + match cli.command { + Command::Init { name, force } => match cmd::create_new_project(&name, force) { + Ok(()) => (), + Err(e) => { + console::unravel_errors("Failed to create the proejct", &e); + std::process::exit(1); + } + }, + Command::Build { base_url, output_dir, drafts } => { console::info("Building site..."); let start = Instant::now(); - let output_dir = matches.value_of("output_dir").map(|output_dir| Path::new(output_dir)); match cmd::build( &root_dir, &config_file, - matches.value_of("base_url"), - output_dir, - matches.is_present("drafts"), + base_url.as_deref(), + output_dir.as_deref(), + drafts, ) { Ok(()) => console::report_elapsed_time(start), Err(e) => { console::unravel_errors("Failed to build the site", &e); - ::std::process::exit(1); + std::process::exit(1); } - }; + } } - ("serve", Some(matches)) => { - let interface = matches.value_of("interface").unwrap_or("127.0.0.1"); - let mut port: u16 = match matches.value_of("port").unwrap_or("1111").parse() { - Ok(x) => x, - Err(_) => { - console::error("The request port needs to be an integer"); - ::std::process::exit(1); - } - }; - let open = matches.is_present("open"); - let include_drafts = matches.is_present("drafts"); - let fast = matches.is_present("fast"); - - // Default one + Command::Serve { interface, mut port, output_dir, base_url, drafts, open, fast } => { if port != 1111 && !port_is_available(port) { console::error("The requested port is not available"); - ::std::process::exit(1); + std::process::exit(1); } if !port_is_available(port) { - port = if let Some(p) = get_available_port(1111) { - p - } else { - console::error("No port available."); - ::std::process::exit(1); - } + port = get_available_port(1111).unwrap_or_else(|| { + console::error("No port available"); + std::process::exit(1); + }); } - let output_dir = matches.value_of("output_dir").map(|output_dir| Path::new(output_dir)); - let base_url = matches.value_of("base_url").unwrap_or("127.0.0.1"); + console::info("Building site..."); match cmd::serve( &root_dir, - interface, + &interface, port, - output_dir, - base_url, + output_dir.as_deref(), + &base_url, &config_file, open, - include_drafts, + drafts, fast, ) { Ok(()) => (), Err(e) => { - console::unravel_errors("", &e); - ::std::process::exit(1); + console::unravel_errors("Failed to serve the site", &e); + std::process::exit(1); } - }; + } } - ("check", Some(matches)) => { + Command::Check { drafts } => { console::info("Checking site..."); let start = Instant::now(); - match cmd::check( - &root_dir, - &config_file, - matches.value_of("base_path"), - matches.value_of("base_url"), - matches.is_present("drafts"), - ) { + match cmd::check(&root_dir, &config_file, None, None, drafts) { Ok(()) => console::report_elapsed_time(start), Err(e) => { console::unravel_errors("Failed to check the site", &e); - ::std::process::exit(1); + std::process::exit(1); } - }; + } } - _ => unreachable!(), } } From 2a1263688cf717f32eac3fb942eba87248bca039 Mon Sep 17 00:00:00 2001 From: Marcin Puc Date: Tue, 4 Jan 2022 12:13:07 +0100 Subject: [PATCH 2/4] Add small flag definition adjustments --- src/cli.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index 4f78f8b4c..43daf7376 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -6,11 +6,11 @@ use clap::{Parser, Subcommand}; #[clap(version, author, about)] pub struct Cli { /// Directory to use as root of project - #[clap(short, long, default_value = ".")] + #[clap(short = 'r', long, default_value = ".")] pub root: PathBuf, /// Path to a config file other than config.toml in the root of project - #[clap(short, long)] + #[clap(short = 'c', long)] pub config: Option, #[clap(subcommand)] @@ -26,7 +26,7 @@ pub enum Command { name: String, /// Force creation of project even if directory is non-empty - #[clap(short, long)] + #[clap(short = 'f', long)] force: bool, }, @@ -37,7 +37,7 @@ pub enum Command { base_url: Option, /// Outputs the generated site in the given path (by default 'public' dir in project root) - #[clap(short, long)] + #[clap(short = 'o', long)] output_dir: Option, /// Include drafts when loading the site @@ -48,16 +48,16 @@ pub enum Command { /// Serve the site. Rebuild and reload on change automatically Serve { /// Interface to bind on - #[clap(short, long, default_value = "127.0.0.1")] + #[clap(short = 'i', long, default_value = "127.0.0.1")] interface: String, /// Which port to use - #[clap(short, long, default_value = "1111")] + #[clap(short = 'p', long, default_value_t = 1111)] port: u16, /// Outputs assets of the generated site in the given path (by default 'public' dir in project root). /// HTML/XML will be stored in memory. - #[clap(short, long)] + #[clap(short = 'o', long)] output_dir: Option, /// Changes the base_url @@ -73,7 +73,7 @@ pub enum Command { open: bool, /// Only rebuild the minimum on change - useful when working on a specific page/section - #[clap(short, long)] + #[clap(short = 'f', long)] fast: bool, }, From 134c7e6548e8e4c49268176d6f72b240e0e35e91 Mon Sep 17 00:00:00 2001 From: Marcin Puc Date: Wed, 5 Jan 2022 00:19:48 +0100 Subject: [PATCH 3/4] Handle simple errors with if let --- src/main.rs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/main.rs b/src/main.rs index a7ecb7332..764d9e320 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,13 +25,12 @@ fn main() { .unwrap_or_else(|| root_dir.join("config.toml")); match cli.command { - Command::Init { name, force } => match cmd::create_new_project(&name, force) { - Ok(()) => (), - Err(e) => { + Command::Init { name, force } => { + if let Err(e) = cmd::create_new_project(&name, force) { console::unravel_errors("Failed to create the proejct", &e); std::process::exit(1); } - }, + } Command::Build { base_url, output_dir, drafts } => { console::info("Building site..."); let start = Instant::now(); @@ -63,7 +62,7 @@ fn main() { } console::info("Building site..."); - match cmd::serve( + if let Err(e) = cmd::serve( &root_dir, &interface, port, @@ -74,11 +73,8 @@ fn main() { drafts, fast, ) { - Ok(()) => (), - Err(e) => { - console::unravel_errors("Failed to serve the site", &e); - std::process::exit(1); - } + console::unravel_errors("Failed to serve the site", &e); + std::process::exit(1); } } Command::Check { drafts } => { From 0ee487d0b5307cd5b1569b3d97c7586bd1120a3f Mon Sep 17 00:00:00 2001 From: Marcin Puc Date: Sat, 15 Jan 2022 11:49:20 +0100 Subject: [PATCH 4/4] Update pinned Rust version to 1.54 --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 86e68f43a..ba81d671f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -21,7 +21,7 @@ stages: rustup_toolchain: stable linux-pinned: imageName: 'ubuntu-20.04' - rustup_toolchain: 1.53.0 + rustup_toolchain: 1.54.0 pool: vmImage: $(imageName) steps: