From 8af677c8117beb52e0c419eff1ed5d0102c86ae3 Mon Sep 17 00:00:00 2001 From: David Knaack Date: Tue, 6 Apr 2021 22:12:37 +0200 Subject: [PATCH] feat(config): print a suggestion for unknown fields (#2560) * feat(config): print a suggestion for unknown fields * Fix typo Co-authored-by: Thomas O'Donnell Co-authored-by: Thomas O'Donnell --- Cargo.lock | 11 ++++++++-- Cargo.toml | 3 ++- src/configs/starship_root.rs | 26 ++++++++++++++++++++++++ starship_module_config_derive/Cargo.toml | 2 +- starship_module_config_derive/src/lib.rs | 25 +++++++++++++++++++++++ 5 files changed, 63 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ae11efb0..068b49c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -247,7 +247,7 @@ dependencies = [ "ansi_term 0.11.0", "atty", "bitflags 1.2.1", - "strsim", + "strsim 0.8.0", "textwrap", "unicode-width", "vec_map", @@ -1614,6 +1614,7 @@ dependencies = [ "shadow-rs", "shell-words", "starship_module_config_derive", + "strsim 0.10.0", "sys-info", "tempfile", "term_size", @@ -1628,7 +1629,7 @@ dependencies = [ [[package]] name = "starship_module_config_derive" -version = "0.2.0" +version = "0.2.1" dependencies = [ "proc-macro2", "quote 1.0.9", @@ -1641,6 +1642,12 @@ 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" + [[package]] name = "strum" version = "0.8.0" diff --git a/Cargo.toml b/Cargo.toml index 1b4cc19d..34303b50 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,7 +49,7 @@ once_cell = "1.7.2" chrono = "0.4.19" sys-info = "0.8.0" byte-unit = "4.0.10" -starship_module_config_derive = { version = "0.2.0", path = "starship_module_config_derive" } +starship_module_config_derive = { version = "0.2.1", path = "starship_module_config_derive" } yaml-rust = "0.4.5" pest = "2.1.3" pest_derive = "2.1.0" @@ -67,6 +67,7 @@ notify-rust = { version = "4.3.0", optional = true } semver = "0.11.0" which = "4.1.0" shadow-rs = "0.5.25" +strsim = "0.10.0" process_control = { version = "3.0.1", features = ["crossbeam-channel"] } diff --git a/src/configs/starship_root.rs b/src/configs/starship_root.rs index 1c96cacd..add0734b 100644 --- a/src/configs/starship_root.rs +++ b/src/configs/starship_root.rs @@ -1,6 +1,7 @@ use crate::{config::ModuleConfig, module::ALL_MODULES}; use serde::Serialize; +use std::cmp::Ordering; // On changes please also update the `FullConfig` struct in `mod.rs` #[derive(Clone, Serialize)] @@ -100,6 +101,31 @@ impl<'a> ModuleConfig<'a> for StarshipRootConfig<'a> { unknown => { if !ALL_MODULES.contains(&unknown) && unknown != "custom" { log::warn!("Unknown config key '{}'", unknown); + + let did_you_mean = &[ + // Root options + "format", + "scan_timeout", + "command_timeout", + "add_newline", + // Modules + "custom", + ] + .iter() + .chain(ALL_MODULES.iter()) + .filter_map(|field| { + let score = strsim::jaro_winkler(unknown, field); + (score > 0.8).then(|| (score, field)) + }) + .max_by( + |(score_a, _field_a), (score_b, _field_b)| { + score_a.partial_cmp(score_b).unwrap_or(Ordering::Equal) + }, + ); + + if let Some((_score, field)) = did_you_mean { + log::warn!("Did you mean '{}'?", field); + } } } }); diff --git a/starship_module_config_derive/Cargo.toml b/starship_module_config_derive/Cargo.toml index c798ef9b..928eef5f 100644 --- a/starship_module_config_derive/Cargo.toml +++ b/starship_module_config_derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "starship_module_config_derive" -version = "0.2.0" +version = "0.2.1" edition = "2018" authors = ["Matan Kushner "] homepage = "https://starship.rs" diff --git a/starship_module_config_derive/src/lib.rs b/starship_module_config_derive/src/lib.rs index 254b0a3c..40d4301b 100644 --- a/starship_module_config_derive/src/lib.rs +++ b/starship_module_config_derive/src/lib.rs @@ -18,6 +18,7 @@ fn impl_module_config(dinput: DeriveInput) -> proc_macro::TokenStream { if let syn::Data::Struct(data) = dinput.data { if let syn::Fields::Named(fields_named) = data.fields { let mut load_tokens = quote! {}; + let mut fields = quote! {}; for field in fields_named.named.iter() { let ident = field.ident.as_ref().unwrap(); @@ -26,10 +27,19 @@ fn impl_module_config(dinput: DeriveInput) -> proc_macro::TokenStream { stringify!(#ident) => self.#ident.load_config(v), }; + let new_field = quote! { + stringify!(#ident), + }; + load_tokens = quote! { #load_tokens #new_load_tokens }; + + fields = quote! { + #fields + #new_field + }; } load_config = quote! { @@ -40,6 +50,21 @@ fn impl_module_config(dinput: DeriveInput) -> proc_macro::TokenStream { #load_tokens unknown => { ::log::warn!("Unknown config key '{}'", unknown); + + let did_you_mean = ::std::array::IntoIter::new([#fields]) + .filter_map(|field| { + let score = ::strsim::jaro_winkler(unknown, field); + (score > 0.8).then(|| (score, field)) + }) + .max_by( + |(score_a, _field_a), (score_b, _field_b)| { + score_a.partial_cmp(score_b).unwrap_or(::std::cmp::Ordering::Equal) + }, + ); + + if let Some((_score, field)) = did_you_mean { + ::log::warn!("Did you mean '{}'?", field); + } }, } });