diff --git a/src/cli.rs b/src/cli.rs index 650c5b75..80d869f3 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,5 +1,5 @@ use anyhow::{bail, format_err, Error}; -use std::{env, fmt, mem, str::FromStr}; +use std::{env, fmt, iter::Peekable, mem, str::FromStr}; use termcolor::ColorChoice; use crate::{ProcessBuilder, Result}; @@ -173,18 +173,18 @@ Some common cargo commands are (see all commands with --list): } } -pub(crate) struct Args { - pub(crate) leading_args: Vec, - pub(crate) trailing_args: Vec, +pub(crate) struct Args<'a> { + pub(crate) leading_args: Vec<&'a str>, + pub(crate) trailing_args: Vec<&'a str>, - pub(crate) subcommand: Option, + pub(crate) subcommand: Option<&'a str>, /// --manifest-path - pub(crate) manifest_path: Option, + pub(crate) manifest_path: Option<&'a str>, /// -p, --package ... - pub(crate) package: Vec, + pub(crate) package: Vec<&'a str>, /// --exclude ... - pub(crate) exclude: Vec, + pub(crate) exclude: Vec<&'a str>, /// --workspace, (--all) pub(crate) workspace: bool, /// --each-feature @@ -200,13 +200,13 @@ pub(crate) struct Args { /// --ignore-unknown-features pub(crate) ignore_unknown_features: bool, /// --optional-deps [DEPS]... - pub(crate) optional_deps: Option>, + pub(crate) optional_deps: Option>, /// --clean-per-run pub(crate) clean_per_run: bool, /// --depth pub(crate) depth: Option, /// --include-features - pub(crate) include_features: Vec, + pub(crate) include_features: Vec<&'a str>, /// --no-default-features pub(crate) no_default_features: bool, @@ -216,7 +216,7 @@ pub(crate) struct Args { // Note: These values are not always exactly the same as the input. // Error messages should not assume that these options have been specified. /// --exclude-features ..., --skip ... - pub(crate) exclude_features: Vec, + pub(crate) exclude_features: Vec<&'a str>, /// --exclude-no-default-features, (--skip-no-default-features) pub(crate) exclude_no_default_features: bool, /// --exclude-all-features @@ -224,7 +224,7 @@ pub(crate) struct Args { // flags that will be propagated to cargo /// --features ... - pub(crate) features: Vec, + pub(crate) features: Vec<&'a str>, /// --color pub(crate) color: Option, } @@ -267,10 +267,28 @@ impl FromStr for Coloring { } } -pub(crate) fn args(coloring: &mut Option) -> Result> { - let mut args = env::args().peekable(); - let _ = args.next(); // executable name - match &args.next() { +pub(crate) struct RawArgs { + inner: Vec, +} + +impl RawArgs { + pub(crate) fn new() -> Self { + let mut args = env::args(); + let _ = args.next(); // executable name + Self { inner: args.collect() } + } + + fn iter(&self) -> Peekable + '_> { + self.inner.iter().map(String::as_str).peekable() + } +} + +pub(crate) fn args<'a>( + args: &'a RawArgs, + coloring: &mut Option, +) -> Result>> { + let mut args = args.iter(); + match args.next() { Some(a) if a == "hack" => {} Some(_) | None => { println!("{}", Help::new(false)); @@ -279,7 +297,7 @@ pub(crate) fn args(coloring: &mut Option) -> Result> { } let mut leading = Vec::new(); - let mut subcommand: Option = None; + let mut subcommand: Option<&'a str> = None; let mut manifest_path = None; let mut color = None; @@ -323,7 +341,7 @@ pub(crate) fn args(coloring: &mut Option) -> Result> { } if !arg.starts_with('-') { - subcommand.get_or_insert_with(|| arg.clone()); + subcommand.get_or_insert(arg); leading.push(arg); continue; } @@ -332,21 +350,18 @@ pub(crate) fn args(coloring: &mut Option) -> Result> { ($opt:ident, $pat:expr, $help:expr) => { if arg == $pat { if $opt.is_some() { - return Err(multi_arg($help, subcommand.as_ref())); + return Err(multi_arg($help, subcommand)); } - let next = - args.next().ok_or_else(|| req_arg($help, subcommand.as_ref()))?; + let next = args.next().ok_or_else(|| req_arg($help, subcommand))?; $opt = Some(next); continue; } else if arg.starts_with(concat!($pat, "=")) { if $opt.is_some() { - return Err(multi_arg($help, subcommand.as_ref())); + return Err(multi_arg($help, subcommand)); } - let next = arg - .splitn(2, '=') - .nth(1) - .ok_or_else(|| req_arg($help, subcommand.as_ref()))?; - $opt = Some(next.to_string()); + let next = + arg.splitn(2, '=').nth(1).ok_or_else(|| req_arg($help, subcommand))?; + $opt = Some(next); continue; } }; @@ -358,22 +373,20 @@ pub(crate) fn args(coloring: &mut Option) -> Result> { if !$require_value && args.peek().map_or(true, |s| s.starts_with('-')) { continue; } - let arg = args.next().ok_or_else(|| req_arg($help, subcommand.as_ref()))?; + let arg = args.next().ok_or_else(|| req_arg($help, subcommand))?; if $allow_split { if arg.contains(',') { - $v.extend(arg.split(',').map(ToString::to_string)); + $v.extend(arg.split(',')); } else { - $v.extend(arg.split(' ').map(ToString::to_string)); + $v.extend(arg.split(' ')); } } else { $v.push(arg); } continue; } else if arg.starts_with(concat!($pat, "=")) { - let mut arg = arg - .splitn(2, '=') - .nth(1) - .ok_or_else(|| req_arg($help, subcommand.as_ref()))?; + let mut arg = + arg.splitn(2, '=').nth(1).ok_or_else(|| req_arg($help, subcommand))?; if $allow_split { if arg.starts_with('\'') && arg.ends_with('\'') || arg.starts_with('"') && arg.ends_with('"') @@ -381,12 +394,12 @@ pub(crate) fn args(coloring: &mut Option) -> Result> { arg = &arg[1..arg.len() - 1]; } if arg.contains(',') { - $v.extend(arg.split(',').map(ToString::to_string)); + $v.extend(arg.split(',')); } else { - $v.extend(arg.split(' ').map(ToString::to_string)); + $v.extend(arg.split(' ')); } } else { - $v.push(arg.to_string()); + $v.push(arg); } continue; } @@ -396,7 +409,7 @@ pub(crate) fn args(coloring: &mut Option) -> Result> { macro_rules! parse_flag { ($flag:ident) => { if mem::replace(&mut $flag, true) { - return Err(multi_arg(&arg, subcommand.as_ref())); + return Err(multi_arg(&arg, subcommand)); } else { continue; } @@ -429,7 +442,7 @@ pub(crate) fn args(coloring: &mut Option) -> Result> { if arg.starts_with("--optional-deps") { if optional_deps.is_some() { - return Err(multi_arg(&arg, subcommand.as_ref())); + return Err(multi_arg(arg, subcommand)); } let optional_deps = optional_deps.get_or_insert_with(Vec::new); parse_multi_opt!( @@ -444,7 +457,7 @@ pub(crate) fn args(coloring: &mut Option) -> Result> { match &*arg { "--workspace" | "--all" => { if let Some(arg) = workspace.replace(arg) { - return Err(multi_arg(&arg, subcommand.as_ref())); + return Err(multi_arg(arg, subcommand)); } continue; } @@ -455,17 +468,14 @@ pub(crate) fn args(coloring: &mut Option) -> Result> { "--ignore-private" => parse_flag!(ignore_private), "--exclude-no-default-features" => { if exclude_no_default_features || skip_no_default_features { - return Err(multi_arg(&arg, subcommand.as_ref())); + return Err(multi_arg(arg, subcommand)); } exclude_no_default_features = true; continue; } "--skip-no-default-features" => { if exclude_no_default_features || skip_no_default_features { - return Err(multi_arg( - "--exclude-no-default-features", - subcommand.as_ref(), - )); + return Err(multi_arg("--exclude-no-default-features", subcommand)); } skip_no_default_features = true; continue; @@ -497,15 +507,13 @@ pub(crate) fn args(coloring: &mut Option) -> Result> { res?; - if leading.is_empty() && !remove_dev_deps - || subcommand.is_none() && leading.iter().any(|a| a == "-h") - { + if leading.is_empty() && !remove_dev_deps || subcommand.is_none() && leading.contains(&"-h") { println!("{}", Help::new(false)); return Ok(None); - } else if subcommand.is_none() && leading.iter().any(|a| a == "--help") { + } else if subcommand.is_none() && leading.contains(&"--help") { println!("{}", Help::new(true)); return Ok(None); - } else if leading.iter().any(|a| a == "--version" || a == "-V" || a == "-vV" || a == "-Vv") { + } else if leading.iter().any(|&a| a == "--version" || a == "-V" || a == "-vV" || a == "-Vv") { print_version(); return Ok(None); } @@ -548,7 +556,7 @@ pub(crate) fn args(coloring: &mut Option) -> Result> { } let depth = depth.map(|s| s.parse::()).transpose()?; - if let Some(subcommand) = &subcommand { + if let Some(subcommand) = subcommand { if subcommand == "test" || subcommand == "bench" { if remove_dev_deps { bail!("--remove-dev-deps may not be used together with {} subcommand", subcommand); @@ -583,7 +591,7 @@ pub(crate) fn args(coloring: &mut Option) -> Result> { } if subcommand.is_none() { - if leading.iter().any(|a| a == "--list") { + if leading.contains(&"--list") { let mut line = ProcessBuilder::new(crate::cargo_binary(), verbose); line.arg("--list"); line.exec()?; @@ -653,7 +661,7 @@ For more information try --help })) } -fn req_arg(arg: &str, subcommand: Option<&String>) -> Error { +fn req_arg(arg: &str, subcommand: Option<&str>) -> Error { format_err!( "\ The argument '{0}' requires a value but none was supplied @@ -672,7 +680,7 @@ For more information try --help ) } -fn multi_arg(arg: &str, subcommand: Option<&String>) -> Error { +fn multi_arg(arg: &str, subcommand: Option<&str>) -> Error { format_err!( "\ The argument '{0}' was provided more than once, but cannot be used multiple times diff --git a/src/main.rs b/src/main.rs index da2dab8e..098ae5bd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -42,7 +42,8 @@ fn main() { } fn try_main(coloring: &mut Option) -> Result<()> { - let args = cli::args(coloring)?.unwrap_or_else(|| std::process::exit(0)); + let args = cli::RawArgs::new(); + let args = cli::args(&args, coloring)?.unwrap_or_else(|| std::process::exit(0)); let metadata = Metadata::new(&args)?; let current_manifest = match &args.manifest_path { @@ -61,7 +62,11 @@ fn try_main(coloring: &mut Option) -> Result<()> { exec_on_workspace(&args, ¤t_manifest, &metadata) } -fn exec_on_workspace(args: &Args, current_manifest: &Manifest, metadata: &Metadata) -> Result<()> { +fn exec_on_workspace( + args: &Args<'_>, + current_manifest: &Manifest, + metadata: &Metadata, +) -> Result<()> { assert!( args.subcommand.is_some() || args.remove_dev_deps, "no subcommand or valid flag specified" @@ -89,7 +94,7 @@ fn exec_on_workspace(args: &Args, current_manifest: &Manifest, metadata: &Metada }); let packages = - metadata.packages.iter().filter(|package| !args.exclude.contains(&package.name)); + metadata.packages.iter().filter(|package| !args.exclude.contains(&&*package.name)); Package::from_iter(args, &mut total, packages)? } else if !args.package.is_empty() { if let Some(spec) = args @@ -101,7 +106,7 @@ fn exec_on_workspace(args: &Args, current_manifest: &Manifest, metadata: &Metada } let packages = - metadata.packages.iter().filter(|package| args.package.contains(&package.name)); + metadata.packages.iter().filter(|package| args.package.contains(&&*package.name)); Package::from_iter(args, &mut total, packages)? } else if current_manifest.is_virtual() { Package::from_iter(args, &mut total, &metadata.packages)? @@ -123,7 +128,7 @@ struct Info { } fn exec_on_package( - args: &Args, + args: &Args<'_>, package: &Package<'_>, line: &ProcessBuilder<'_>, restore: &Restore, @@ -144,7 +149,7 @@ fn exec_on_package( } fn no_dev_deps( - args: &Args, + args: &Args<'_>, package: &Package<'_>, line: &mut ProcessBuilder<'_>, restore: &Restore, diff --git a/src/metadata.rs b/src/metadata.rs index 1010dc92..83390fe1 100644 --- a/src/metadata.rs +++ b/src/metadata.rs @@ -25,7 +25,7 @@ pub(crate) struct Metadata { } impl Metadata { - pub(crate) fn new(args: &Args) -> Result { + pub(crate) fn new(args: &Args<'_>) -> Result { let cargo = env::var_os("CARGO").unwrap_or_else(|| OsString::from("cargo")); let mut command = Command::new(cargo); command.args(&["metadata", "--no-deps", "--format-version=1"]); @@ -91,7 +91,7 @@ impl Package { Some(Self { name, dependencies, features, manifest_path }) } - pub(crate) fn name_verbose(&self, args: &Args) -> Cow<'_, str> { + pub(crate) fn name_verbose(&self, args: &Args<'_>) -> Cow<'_, str> { if args.verbose { Cow::Owned(format!( "{} ({})", @@ -128,7 +128,7 @@ impl Dependency { Some(Self { name, optional, rename }) } - pub(crate) fn as_feature(&self) -> Option<&String> { + pub(crate) fn as_feature(&self) -> Option<&str> { if self.optional { Some(self.rename.as_ref().unwrap_or(&self.name)) } else { None } } } diff --git a/src/package.rs b/src/package.rs index 801d6d71..d067629f 100644 --- a/src/package.rs +++ b/src/package.rs @@ -12,7 +12,7 @@ pub(crate) struct Package<'a> { } impl<'a> Package<'a> { - fn new(args: &'a Args, total: &mut usize, package: &'a metadata::Package) -> Result { + fn new(args: &'a Args<'_>, total: &mut usize, package: &'a metadata::Package) -> Result { let manifest = Manifest::new(&package.manifest_path)?; if args.ignore_private && manifest.is_private() { @@ -23,7 +23,7 @@ impl<'a> Package<'a> { } pub(crate) fn from_iter( - args: &'a Args, + args: &'a Args<'_>, total: &mut usize, packages: impl IntoIterator, ) -> Result> { @@ -47,12 +47,12 @@ pub(crate) enum Kind<'a> { NoSubcommand, SkipAsPrivate, Nomal { show_progress: bool }, - Each { features: Vec<&'a String> }, - Powerset { features: Vec> }, + Each { features: Vec<&'a str> }, + Powerset { features: Vec> }, } impl<'a> Kind<'a> { - fn determine(args: &'a Args, package: &'a metadata::Package, total: &mut usize) -> Self { + fn determine(args: &'a Args<'_>, package: &'a metadata::Package, total: &mut usize) -> Self { if args.subcommand.is_none() { return Kind::NoSubcommand; } @@ -66,14 +66,15 @@ impl<'a> Kind<'a> { let mut features: Vec<_> = package .features .keys() - .filter(|f| *f != "default" && !args.exclude_features.contains(f)) + .filter(|&f| f != "default" && !args.exclude_features.contains(&&**f)) + .map(String::as_str) .collect(); if let Some(opt_deps) = &args.optional_deps { features.extend( package.dependencies.iter().filter_map(Dependency::as_feature).filter( - move |f| { - !args.exclude_features.contains(f) - && (opt_deps.is_empty() || opt_deps.contains(f)) + move |&f| { + !args.exclude_features.contains(&f) + && (opt_deps.is_empty() || opt_deps.contains(&f)) }, ), ); @@ -82,7 +83,8 @@ impl<'a> Kind<'a> { } else { args.include_features .iter() - .filter(|f| *f != "default" && !args.exclude_features.contains(f)) + .filter(|&&f| f != "default" && !args.exclude_features.contains(&f)) + .copied() .collect() }; @@ -94,7 +96,7 @@ impl<'a> Kind<'a> { Kind::Nomal { show_progress: true } } else { *total += features.len() - + (!args.exclude_features.iter().any(|x| x == "default")) as usize + + (!args.exclude_features.contains(&"default")) as usize + (!args.exclude_no_default_features) as usize + (!args.exclude_all_features) as usize; Kind::Each { features } @@ -110,7 +112,7 @@ impl<'a> Kind<'a> { } else { // -1: the first element of a powerset is `[]` *total += features.len() - 1 - + (!args.exclude_features.iter().any(|x| x == "default")) as usize + + (!args.exclude_features.contains(&"default")) as usize + (!args.exclude_no_default_features) as usize + (!args.exclude_all_features) as usize; Kind::Powerset { features } @@ -122,7 +124,7 @@ impl<'a> Kind<'a> { } pub(crate) fn exec( - args: &Args, + args: &Args<'_>, package: &Package<'_>, line: &mut ProcessBuilder<'_>, info: &mut Info, @@ -139,7 +141,7 @@ pub(crate) fn exec( let mut line = line.clone(); - if !args.exclude_features.iter().any(|x| x == "default") { + if !args.exclude_features.contains(&"default") { // run with default features exec_cargo(args, package, &mut line, info, true)?; } @@ -182,7 +184,7 @@ pub(crate) fn exec( } fn exec_cargo_with_features( - args: &Args, + args: &Args<'_>, package: &Package<'_>, line: &ProcessBuilder<'_>, info: &mut Info, @@ -194,7 +196,7 @@ fn exec_cargo_with_features( } fn exec_cargo( - args: &Args, + args: &Args<'_>, package: &Package<'_>, line: &mut ProcessBuilder<'_>, info: &mut Info, @@ -221,7 +223,7 @@ fn exec_cargo( line.exec() } -fn cargo_clean(cargo: &OsStr, args: &Args, package: &Package<'_>) -> Result<()> { +fn cargo_clean(cargo: &OsStr, args: &Args<'_>, package: &Package<'_>) -> Result<()> { let mut line = ProcessBuilder::new(cargo, args.verbose); line.arg("clean"); line.arg("--package"); diff --git a/src/process.rs b/src/process.rs index 2dfbc11d..400dbd11 100644 --- a/src/process.rs +++ b/src/process.rs @@ -19,9 +19,9 @@ pub(crate) struct ProcessBuilder<'a> { /// The program to execute. program: Rc, /// A list of arguments to pass to the program (until '--'). - leading_args: &'a [String], + leading_args: &'a [&'a str], /// A list of arguments to pass to the program (after '--'). - trailing_args: &'a [String], + trailing_args: &'a [&'a str], /// A list of arguments to pass to the program (between `leading_args` and '--'). args: Vec, @@ -48,7 +48,7 @@ impl<'a> ProcessBuilder<'a> { } /// Creates a new `ProcessBuilder` from `Args`. - pub(crate) fn from_args(program: impl Into>, args: &'a Args) -> Self { + pub(crate) fn from_args(program: impl Into>, args: &'a Args<'a>) -> Self { Self { program: program.into(), leading_args: &args.leading_args, @@ -66,11 +66,11 @@ impl<'a> ProcessBuilder<'a> { } } - pub(crate) fn append_features_from_args(&mut self, args: &Args, package: &Package) { + pub(crate) fn append_features_from_args(&mut self, args: &Args<'_>, package: &Package) { if args.ignore_unknown_features { - self.append_features(args.features.iter().filter(|f| { - if package.features.get(*f).is_some() - || package.dependencies.iter().any(|dep| dep.as_feature() == Some(*f)) + self.append_features(args.features.iter().filter(|&&f| { + if package.features.get(f).is_some() + || package.dependencies.iter().any(|dep| dep.as_feature() == Some(f)) { true } else { diff --git a/src/restore.rs b/src/restore.rs index d3705427..9b124a2c 100644 --- a/src/restore.rs +++ b/src/restore.rs @@ -27,7 +27,7 @@ struct Current { } impl Restore { - pub(crate) fn new(args: &Args) -> Self { + pub(crate) fn new(args: &Args<'_>) -> Self { let this = Self { color: args.color, // if `--remove-dev-deps` flag is off, restore manifest file.