Skip to main content

kernal/
character.rs

1//! Contains assertions for [char] values. See the [CharacterAssertions] trait for more details.
2
3use std::borrow::Borrow;
4
5use crate::{AssertThat, Failure};
6
7/// An extension trait to be used on the output of [assert_that](crate::assert_that) with [char]
8/// argument.
9///
10/// Examples:
11///
12/// ```
13/// use kernal::prelude::*;
14///
15/// assert_that!('a').is_alphabetic().is_lowercase().is_not_numeric().is_not_whitespace();
16/// assert_that!('o').is_contained_in("hello").is_not_prefix_of("world");
17/// ```
18pub trait CharacterAssertions {
19    /// Asserts that the tested character is a whitespace character, i.e. that
20    /// [char::is_whitespace] is `true`.
21    fn is_whitespace(self) -> Self;
22
23    /// Asserts that the tested character is not a whitespace character, i.e. that
24    /// [char::is_whitespace] is `false`.
25    fn is_not_whitespace(self) -> Self;
26
27    /// Asserts that the tested character is an alphabetic character, i.e. that
28    /// [char::is_alphabetic] is `true`.
29    fn is_alphabetic(self) -> Self;
30
31    /// Asserts that the tested character is not an alphabetic character, i.e. that
32    /// [char::is_alphabetic] is `false`.
33    fn is_not_alphabetic(self) -> Self;
34
35    /// Asserts that the tested character is a numeric character, i.e. that  [char::is_numeric] is
36    /// `true`.
37    fn is_numeric(self) -> Self;
38
39    /// Asserts that the tested character is not a numeric character, i.e. that  [char::is_numeric]
40    /// is `false`.
41    fn is_not_numeric(self) -> Self;
42
43    /// Asserts that the tested character is an alphanumeric character, i.e. that
44    /// [char::is_alphanumeric] is `true`.
45    fn is_alphanumeric(self) -> Self;
46
47    /// Asserts that the tested character is not an alphanumeric character, i.e. that
48    /// [char::is_alphanumeric] is `false`.
49    fn is_not_alphanumeric(self) -> Self;
50
51    /// Asserts that the tested character is an uppercase letter, i.e. that [char::is_uppercase] is
52    /// `true`.
53    fn is_uppercase(self) -> Self;
54
55    /// Asserts that the tested character is not an uppercase letter, i.e. that [char::is_uppercase]
56    /// is `false`.
57    fn is_not_uppercase(self) -> Self;
58
59    /// Asserts that the tested character is a lowercase letter, i.e. that [char::is_lowercase] is
60    /// `true`.
61    fn is_lowercase(self) -> Self;
62
63    /// Asserts that the tested character is not a lowercase letter, i.e. that [char::is_lowercase]
64    /// is `false`.
65    fn is_not_lowercase(self) -> Self;
66
67    /// Asserts that the tested character is a control character, i.e. that [char::is_control] is
68    /// `true`.
69    fn is_control(self) -> Self;
70
71    /// Asserts that the tested character is not a control character, i.e. that [char::is_control]
72    /// is `false`.
73    fn is_not_control(self) -> Self;
74
75    /// Asserts that any of the characters of the given `string` (obtained by [str::chars]) is equal
76    /// to the tested character.
77    fn is_contained_in<S: Borrow<str>>(self, string: S) -> Self;
78
79    /// Asserts that none of the characters of the given `string` (obtained by [str::chars]) is
80    /// equal to the tested character.
81    fn is_not_contained_in<S: Borrow<str>>(self, string: S) -> Self;
82
83    /// Asserts that the tested character is equal to the first character of the given `string`
84    /// (obtained by [str::chars]).
85    fn is_prefix_of<S: Borrow<str>>(self, string: S) -> Self;
86
87    /// Asserts that the tested character is not equal to the first character of the given `string`
88    /// (obtained by [str::chars]).
89    fn is_not_prefix_of<S: Borrow<str>>(self, string: S) -> Self;
90
91    /// Asserts that the tested character is equal to the last character of the given `string`
92    /// (obtained by [str::chars]).
93    fn is_suffix_of<S: Borrow<str>>(self, string: S) -> Self;
94
95    /// Asserts that the tested character is not equal to the last character of the given `string`
96    /// (obtained by [str::chars]).
97    fn is_not_suffix_of<S: Borrow<str>>(self, string: S) -> Self;
98
99    /// Asserts that the tested character is equal to the given `expected` character ignoring
100    /// casing, i.e. upper-case letters are considered equal to their corresponding lower-case
101    /// letters. That is, this assertion will pass if `'a'` is compared to `'A'`.
102    fn is_equal_to_ignoring_case(self, expected: char) -> Self;
103
104    /// Asserts that the tested character is different from the given `unexpected` character
105    /// ignoring casing, i.e. upper-case letters are considered equal to their corresponding
106    /// lower-case letters. That is, this assertion will fail if `'a'` is compared to `'A'`.
107    fn is_not_equal_to_ignoring_case(self, unexpected: char) -> Self;
108}
109
110fn assert_char_matches_predicate<P, S>(
111    assert_that: AssertThat<char>,
112    predicate: P,
113    expected_it: S,
114) -> AssertThat<char>
115where
116    P: Fn(char) -> bool,
117    S: Into<String>,
118{
119    if !predicate(assert_that.data) {
120        Failure::new(&assert_that)
121            .expected_it(expected_it)
122            .but_it(format!("was <{}>", assert_that.data.escape_debug()))
123            .fail();
124    }
125
126    assert_that
127}
128
129impl CharacterAssertions for AssertThat<char> {
130    fn is_whitespace(self) -> Self {
131        assert_char_matches_predicate(
132            self,
133            |character| character.is_whitespace(),
134            "to be a whitespace character",
135        )
136    }
137
138    fn is_not_whitespace(self) -> Self {
139        assert_char_matches_predicate(
140            self,
141            |character| !character.is_whitespace(),
142            "not to be a whitespace character",
143        )
144    }
145
146    fn is_alphabetic(self) -> Self {
147        assert_char_matches_predicate(
148            self,
149            |character| character.is_alphabetic(),
150            "to be an alphabetic character",
151        )
152    }
153
154    fn is_not_alphabetic(self) -> Self {
155        assert_char_matches_predicate(
156            self,
157            |character| !character.is_alphabetic(),
158            "not to be an alphabetic character",
159        )
160    }
161
162    fn is_numeric(self) -> Self {
163        assert_char_matches_predicate(
164            self,
165            |character| character.is_numeric(),
166            "to be a numeric character",
167        )
168    }
169
170    fn is_not_numeric(self) -> Self {
171        assert_char_matches_predicate(
172            self,
173            |character| !character.is_numeric(),
174            "not to be a numeric character",
175        )
176    }
177
178    fn is_alphanumeric(self) -> Self {
179        assert_char_matches_predicate(
180            self,
181            |character| character.is_alphanumeric(),
182            "to be an alphanumeric character",
183        )
184    }
185
186    fn is_not_alphanumeric(self) -> Self {
187        assert_char_matches_predicate(
188            self,
189            |character| !character.is_alphanumeric(),
190            "not to be an alphanumeric character",
191        )
192    }
193
194    fn is_uppercase(self) -> Self {
195        assert_char_matches_predicate(
196            self,
197            |character| character.is_uppercase(),
198            "to be an uppercase character",
199        )
200    }
201
202    fn is_not_uppercase(self) -> Self {
203        assert_char_matches_predicate(
204            self,
205            |character| !character.is_uppercase(),
206            "not to be an uppercase character",
207        )
208    }
209
210    fn is_lowercase(self) -> Self {
211        assert_char_matches_predicate(
212            self,
213            |character| character.is_lowercase(),
214            "to be a lowercase character",
215        )
216    }
217
218    fn is_not_lowercase(self) -> Self {
219        assert_char_matches_predicate(
220            self,
221            |character| !character.is_lowercase(),
222            "not to be a lowercase character",
223        )
224    }
225
226    fn is_control(self) -> Self {
227        assert_char_matches_predicate(
228            self,
229            |character| character.is_control(),
230            "to be a control character",
231        )
232    }
233
234    fn is_not_control(self) -> Self {
235        assert_char_matches_predicate(
236            self,
237            |character| !character.is_control(),
238            "not to be a control character",
239        )
240    }
241
242    fn is_contained_in<S: Borrow<str>>(self, string: S) -> Self {
243        let string = string.borrow();
244
245        assert_char_matches_predicate(
246            self,
247            |character| {
248                string
249                    .chars()
250                    .any(|string_character| character == string_character)
251            },
252            format!("to be contained in <{}>", string.escape_debug()),
253        )
254    }
255
256    fn is_not_contained_in<S: Borrow<str>>(self, string: S) -> Self {
257        let string = string.borrow();
258
259        assert_char_matches_predicate(
260            self,
261            |character| {
262                string
263                    .chars()
264                    .all(|string_character| character != string_character)
265            },
266            format!("not to be contained in <{}>", string.escape_debug()),
267        )
268    }
269
270    fn is_prefix_of<S: Borrow<str>>(self, string: S) -> Self {
271        let string = string.borrow();
272
273        assert_char_matches_predicate(
274            self,
275            |character| string.starts_with(character),
276            format!("to be the first character of <{}>", string.escape_debug()),
277        )
278    }
279
280    fn is_not_prefix_of<S: Borrow<str>>(self, string: S) -> Self {
281        let string = string.borrow();
282
283        assert_char_matches_predicate(
284            self,
285            |character| !string.starts_with(character),
286            format!(
287                "not to be the first character of <{}>",
288                string.escape_debug()
289            ),
290        )
291    }
292
293    fn is_suffix_of<S: Borrow<str>>(self, string: S) -> Self {
294        let string = string.borrow();
295
296        assert_char_matches_predicate(
297            self,
298            |character| string.ends_with(character),
299            format!("to be the last character of <{}>", string.escape_debug()),
300        )
301    }
302
303    fn is_not_suffix_of<S: Borrow<str>>(self, string: S) -> Self {
304        let string = string.borrow();
305
306        assert_char_matches_predicate(
307            self,
308            |character| !string.ends_with(character),
309            format!(
310                "not to be the last character of <{}>",
311                string.escape_debug()
312            ),
313        )
314    }
315
316    fn is_equal_to_ignoring_case(self, expected: char) -> Self {
317        assert_char_matches_predicate(
318            self,
319            |character| character.to_lowercase().to_string() == expected.to_lowercase().to_string(),
320            format!("to equal <{}> ignoring case", expected.escape_debug()),
321        )
322    }
323
324    fn is_not_equal_to_ignoring_case(self, expected: char) -> Self {
325        assert_char_matches_predicate(
326            self,
327            |character| character.to_lowercase().to_string() != expected.to_lowercase().to_string(),
328            format!("not to equal <{}> ignoring case", expected.escape_debug()),
329        )
330    }
331}
332
333#[cfg(test)]
334mod tests {
335
336    use super::*;
337    use crate::{assert_fails, assert_that};
338
339    #[test]
340    fn is_whitespace_passes_for_space() {
341        assert_that!(' ').is_whitespace();
342    }
343
344    #[test]
345    fn is_whitespace_passes_for_newline() {
346        assert_that!('\n').is_whitespace();
347    }
348
349    #[test]
350    fn is_whitespace_fails_for_a() {
351        assert_fails!(('a').is_whitespace(),
352            expected it "to be a whitespace character"
353            but it "was <a>");
354    }
355
356    #[test]
357    fn is_not_whitespace_passes_for_a() {
358        assert_that!('a').is_not_whitespace();
359    }
360
361    #[test]
362    fn is_not_whitespace_fails_for_space() {
363        assert_fails!((' ').is_not_whitespace(),
364            expected it "not to be a whitespace character"
365            but it "was < >");
366    }
367
368    #[test]
369    fn is_not_whitespace_fails_for_newline() {
370        assert_fails!(('\n').is_not_whitespace(),
371            expected it "not to be a whitespace character"
372            but it "was <\\n>");
373    }
374
375    #[test]
376    fn is_alphabetic_passes_for_a() {
377        assert_that!('a').is_alphabetic();
378    }
379
380    #[test]
381    fn is_alphabetic_fails_for_0() {
382        assert_fails!(('0').is_alphabetic(),
383            expected it "to be an alphabetic character"
384            but it "was <0>");
385    }
386
387    #[test]
388    fn is_not_alphabetic_passes_for_0() {
389        assert_that!('0').is_not_alphabetic();
390    }
391
392    #[test]
393    fn is_not_alphabetic_fails_for_a() {
394        assert_fails!(('a').is_not_alphabetic(),
395            expected it "not to be an alphabetic character"
396            but it "was <a>");
397    }
398
399    #[test]
400    fn is_numeric_passes_for_0() {
401        assert_that!('0').is_numeric();
402    }
403
404    #[test]
405    fn is_numeric_fails_for_a() {
406        assert_fails!(('a').is_numeric(),
407            expected it "to be a numeric character"
408            but it "was <a>");
409    }
410
411    #[test]
412    fn is_not_numeric_passes_for_a() {
413        assert_that!('a').is_not_numeric();
414    }
415
416    #[test]
417    fn is_not_numeric_fails_for_0() {
418        assert_fails!(('0').is_not_numeric(),
419            expected it "not to be a numeric character"
420            but it "was <0>");
421    }
422
423    #[test]
424    fn is_alphanumeric_passes_for_0() {
425        assert_that!('0').is_alphanumeric();
426    }
427
428    #[test]
429    fn is_alphanumeric_passes_for_a() {
430        assert_that!('a').is_alphanumeric();
431    }
432
433    #[test]
434    fn is_alphanumeric_fails_for_period() {
435        assert_fails!(('.').is_alphanumeric(),
436            expected it "to be an alphanumeric character"
437            but it "was <.>");
438    }
439
440    #[test]
441    fn is_not_alphanumeric_passes_for_period() {
442        assert_that!('.').is_not_alphanumeric();
443    }
444
445    #[test]
446    fn is_not_alphanumeric_fails_for_0() {
447        assert_fails!(('0').is_not_alphanumeric(),
448            expected it "not to be an alphanumeric character"
449            but it "was <0>");
450    }
451
452    #[test]
453    fn is_not_alphanumeric_fails_for_a() {
454        assert_fails!(('a').is_not_alphanumeric(),
455            expected it "not to be an alphanumeric character"
456            but it "was <a>");
457    }
458
459    #[test]
460    fn is_uppercase_passes_for_uppercase_a() {
461        assert_that!('A').is_uppercase();
462    }
463
464    #[test]
465    fn is_uppercase_fails_for_lowercase_a() {
466        assert_fails!(('a').is_uppercase(),
467            expected it "to be an uppercase character"
468            but it "was <a>");
469    }
470
471    #[test]
472    fn is_not_uppercase_passes_for_lowercase_a() {
473        assert_that!('a').is_not_uppercase();
474    }
475
476    #[test]
477    fn is_not_uppercase_fails_for_uppercase_a() {
478        assert_fails!(('A').is_not_uppercase(),
479            expected it "not to be an uppercase character"
480            but it "was <A>");
481    }
482
483    #[test]
484    fn is_lowercase_passes_for_lowercase_a() {
485        assert_that!('a').is_lowercase();
486    }
487
488    #[test]
489    fn is_lowercase_fails_for_uppercase_a() {
490        assert_fails!(('A').is_lowercase(),
491            expected it "to be a lowercase character"
492            but it "was <A>");
493    }
494
495    #[test]
496    fn is_not_lowercase_passes_for_uppercase_a() {
497        assert_that!('A').is_not_lowercase();
498    }
499
500    #[test]
501    fn is_not_lowercase_fails_for_lowercase_a() {
502        assert_fails!(('a').is_not_lowercase(),
503            expected it "not to be a lowercase character"
504            but it "was <a>");
505    }
506
507    #[test]
508    fn is_control_passes_for_backspace() {
509        assert_that!('\x08').is_control();
510    }
511
512    #[test]
513    fn is_control_fails_for_a() {
514        assert_fails!(('a').is_control(),
515            expected it "to be a control character"
516            but it "was <a>");
517    }
518
519    #[test]
520    fn is_not_control_passes_for_a() {
521        assert_that!('a').is_not_control();
522    }
523
524    #[test]
525    fn is_not_control_fails_for_backspace() {
526        assert_fails!(('\x08').is_not_control(),
527            expected it "not to be a control character"
528            but it "was <\\u{8}>");
529    }
530
531    #[test]
532    fn is_contained_in_passes_for_only_character_in_string() {
533        assert_that!('a').is_contained_in("a");
534    }
535
536    #[test]
537    fn is_contained_in_passes_for_second_character_in_string() {
538        assert_that!('b').is_contained_in("ab");
539    }
540
541    #[test]
542    fn is_contained_in_fails_for_empty_string() {
543        assert_fails!(('a').is_contained_in(""),
544            expected it "to be contained in <>"
545            but it "was <a>");
546    }
547
548    #[test]
549    fn is_contained_in_fails_for_character_which_is_not_in_non_empty_string() {
550        assert_fails!(('a').is_contained_in("bc"),
551            expected it "to be contained in <bc>"
552            but it "was <a>");
553    }
554
555    #[test]
556    fn is_not_contained_in_passes_for_empty_string() {
557        assert_that!('a').is_not_contained_in("");
558    }
559
560    #[test]
561    fn is_not_contained_in_passes_for_character_which_is_not_in_non_empty_string() {
562        assert_that!('a').is_not_contained_in("bc");
563    }
564
565    #[test]
566    fn is_not_contained_in_fails_for_only_character_in_string() {
567        assert_fails!(('a').is_not_contained_in("a"),
568            expected it "not to be contained in <a>"
569            but it "was <a>");
570    }
571
572    #[test]
573    fn is_not_contained_in_fails_for_second_character_in_string() {
574        assert_fails!(('b').is_not_contained_in("ab"),
575            expected it "not to be contained in <ab>"
576            but it "was <b>");
577    }
578
579    #[test]
580    fn is_prefix_passes_for_only_character_in_string() {
581        assert_that!('a').is_prefix_of("a");
582    }
583
584    #[test]
585    fn is_prefix_passes_for_first_character_in_string() {
586        assert_that!('a').is_prefix_of("ab");
587    }
588
589    #[test]
590    fn is_prefix_fails_for_empty_string() {
591        assert_fails!(('a').is_prefix_of(""),
592            expected it "to be the first character of <>"
593            but it "was <a>");
594    }
595
596    #[test]
597    fn is_prefix_fails_for_second_character_in_string() {
598        assert_fails!(('b').is_prefix_of("ab"),
599            expected it "to be the first character of <ab>"
600            but it "was <b>");
601    }
602
603    #[test]
604    fn is_not_prefix_passes_for_empty_string() {
605        assert_that!('a').is_not_prefix_of("");
606    }
607
608    #[test]
609    fn is_not_prefix_passes_for_second_character_in_string() {
610        assert_that!('b').is_not_prefix_of("ab");
611    }
612
613    #[test]
614    fn is_not_prefix_fails_for_only_character_in_string() {
615        assert_fails!(('a').is_not_prefix_of("a"),
616            expected it "not to be the first character of <a>"
617            but it "was <a>");
618    }
619
620    #[test]
621    fn is_not_prefix_fails_for_first_character_in_string() {
622        assert_fails!(('a').is_not_prefix_of("ab"),
623            expected it "not to be the first character of <ab>"
624            but it "was <a>");
625    }
626
627    #[test]
628    fn is_suffix_passes_for_only_character_in_string() {
629        assert_that!('a').is_suffix_of("a");
630    }
631
632    #[test]
633    fn is_suffix_passes_for_last_character_in_string() {
634        assert_that!('b').is_suffix_of("ab");
635    }
636
637    #[test]
638    fn is_suffix_fails_for_empty_string() {
639        assert_fails!(('a').is_suffix_of(""),
640            expected it "to be the last character of <>"
641            but it "was <a>");
642    }
643
644    #[test]
645    fn is_suffix_fails_for_second_to_last_character_in_string() {
646        assert_fails!(('a').is_suffix_of("ab"),
647            expected it "to be the last character of <ab>"
648            but it "was <a>");
649    }
650
651    #[test]
652    fn is_not_suffix_passes_for_empty_string() {
653        assert_that!('a').is_not_suffix_of("");
654    }
655
656    #[test]
657    fn is_not_suffix_passes_for_second_to_last_character_in_string() {
658        assert_that!('a').is_not_suffix_of("ab");
659    }
660
661    #[test]
662    fn is_not_suffix_fails_for_only_character_in_string() {
663        assert_fails!(('a').is_not_suffix_of("a"),
664            expected it "not to be the last character of <a>"
665            but it "was <a>");
666    }
667
668    #[test]
669    fn is_not_suffix_fails_for_last_character_in_string() {
670        assert_fails!(('b').is_not_suffix_of("ab"),
671            expected it "not to be the last character of <ab>"
672            but it "was <b>");
673    }
674
675    #[test]
676    fn is_equal_to_ignoring_case_passes_for_same_character() {
677        assert_that!('.').is_equal_to_ignoring_case('.');
678    }
679
680    #[test]
681    fn is_equal_to_ignoring_case_passes_for_lowercase_a_and_uppercase_a() {
682        assert_that!('a').is_equal_to_ignoring_case('A');
683    }
684
685    #[test]
686    fn is_equal_to_ignoring_case_fails_for_different_letters() {
687        assert_fails!(('a').is_equal_to_ignoring_case('b'),
688            expected it "to equal <b> ignoring case"
689            but it "was <a>");
690    }
691
692    #[test]
693    fn is_not_equal_to_ignoring_case_passes_for_different_letters() {
694        assert_that!('a').is_not_equal_to_ignoring_case('b');
695    }
696
697    #[test]
698    fn is_not_equal_to_ignoring_case_fails_for_same_character() {
699        assert_fails!(('.').is_not_equal_to_ignoring_case('.'),
700            expected it "not to equal <.> ignoring case"
701            but it "was <.>");
702    }
703
704    #[test]
705    fn is_not_equal_to_ignoring_case_fails_for_lowercase_a_and_uppercase_a() {
706        assert_fails!(('a').is_not_equal_to_ignoring_case('A'),
707            expected it "not to equal <A> ignoring case"
708            but it "was <a>");
709    }
710}