supa_mdx_lint/rules/rule003_spelling/
suggestions.rs
1use std::path::PathBuf;
2
3use gag::Gag;
4use symspell::{AsciiStringStrategy, SymSpell, Verbosity};
5
6#[cfg(not(test))]
7const DICTIONARY_PATH: &str = "src/rules/rule003_spelling/dictionary.txt";
8
9#[cfg(test)]
10const DICTIONARY_PATH: &str = "src/rules/rule003_spelling/test_dictionary.txt";
11
12#[derive(Default)]
13pub struct SuggestionMatcher {
14 dictionary: SymSpell<AsciiStringStrategy>,
15}
16
17impl SuggestionMatcher {
18 pub fn new(exceptions: &[impl AsRef<str>]) -> Self {
19 let mut symspell = SymSpell::default();
20
21 let dictionary_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(DICTIONARY_PATH);
22 {
26 let _silencer = Gag::stderr();
27 symspell.load_dictionary(dictionary_path.to_str().unwrap(), 0, 1, " ");
28 }
29
30 let dummy_frequency = 1_000_000_000;
35 for exception in exceptions {
36 symspell.load_dictionary_line(
37 &format!("{}\t{}", exception.as_ref(), dummy_frequency),
38 0,
39 1,
40 "\t",
41 );
42 }
43
44 Self {
45 dictionary: symspell,
46 }
47 }
48
49 pub fn suggest(&self, word: &str) -> Vec<String> {
50 self.dictionary
51 .lookup(word, Verbosity::Top, 2)
52 .into_iter()
53 .map(|s| s.term)
54 .collect()
55 }
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61
62 #[test]
63 fn test_suggestion_matcher() {
64 let words: Vec<String> = vec![];
65 let matcher = SuggestionMatcher::new(&words);
66 let suggestions = matcher.suggest("heloo");
67 assert!(suggestions.contains(&"hello".to_string()));
68 }
69
70 #[test]
71 fn test_suggestion_matcher_with_custom_words() {
72 let words: Vec<String> = vec!["asdfghjkl".to_string()];
73 let matcher = SuggestionMatcher::new(&words);
74 let suggestions = matcher.suggest("asdfhjk");
75 assert!(suggestions.contains(&"asdfghjkl".to_string()));
76 }
77}