From cf94bdc0742c13e2a0cac864c478b8626b266e1b Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:11:38 +0200 Subject: Merging upstream version 1.66.0+dfsg1. Signed-off-by: Daniel Baumann --- vendor/clap-3.2.20/src/parser/features/mod.rs | 1 + .../clap-3.2.20/src/parser/features/suggestions.rs | 105 +++++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 vendor/clap-3.2.20/src/parser/features/mod.rs create mode 100644 vendor/clap-3.2.20/src/parser/features/suggestions.rs (limited to 'vendor/clap-3.2.20/src/parser/features') diff --git a/vendor/clap-3.2.20/src/parser/features/mod.rs b/vendor/clap-3.2.20/src/parser/features/mod.rs new file mode 100644 index 000000000..bdeb766ec --- /dev/null +++ b/vendor/clap-3.2.20/src/parser/features/mod.rs @@ -0,0 +1 @@ +pub(crate) mod suggestions; diff --git a/vendor/clap-3.2.20/src/parser/features/suggestions.rs b/vendor/clap-3.2.20/src/parser/features/suggestions.rs new file mode 100644 index 000000000..9e46f3c9e --- /dev/null +++ b/vendor/clap-3.2.20/src/parser/features/suggestions.rs @@ -0,0 +1,105 @@ +#[cfg(feature = "suggestions")] +use std::cmp::Ordering; + +// Internal +use crate::builder::Command; + +/// Produces multiple strings from a given list of possible values which are similar +/// to the passed in value `v` within a certain confidence by least confidence. +/// Thus in a list of possible values like ["foo", "bar"], the value "fop" will yield +/// `Some("foo")`, whereas "blark" would yield `None`. +#[cfg(feature = "suggestions")] +pub(crate) fn did_you_mean(v: &str, possible_values: I) -> Vec +where + T: AsRef, + I: IntoIterator, +{ + let mut candidates: Vec<(f64, String)> = possible_values + .into_iter() + .map(|pv| (strsim::jaro_winkler(v, pv.as_ref()), pv.as_ref().to_owned())) + .filter(|(confidence, _)| *confidence > 0.8) + .collect(); + candidates.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(Ordering::Equal)); + candidates.into_iter().map(|(_, pv)| pv).collect() +} + +#[cfg(not(feature = "suggestions"))] +pub(crate) fn did_you_mean(_: &str, _: I) -> Vec +where + T: AsRef, + I: IntoIterator, +{ + Vec::new() +} + +/// Returns a suffix that can be empty, or is the standard 'did you mean' phrase +pub(crate) fn did_you_mean_flag<'a, 'help, I, T>( + arg: &str, + remaining_args: &[&str], + longs: I, + subcommands: impl IntoIterator>, +) -> Option<(String, Option)> +where + 'help: 'a, + T: AsRef, + I: IntoIterator, +{ + use crate::mkeymap::KeyType; + + match did_you_mean(arg, longs).pop() { + Some(candidate) => Some((candidate, None)), + None => subcommands + .into_iter() + .filter_map(|subcommand| { + subcommand._build_self(); + + let longs = subcommand.get_keymap().keys().filter_map(|a| { + if let KeyType::Long(v) = a { + Some(v.to_string_lossy().into_owned()) + } else { + None + } + }); + + let subcommand_name = subcommand.get_name(); + + let candidate = did_you_mean(arg, longs).pop()?; + let score = remaining_args.iter().position(|x| *x == subcommand_name)?; + Some((score, (candidate, Some(subcommand_name.to_string())))) + }) + .min_by_key(|(x, _)| *x) + .map(|(_, suggestion)| suggestion), + } +} + +#[cfg(all(test, features = "suggestions"))] +mod test { + use super::*; + + #[test] + fn possible_values_match() { + let p_vals = ["test", "possible", "values"]; + assert_eq!(did_you_mean("tst", p_vals.iter()), Some("test")); + } + + #[test] + fn possible_values_match() { + let p_vals = ["test", "temp"]; + assert_eq!(did_you_mean("te", p_vals.iter()), Some("test")); + } + + #[test] + fn possible_values_nomatch() { + let p_vals = ["test", "possible", "values"]; + assert!(did_you_mean("hahaahahah", p_vals.iter()).is_none()); + } + + #[test] + fn flag() { + let p_vals = ["test", "possible", "values"]; + assert_eq!( + did_you_mean_flag("tst", p_vals.iter(), []), + Some(("test", None)) + ); + } +} -- cgit v1.2.3