Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update arg parsing to clap v3 #1717

Merged
merged 4 commits into from
Jan 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 89 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@ keywords = ["static", "site", "generator", "blog"]
include = ["src/**/*", "LICENSE", "README.md"]

[build-dependencies]
clap = "2"
clap = "3"
clap_complete = "3"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does this do and where is it used?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In v3 the completion functionality has been split off to the separate clap_complete package, I noticed that there is some completion code in build.rs that is commented out, so admittedly this is a bit preemptive.

I guess we could instead add it in a separate PR focused on bringing back completion


[[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"
Expand Down
2 changes: 1 addition & 1 deletion azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
186 changes: 85 additions & 101 deletions src/cli.rs
Original file line number Diff line number Diff line change
@@ -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 = 'r', long, default_value = ".")]
pub root: PathBuf,

/// Path to a config file other than config.toml in the root of project
#[clap(short = 'c', long)]
pub config: Option<PathBuf>,

#[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 = 'f', 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<String>,

/// Outputs the generated site in the given path (by default 'public' dir in project root)
#[clap(short = 'o', long)]
output_dir: Option<PathBuf>,

/// 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 = 'i', long, default_value = "127.0.0.1")]
interface: String,

/// Which port to use
#[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 = 'o', long)]
output_dir: Option<PathBuf>,

/// 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
Keats marked this conversation as resolved.
Show resolved Hide resolved
#[clap(short = 'f', long)]
fast: bool,
},

/// Try to build the project without rendering it. Checks links
Check {
/// Include drafts when loading the site
#[clap(long)]
drafts: bool,
},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we be explicit with all the default values as well? eg set false for drafts etc. I guess Subcommand relies on default but I'd like to be able to see the full set of option values at a glance

Copy link
Contributor Author

@tranzystorekk tranzystorekk Jan 4, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've looked at the documentation, but especially for bool flags this is neither elegant to write nor widely accepted style to specify the default explicitly.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh :(
Just passing true/false to default doesn't work? It's kind of weird that non bool gets to have their default value in the struct but not bool since there are only 2 possible values...

Copy link
Contributor Author

@tranzystorekk tranzystorekk Jan 4, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did a quick check and clap protests with a "default value is meaningless for bool" error.

I'm guessing the rationale is that bool flags are simple on-off switches and don't require any special handling.

}
Loading