From 1b6a04ca5504955c571d1c97504fb45ea0befee4 Mon Sep 17 00:00:00 2001
From: Valentin Popov <valentin@popov.link>
Date: Mon, 8 Jan 2024 01:21:28 +0400
Subject: Initial vendor packages

Signed-off-by: Valentin Popov <valentin@popov.link>
---
 vendor/clap_builder/src/parser/features/mod.rs     |   1 +
 .../src/parser/features/suggestions.rs             | 167 +++++++++++++++++++++
 2 files changed, 168 insertions(+)
 create mode 100644 vendor/clap_builder/src/parser/features/mod.rs
 create mode 100644 vendor/clap_builder/src/parser/features/suggestions.rs

(limited to 'vendor/clap_builder/src/parser/features')

diff --git a/vendor/clap_builder/src/parser/features/mod.rs b/vendor/clap_builder/src/parser/features/mod.rs
new file mode 100644
index 0000000..bdeb766
--- /dev/null
+++ b/vendor/clap_builder/src/parser/features/mod.rs
@@ -0,0 +1 @@
+pub(crate) mod suggestions;
diff --git a/vendor/clap_builder/src/parser/features/suggestions.rs b/vendor/clap_builder/src/parser/features/suggestions.rs
new file mode 100644
index 0000000..b8bb7ad
--- /dev/null
+++ b/vendor/clap_builder/src/parser/features/suggestions.rs
@@ -0,0 +1,167 @@
+#[cfg(feature = "suggestions")]
+use std::cmp::Ordering;
+
+// Internal
+use crate::builder::Command;
+
+/// Find strings from an iterable of `possible_values` similar to a given value `v`
+/// Returns a Vec of all possible values that exceed a similarity threshold
+/// sorted by ascending similarity, most similar comes last
+#[cfg(feature = "suggestions")]
+pub(crate) fn did_you_mean<T, I>(v: &str, possible_values: I) -> Vec<String>
+where
+    T: AsRef<str>,
+    I: IntoIterator<Item = T>,
+{
+    let mut candidates: Vec<(f64, String)> = possible_values
+        .into_iter()
+        // GH #4660: using `jaro` because `jaro_winkler` implementation in `strsim-rs` is wrong
+        // causing strings with common prefix >=10 to be considered perfectly similar
+        .map(|pv| (strsim::jaro(v, pv.as_ref()), pv.as_ref().to_owned()))
+        // Confidence of 0.7 so that bar -> baz is suggested
+        .filter(|(confidence, _)| *confidence > 0.7)
+        .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<T, I>(_: &str, _: I) -> Vec<String>
+where
+    T: AsRef<str>,
+    I: IntoIterator<Item = T>,
+{
+    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: &[&std::ffi::OsStr],
+    longs: I,
+    subcommands: impl IntoIterator<Item = &'a mut Command>,
+) -> Option<(String, Option<String>)>
+where
+    'help: 'a,
+    T: AsRef<str>,
+    I: IntoIterator<Item = T>,
+{
+    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(false);
+
+                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 = some!(did_you_mean(arg, longs).pop());
+                let score = some!(remaining_args.iter().position(|x| subcommand_name == *x));
+                Some((score, (candidate, Some(subcommand_name.to_string()))))
+            })
+            .min_by_key(|(x, _)| *x)
+            .map(|(_, suggestion)| suggestion),
+    }
+}
+
+#[cfg(all(test, feature = "suggestions"))]
+mod test {
+    use super::*;
+
+    #[test]
+    fn missing_letter() {
+        let p_vals = ["test", "possible", "values"];
+        assert_eq!(did_you_mean("tst", p_vals.iter()), vec!["test"]);
+    }
+
+    #[test]
+    fn ambiguous() {
+        let p_vals = ["test", "temp", "possible", "values"];
+        assert_eq!(did_you_mean("te", p_vals.iter()), vec!["test", "temp"]);
+    }
+
+    #[test]
+    fn unrelated() {
+        let p_vals = ["test", "possible", "values"];
+        assert_eq!(
+            did_you_mean("hahaahahah", p_vals.iter()),
+            Vec::<String>::new()
+        );
+    }
+
+    #[test]
+    fn best_fit() {
+        let p_vals = [
+            "test",
+            "possible",
+            "values",
+            "alignmentStart",
+            "alignmentScore",
+        ];
+        assert_eq!(
+            did_you_mean("alignmentScorr", p_vals.iter()),
+            vec!["alignmentStart", "alignmentScore"]
+        );
+    }
+
+    #[test]
+    fn best_fit_long_common_prefix_issue_4660() {
+        let p_vals = ["alignmentScore", "alignmentStart"];
+        assert_eq!(
+            did_you_mean("alignmentScorr", p_vals.iter()),
+            vec!["alignmentStart", "alignmentScore"]
+        );
+    }
+
+    #[test]
+    fn flag_missing_letter() {
+        let p_vals = ["test", "possible", "values"];
+        assert_eq!(
+            did_you_mean_flag("tst", &[], p_vals.iter(), []),
+            Some(("test".to_owned(), None))
+        );
+    }
+
+    #[test]
+    fn flag_ambiguous() {
+        let p_vals = ["test", "temp", "possible", "values"];
+        assert_eq!(
+            did_you_mean_flag("te", &[], p_vals.iter(), []),
+            Some(("temp".to_owned(), None))
+        );
+    }
+
+    #[test]
+    fn flag_unrelated() {
+        let p_vals = ["test", "possible", "values"];
+        assert_eq!(
+            did_you_mean_flag("hahaahahah", &[], p_vals.iter(), []),
+            None
+        );
+    }
+
+    #[test]
+    fn flag_best_fit() {
+        let p_vals = [
+            "test",
+            "possible",
+            "values",
+            "alignmentStart",
+            "alignmentScore",
+        ];
+        assert_eq!(
+            did_you_mean_flag("alignmentScorr", &[], p_vals.iter(), []),
+            Some(("alignmentScore".to_owned(), None))
+        );
+    }
+}
-- 
cgit v1.2.3