Merge #312
312: Update clap to v3.1 r=kbknapp a=CosmicHorrorDev
Co-authored-by: Lovecraftian Horror <LovecraftianHorror@pm.me>
Co-authored-by: Kevin K <kbknapp@gmail.com>
diff --git a/Cargo.lock b/Cargo.lock
index 3dc3f0d..cabc7e7 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -18,15 +18,6 @@
]
[[package]]
-name = "ansi_term"
-version = "0.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
-dependencies = [
- "winapi",
-]
-
-[[package]]
name = "anyhow"
version = "1.0.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -114,7 +105,7 @@
"bytesize",
"cargo-platform",
"cargo-util",
- "clap 3.2.23",
+ "clap",
"crates-io",
"crossbeam-utils",
"curl",
@@ -171,9 +162,10 @@
dependencies = [
"anyhow",
"cargo",
- "clap 2.34.0",
+ "clap",
"env_logger 0.10.0",
"git2-curl",
+ "pretty_assertions",
"semver",
"serde",
"serde_derive",
@@ -232,32 +224,32 @@
[[package]]
name = "clap"
-version = "2.34.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
-dependencies = [
- "ansi_term",
- "atty",
- "bitflags",
- "strsim 0.8.0",
- "textwrap 0.11.0",
- "unicode-width",
- "vec_map",
-]
-
-[[package]]
-name = "clap"
version = "3.2.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5"
dependencies = [
"atty",
"bitflags",
+ "clap_derive",
"clap_lex",
"indexmap",
- "strsim 0.10.0",
+ "once_cell",
+ "strsim",
"termcolor",
- "textwrap 0.16.0",
+ "textwrap",
+]
+
+[[package]]
+name = "clap_derive"
+version = "3.2.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65"
+dependencies = [
+ "heck",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn",
]
[[package]]
@@ -358,6 +350,16 @@
]
[[package]]
+name = "ctor"
+version = "0.1.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096"
+dependencies = [
+ "quote",
+ "syn",
+]
+
+[[package]]
name = "curl"
version = "0.4.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -389,6 +391,12 @@
]
[[package]]
+name = "diff"
+version = "0.1.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
+
+[[package]]
name = "either"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -566,6 +574,12 @@
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
+name = "heck"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
+
+[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -931,6 +945,15 @@
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
[[package]]
+name = "output_vt100"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
name = "pathdiff"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -949,6 +972,42 @@
checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
[[package]]
+name = "pretty_assertions"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a25e9bcb20aa780fd0bb16b72403a9064d6b3f22f026946029acb941a50af755"
+dependencies = [
+ "ctor",
+ "diff",
+ "output_vt100",
+ "yansi",
+]
+
+[[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.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1170,12 +1229,6 @@
[[package]]
name = "strsim"
-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"
@@ -1235,15 +1288,6 @@
[[package]]
name = "textwrap"
-version = "0.11.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
-dependencies = [
- "unicode-width",
-]
-
-[[package]]
-name = "textwrap"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
@@ -1357,12 +1401,6 @@
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[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.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1487,3 +1525,9 @@
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
+
+[[package]]
+name = "yansi"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
diff --git a/Cargo.toml b/Cargo.toml
index 635899b..4825db1 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -39,7 +39,7 @@
tabwriter = "1.2.1"
tempfile = "3"
toml = "~0.5.0"
-clap = "2.33.3"
+clap = { version = "3.1.6", features = ["derive"] }
[dependencies.termcolor]
optional = true
@@ -52,3 +52,6 @@
[profile.release]
lto = true
+
+[dev-dependencies]
+pretty_assertions = "1.2.0"
diff --git a/src/cli.rs b/src/cli.rs
index 7842b15..cc22490 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -1,52 +1,106 @@
-use clap::{
- arg_enum, crate_version, value_t, value_t_or_exit, App, AppSettings, Arg, ArgMatches,
- SubCommand,
-};
+use std::{ffi::OsString, fmt};
-arg_enum! {
- #[derive(Copy, Clone, Debug, PartialEq)]
- pub enum Format {
- List,
- Json,
- }
+use clap::{ArgEnum, Parser, Subcommand};
+
+#[derive(ArgEnum, Copy, Clone, Debug, PartialEq)]
+pub enum Format {
+ List,
+ Json,
}
impl Default for Format {
fn default() -> Self { Format::List }
}
-arg_enum! {
- #[derive(Copy, Clone, Debug, PartialEq)]
- pub enum Color {
- Auto,
- Never,
- Always
- }
+#[derive(ArgEnum, Copy, Clone, Debug, PartialEq)]
+pub enum Color {
+ Auto,
+ Never,
+ Always,
}
impl Default for Color {
fn default() -> Self { Color::Auto }
}
+impl fmt::Display for Color {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str(&format!("{:?}", self).to_ascii_lowercase())
+ }
+}
+
+#[derive(Parser, Debug)]
+#[clap(bin_name = "cargo")]
+struct Cargo {
+ #[clap(subcommand)]
+ command: CargoCommand,
+}
+
+#[derive(Subcommand, Debug)]
+enum CargoCommand {
+ Outdated(Options),
+}
+
/// Options from CLI arguments
-#[derive(Debug, PartialEq, Default)]
+#[derive(Parser, Debug, PartialEq, Default)]
+#[clap(version)]
+#[clap(about = "Displays information about project dependency versions")]
pub struct Options {
+ /// Output formatting
+ #[clap(long, arg_enum, ignore_case = true, default_value_t = Default::default())]
pub format: Format,
+ /// Output coloring
+ #[clap(long, arg_enum, ignore_case = true, default_value_t = Default::default())]
pub color: Color,
+ /// Space-separated list of features
+ #[clap(long, use_value_delimiter = true)]
pub features: Vec<String>,
+ /// Dependencies to not print in the output (comma separated or one per '--ignore' argument)
+ #[clap(short, long, value_name = "DEPENDENCIES", use_value_delimiter = true)]
pub ignore: Vec<String>,
+ /// Dependencies to exclude from building (comma separated or one per '--exclude' argument)
+ #[clap(
+ short = 'x',
+ long,
+ value_name = "DEPENDENCIES",
+ use_value_delimiter = true
+ )]
pub exclude: Vec<String>,
+ /// Path to the Cargo.toml file to use (Default to Cargo.toml in project root)
+ #[clap(short, long, value_name = "PATH")]
pub manifest_path: Option<String>,
+ /// Suppresses warnings
+ #[clap(short, long)]
pub quiet: bool,
+ /// Use verbose output
+ #[clap(short, long, parse(from_occurrences))]
pub verbose: u64,
+ /// The exit code to return on new versions found
+ #[clap(long, value_name = "NUM", default_value_t = Default::default())]
pub exit_code: i32,
+ /// Packages to inspect for updates (comma separated or one per --packages' argument)
+ #[clap(short, long, value_name = "PKGS", use_value_delimiter = true)]
pub packages: Vec<String>,
+ /// Package to treat as the root package
+ #[clap(short, long)]
pub root: Option<String>,
+ /// How deep in the dependency chain to search (Defaults to all dependencies)
+ #[clap(short, long, value_name = "NUM")]
pub depth: Option<i32>,
+ /// Only check root dependencies (Equivalent to --depth=1)
+ #[clap(short = 'R', long)]
pub root_deps_only: bool,
+ /// Checks updates for all workspace members rather than only the root package
+ #[clap(short, long)]
pub workspace: bool,
+ /// Ignores channels for latest updates
+ #[clap(short, long)]
pub aggressive: bool,
+ /// Ignore relative dependencies external to workspace and check root dependencies only
+ #[clap(short = 'e', long = "ignore-external-rel")]
pub workspace_only: bool,
+ /// Run without accessing the network (useful for testing w/ local registries)
+ #[clap(short, long)]
pub offline: bool,
}
@@ -62,225 +116,55 @@
pub fn frozen(&self) -> bool { false }
}
-impl<'a> From<&ArgMatches<'a>> for Options {
- fn from(m: &ArgMatches<'a>) -> Self {
- let mut opts = Options {
- format: value_t_or_exit!(m.value_of("format"), Format),
- color: value_t_or_exit!(m.value_of("color"), Color),
- features: m
- .values_of("features")
- .map(|vals| {
- vals.flat_map(|x| x.split_ascii_whitespace().collect::<Vec<_>>())
- .map(ToOwned::to_owned)
- .collect()
- })
- .unwrap_or_else(Vec::new),
- ignore: m
- .values_of("ignore")
- .map(|vals| {
- vals.flat_map(|x| x.split_ascii_whitespace().collect::<Vec<_>>())
- .map(ToOwned::to_owned)
- .collect()
- })
- .unwrap_or_else(Vec::new),
- exclude: m
- .values_of("exclude")
- .map(|vals| {
- vals.flat_map(|x| x.split_ascii_whitespace().collect::<Vec<_>>())
- .map(ToOwned::to_owned)
- .collect()
- })
- .unwrap_or_else(Vec::new),
- manifest_path: m.value_of("manifest-path").map(ToOwned::to_owned),
- quiet: m.is_present("quiet"),
- verbose: m.occurrences_of("verbose"),
- exit_code: value_t!(m, "exit-code", i32).ok().unwrap_or(0),
- packages: m
- .values_of("packages")
- .map(|vals| {
- vals.flat_map(|x| x.split_ascii_whitespace().collect::<Vec<_>>())
- .map(ToOwned::to_owned)
- .collect::<Vec<_>>()
- })
- .unwrap_or_else(Vec::new),
- root: m.value_of("root").map(ToOwned::to_owned),
- depth: value_t!(m, "depth", i32).ok(),
- root_deps_only: m.is_present("root-deps-only"),
- workspace_only: m.is_present("ignore-external-rel"),
- workspace: m.is_present("workspace"),
- aggressive: m.is_present("aggressive"),
- offline: m.is_present("offline"),
- };
-
- if m.is_present("root-deps-only") {
- opts.depth = Some(1);
- }
-
- if m.is_present("ignore-external-rel") {
- opts.depth = Some(1);
- opts.root_deps_only = true;
- }
-
- opts
+pub fn parse() -> Options {
+ match try_parse_from(std::env::args_os()) {
+ Ok(opts) => opts,
+ Err(clap_err) => clap_err.exit(),
}
}
-fn build() -> App<'static, 'static> {
- App::new("cargo-outdated")
- .bin_name("cargo")
- .setting(AppSettings::SubcommandRequired)
- .subcommand(
- SubCommand::with_name("outdated")
- .setting(AppSettings::UnifiedHelpMessage)
- .about("Displays information about project dependency versions")
- .version(crate_version!())
- .arg(
- Arg::with_name("aggressive")
- .short("a")
- .long("aggressive")
- .help("Ignores channels for latest updates"),
- )
- .arg(
- Arg::with_name("quiet")
- .short("q")
- .long("quiet")
- .help("Suppresses warnings"),
- )
- .arg(
- Arg::with_name("root-deps-only")
- .short("R")
- .long("root-deps-only")
- .help("Only check root dependencies (Equivalent to --depth=1)"),
- )
- .arg(
- Arg::with_name("ignore-external-rel")
- .short("e")
- .long("ignore-external-rel")
- .help("Ignore relative dependencies external to workspace and check root dependencies only."),
- )
- .arg(
- Arg::with_name("workspace")
- .short("w")
- .long("workspace")
- .help("Checks updates for all workspace members rather than only the root package"),
- )
- .arg(
- Arg::with_name("offline")
- .short("o")
- .long("offline")
- .help("Run without accessing the network (useful for testing w/ local registries)"),
- )
- .arg(
- Arg::with_name("format")
- .long("format")
- .default_value("list")
- .case_insensitive(true)
- .possible_values(&Format::variants())
- .value_name("FORMAT")
- .help("Output formatting"),
- )
- .arg(
- Arg::with_name("ignore")
- .short("i")
- .long("ignore")
- .help("Dependencies to not print in the output (comma separated or one per '--ignore' argument)")
- .value_delimiter(",")
- .number_of_values(1)
- .multiple(true)
- .value_name("DEPENDENCIES"),
- )
- .arg(
- Arg::with_name("exclude")
- .short("x")
- .long("exclude")
- .help("Dependencies to exclude from building (comma separated or one per '--exclude' argument)")
- .value_delimiter(",")
- .multiple(true)
- .number_of_values(1)
- .value_name("DEPENDENCIES"),
- )
- .arg(
- Arg::with_name("verbose")
- .short("v")
- .long("verbose")
- .multiple(true)
- .help("Use verbose output")
- )
- .arg(
- Arg::with_name("color")
- .long("color")
- .possible_values(&Color::variants())
- .default_value("auto")
- .value_name("COLOR")
- .case_insensitive(true)
- .help("Output coloring")
- )
- .arg(
- Arg::with_name("depth")
- .short("d")
- .long("depth")
- .value_name("NUM")
- .help("How deep in the dependency chain to search (Defaults to all dependencies when omitted)")
- )
- .arg(
- Arg::with_name("exit-code")
- .long("exit-code")
- .help("The exit code to return on new versions found")
- .default_value("0")
- .value_name("NUM"))
- .arg(
- Arg::with_name("manifest-path")
- .short("m")
- .long("manifest-path")
- .help("Path to the Cargo.toml file to use (Defaults to Cargo.toml in project root)")
- .value_name("PATH"))
- .arg(
- Arg::with_name("root")
- .short("r")
- .long("root")
- .help("Package to treat as the root package")
- .value_name("ROOT"))
- .arg(
- Arg::with_name("packages")
- .short("p")
- .long("packages")
- .help("Packages to inspect for updates (comma separated or one per '--packages' argument)")
- .value_delimiter(",")
- .number_of_values(1)
- .multiple(true)
- .value_name("PKGS"))
- .arg(
- Arg::with_name("features")
- .long("features")
- .value_delimiter(",")
- .help("Space-separated list of features")
- .multiple(true)
- .number_of_values(1)
- .value_name("FEATURES"))
- )
+fn split_elem_by_ascii_whitespace(slice: &[String]) -> Vec<String> {
+ slice
+ .iter()
+ .flat_map(|x| x.split_ascii_whitespace())
+ .map(ToOwned::to_owned)
+ .collect()
}
-pub fn parse() -> Options {
- let matches = build().get_matches();
+fn try_parse_from(
+ args: impl IntoIterator<Item = impl Into<OsString> + Clone>,
+) -> clap::Result<Options> {
+ let CargoCommand::Outdated(mut opts) = Cargo::try_parse_from(args)?.command;
- Options::from(matches.subcommand_matches("outdated").unwrap())
+ opts.exclude = split_elem_by_ascii_whitespace(&opts.exclude);
+ opts.features = split_elem_by_ascii_whitespace(&opts.features);
+ opts.ignore = split_elem_by_ascii_whitespace(&opts.ignore);
+ opts.packages = split_elem_by_ascii_whitespace(&opts.packages);
+
+ if opts.root_deps_only {
+ opts.depth = Some(1);
+ }
+
+ if opts.workspace_only {
+ opts.depth = Some(1);
+ opts.root_deps_only = true;
+ }
+
+ Ok(opts)
}
#[cfg(test)]
mod test {
use super::*;
- fn options(args: &[&str]) -> Options {
- let mut argv = vec!["cargo", "outdated"];
- argv.extend(args);
- let m = build().get_matches_from(argv);
- Options::from(m.subcommand_matches("outdated").unwrap())
- }
+ use pretty_assertions::assert_eq;
- fn options_fail(args: &[&str]) -> clap::Result<ArgMatches<'static>> {
+ fn options(args: &[&str]) -> Options { options_fail(args).unwrap() }
+
+ fn options_fail(args: &[&str]) -> clap::Result<Options> {
let mut argv = vec!["cargo", "outdated"];
argv.extend(args);
- build().get_matches_from_safe(argv)
+ try_parse_from(argv)
}
#[test]
@@ -348,10 +232,8 @@
let res = options_fail(&["--features", "one", "two"]);
assert!(res.is_err());
assert_eq!(
- res.as_ref().unwrap_err().kind,
+ res.as_ref().unwrap_err().kind(),
clap::ErrorKind::UnknownArgument,
- "{:?}",
- res.as_ref().unwrap_err().kind
);
}
@@ -379,10 +261,8 @@
let res = options_fail(&["--exclude", "one", "two"]);
assert!(res.is_err());
assert_eq!(
- res.as_ref().unwrap_err().kind,
+ res.as_ref().unwrap_err().kind(),
clap::ErrorKind::UnknownArgument,
- "{:?}",
- res.as_ref().unwrap_err().kind
);
}
@@ -410,10 +290,8 @@
let res = options_fail(&["--ignore", "one", "two"]);
assert!(res.is_err());
assert_eq!(
- res.as_ref().unwrap_err().kind,
+ res.as_ref().unwrap_err().kind(),
clap::ErrorKind::UnknownArgument,
- "{:?}",
- res.as_ref().unwrap_err().kind
);
}
@@ -448,10 +326,8 @@
let res = options_fail(&["--packages", "one", "two"]);
assert!(res.is_err());
assert_eq!(
- res.as_ref().unwrap_err().kind,
+ res.as_ref().unwrap_err().kind(),
clap::ErrorKind::UnknownArgument,
- "{:?}",
- res.as_ref().unwrap_err().kind
);
}
@@ -471,10 +347,8 @@
let res = options_fail(&["--format", "foobar"]);
assert!(res.is_err());
assert_eq!(
- res.as_ref().unwrap_err().kind,
+ res.as_ref().unwrap_err().kind(),
clap::ErrorKind::InvalidValue,
- "{:?}",
- res.as_ref().unwrap_err().kind
);
}
@@ -494,10 +368,8 @@
let res = options_fail(&["--color", "foobar"]);
assert!(res.is_err());
assert_eq!(
- res.as_ref().unwrap_err().kind,
+ res.as_ref().unwrap_err().kind(),
clap::ErrorKind::InvalidValue,
- "{:?}",
- res.as_ref().unwrap_err().kind
);
}
}