Skip to content

Commit 28b7c84

Browse files
committed
refactor(install): Shift crate argument parsing to clap
1 parent 0c51462 commit 28b7c84

File tree

3 files changed

+54
-30
lines changed

3 files changed

+54
-30
lines changed

src/bin/cargo/commands/install.rs

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,7 @@ use cargo_util::paths;
1010
pub fn cli() -> Command {
1111
subcommand("install")
1212
.about("Install a Rust binary. Default location is $HOME/.cargo/bin")
13-
.arg(
14-
Arg::new("crate")
15-
.value_parser(clap::builder::NonEmptyStringValueParser::new())
16-
.num_args(0..),
17-
)
13+
.arg(Arg::new("crate").value_parser(parse_crate).num_args(0..))
1814
.arg(
1915
opt("version", "Specify a version to install")
2016
.alias("vers")
@@ -104,9 +100,10 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
104100

105101
let version = args.get_one::<String>("version").map(String::as_str);
106102
let krates = args
107-
.get_many::<String>("crate")
103+
.get_many::<CrateVersion>("crate")
108104
.unwrap_or_default()
109-
.map(|k| resolve_crate(k, version))
105+
.cloned()
106+
.map(|(krate, local_version)| resolve_crate(krate, local_version, version))
110107
.collect::<crate::CargoResult<Vec<_>>>()?;
111108

112109
for (crate_name, _) in krates.iter() {
@@ -190,20 +187,42 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
190187
Ok(())
191188
}
192189

193-
fn resolve_crate<'k>(
194-
mut krate: &'k str,
195-
mut version: Option<&'k str>,
196-
) -> crate::CargoResult<(&'k str, Option<&'k str>)> {
197-
if let Some((k, v)) = krate.split_once('@') {
198-
if version.is_some() {
199-
anyhow::bail!("cannot specify both `@{v}` and `--version`");
200-
}
190+
type CrateVersion = (String, Option<String>);
191+
192+
fn parse_crate(krate: &str) -> crate::CargoResult<CrateVersion> {
193+
let (krate, version) = if let Some((k, v)) = krate.split_once('@') {
201194
if k.is_empty() {
202195
// by convention, arguments starting with `@` are response files
203-
anyhow::bail!("missing crate name for `@{v}`");
196+
anyhow::bail!("missing crate name before '@'");
204197
}
205-
krate = k;
206-
version = Some(v);
198+
let krate = k.to_owned();
199+
let version = Some(v.to_owned());
200+
(krate, version)
201+
} else {
202+
let krate = krate.to_owned();
203+
let version = None;
204+
(krate, version)
205+
};
206+
207+
if krate.is_empty() {
208+
anyhow::bail!("crate name is empty");
207209
}
210+
211+
Ok((krate, version))
212+
}
213+
214+
fn resolve_crate(
215+
krate: String,
216+
local_version: Option<String>,
217+
version: Option<&str>,
218+
) -> crate::CargoResult<CrateVersion> {
219+
let version = match (local_version, version) {
220+
(Some(l), Some(g)) => {
221+
anyhow::bail!("cannot specify both `@{l}` and `--version {g}`");
222+
}
223+
(Some(l), None) => Some(l),
224+
(None, Some(g)) => Some(g.to_owned()),
225+
(None, None) => None,
226+
};
208227
Ok((krate, version))
209228
}

src/cargo/ops/cargo_install.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -604,7 +604,7 @@ Consider enabling some of the needed features by passing, e.g., `--features=\"{e
604604
pub fn install(
605605
config: &Config,
606606
root: Option<&str>,
607-
krates: Vec<(&str, Option<&str>)>,
607+
krates: Vec<(String, Option<String>)>,
608608
source_id: SourceId,
609609
from_cwd: bool,
610610
opts: &ops::CompileOptions,
@@ -617,9 +617,9 @@ pub fn install(
617617

618618
let (installed_anything, scheduled_error) = if krates.len() <= 1 {
619619
let (krate, vers) = krates
620-
.into_iter()
620+
.iter()
621621
.next()
622-
.map(|(k, v)| (Some(k), v))
622+
.map(|(k, v)| (Some(k.as_str()), v.as_deref()))
623623
.unwrap_or((None, None));
624624
let installable_pkg = InstallablePackage::new(
625625
config, root, map, krate, source_id, from_cwd, vers, opts, force, no_track, true,
@@ -637,18 +637,18 @@ pub fn install(
637637
let mut did_update = false;
638638

639639
let pkgs_to_install: Vec<_> = krates
640-
.into_iter()
640+
.iter()
641641
.filter_map(|(krate, vers)| {
642642
let root = root.clone();
643643
let map = map.clone();
644644
match InstallablePackage::new(
645645
config,
646646
root,
647647
map,
648-
Some(krate),
648+
Some(krate.as_str()),
649649
source_id,
650650
from_cwd,
651-
vers,
651+
vers.as_deref(),
652652
opts,
653653
force,
654654
no_track,
@@ -660,12 +660,12 @@ pub fn install(
660660
}
661661
Ok(None) => {
662662
// Already installed
663-
succeeded.push(krate);
663+
succeeded.push(krate.as_str());
664664
None
665665
}
666666
Err(e) => {
667667
crate::display_error(&e, &mut config.shell());
668-
failed.push(krate);
668+
failed.push(krate.as_str());
669669
// We assume an update was performed if we got an error.
670670
did_update = true;
671671
None

tests/testsuite/install.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1600,8 +1600,13 @@ fn inline_version_without_name() {
16001600
pkg("foo", "0.1.2");
16011601

16021602
cargo_process("install @0.1.1")
1603-
.with_status(101)
1604-
.with_stderr("error: missing crate name for `@0.1.1`")
1603+
.with_status(1)
1604+
.with_stderr(
1605+
"error: invalid value '@0.1.1' for '[crate]...': missing crate name before '@'
1606+
1607+
For more information, try '--help'.
1608+
",
1609+
)
16051610
.run();
16061611
}
16071612

@@ -1612,7 +1617,7 @@ fn inline_and_explicit_version() {
16121617

16131618
cargo_process("install foo@0.1.1 --version 0.1.1")
16141619
.with_status(101)
1615-
.with_stderr("error: cannot specify both `@0.1.1` and `--version`")
1620+
.with_stderr("error: cannot specify both `@0.1.1` and `--version 0.1.1`")
16161621
.run();
16171622
}
16181623

@@ -1827,7 +1832,7 @@ fn install_empty_argument() {
18271832
cargo_process("install")
18281833
.arg("")
18291834
.with_status(1)
1830-
.with_stderr_contains("[ERROR] a value is required for '[crate]...' but none was supplied")
1835+
.with_stderr_contains("[ERROR] invalid value '' for '[crate]...': crate name is empty")
18311836
.run();
18321837
}
18331838

0 commit comments

Comments
 (0)