wasm_css/components/
from_css.rs

1// Authors: Robert Lopez
2
3use super::{effect::Effect, Components};
4use std::collections::BTreeMap;
5
6// TODO: Handle nested effects like: @media (max-width: 800px) { &:hover { ... } }
7
8#[derive(Debug)]
9enum State {
10    FieldKey,
11    EffectKey,
12    FieldValue,
13    EffectKeyInner,
14    EffectValueInner,
15}
16
17impl Components {
18    pub(crate) fn from_css(css: &str) -> Self {
19        let css_bytes = css.as_bytes();
20        let mut fields = BTreeMap::new();
21        let mut effects = vec![];
22
23        let mut state = None;
24        let mut key_buffer = vec![];
25        let mut value_buffer = vec![];
26        let mut effect_buffer = Effect::new("".to_string());
27        let mut previous_byte = None::<u8>;
28        let mut key_inner_buffer = vec![];
29        let mut value_inner_buffer = vec![];
30
31        for (byte_index, byte) in css.as_bytes().iter().enumerate() {
32            match byte {
33                b'\n' => {}
34                b' ' => {
35                    macro_rules! handle_space {
36                        ($buffer:expr) => {
37                            if !$buffer.is_empty() {
38                                let previous_byte = previous_byte.unwrap();
39                                if previous_byte != b' '
40                                    && previous_byte != b'('
41                                    && !previous_byte.is_ascii_digit()
42                                    && !css_bytes.get(byte_index + 1).is_some_and(|b| b == &b')')
43                                {
44                                    $buffer.push(*byte);
45                                }
46                            }
47                        };
48                    }
49
50                    match state {
51                        Some(State::EffectKey) => {
52                            handle_space!(&mut key_buffer);
53                        }
54                        Some(State::FieldValue) => {
55                            handle_space!(&mut value_buffer);
56                        }
57                        Some(State::EffectValueInner) => {
58                            handle_space!(&mut value_inner_buffer);
59                        }
60                        _ => {}
61                    }
62                }
63                b'&' | b'@' => {
64                    state = Some(State::EffectKey);
65
66                    key_buffer.push(*byte);
67                }
68                b'{' => {
69                    state = Some(State::EffectKeyInner);
70                }
71                b':' => match state {
72                    Some(State::FieldKey) => {
73                        state = Some(State::FieldValue);
74                    }
75                    Some(State::EffectKey) => {
76                        key_buffer.push(*byte);
77                    }
78                    Some(State::EffectKeyInner) => {
79                        state = Some(State::EffectValueInner);
80                    }
81                    _ => {
82                        // TODO
83                    }
84                },
85                b';' => match state {
86                    Some(State::FieldValue) => {
87                        let mut key_bytes = vec![b'\t'];
88                        key_bytes.append(&mut key_buffer);
89
90                        match state {
91                            Some(State::FieldValue) => {
92                                let mut value_bytes = vec![];
93                                value_bytes.append(&mut value_buffer);
94
95                                key_bytes.extend_from_slice(b": ");
96                                value_bytes.extend_from_slice(b";\n");
97
98                                unsafe {
99                                    fields.insert(
100                                        String::from_utf8_unchecked(key_bytes),
101                                        String::from_utf8_unchecked(value_bytes),
102                                    );
103                                }
104                            }
105                            _ => {
106                                // TODO
107                            }
108                        }
109
110                        state = None;
111                    }
112                    Some(State::EffectValueInner) => {
113                        let mut effect_key_bytes = vec![b'\t'; 2];
114                        effect_key_bytes.append(&mut key_inner_buffer);
115                        effect_key_bytes.extend_from_slice(b": ");
116
117                        let mut effect_value_bytes = vec![];
118                        effect_value_bytes.append(&mut value_inner_buffer);
119                        effect_value_bytes.extend_from_slice(b";\n");
120
121                        unsafe {
122                            effect_buffer.fields.insert(
123                                String::from_utf8_unchecked(effect_key_bytes),
124                                String::from_utf8_unchecked(effect_value_bytes),
125                            );
126                        }
127
128                        state = Some(State::EffectKeyInner);
129                    }
130                    _ => {
131                        // TODO
132                    }
133                },
134                b'}' => match state {
135                    Some(State::EffectKeyInner) => {
136                        let mut key_bytes = vec![b'\t'];
137                        key_bytes.append(&mut key_buffer);
138
139                        match state {
140                            Some(State::EffectKeyInner) => {
141                                key_bytes.extend_from_slice(b"{\n");
142
143                                let mut effect =
144                                    Effect::new(unsafe { String::from_utf8_unchecked(key_bytes) });
145                                std::mem::swap(&mut effect.fields, &mut effect_buffer.fields);
146
147                                effects.push(effect);
148                            }
149                            _ => {
150                                // TODO
151                            }
152                        }
153
154                        state = None;
155                    }
156                    _ => {
157                        // TODO
158                    }
159                },
160                byte => match state {
161                    Some(State::FieldKey) | Some(State::EffectKey) => {
162                        key_buffer.push(*byte);
163                    }
164                    Some(State::FieldValue) => {
165                        value_buffer.push(*byte);
166                    }
167                    Some(State::EffectKeyInner) => {
168                        key_inner_buffer.push(*byte);
169                    }
170                    Some(State::EffectValueInner) => {
171                        value_inner_buffer.push(*byte);
172                    }
173                    None => {
174                        state = Some(State::FieldKey);
175
176                        key_buffer.push(*byte);
177                    }
178                },
179            }
180
181            previous_byte = Some(*byte);
182        }
183
184        Self { fields, effects }
185    }
186}