mate_rs/
utils.rs

1//
2// Copyright 2022-present theiskaa. All rights reserved.
3// Use of this source code is governed by MIT license
4// that can be found in the LICENSE file.
5//
6
7use crate::token::{Token, TokenType};
8use regex::Regex;
9
10//
11// A interface for custom char-type-checking utility methods.
12// Has a various methods (checkers) based on [&str].
13//
14pub trait ChUtils {
15    // Checks if the given [&self] object is number or not.
16    fn is_number(&self) -> bool;
17
18    // Checks if the given [&self] object is point(comma, dot) or not.
19    //
20    // Like <.> in 3.14 or <,> in 3,14
21    fn is_point(&self) -> bool;
22
23    // Checks if the given [&self] object is plus sign or minus sign.
24    //
25    // Plus signs   --> <+>
26    // Minus signs  --> <->
27    fn is_plus_or_minus(&self) -> bool;
28
29    // Checks if the given [&self] object is division sign or multiplication sign.
30    //
31    // Division signs        --> <:> and </>
32    // Multiplication signs  --> <*> and <•>
33    fn is_div_or_prod(&self) -> bool;
34
35    // A function that combines [is_plus_or_minus] and [is_div_or_prod].
36    // So, it checks if [&self] object is operation sign or not.
37    //
38    // Plus signs            --> <+>
39    // Minus signs           --> <->
40    // Division signs        --> <:> and </>
41    // Multiplication signs  --> <*> and <•>
42    fn is_operation_sign(&self) -> bool;
43
44    // Checks if the given [&self] object is left parentheses or right parentheses sign.
45    //
46    // Left  Parentheses --> (
47    // Right Parentheses --> )
48    fn is_parentheses(&self) -> (bool, bool);
49
50    // Checks if the given [&self] object is left abs or right abs sign.
51    //
52    // Left  ABS --> [
53    // Right ABS --> ]
54    fn is_abs(&self) -> (bool, bool);
55
56    // Checks if the given [%self] object is percentage sign or not.
57    fn is_percentage(&self) -> bool;
58}
59
60impl ChUtils for String {
61    fn is_number(&self) -> bool {
62        Regex::new("[[:digit:]]").unwrap().is_match(self)
63    }
64
65    fn is_point(&self) -> bool {
66        self.trim().eq(".") || self.trim().eq(",")
67    }
68
69    fn is_plus_or_minus(&self) -> bool {
70        self.trim().eq("+") || self.trim().eq("-")
71    }
72
73    fn is_div_or_prod(&self) -> bool {
74        let is_div: bool = self.trim().eq(":") || self.trim().eq("/");
75        let is_prod: bool = self.trim().eq("*") || self.trim().eq("•");
76
77        is_div || is_prod
78    }
79
80    fn is_operation_sign(&self) -> bool {
81        self.is_plus_or_minus() || self.is_div_or_prod()
82    }
83
84    fn is_parentheses(&self) -> (bool, bool) {
85        (self.trim().eq("("), self.trim().eq(")"))
86    }
87
88    fn is_abs(&self) -> (bool, bool) {
89        (self.trim().eq("["), self.trim().eq("]"))
90    }
91
92    fn is_percentage(&self) -> bool {
93        self.trim().eq("%")
94    }
95}
96
97impl ChUtils for Token {
98    fn is_number(&self) -> bool {
99        match self.typ {
100            TokenType::NUMBER => true,
101            _ => false,
102        }
103    }
104
105    fn is_point(&self) -> bool {
106        false // token has not point type at all.
107    }
108
109    fn is_plus_or_minus(&self) -> bool {
110        match self.typ {
111            TokenType::PLUS => true,
112            TokenType::MINUS => true,
113            _ => false,
114        }
115    }
116
117    fn is_div_or_prod(&self) -> bool {
118        match self.typ {
119            TokenType::PRODUCT => true,
120            TokenType::DIVIDE => true,
121            _ => false,
122        }
123    }
124
125    fn is_operation_sign(&self) -> bool {
126        self.is_plus_or_minus() || self.is_div_or_prod()
127    }
128
129    fn is_parentheses(&self) -> (bool, bool) {
130        match self.typ {
131            TokenType::LPAREN => (true, false),
132            TokenType::RPAREN => (false, true),
133            _ => (false, false),
134        }
135    }
136
137    fn is_abs(&self) -> (bool, bool) {
138        match self.typ {
139            TokenType::LABS => (true, false),
140            TokenType::RABS => (false, true),
141            _ => (false, false),
142        }
143    }
144
145    fn is_percentage(&self) -> bool {
146        match self.typ {
147            TokenType::PERCENTAGE => true,
148            _ => false,
149        }
150    }
151}
152
153// Includes tests for only String implementation of [ChUtils].
154#[cfg(test)]
155mod tests {
156    use super::*;
157    use std::collections::HashMap;
158
159    #[test]
160    fn is_number() {
161        let test_data: HashMap<String, bool> = HashMap::from([
162            (String::from("42"), true),
163            (String::from("-25"), true),
164            (String::from("+50"), true),
165            (String::from("-"), false),
166            (String::from("+"), false),
167            (String::from("/"), false),
168        ]);
169
170        for (target, expected) in test_data {
171            assert_eq!(target.is_number(), expected);
172            assert_eq!(Token::from(target, (0, 0)).is_number(), expected);
173        }
174    }
175
176    #[test]
177    fn is_point() {
178        let test_data: HashMap<String, bool> = HashMap::from([
179            (String::from("."), true),
180            (String::from(","), true),
181            (String::from("-"), false),
182            (String::from("+"), false),
183            (String::from("/"), false),
184            (String::from("5"), false),
185        ]);
186
187        for (target, expected) in test_data {
188            assert_eq!(target.is_point(), expected);
189        }
190    }
191
192    #[test]
193    fn is_plus_or_minus() {
194        let test_data: HashMap<String, bool> = HashMap::from([
195            (String::from("-"), true),
196            (String::from("+"), true),
197            (String::from("/"), false),
198            (String::from(".5"), false),
199            (String::from("/"), false),
200            (String::from("*"), false),
201        ]);
202
203        for (target, expected) in test_data {
204            assert_eq!(target.is_plus_or_minus(), expected);
205            assert_eq!(Token::from(target, (0, 0)).is_plus_or_minus(), expected);
206        }
207    }
208
209    #[test]
210    fn is_div_or_prod() {
211        let test_data: HashMap<String, bool> = HashMap::from([
212            (String::from("/"), true),
213            (String::from("*"), true),
214            (String::from(":"), true),
215            (String::from("•"), true),
216            (String::from("-"), false),
217            (String::from("+"), false),
218            (String::from(".5"), false),
219        ]);
220
221        for (target, expected) in test_data {
222            assert_eq!(target.is_div_or_prod(), expected);
223            assert_eq!(Token::from(target, (0, 0)).is_div_or_prod(), expected);
224        }
225    }
226
227    #[test]
228    fn is_operation_sign() {
229        let test_data: HashMap<String, bool> = HashMap::from([
230            (String::from("/"), true),
231            (String::from("*"), true),
232            (String::from(":"), true),
233            (String::from("•"), true),
234            (String::from("-"), true),
235            (String::from("+"), true),
236            (String::from("5"), false),
237            (String::from("."), false),
238            (String::from(","), false),
239        ]);
240
241        for (target, expected) in test_data {
242            assert_eq!(target.is_operation_sign(), expected);
243            assert_eq!(Token::from(target, (0, 0)).is_operation_sign(), expected);
244        }
245    }
246
247    #[test]
248    fn is_parentheses() {
249        let test_data: HashMap<String, (bool, bool)> = HashMap::from([
250            (String::from("/"), (false, false)),
251            (String::from("*"), (false, false)),
252            (String::from(":"), (false, false)),
253            (String::from("•"), (false, false)),
254            (String::from("-"), (false, false)),
255            (String::from("+"), (false, false)),
256            (String::from("5"), (false, false)),
257            (String::from(")"), (false, true)),
258            (String::from("("), (true, false)),
259        ]);
260
261        for (target, expected) in test_data {
262            assert_eq!(target.is_parentheses(), expected);
263            assert_eq!(Token::from(target, (0, 0)).is_parentheses(), expected);
264        }
265    }
266
267    #[test]
268    fn is_abs() {
269        let test_data: HashMap<String, (bool, bool)> = HashMap::from([
270            (String::from("/"), (false, false)),
271            (String::from("*"), (false, false)),
272            (String::from(":"), (false, false)),
273            (String::from("•"), (false, false)),
274            (String::from("-"), (false, false)),
275            (String::from("+"), (false, false)),
276            (String::from("5"), (false, false)),
277            (String::from("]"), (false, true)),
278            (String::from("["), (true, false)),
279        ]);
280
281        for (target, expected) in test_data {
282            assert_eq!(target.is_abs(), expected);
283            assert_eq!(Token::from(target, (0, 0)).is_abs(), expected);
284        }
285    }
286
287    #[test]
288    fn is_percentage() {
289        let test_data: HashMap<bool, String> = HashMap::from([
290            (false, String::from("-25")),
291            (false, String::from("-")),
292            (false, String::from("(")),
293            (true, String::from("%")),
294        ]);
295
296        for (expected, data) in test_data {
297            assert_eq!(expected, data.is_percentage());
298            assert_eq!(expected, Token::from(data, (0, 0)).is_percentage());
299        }
300    }
301}