cadmus_core/document/html/
css.rs1use super::style::StyleSheet;
2use fxhash::FxHashSet;
3
4#[derive(Debug, Clone)]
5pub struct Selector {
6 pub simple_selectors: Vec<SimpleSelector>,
7 pub combinators: Vec<Combinator>,
8}
9
10#[derive(Debug, Clone)]
11pub struct SimpleSelector {
12 pub tag_name: Option<String>,
13 pub classes: FxHashSet<String>,
14 pub pseudo_classes: Vec<PseudoClass>,
15 pub attributes: Vec<Attribute>,
16 pub id: Option<String>,
17}
18
19#[derive(Debug, Clone)]
20pub struct Attribute {
21 pub name: String,
22 pub operator: AttributeOperator,
23}
24
25#[derive(Debug, Clone)]
26pub enum PseudoClass {
27 FirstChild,
28 LastChild,
29}
30
31#[derive(Debug, Clone)]
32pub enum AttributeOperator {
33 Exists,
35 Matches(String),
37 Contains(String),
39 StartsWith(String),
41}
42
43#[derive(Debug, Copy, Clone)]
44pub enum Combinator {
45 None,
46 Child,
48 Descendant,
50 NextSibling,
52 SubsequentSibling,
54}
55
56impl Default for Selector {
57 fn default() -> Self {
58 Selector {
59 simple_selectors: Vec::new(),
60 combinators: Vec::new(),
61 }
62 }
63}
64
65impl Default for SimpleSelector {
66 fn default() -> Self {
67 SimpleSelector {
68 tag_name: None,
69 classes: FxHashSet::default(),
70 pseudo_classes: Vec::new(),
71 attributes: Vec::new(),
72 id: None,
73 }
74 }
75}
76
77pub type Specificity = [usize; 3];
78
79impl Selector {
80 pub fn specificity(&self) -> Specificity {
81 let mut spec = [0usize; 3];
82 for sel in &self.simple_selectors {
83 spec[0] = spec[0].saturating_add(sel.id.iter().count());
84 spec[1] = spec[1].saturating_add(sel.classes.len());
85 spec[1] = spec[1].saturating_add(sel.pseudo_classes.len());
86 spec[1] = spec[1].saturating_add(sel.attributes.len());
87 spec[2] = spec[2].saturating_add(sel.tag_name.iter().count());
88 }
89 spec
90 }
91}
92
93#[derive(Debug, Clone)]
94pub struct Declaration {
95 pub name: String,
96 pub value: String,
97 pub important: bool,
98}
99
100impl Default for Declaration {
101 fn default() -> Declaration {
102 Declaration {
103 name: String::default(),
104 value: String::default(),
105 important: false,
106 }
107 }
108}
109
110#[derive(Debug, Clone)]
111pub struct Rule {
112 pub selector: Selector,
113 pub declarations: Vec<Declaration>,
114}
115
116#[derive(Debug)]
117pub struct CssParser<'a> {
118 input: &'a str,
119 offset: usize,
120}
121
122impl<'a> CssParser<'a> {
123 pub fn new(input: &str) -> CssParser<'_> {
124 CssParser { input, offset: 0 }
125 }
126
127 fn eof(&self) -> bool {
128 self.offset >= self.input.len()
129 }
130
131 fn next(&self) -> Option<char> {
132 self.input[self.offset..].chars().next()
133 }
134
135 fn starts_with(&self, s: &str) -> bool {
136 self.input[self.offset..].starts_with(s)
137 }
138
139 fn advance(&mut self, n: usize) {
140 for c in self.input[self.offset..].chars().take(n) {
141 self.offset += c.len_utf8();
142 }
143 }
144
145 fn advance_while<F>(&mut self, test: F)
146 where
147 F: FnMut(&char) -> bool,
148 {
149 for c in self.input[self.offset..].chars().take_while(test) {
150 self.offset += c.len_utf8();
151 }
152 }
153
154 fn advance_until(&mut self, target: &str) {
155 while !self.eof() && !self.starts_with(target) {
156 self.advance(1);
157 }
158 self.advance(target.chars().count());
159 }
160
161 fn skip_spaces_and_comments(&mut self) {
162 loop {
163 let offset = self.offset;
164 self.advance_while(|&c| c.is_whitespace());
165 if self.starts_with("/*") {
166 self.advance(2);
167 self.advance_until("*/");
168 }
169 if offset == self.offset {
170 break;
171 }
172 }
173 }
174
175 fn skip_ident(&mut self) {
176 self.advance_while(|&c| c.is_ascii_alphanumeric() || c == '-' || c == '_');
177 }
178
179 fn skip_block(&mut self) {
180 let mut balance = 0u8;
181
182 while !self.eof() {
183 match self.next() {
184 Some('{') => {
185 self.advance(1);
186 balance = balance.saturating_add(1);
187 }
188 Some('}') => {
189 self.advance(1);
190 balance = balance.saturating_sub(1);
191 }
192 _ => break,
193 }
194 if balance == 0 {
195 break;
196 }
197 self.advance_while(|&c| c != '{' && c != '}')
198 }
199 }
200
201 fn skip_at_rule(&mut self) {
202 self.advance_while(|&c| c != ';' && c != '{');
203
204 match self.next() {
205 Some(';') => self.advance(1),
206 Some('{') => self.skip_block(),
207 _ => (),
208 }
209 }
210
211 fn attribute_value(&mut self) -> String {
212 match self.next() {
213 Some(delim @ '"' | delim @ '\'') => {
214 self.advance(1);
215 let start_offset = self.offset;
216 self.advance_while(|&c| c != delim);
217 let end_offset = self.offset;
218 self.advance(1);
219 self.input[start_offset..end_offset].to_string()
220 }
221 _ => {
222 let offset = self.offset;
223 self.skip_ident();
224 self.input[offset..self.offset].to_string()
225 }
226 }
227 }
228
229 fn parse_selectors(&mut self) -> Vec<Selector> {
230 let mut supported = true;
231 let mut selectors = Vec::new();
232 let mut s = Selector::default();
233 let mut selec = SimpleSelector::default();
234 let mut comb = Combinator::None;
235
236 while !self.eof() {
237 match self.next() {
238 Some('#') => {
239 self.advance(1);
240 let offset = self.offset;
241 self.skip_ident();
242 selec.id = Some(self.input[offset..self.offset].to_string());
243 }
244 Some('.') => {
245 self.advance(1);
246 let offset = self.offset;
247 self.skip_ident();
248 selec
249 .classes
250 .insert(self.input[offset..self.offset].to_string());
251 }
252 Some('[') => {
253 self.advance(1);
254 self.skip_spaces_and_comments();
255 let offset = self.offset;
256 self.skip_ident();
257 let mut name = self.input[offset..self.offset].to_string();
258 if self.next() == Some('|') && !self.starts_with("|=") {
259 self.advance(1);
260 name += ":";
261 let offset = self.offset;
262 self.skip_ident();
263 name += &self.input[offset..self.offset];
264 }
265 self.skip_spaces_and_comments();
266 match self.next() {
267 Some(']') => {
268 self.advance(1);
269 selec.attributes.push(Attribute {
270 name,
271 operator: AttributeOperator::Exists,
272 });
273 }
274 Some('=') => {
275 self.advance(1);
276 self.skip_spaces_and_comments();
277 let value = self.attribute_value();
278 selec.attributes.push(Attribute {
279 name,
280 operator: AttributeOperator::Matches(value),
281 });
282 self.skip_spaces_and_comments();
283 self.advance(1);
284 }
285 Some('~') => {
286 self.advance(2);
287 self.skip_spaces_and_comments();
288 let value = self.attribute_value();
289 selec.attributes.push(Attribute {
290 name,
291 operator: AttributeOperator::Contains(value),
292 });
293 self.skip_spaces_and_comments();
294 self.advance(1);
295 }
296 Some('|') => {
297 self.advance(2);
298 self.skip_spaces_and_comments();
299 let value = self.attribute_value();
300 selec.attributes.push(Attribute {
301 name,
302 operator: AttributeOperator::StartsWith(value),
303 });
304 self.skip_spaces_and_comments();
305 self.advance(1);
306 }
307 _ => {
308 self.advance(2);
309 self.skip_spaces_and_comments();
310 self.advance(1);
311 supported = false;
312 }
313 }
314 }
315 Some(':') => {
316 self.advance(1);
317 if self.next() == Some(':') {
318 supported = false;
319 self.advance(1);
320 }
321 let offset = self.offset;
322 self.skip_ident();
323 match &self.input[offset..self.offset] {
324 "first-child" => {
325 selec.pseudo_classes.push(PseudoClass::FirstChild);
326 }
327 "last-child" => {
328 selec.pseudo_classes.push(PseudoClass::LastChild);
329 }
330 _ => {
331 supported = false;
332 }
333 }
334 if self.next() == Some('(') {
335 self.advance_while(|&c| c != ')');
336 self.advance(1);
337 }
338 }
339 Some('*') => {
340 self.advance(1);
341 }
342 _ => {
343 let offset = self.offset;
344 self.skip_ident();
345 if self.offset > offset {
346 selec.tag_name = Some(self.input[offset..self.offset].to_string());
347 } else {
348 let offset = self.offset;
349 self.skip_spaces_and_comments();
350
351 s.simple_selectors.push(selec);
352 s.combinators.push(comb);
353 selec = SimpleSelector::default();
354
355 match self.next() {
356 Some(',') => {
357 self.advance(1);
358 self.skip_spaces_and_comments();
359 if supported {
360 selectors.push(s);
361 }
362 s = Selector::default();
363 comb = Combinator::None;
364 supported = true;
365 }
366 Some('{') => {
367 self.advance(1);
368 break;
369 }
370 Some('>') => {
371 self.advance(1);
372 self.skip_spaces_and_comments();
373 comb = Combinator::Child;
374 }
375 Some('+') => {
376 self.advance(1);
377 self.skip_spaces_and_comments();
378 comb = Combinator::NextSibling;
379 }
380 Some('~') => {
381 self.advance(1);
382 self.skip_spaces_and_comments();
383 comb = Combinator::SubsequentSibling;
384 }
385 _ => {
386 if self.offset > offset {
387 comb = Combinator::Descendant;
388 } else {
389 self.advance(1);
390 }
391 }
392 }
393 }
394 }
395 }
396 }
397
398 if supported {
399 selectors.push(s);
400 }
401
402 selectors
403 }
404
405 pub fn parse_declarations(&mut self) -> Vec<Declaration> {
406 let mut declarations = Vec::new();
407 let mut d = Declaration::default();
408
409 while !self.eof() {
410 self.skip_spaces_and_comments();
411
412 match self.next() {
413 Some(':') => {
414 self.advance(1);
415 self.skip_spaces_and_comments();
416 let offset = self.offset;
417
418 while !self.eof() {
419 self.advance_while(|&c| c != '"' && c != ';' && c != '}' && c != '!');
420 match self.next() {
421 Some('"') => {
422 self.advance(1);
423 self.advance_while(|&c| c != '"');
424 self.advance(1);
425 }
426 Some('!') => {
427 d.important = true;
428 break;
429 }
430 _ => break,
431 }
432 }
433
434 d.value = self.input[offset..self.offset].trim().to_string();
435 if d.important {
436 self.advance_while(|&c| c != ';' && c != '}');
437 }
438 }
439 Some(';') => {
440 self.advance(1);
441 declarations.push(d);
442 d = Declaration::default();
443 }
444 Some('}') => {
445 self.advance(1);
446 break;
447 }
448 _ => {
449 let offset = self.offset;
450 self.skip_ident();
451 if self.offset > offset {
452 d.name = self.input[offset..self.offset].trim().to_string();
453 } else {
454 self.advance(1);
455 }
456 }
457 }
458 }
459
460 if !d.name.is_empty() {
461 declarations.push(d);
462 }
463
464 declarations
465 }
466
467 fn parse_rules(&mut self, rules: &mut Vec<Rule>) {
468 let selectors = self.parse_selectors();
469 let declarations = self.parse_declarations();
470 for selector in selectors.into_iter() {
471 rules.push(Rule {
472 selector,
473 declarations: declarations.clone(),
474 });
475 }
476 }
477
478 pub fn parse(&mut self) -> StyleSheet {
479 let mut rules = Vec::new();
480
481 while !self.eof() {
482 self.skip_spaces_and_comments();
483
484 match self.next() {
485 None => break,
486 Some('@') => self.skip_at_rule(),
487 _ => self.parse_rules(&mut rules),
488 }
489 }
490
491 StyleSheet { rules }
492 }
493}
494
495#[cfg(test)]
496mod tests {
497 use super::*;
498 use tracing::debug;
499
500 #[test]
501 fn simple_css() {
502 let text = "a, .b { b: c; d: e }";
503 let css = CssParser::new(text).parse();
504 debug!("{:?}", css);
505 }
506
507 #[test]
508 fn combinators_css() {
509 let text = "a#i.j.k > b { b: c } a + .l { u: v } a { x: y }";
510 let css = CssParser::new(text).parse();
511 println!("{:?}", css);
512 }
513}