alith_client/components/grammar/
integer.rs

1use super::{Grammar, GrammarError, GrammarSetterTrait};
2use std::cell::RefCell;
3
4#[derive(Clone, Default, PartialEq)]
5pub struct IntegerGrammar {
6    pub stop_word_done: Option<String>,
7    pub stop_word_no_result: Option<String>,
8    pub lower_bound: u32,
9    pub upper_bound: u32,
10    grammar_string: RefCell<Option<String>>,
11}
12
13impl IntegerGrammar {
14    pub fn new() -> Self {
15        Self {
16            stop_word_done: None,
17            stop_word_no_result: None,
18            lower_bound: 1,
19            upper_bound: 9,
20            grammar_string: RefCell::new(None),
21        }
22    }
23
24    pub fn wrap(self) -> Grammar {
25        Grammar::Integer(self)
26    }
27
28    pub fn lower_bound(mut self, lower_bound: u32) -> Self {
29        self.lower_bound = lower_bound;
30
31        self
32    }
33
34    pub fn upper_bound(mut self, upper_bound: u32) -> Self {
35        self.upper_bound = upper_bound;
36
37        self
38    }
39
40    pub fn grammar_string(&self) -> String {
41        let mut grammar_string = self.grammar_string.borrow_mut();
42        if grammar_string.is_none() {
43            *grammar_string = Some(integer_grammar(
44                self.lower_bound,
45                self.upper_bound,
46                &self.stop_word_done,
47                &self.stop_word_no_result,
48            ));
49        }
50        grammar_string.as_ref().unwrap().clone()
51    }
52
53    pub fn validate_clean(&self, content: &str) -> Result<String, GrammarError> {
54        integer_validate_clean(content)
55    }
56
57    pub fn grammar_parse(&self, content: &str) -> Result<u32, GrammarError> {
58        integer_parse(content)
59    }
60}
61
62impl GrammarSetterTrait for IntegerGrammar {
63    fn stop_word_done_mut(&mut self) -> &mut Option<String> {
64        &mut self.stop_word_done
65    }
66
67    fn stop_word_no_result_mut(&mut self) -> &mut Option<String> {
68        &mut self.stop_word_no_result
69    }
70}
71
72pub fn integer_grammar<T: AsRef<str>>(
73    lower_bound: u32,
74    upper_bound: u32,
75    stop_word_done: &Option<T>,
76    stop_word_no_result: &Option<T>,
77) -> String {
78    match upper_bound.cmp(&lower_bound) {
79        std::cmp::Ordering::Less => {
80            panic!("Upper bound must be greater than or equal to lower bound.")
81        }
82        std::cmp::Ordering::Equal => panic!("Bounds must not be the same."),
83        _ => (),
84    }
85    // let mut base = "root ::= \" \" ".to_string();
86    let range = create_range(lower_bound, upper_bound, stop_word_done);
87    match (stop_word_done, stop_word_no_result) {
88        (Some(stop_word_done), Some(stop_word_no_result)) => format!(
89            "root ::= \" \" ( {range} | \"{}\" ) \" {}\"",
90            stop_word_no_result.as_ref(),
91            stop_word_done.as_ref()
92        ),
93        (None, Some(stop_word_no_result)) => {
94            format!(
95                "root ::= \" \" ( {range} | \"{}\" )",
96                stop_word_no_result.as_ref()
97            )
98        }
99        (Some(stop_word_done), None) => {
100            format!("root ::= \" \" {range} \" {}\"", stop_word_done.as_ref())
101        }
102        (None, None) => format!("root ::= \" \" {range}"),
103    }
104}
105
106fn create_range<T: AsRef<str>>(
107    lower_bound: u32,
108    upper_bound: u32,
109    stop_word_done: &Option<T>,
110) -> String {
111    let digits = (upper_bound as f64).log10().floor() as u32 + 1;
112    let mut range = String::new();
113    if digits == 1 {
114        range.push_str(&format!("[{}-{}]", lower_bound, upper_bound));
115        return range;
116    }
117
118    // Need to add the actual math here to restrict the range.
119    for i in 1..=digits {
120        if i > 1 && 10_u32.pow(i - 1) > lower_bound {
121            if let Some(stop_word_done) = stop_word_done {
122                range.push_str(&format!("([0-9] | \" {}\")", stop_word_done.as_ref()));
123            } else {
124                range.push_str("[0-9]?");
125            }
126        } else {
127            range.push_str("[0-9]");
128        }
129    }
130    range
131}
132
133pub fn integer_validate_clean(content: &str) -> Result<String, GrammarError> {
134    let content: &str = content.trim();
135    if integer_parse(content).is_ok() {
136        Ok(content.to_string())
137    } else {
138        Err(GrammarError::ParseValueError {
139            content: content.to_string(),
140            parse_type: "u32".to_string(),
141        })
142    }
143}
144
145pub fn integer_parse(content: &str) -> Result<u32, GrammarError> {
146    content
147        .trim()
148        .parse::<u32>()
149        .map_err(|_| GrammarError::ParseValueError {
150            content: content.to_string(),
151            parse_type: "u32".to_string(),
152        })
153}