Suggest toolchain (channel) fix on bad toolchain name
In order to improve the situation when a user tries to install
a toolchain with a name we don't recognise, if possible, suggest
a better name the user might try.
Fixes
@@ -8,7 +8,10 @@ use std::path::PathBuf;
use clap::Shell;
use error_chain::error_chain;
+use lazy_static::lazy_static;
+use regex::Regex;
use rustup::dist::temp;
+use strsim::damerau_levenshtein;
error_chain! {
links {
@@ -31,7 +34,7 @@ error_chain! {
}
InvalidToolchainName(t: String) {
description("invalid toolchain name")
- display("invalid toolchain name: '{}'", t)
+ display("invalid toolchain name: '{}'{}", t, maybe_suggest_toolchain(t))
}
InfiniteRecursion {
description("infinite recursion detected")
@@ -65,3 +68,40 @@ error_chain! {
}
}
}
+
+fn maybe_suggest_toolchain(bad_name: &str) -> String {
+ let bad_name = &bad_name.to_ascii_lowercase();
+ static VALID_CHANNELS: &[&str] = &["stable", "beta", "nightly"];
+ lazy_static! {
+ static ref NUMBERED: Regex = Regex::new(r"^\d+\.\d+$").unwrap();
+ }
+
+ if NUMBERED.is_match(bad_name) {
+ return format!(
+ ". Toolchain numbers tend to have three parts, e.g. {}.0",
+ bad_name
+ );
+ }
+
+ // Suggest only for very small differences
+ // High number can result in inaccurate suggestions for short queries e.g. `rls`
+ const MAX_DISTANCE: usize = 3;
+
+ let mut scored: Vec<_> = VALID_CHANNELS
+ .iter()
+ .filter_map(|s| {
+ let distance = damerau_levenshtein(bad_name, s);
+ if distance <= MAX_DISTANCE {
+ Some((distance, s))
+ } else {
+ None
+ }
+ })
+ .collect();
+ scored.sort();
+ if scored.is_empty() {
+ String::new()
+ } else {
+ format!(". Did you mean '{}'?", scored[0].1)
+ }
+}
