to_be/
lib.rs

1// lib.rs - to-be API
2
3use base_traits::AsStr;
4
5
6#[rustfmt::skip]
7mod constants {
8
9    // NOTE: `FALSEY_PRECISE_STRINGS` and `TRUEY_PRECISE_STRINGS` must be in sorted order as
10    // they are consumed in binary search; the others in most-likely order.
11
12    pub(super) const FALSEY_PRECISE_STRINGS : &'static [&'static str; 10] = &[
13        "0",
14        "FALSE",
15        "False",
16        "NO",
17        "No",
18        "OFF",
19        "Off",
20        "false",
21        "no",
22        "off",
23    ];
24
25    pub(super) const TRUEY_PRECISE_STRINGS : &'static [&'static str; 10] = &[
26        "1",
27        "ON",
28        "On",
29        "TRUE",
30        "True",
31        "YES",
32        "Yes",
33        "on",
34        "true",
35        "yes",
36    ];
37
38    pub(super) const FALSEY_LOWERCASE_STRINGS : &'static [&'static str; 4] = &[
39        "false",
40        "no",
41        "off",
42        "0",
43    ];
44
45    pub(super) const TRUEY_LOWERCASE_STRINGS : &'static [&'static str; 4] = &[
46        "true",
47        "yes",
48        "on",
49        "1",
50    ];
51}
52
53
54/// Directs custom truthyness behaviour.
55#[derive(Clone)]
56#[derive(Debug)]
57pub enum Terms<'a> {
58    /// Use the built-in comparison strings.
59    Default,
60    /// Use the given `*precise_strings` and, optionally, the given
61    /// `*lower_strings` to evaluate the truthyness of a given string.
62    Strings {
63        falsey_precise_strings :   &'a [&'a str],
64        falsey_lowercase_strings : &'a [&'a str],
65        truey_precise_strings :    &'a [&'a str],
66        truey_lowercase_strings :  &'a [&'a str],
67    },
68}
69
70fn string_is_truthy_against_(
71    s : &str,
72    sorted_precise_strings : &[&str],
73    lowercase_strings : &[&str],
74) -> bool {
75    let s = s.trim();
76
77    if sorted_precise_strings.binary_search(&s).is_ok() {
78        true
79    } else {
80        let l = s.to_ascii_lowercase();
81
82        lowercase_strings.iter().any(|&f| f == l)
83    }
84}
85
86fn string_is_truthy_with_(
87    s : &str,
88    terms : Terms,
89    stock_falsey_sorted_precise_strings : &[&str],
90    stock_falsey_lowercase_strings : &[&str],
91    stock_truey_sorted_precise_strings : &[&str],
92    stock_truey_lowercase_strings : &[&str],
93) -> Option<bool> {
94    let s = s.trim();
95
96    match terms {
97        Terms::Default => {
98            if stock_falsey_sorted_precise_strings.binary_search(&s).is_ok() {
99                return Some(false);
100            }
101            if stock_truey_sorted_precise_strings.binary_search(&s).is_ok() {
102                return Some(true);
103            }
104        },
105        Terms::Strings {
106            falsey_precise_strings,
107            truey_precise_strings,
108            ..
109        } => {
110            if falsey_precise_strings.iter().any(|&f| f == s) {
111                return Some(false);
112            }
113            if truey_precise_strings.iter().any(|&f| f == s) {
114                return Some(true);
115            }
116        },
117    };
118
119    let l = s.to_ascii_lowercase();
120    let (falsey_lowercase_strings, truey_lowercase_strings) = match terms {
121        Terms::Default => (stock_falsey_lowercase_strings, stock_truey_lowercase_strings),
122        Terms::Strings {
123            falsey_lowercase_strings,
124            truey_lowercase_strings,
125            ..
126        } => (falsey_lowercase_strings, truey_lowercase_strings),
127    };
128
129    if falsey_lowercase_strings.iter().any(|&f| f == l) {
130        return Some(false);
131    }
132    if truey_lowercase_strings.iter().any(|&f| f == l) {
133        return Some(true);
134    }
135
136    None
137}
138
139/// Obtain the stock term strings of the library.
140///
141/// This may be handy when you want to, say, provide your own "truey" term
142/// strings but rely on the stock "falsey" term strings.
143pub fn stock_term_strings() -> Terms<'static> {
144    Terms::Strings {
145        falsey_precise_strings :   constants::FALSEY_PRECISE_STRINGS,
146        falsey_lowercase_strings : constants::FALSEY_LOWERCASE_STRINGS,
147        truey_precise_strings :    constants::TRUEY_PRECISE_STRINGS,
148        truey_lowercase_strings :  constants::TRUEY_LOWERCASE_STRINGS,
149    }
150}
151
152/// Indicates that the given string, when trimmed, is deemed as "truey".
153///
154/// # Note:
155/// It is NOT guaranteed that `string_is_falsey(x) == !string_is_truey(x)`.
156pub fn string_is_falsey(s : &str) -> bool {
157    string_is_truthy_against_(
158        s,
159        constants::FALSEY_PRECISE_STRINGS,
160        constants::FALSEY_LOWERCASE_STRINGS,
161    )
162}
163
164/// Indicates that the given string, when trimmed, is deemed as "falsy".
165///
166/// # Note:
167/// It is NOT guaranteed that `string_is_falsey(x) == !string_is_truey(x)`.
168///
169/// # Returns:
170/// - `None` - string is not classified as "truthy";
171/// - `Some(false)` - string (is classified as "truthy" and) is deemed
172///   "falsey";
173/// - `Some(true)` - string (is classified as "truthy" and) is deemed
174///   "truey";
175pub fn string_is_truey(s : &str) -> bool {
176    string_is_truthy_against_(
177        s,
178        constants::TRUEY_PRECISE_STRINGS,
179        constants::TRUEY_LOWERCASE_STRINGS,
180    )
181}
182
183/// Indicates whether the given string is "truthy" and, if so, whether it is
184/// "truey" or "falsey".
185///
186/// # Returns:
187/// - `None` - string is not classified as "truthy";
188/// - `Some(false)` - string (is classified as "truthy" and) is deemed
189///   "falsey";
190/// - `Some(true)` - string (is classified as "truthy" and) is deemed
191///   "truey";
192pub fn string_is_truthy(s : &str) -> Option<bool> {
193    string_is_truthy_with_(
194        s,
195        Terms::Default,
196        constants::FALSEY_PRECISE_STRINGS,
197        constants::FALSEY_LOWERCASE_STRINGS,
198        constants::TRUEY_PRECISE_STRINGS,
199        constants::TRUEY_LOWERCASE_STRINGS,
200    )
201}
202
203/// Indicates whether the instance can be classed as "truthy" when evaluated
204/// against the given terms strings.
205pub fn string_is_truthy_with(
206    s : &str,
207    terms : Terms,
208) -> Option<bool> {
209    string_is_truthy_with_(
210        s,
211        terms,
212        constants::FALSEY_PRECISE_STRINGS,
213        constants::FALSEY_LOWERCASE_STRINGS,
214        constants::TRUEY_PRECISE_STRINGS,
215        constants::TRUEY_LOWERCASE_STRINGS,
216    )
217}
218
219/// Trait that provides truthy attributes for an implementing type.
220pub trait Truthy {
221    /// Indicates whether the instance can be classed as "falsey".
222    fn is_falsey(&self) -> bool {
223        Some(false) == self.is_truthy()
224    }
225    /// Indicates whether the instance can be classed as "truey".
226    fn is_truey(&self) -> bool {
227        Some(true) == self.is_truthy()
228    }
229    /// Indicates whether the instance can be classed as "truthy", and, if
230    /// so, whether it is "truey" or "falsey".
231    fn is_truthy(&self) -> Option<bool>;
232}
233
234/// Specialisation of [Truthy] for type `T` for any type that implements
235/// [AsStr].
236impl<T> Truthy for T
237where
238    T : AsStr,
239{
240    fn is_truthy(&self) -> Option<bool> {
241        string_is_truthy(self.as_str())
242    }
243}
244
245
246#[cfg(test)]
247mod tests {
248    #![allow(non_snake_case)]
249
250    use super::{
251        string_is_falsey,
252        string_is_truey,
253        string_is_truthy,
254        string_is_truthy_with,
255        Terms,
256        Truthy as _,
257    };
258
259
260    #[test]
261    fn TEST_string_is_falsey_1() {
262        assert_eq!(false, string_is_falsey(""));
263
264        assert_eq!(true, string_is_falsey("0"));
265        assert_eq!(true, string_is_falsey("false"));
266        assert_eq!(true, string_is_falsey(" FALSE"));
267        assert_eq!(true, string_is_falsey("False"));
268        assert_eq!(true, string_is_falsey("FaLSe"));
269        assert_eq!(true, string_is_falsey("no"));
270        assert_eq!(true, string_is_falsey("No "));
271        assert_eq!(true, string_is_falsey("NO"));
272        assert_eq!(true, string_is_falsey(" Off "));
273        assert_eq!(true, string_is_falsey("off"));
274        assert_eq!(true, string_is_falsey("OFF"));
275
276        assert_eq!(false, string_is_falsey("1"));
277        assert_eq!(false, string_is_falsey("true"));
278        assert_eq!(false, string_is_falsey("TRUE"));
279        assert_eq!(false, string_is_falsey("True"));
280        assert_eq!(false, string_is_falsey("tRuE"));
281        assert_eq!(false, string_is_falsey("yes"));
282        assert_eq!(false, string_is_falsey(" YES"));
283        assert_eq!(false, string_is_falsey("Yes   "));
284        assert_eq!(false, string_is_falsey("yEs"));
285    }
286
287    #[test]
288    fn TEST_string_is_truey_1() {
289        assert_eq!(false, string_is_truey(""));
290
291        assert_eq!(false, string_is_truey("0"));
292        assert_eq!(false, string_is_truey("false"));
293        assert_eq!(false, string_is_truey(" FALSE"));
294        assert_eq!(false, string_is_truey("False"));
295        assert_eq!(false, string_is_truey("FaLSe"));
296        assert_eq!(false, string_is_truey("no"));
297        assert_eq!(false, string_is_truey("No "));
298        assert_eq!(false, string_is_truey("NO"));
299        assert_eq!(false, string_is_truey(" Off "));
300        assert_eq!(false, string_is_truey("off"));
301        assert_eq!(false, string_is_truey("OFF"));
302
303        assert_eq!(true, string_is_truey("1"));
304        assert_eq!(true, string_is_truey("true"));
305        assert_eq!(true, string_is_truey("TRUE"));
306        assert_eq!(true, string_is_truey("True"));
307        assert_eq!(true, string_is_truey("tRuE"));
308        assert_eq!(true, string_is_truey("yes"));
309        assert_eq!(true, string_is_truey(" YES"));
310        assert_eq!(true, string_is_truey("Yes   "));
311        assert_eq!(true, string_is_truey("yEs"));
312    }
313
314    #[test]
315    fn TEST_string_is_truthy_1() {
316        assert_eq!(None, string_is_truthy(""));
317
318        assert_eq!(None, string_is_truthy("Nyet"));
319        assert_eq!(None, string_is_truthy("NYET"));
320        assert_eq!(None, string_is_truthy("nyET"));
321        assert_eq!(None, string_is_truthy("nope"));
322        assert_eq!(None, string_is_truthy("Nope"));
323        assert_eq!(None, string_is_truthy("NOPE"));
324
325        assert_eq!(None, string_is_truthy("Da"));
326        assert_eq!(None, string_is_truthy("DA"));
327        assert_eq!(None, string_is_truthy("dA"));
328        assert_eq!(None, string_is_truthy("yup"));
329        assert_eq!(None, string_is_truthy("Yup"));
330        assert_eq!(None, string_is_truthy("yUp"));
331
332        assert_eq!(Some(false), string_is_truthy("0"));
333        assert_eq!(Some(false), string_is_truthy("false"));
334        assert_eq!(Some(false), string_is_truthy(" FALSE"));
335        assert_eq!(Some(false), string_is_truthy("False"));
336        assert_eq!(Some(false), string_is_truthy("FaLSe"));
337        assert_eq!(Some(false), string_is_truthy("no"));
338        assert_eq!(Some(false), string_is_truthy("No "));
339        assert_eq!(Some(false), string_is_truthy("NO"));
340        assert_eq!(Some(false), string_is_truthy(" Off "));
341        assert_eq!(Some(false), string_is_truthy("off"));
342        assert_eq!(Some(false), string_is_truthy("OFF"));
343
344        assert_eq!(Some(true), string_is_truthy("1"));
345        assert_eq!(Some(true), string_is_truthy("true"));
346        assert_eq!(Some(true), string_is_truthy("TRUE"));
347        assert_eq!(Some(true), string_is_truthy("True"));
348        assert_eq!(Some(true), string_is_truthy("tRuE"));
349        assert_eq!(Some(true), string_is_truthy("yes"));
350        assert_eq!(Some(true), string_is_truthy(" YES"));
351        assert_eq!(Some(true), string_is_truthy("Yes   "));
352        assert_eq!(Some(true), string_is_truthy("yEs"));
353    }
354
355    #[test]
356    fn TEST_string_is_truthy_with_1() {
357        #[rustfmt::skip]
358        const TRUEY_PRECISE_STRINGS : &[&str] = &[
359            "Da",
360            "YUP",
361            "Yup",
362        ];
363        #[rustfmt::skip]
364        const TRUEY_LOWERCASE_STRINGS : &[&str] = &[
365            "da",
366            "yup",
367        ];
368        #[rustfmt::skip]
369        const FALSEY_PRECISE_STRINGS : &[&str] = &[
370            "Nyet",
371            "Nope",
372        ];
373        #[rustfmt::skip]
374        const FALSEY_LOWERCASE_STRINGS : &[&str] = &[
375            "nyet",
376            "nope",
377        ];
378
379        let terms = Terms::Strings {
380            falsey_precise_strings :   FALSEY_PRECISE_STRINGS,
381            falsey_lowercase_strings : FALSEY_LOWERCASE_STRINGS,
382            truey_precise_strings :    TRUEY_PRECISE_STRINGS,
383            truey_lowercase_strings :  TRUEY_LOWERCASE_STRINGS,
384        };
385
386        assert_eq!(Some(false), string_is_truthy_with("Nyet", terms.clone()));
387        assert_eq!(Some(false), string_is_truthy_with("NYET", terms.clone()));
388        assert_eq!(Some(false), string_is_truthy_with("nyET", terms.clone()));
389        assert_eq!(Some(false), string_is_truthy_with("nope", terms.clone()));
390        assert_eq!(Some(false), string_is_truthy_with("Nope", terms.clone()));
391        assert_eq!(Some(false), string_is_truthy_with("NOPE", terms.clone()));
392
393        assert_eq!(Some(true), string_is_truthy_with("Da", terms.clone()));
394        assert_eq!(Some(true), string_is_truthy_with("DA", terms.clone()));
395        assert_eq!(Some(true), string_is_truthy_with("dA", terms.clone()));
396        assert_eq!(Some(true), string_is_truthy_with("yup", terms.clone()));
397        assert_eq!(Some(true), string_is_truthy_with("Yup", terms.clone()));
398        assert_eq!(Some(true), string_is_truthy_with("yUp", terms.clone()));
399
400        assert_eq!(None, string_is_truthy_with("", terms.clone()));
401
402        assert_eq!(None, string_is_truthy_with("0", terms.clone()));
403        assert_eq!(None, string_is_truthy_with("false", terms.clone()));
404        assert_eq!(None, string_is_truthy_with(" FALSE", terms.clone()));
405        assert_eq!(None, string_is_truthy_with("False", terms.clone()));
406        assert_eq!(None, string_is_truthy_with("FaLSe", terms.clone()));
407        assert_eq!(None, string_is_truthy_with("no", terms.clone()));
408        assert_eq!(None, string_is_truthy_with("No ", terms.clone()));
409        assert_eq!(None, string_is_truthy_with("NO", terms.clone()));
410        assert_eq!(None, string_is_truthy_with(" Off ", terms.clone()));
411        assert_eq!(None, string_is_truthy_with("off", terms.clone()));
412        assert_eq!(None, string_is_truthy_with("OFF", terms.clone()));
413
414        assert_eq!(None, string_is_truthy_with("1", terms.clone()));
415        assert_eq!(None, string_is_truthy_with("true", terms.clone()));
416        assert_eq!(None, string_is_truthy_with("TRUE", terms.clone()));
417        assert_eq!(None, string_is_truthy_with("True", terms.clone()));
418        assert_eq!(None, string_is_truthy_with("tRuE", terms.clone()));
419        assert_eq!(None, string_is_truthy_with("yes", terms.clone()));
420        assert_eq!(None, string_is_truthy_with(" YES", terms.clone()));
421        assert_eq!(None, string_is_truthy_with("Yes   ", terms.clone()));
422        assert_eq!(None, string_is_truthy_with("yEs", terms.clone()));
423    }
424
425    #[test]
426    fn TEST_str_Truthy_is_falsey_1() {
427        assert_eq!(false, "".is_falsey());
428
429        assert_eq!(true, "0".is_falsey());
430        assert_eq!(true, "false".is_falsey());
431        assert_eq!(true, " FALSE".is_falsey());
432        assert_eq!(true, "False".is_falsey());
433        assert_eq!(true, "FaLSe".is_falsey());
434        assert_eq!(true, "no".is_falsey());
435        assert_eq!(true, "No ".is_falsey());
436        assert_eq!(true, "NO".is_falsey());
437        assert_eq!(true, " Off ".is_falsey());
438        assert_eq!(true, "off".is_falsey());
439        assert_eq!(true, "OFF".is_falsey());
440
441        assert_eq!(false, "1".is_falsey());
442        assert_eq!(false, "true".is_falsey());
443        assert_eq!(false, "TRUE".is_falsey());
444        assert_eq!(false, "True".is_falsey());
445        assert_eq!(false, "tRuE".is_falsey());
446        assert_eq!(false, "yes".is_falsey());
447        assert_eq!(false, " YES".is_falsey());
448        assert_eq!(false, "Yes   ".is_falsey());
449        assert_eq!(false, "yEs".is_falsey());
450    }
451
452    #[test]
453    fn TEST_str_Truthy_is_truey_1() {
454        assert_eq!(false, "".is_truey());
455
456        assert_eq!(false, "0".is_truey());
457        assert_eq!(false, "false".is_truey());
458        assert_eq!(false, " FALSE".is_truey());
459        assert_eq!(false, "False".is_truey());
460        assert_eq!(false, "FaLSe".is_truey());
461        assert_eq!(false, "no".is_truey());
462        assert_eq!(false, "No ".is_truey());
463        assert_eq!(false, "NO".is_truey());
464        assert_eq!(false, " Off ".is_truey());
465        assert_eq!(false, "off".is_truey());
466        assert_eq!(false, "OFF".is_truey());
467
468        assert_eq!(true, "1".is_truey());
469        assert_eq!(true, "true".is_truey());
470        assert_eq!(true, "TRUE".is_truey());
471        assert_eq!(true, "True".is_truey());
472        assert_eq!(true, "tRuE".is_truey());
473        assert_eq!(true, "yes".is_truey());
474        assert_eq!(true, " YES".is_truey());
475        assert_eq!(true, "Yes   ".is_truey());
476        assert_eq!(true, "yEs".is_truey());
477    }
478}