alith_client/components/grammar/
faux_url.rs

1use super::{Grammar, GrammarError, GrammarSetterTrait};
2use std::cell::RefCell;
3
4#[derive(Clone, PartialEq)]
5pub struct FauxUrlGrammar {
6    pub min_count: u8,
7    pub max_count: u8,
8    pub word_char_length: u8,
9    pub base_url: String,
10    pub stop_word_done: Option<String>,
11    pub stop_word_no_result: Option<String>,
12    grammar_string: RefCell<Option<String>>,
13}
14
15impl Default for FauxUrlGrammar {
16    fn default() -> Self {
17        Self {
18            min_count: 1,
19            max_count: 3,
20            word_char_length: 12,
21            base_url: "https://example.com/".to_string(),
22            stop_word_done: None,
23            stop_word_no_result: None,
24            grammar_string: RefCell::new(None),
25        }
26    }
27}
28
29impl FauxUrlGrammar {
30    #[inline]
31    pub fn wrap(self) -> Grammar {
32        Grammar::FauxUrl(self)
33    }
34
35    #[inline]
36    pub fn min_count(mut self, min_count: u8) -> Self {
37        self.min_count = min_count;
38        self
39    }
40
41    #[inline]
42    pub fn max_count(mut self, max_count: u8) -> Self {
43        self.max_count = max_count;
44        self
45    }
46
47    #[inline]
48    pub fn word_char_length(mut self, word_char_length: u8) -> Self {
49        self.word_char_length = word_char_length;
50        self
51    }
52
53    #[inline]
54    pub fn base_url<T: AsRef<str>>(mut self, base_url: T) -> Self {
55        self.base_url = base_url.as_ref().to_string();
56        self
57    }
58
59    pub fn grammar_string(&self) -> String {
60        let mut grammar_string = self.grammar_string.borrow_mut();
61        if grammar_string.is_none() {
62            *grammar_string = Some(faux_url_grammar(
63                self.min_count,
64                self.max_count,
65                self.word_char_length,
66                &self.base_url,
67                &self.stop_word_done,
68                &self.stop_word_no_result,
69            ));
70        }
71        grammar_string.as_ref().unwrap().clone()
72    }
73
74    #[inline]
75    pub fn validate_clean(&self, content: &str) -> Result<String, GrammarError> {
76        faux_url_validate_clean(content, &self.base_url)
77    }
78
79    #[inline]
80    pub fn grammar_parse(&self, content: &str) -> Result<Vec<String>, GrammarError> {
81        faux_url_parse(content, &self.base_url)
82    }
83}
84
85impl GrammarSetterTrait for FauxUrlGrammar {
86    fn stop_word_done_mut(&mut self) -> &mut Option<String> {
87        &mut self.stop_word_done
88    }
89
90    fn stop_word_no_result_mut(&mut self) -> &mut Option<String> {
91        &mut self.stop_word_no_result
92    }
93}
94
95pub fn faux_url_grammar<T: AsRef<str>>(
96    min_count: u8,
97    max_count: u8,
98    word_char_length: u8,
99    base_url: &str,
100    stop_word_done: &Option<T>,
101    stop_word_no_result: &Option<T>,
102) -> String {
103    let range = create_range(min_count, max_count, stop_word_done);
104    let first = format!("first ::= [a-z]{{3,{word_char_length}}}");
105    let item = format!("item ::= \"-\" [a-z]{{3,{word_char_length}}}");
106    match (stop_word_done, stop_word_no_result) {
107        (Some(stop_word_done), Some(stop_word_no_result)) => format!(
108            "root ::= \" \" ( \"{base_url}\" {range} | \"{}\" ) \" {}\"\n{item}\n{first}",
109            stop_word_no_result.as_ref(),
110            stop_word_done.as_ref()
111        ),
112        (None, Some(stop_word_no_result)) => {
113            format!(
114                "root ::= \" \" ( \"{base_url}\" {range} | \"{}\" )\n{item}\n{first}",
115                stop_word_no_result.as_ref()
116            )
117        }
118        (Some(stop_word_done), None) => {
119            format!(
120                "root ::= \" \" \"{base_url}\" {range} \" {}\"\n{item}\n{first}",
121                stop_word_done.as_ref()
122            )
123        }
124        (None, None) => format!("root ::= \" \" \"{base_url}\" {range}\n{item}\n{first}"),
125    }
126}
127
128fn create_range<T: AsRef<str>>(min_count: u8, max_count: u8, stop_word_done: &Option<T>) -> String {
129    let min_count = if min_count == 0 {
130        eprintln!("Min count must be greater than 0. Setting min count to 1.");
131        1
132    } else {
133        min_count
134    };
135    let max_count = match max_count.cmp(&min_count) {
136        std::cmp::Ordering::Less => {
137            eprintln!(
138                "Max count must be greater than or equal to min count. Setting max count to min count."
139            );
140            min_count
141        }
142        _ => max_count,
143    };
144    if min_count == 1 && max_count == 1 {
145        "first".to_owned()
146    } else {
147        let mut range = String::new();
148        range.push_str("first ");
149        if min_count > 1 {
150            range.push_str(&format!("item{{{}}} ", min_count - 1));
151        }
152        if max_count > min_count {
153            let opt_count = max_count - min_count;
154            if let Some(stop_word_done) = stop_word_done {
155                range.push_str(&format!(
156                    "( \"{}\" | item ){{0,{opt_count}}}",
157                    stop_word_done.as_ref()
158                ))
159            } else {
160                range.push_str(&format!("item{{0,{opt_count}}}"));
161            };
162        }
163        range
164    }
165}
166pub fn faux_url_validate_clean(content: &str, base: &str) -> Result<String, GrammarError> {
167    if faux_url_parse(content, base).is_ok() {
168        Ok(content
169            .trim()
170            .trim_end_matches(|c: char| !c.is_alphanumeric())
171            .to_string())
172    } else {
173        Err(GrammarError::ParseValueError {
174            content: content.to_string(),
175            parse_type: "FauxUrl".to_string(),
176        })
177    }
178}
179
180pub fn faux_url_parse(content: &str, base: &str) -> Result<Vec<String>, GrammarError> {
181    if let Some(trimmed_content) = content.trim().strip_prefix(base) {
182        Ok(trimmed_content
183            .trim_end_matches('-')
184            .split('-')
185            .map(String::from)
186            .collect())
187    } else {
188        Err(GrammarError::ParseValueError {
189            content: content.to_string(),
190            parse_type: "FauxUrl".to_string(),
191        })
192    }
193}