crml_core/
selector.rs

1/// The extracted data from the given [`Selector].
2#[derive(Clone, Debug)]
3pub struct SelectorState {
4    pub tag: String,
5    pub classes: Option<Vec<String>>,
6    pub id: Option<String>,
7    pub attributes: Option<Vec<String>>,
8}
9
10impl SelectorState {
11    /// Try to save the current buffer to the state (in the correct field).
12    pub fn try_save(&mut self, mode: ParserMode, mut buffer: String) -> String {
13        match mode {
14            ParserMode::None => {
15                if self.tag.is_empty() {
16                    // tag cannot be overwritten
17                    self.tag = buffer;
18                    buffer = String::new();
19                }
20            }
21            ParserMode::Class => {
22                if self.classes.is_none() {
23                    // classes is none; init classes with Some(vec![buffer])
24                    self.classes = Some(vec![buffer]);
25                    buffer = String::new();
26                } else if let Some(ref mut classes) = self.classes {
27                    // classes exists; borrow as mut ref and push buffer
28                    classes.push(buffer);
29                    buffer = String::new();
30                }
31            }
32            ParserMode::Id => {
33                if self.id.is_none() {
34                    // id is none; set to buffer
35                    self.id = Some(buffer);
36                    buffer = String::new();
37                }
38            }
39            ParserMode::Attribute => {
40                if self.attributes.is_none() {
41                    // attributes is none; init attributes with Some(vec![buffer])
42                    self.attributes = Some(vec![buffer]);
43                    buffer = String::new();
44                } else if let Some(ref mut attributes) = self.attributes {
45                    // attributes exists; borrow as mut ref and push buffer
46                    attributes.push(buffer);
47                    buffer = String::new();
48                }
49            }
50        }
51
52        buffer
53    }
54
55    /// Render state to HTML.
56    pub fn render(self) -> String {
57        let mut class_string = String::new();
58        let mut id_string = String::new();
59        let mut attributes_string = String::new();
60
61        if let Some(classes) = self.classes {
62            class_string = " class=\"".to_string();
63
64            for class in classes {
65                class_string.push_str(&(class + " "));
66            }
67
68            class_string += "\"";
69        }
70
71        if let Some(id) = self.id {
72            id_string = format!(" id=\"{id}\"");
73        }
74
75        if let Some(attributes) = self.attributes {
76            for attribute in attributes {
77                attributes_string.push_str(&format!(" {attribute}"));
78            }
79        }
80
81        format!("<{}{class_string}{id_string}{attributes_string}>", self.tag)
82    }
83}
84
85/// The mode of the [`Selector`] parser.
86#[derive(PartialEq, Eq)]
87pub enum ParserMode {
88    None,
89    Class,
90    Id,
91    Attribute,
92}
93
94/// A simple parser for CSS selectors
95pub struct Selector(String);
96
97impl Selector {
98    /// Create a new [`Selector`].
99    pub fn new(input: String) -> Self {
100        Self(input)
101    }
102
103    /// Begin parsing the selector.
104    pub fn parse(self) -> SelectorState {
105        let mut state = SelectorState {
106            tag: String::new(),
107            classes: None,
108            id: None,
109            attributes: None,
110        };
111
112        // parse
113        let mut chars = self.0.chars();
114        let mut mode: ParserMode = ParserMode::None;
115        let mut buffer: String = String::new();
116
117        while let Some(char) = chars.next() {
118            match char {
119                '.' => {
120                    if mode == ParserMode::Attribute {
121                        // this may show up in an attribute commonly!
122                        buffer.push(char);
123                        continue;
124                    }
125
126                    buffer = state.try_save(mode, buffer.clone());
127                    mode = ParserMode::Class
128                }
129                '#' => {
130                    if mode == ParserMode::Attribute {
131                        // this may show up in an attribute commonly!
132                        buffer.push(char);
133                        continue;
134                    }
135
136                    buffer = state.try_save(mode, buffer.clone());
137                    mode = ParserMode::Id
138                }
139                '[' => {
140                    buffer = state.try_save(mode, buffer.clone());
141                    mode = ParserMode::Attribute
142                }
143                ']' => {
144                    buffer = state.try_save(mode, buffer.clone());
145                    mode = ParserMode::None
146                }
147                _ => buffer.push(char),
148            }
149        }
150
151        // return
152        state.try_save(mode, buffer.clone()); // one last save to catch the ending stuff
153        state
154    }
155}