raz_validation/
suggestion.rs1use fuzzy_matcher::{skim::SkimMatcherV2, FuzzyMatcher};
4
5pub struct OptionSuggester {
7 matcher: SkimMatcherV2,
8}
9
10impl Default for OptionSuggester {
11 fn default() -> Self {
12 Self::new()
13 }
14}
15
16impl OptionSuggester {
17 pub fn new() -> Self {
19 Self {
20 matcher: SkimMatcherV2::default(),
21 }
22 }
23
24 pub fn suggest_options(&self, input: &str, valid_options: &[&str]) -> Vec<String> {
26 let mut scored_options: Vec<(i64, String)> = valid_options
27 .iter()
28 .filter_map(|option| {
29 self.matcher
30 .fuzzy_match(option, input)
31 .map(|score| (score, option.to_string()))
32 })
33 .collect();
34
35 scored_options.sort_by(|a, b| b.0.cmp(&a.0));
37
38 scored_options
40 .into_iter()
41 .take(3)
42 .map(|(_, option)| option)
43 .collect()
44 }
45
46 pub fn suggest_options_with_threshold(
48 &self,
49 input: &str,
50 valid_options: &[&str],
51 min_score: i64,
52 ) -> Vec<String> {
53 let suggestions = self.suggest_options(input, valid_options);
54
55 suggestions
57 .into_iter()
58 .filter(|option| {
59 self.matcher
60 .fuzzy_match(option, input)
61 .is_some_and(|score| score >= min_score)
62 })
63 .collect()
64 }
65
66 pub fn is_similar(&self, input: &str, option: &str, threshold: i64) -> bool {
68 self.matcher
69 .fuzzy_match(option, input)
70 .is_some_and(|score| score >= threshold)
71 }
72}
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77
78 #[test]
79 fn test_basic_suggestions() {
80 let suggester = OptionSuggester::new();
81 let options = vec!["--release", "--debug", "--verbose", "--quiet"];
82
83 let suggestions = suggester.suggest_options("--relase", &options);
84 assert!(suggestions.contains(&"--release".to_string()));
85 }
86
87 #[test]
88 fn test_no_suggestions_for_very_different_input() {
89 let suggester = OptionSuggester::new();
90 let options = vec!["--release", "--debug", "--verbose"];
91
92 let suggestions = suggester.suggest_options_with_threshold("--xyz123", &options, 20);
93 assert!(suggestions.is_empty());
94 }
95
96 #[test]
97 fn test_similarity_check() {
98 let suggester = OptionSuggester::new();
99
100 assert!(suggester.is_similar("--relase", "--release", 30));
101 assert!(!suggester.is_similar("--xyz", "--release", 30));
102 }
103}