Skip to content

Commit 02c0813

Browse files
committed
feat: improve error message for rustup which
Also adds more tests for `rustup which`. Includes: - ask binary included by uninstalled component - ask binary included by uninstalled component with non-default toolchain - ask binary not known to be included by what toolchain
1 parent baf16d6 commit 02c0813

File tree

2 files changed

+86
-7
lines changed

2 files changed

+86
-7
lines changed

src/cli/rustup_mode.rs

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ use itertools::Itertools;
1919
use tracing::{info, trace, warn};
2020
use tracing_subscriber::{EnvFilter, Registry, reload::Handle};
2121

22-
use crate::dist::AutoInstallMode;
2322
use crate::{
2423
cli::{
2524
common::{self, PackageUpdate, update_console_filter},
@@ -28,10 +27,10 @@ use crate::{
2827
self_update::{self, SelfUpdateMode, check_rustup_update},
2928
topical_doc,
3029
},
31-
command,
30+
command, component_for_bin,
3231
config::{ActiveReason, Cfg},
3332
dist::{
34-
PartialToolchainDesc, Profile, TargetTriple,
33+
AutoInstallMode, PartialToolchainDesc, Profile, TargetTriple,
3534
manifest::{Component, ComponentStatus},
3635
},
3736
errors::RustupError,
@@ -1034,12 +1033,28 @@ async fn which(
10341033
binary: &str,
10351034
toolchain: Option<ResolvableToolchainName>,
10361035
) -> Result<utils::ExitCode> {
1037-
let binary_path = cfg.resolve_toolchain(toolchain).await?.binary_file(binary);
1036+
let toolchain = cfg.resolve_toolchain(toolchain).await?;
1037+
let binary_path = toolchain.binary_file(binary);
1038+
if utils::is_file(&binary_path) {
1039+
writeln!(cfg.process.stdout().lock(), "{}", binary_path.display())?;
1040+
return Ok(utils::ExitCode(0));
1041+
}
1042+
1043+
let toolchain_name = toolchain.name();
1044+
let Some(component_name) = component_for_bin(binary) else {
1045+
return Err(anyhow!(
1046+
"unknown binary '{binary}' in toolchain '{toolchain_name}'",
1047+
));
1048+
};
10381049

1039-
utils::assert_is_file(&binary_path)?;
1050+
let selector = match cfg.active_toolchain() {
1051+
Ok(Some((t, _))) if &t == toolchain_name => String::new(),
1052+
_ => format!("--toolchain {} ", toolchain.name()),
1053+
};
10401054

1041-
writeln!(cfg.process.stdout().lock(), "{}", binary_path.display())?;
1042-
Ok(utils::ExitCode(0))
1055+
Err(anyhow!(
1056+
"'{binary}' is not installed for the toolchain '{toolchain_name}'.\nTo install, run `rustup component add {selector}{component_name}`"
1057+
))
10431058
}
10441059

10451060
#[tracing::instrument(level = "trace", skip_all)]

tests/suite/cli_misc.rs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1384,6 +1384,70 @@ async fn which_asking_uninstalled_toolchain() {
13841384
.is_ok();
13851385
}
13861386

1387+
#[tokio::test]
1388+
async fn which_asking_uninstalled_components() {
1389+
let cx = CliTestContext::new(Scenario::SimpleV2).await;
1390+
1391+
let path_1 = cx.config.customdir.join("custom-1");
1392+
let path_1 = path_1.to_string_lossy();
1393+
cx.config
1394+
.expect(["rustup", "toolchain", "link", "custom-1", &path_1])
1395+
.await
1396+
.is_ok();
1397+
cx.config
1398+
.expect(["rustup", "default", "custom-1"])
1399+
.await
1400+
.is_ok();
1401+
cx.config
1402+
.expect(["rustup", "which", "rustfmt"])
1403+
.await
1404+
.with_stderr(snapbox::str![[r#"
1405+
error: 'rustfmt' is not installed for the toolchain 'custom-1'.
1406+
[..]`rustup component add rustfmt`
1407+
1408+
"#]])
1409+
.is_err();
1410+
1411+
let path_2 = cx.config.customdir.join("custom-2");
1412+
let path_2 = path_2.to_string_lossy();
1413+
cx.config
1414+
.expect(["rustup", "toolchain", "link", "custom-2", &path_2])
1415+
.await
1416+
.is_ok();
1417+
cx.config
1418+
.expect(["rustup", "which", "--toolchain=custom-2", "rustfmt"])
1419+
.await
1420+
.with_stderr(snapbox::str![[r#"
1421+
[..]'rustfmt' is not installed for the toolchain 'custom-2'.
1422+
[..]`rustup component add --toolchain custom-2 rustfmt`
1423+
1424+
"#]])
1425+
.is_err();
1426+
}
1427+
1428+
#[tokio::test]
1429+
async fn which_unknown_binary() {
1430+
let cx = CliTestContext::new(Scenario::SimpleV2).await;
1431+
let path_1 = cx.config.customdir.join("custom-1");
1432+
let path_1 = path_1.to_string_lossy();
1433+
cx.config
1434+
.expect(["rustup", "toolchain", "link", "custom-1", &path_1])
1435+
.await
1436+
.is_ok();
1437+
cx.config
1438+
.expect(["rustup", "default", "custom-1"])
1439+
.await
1440+
.is_ok();
1441+
cx.config
1442+
.expect(["rustup", "which", "ls"])
1443+
.await
1444+
.with_stderr(snapbox::str![[r#"
1445+
[..]unknown binary 'ls' in toolchain 'custom-1'
1446+
1447+
"#]])
1448+
.is_err();
1449+
}
1450+
13871451
#[tokio::test]
13881452
async fn override_by_toolchain_on_the_command_line() {
13891453
let cx = CliTestContext::new(Scenario::SimpleV2).await;

0 commit comments

Comments
 (0)