1//! Helper functions for suggesting alternative values for a possibly erroneous user input.
23/// Filters multiple strings from a given list of possible values which are similar
4/// to the passed in value `v` within a certain confidence by least confidence.
5///
6/// The jaro winkler similarity boosts candidates that have a common prefix, which is often the case
7/// in the event of typos. Thus, in a list of possible values like ["foo", "bar"], the value "fop"
8/// will yield `Some("foo")`, whereas "blark" would yield `None`.
9pub fn did_you_mean<T, I>(v: &str, candidates: I) -> Vec<String>
10where
11T: AsRef<str>,
12 I: IntoIterator<Item = T>,
13{
14let mut candidates: Vec<(f64, String)> = candidates15 .into_iter()
16 .map(|pv| (strsim::jaro_winkler(v, pv.as_ref()), pv.as_ref().to_owned()))
17 .filter(|(similarity, _)| *similarity > 0.8)
18 .collect();
19candidates.sort_by(|a, b| a.0.total_cmp(&b.0));
20candidates.into_iter().map(|(_, pv)| pv).collect()
21}
2223#[cfg(test)]
24mod tests {
25use super::*;
2627#[test]
28fn possible_artifacts_match() {
29let candidates = ["MyContract", "Erc20"];
30assert_eq!(
31 did_you_mean("MyCtrac", candidates.iter()).pop(),
32Some("MyContract".to_string())
33 );
34 }
3536#[test]
37fn possible_artifacts_nomatch() {
38let candidates = ["MyContract", "Erc20", "Greeter"];
39assert!(did_you_mean("Vault", candidates.iter()).pop().is_none());
40 }
41}