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

Use clap::ArgEnum to auto-generate possible cli arguments from Rust enums #10369

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Expand Up @@ -61,7 +61,7 @@ toml_edit = { version = "0.13.4", features = ["serde", "easy"] }
unicode-xid = "0.2.0"
url = "2.2.2"
walkdir = "2.2"
clap = "3.1.0"
clap = { version = "3.1.0", features = ["derive"] }
unicode-width = "0.1.5"
openssl = { version = '0.10.11', optional = true }
im-rc = "15.0.0"
Expand Down
6 changes: 3 additions & 3 deletions src/bin/cargo/commands/config.rs
Expand Up @@ -12,8 +12,8 @@ pub fn cli() -> App {
.arg(Arg::new("key").help("The config key to display"))
.arg(
opt("format", "Display format")
.possible_values(cargo_config::ConfigFormat::POSSIBLE_VALUES)
.default_value("toml"),
.possible_values(cargo_config::ConfigFormat::possible_values())
.default_value(cargo_config::ConfigFormat::Toml.as_ref()),
)
.arg(opt(
"show-origin",
Expand All @@ -35,7 +35,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
Some(("get", args)) => {
let opts = cargo_config::GetOptions {
key: args.value_of("key"),
format: args.value_of("format").unwrap().parse()?,
format: args.value_of_t("format")?,
show_origin: args.is_present("show-origin"),
merged: args.value_of("merged") == Some("yes"),
};
Expand Down
11 changes: 5 additions & 6 deletions src/bin/cargo/commands/tree.rs
Expand Up @@ -68,8 +68,8 @@ pub fn cli() -> App {
"Change the prefix (indentation) of how each entry is displayed",
)
.value_name("PREFIX")
.possible_values(&["depth", "indent", "none"])
.default_value("indent"),
.possible_values(tree::Prefix::possible_values())
.default_value(tree::Prefix::Indent.as_ref()),
)
.arg(opt(
"no-dedupe",
Expand Down Expand Up @@ -113,16 +113,15 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
config
.shell()
.warn("the --no-indent flag has been changed to --prefix=none")?;
"none"
tree::Prefix::None
} else if args.is_present("prefix-depth") {
config
.shell()
.warn("the --prefix-depth flag has been changed to --prefix=depth")?;
"depth"
tree::Prefix::Depth
} else {
args.value_of("prefix").unwrap()
args.value_of_t("prefix")?
};
let prefix = tree::Prefix::from_str(prefix).map_err(|e| anyhow::anyhow!("{}", e))?;

let no_dedupe = args.is_present("no-dedupe") || args.is_present("all");
if args.is_present("all") {
Expand Down
29 changes: 22 additions & 7 deletions src/cargo/ops/cargo_config.rs
Expand Up @@ -4,20 +4,25 @@ use crate::util::config::{Config, ConfigKey, ConfigValue as CV, Definition};
use crate::util::errors::CargoResult;
use crate::{drop_eprintln, drop_println};
use anyhow::{bail, format_err, Error};
use clap::{ArgEnum, PossibleValue};
use serde_json::json;
use std::borrow::Cow;
use std::fmt;
use std::str::FromStr;

#[derive(clap::ArgEnum, Copy, Clone)]
pub enum ConfigFormat {
Toml,
Json,
JsonValue,
}

impl ConfigFormat {
/// For clap.
pub const POSSIBLE_VALUES: &'static [&'static str] = &["toml", "json", "json-value"];
pub fn possible_values() -> impl Iterator<Item = PossibleValue<'static>> {
ConfigFormat::value_variants()
.iter()
.filter_map(ArgEnum::to_possible_value)
}
}

impl FromStr for ConfigFormat {
Copy link
Contributor

Choose a reason for hiding this comment

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

If we move forward with this, we should switch the FromStrs to be implemented in terms of ArgEnum as well

For example, clap_completes Shell:

impl FromStr for Shell {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        for variant in Self::value_variants() {
            if variant.to_possible_value().expect("No value is skipped").matches(s, false) {
                return Ok(*variant);
            }
        }
        Err(format!("Invalid variant: {}", s))
    }
}

Expand All @@ -34,11 +39,21 @@ impl FromStr for ConfigFormat {

impl fmt::Display for ConfigFormat {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
ConfigFormat::Toml => write!(f, "toml"),
ConfigFormat::Json => write!(f, "json"),
ConfigFormat::JsonValue => write!(f, "json-value"),
}
write!(
f,
"{}",
self.to_possible_value()
.expect("No value is skipped")
.get_name()
)
}
}

impl AsRef<str> for ConfigFormat {
fn as_ref(&self) -> &str {
self.to_possible_value()
.expect("No value is skipped")
.get_name()
}
}

Expand Down
19 changes: 18 additions & 1 deletion src/cargo/ops/tree/mod.rs
Expand Up @@ -9,6 +9,7 @@ use crate::ops::{self, Packages};
use crate::util::{CargoResult, Config};
use crate::{drop_print, drop_println};
use anyhow::Context;
use clap::{ArgEnum, PossibleValue};
use graph::Graph;
use std::collections::{HashMap, HashSet};
use std::str::FromStr;
Expand Down Expand Up @@ -85,13 +86,21 @@ impl FromStr for Charset {
}
}

#[derive(Clone, Copy)]
#[derive(clap::ArgEnum, Copy, Clone)]
pub enum Prefix {
None,
Indent,
Depth,
}

impl Prefix {
pub fn possible_values() -> impl Iterator<Item = PossibleValue<'static>> {
Prefix::value_variants()
.iter()
.filter_map(ArgEnum::to_possible_value)
}
}

impl FromStr for Prefix {
type Err = &'static str;

Expand All @@ -105,6 +114,14 @@ impl FromStr for Prefix {
}
}

impl AsRef<str> for Prefix {
fn as_ref(&self) -> &str {
self.to_possible_value()
.expect("No value is skipped")
.get_name()
}
}

struct Symbols {
down: &'static str,
tee: &'static str,
Expand Down