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 <andytom@users.noreply.github.com>

Co-authored-by: Thomas O'Donnell <andytom@users.noreply.github.com>
This commit is contained in:
David Knaack 2021-04-06 22:12:37 +02:00 committed by GitHub
parent a2cdc912e7
commit 8af677c811
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 63 additions and 4 deletions

11
Cargo.lock generated
View File

@ -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"

View File

@ -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"] }

View File

@ -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);
}
}
}
});

View File

@ -1,6 +1,6 @@
[package]
name = "starship_module_config_derive"
version = "0.2.0"
version = "0.2.1"
edition = "2018"
authors = ["Matan Kushner <hello@matchai.me>"]
homepage = "https://starship.rs"

View File

@ -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);
}
},
}
});