Skip to main content

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