ferroui_base/utilities/
identifier_parser.rs1use super::character_reader::{is_digit, is_identifier_part_category, is_letter, CharacterReader};
4
5impl<'a> CharacterReader<'a> {
6 pub fn parse_identifier(&mut self) -> &'a str {
11 if self.peek().is_some_and(is_valid_identifier_start) {
12 self.take_while(is_valid_identifier_char)
13 } else {
14 ""
15 }
16 }
17
18 pub fn parse_type_identifier(&mut self) -> &'a str {
20 if self.peek().is_some_and(is_valid_identifier_start) {
21 self.take_while(|c| is_valid_identifier_char(c) || c == '+')
22 } else {
23 ""
24 }
25 }
26
27 pub fn parse_number(&mut self) -> &'a str {
29 self.take_while(is_valid_number_char)
30 }
31}
32
33fn is_valid_identifier_start(c: char) -> bool {
34 is_letter(c) || c == '_'
35}
36
37fn is_valid_identifier_char(c: char) -> bool {
38 is_valid_identifier_start(c) || is_identifier_part_category(c)
39}
40
41fn is_valid_number_char(c: char) -> bool {
42 is_digit(c)
43}
44
45#[cfg(test)]
46mod tests {
47 use super::*;
48
49 #[test]
50 fn parses_identifier() {
51 let mut r = CharacterReader::new("_Foo1_b\u{0301}.Bar");
52 assert_eq!(r.parse_identifier(), "_Foo1_b\u{0301}");
53 assert_eq!(r.position(), 8);
54 assert_eq!(r.peek(), Some('.'));
55 assert_eq!(r.parse_identifier(), "");
56 assert_eq!(r.position(), 8);
57 }
58
59 #[test]
60 fn identifier_cannot_start_with_digit_or_symbol() {
61 assert_eq!(CharacterReader::new("1Foo").parse_identifier(), "");
62 assert_eq!(CharacterReader::new("%Foo").parse_identifier(), "");
63 assert_eq!(CharacterReader::new("-Foo").parse_identifier(), "");
64 assert_eq!(CharacterReader::new("").parse_identifier(), "");
65 }
66
67 #[test]
68 fn identifier_stops_at_plus_and_dash() {
69 assert_eq!(CharacterReader::new("Outer+Inner").parse_identifier(), "Outer");
70 assert_eq!(CharacterReader::new("min-width").parse_identifier(), "min");
71 }
72
73 #[test]
74 fn parses_unicode_identifier() {
75 assert_eq!(CharacterReader::new("Żółw9 x").parse_identifier(), "Żółw9");
76 }
77
78 #[test]
79 fn parses_type_identifier() {
80 let mut r = CharacterReader::new("Outer+Inner)");
81 assert_eq!(r.parse_type_identifier(), "Outer+Inner");
82 assert_eq!(r.peek(), Some(')'));
83 assert_eq!(CharacterReader::new("+Inner").parse_type_identifier(), "");
84 assert_eq!(CharacterReader::new("").parse_type_identifier(), "");
85 }
86
87 #[test]
88 fn parses_number() {
89 let mut r = CharacterReader::new("123px");
90 assert_eq!(r.parse_number(), "123");
91 assert_eq!(r.parse_number(), "");
92 assert_eq!(r.position(), 3);
93 assert_eq!(CharacterReader::new("-1").parse_number(), "");
94 }
95}