1use crate::token_as_ident;
2
3use super::{ASTNode, CSSToken, CSSValue, ParseError, Selector, ToStringSettings};
4use source_map::{Span, ToString};
5use tokenizer_lib::{Token, TokenReader};
6
7#[derive(Debug)]
9pub struct Rule {
10 pub selectors: Vec<Selector>,
11 pub nested_rules: Option<Vec<Rule>>,
12 pub declarations: Vec<(String, CSSValue)>,
13 pub position: Option<Span>,
14}
15
16impl ASTNode for Rule {
17 fn from_reader(reader: &mut impl TokenReader<CSSToken, Span>) -> Result<Self, ParseError> {
18 let mut selectors = vec![Selector::from_reader(reader)?];
19 while let Token(CSSToken::Comma, _) = reader.peek().unwrap() {
20 reader.next();
21 selectors.push(Selector::from_reader(reader)?);
22 }
23 let first_span = selectors.first().unwrap().get_position().unwrap();
24 reader.expect_next(CSSToken::OpenCurly)?;
25
26 let mut declarations: Vec<(String, CSSValue)> = Vec::new();
28 let mut nested_rules: Option<Vec<Rule>> = None;
29 while let Some(Token(token_type, _)) = reader.peek() {
30 if token_type == &CSSToken::CloseCurly {
31 break;
32 }
33 let mut is_rule: Option<bool> = None;
34 reader.scan(|token, _| {
35 match token {
36 CSSToken::Colon | CSSToken::CloseCurly => is_rule = Some(false),
37 CSSToken::OpenCurly => is_rule = Some(true),
38 _ => {}
39 }
40 is_rule.is_some()
41 });
42
43 if is_rule.unwrap_or_default() {
44 nested_rules
45 .get_or_insert_with(|| Vec::new())
46 .push(Rule::from_reader(reader)?);
47 } else {
48 let (property_name, _) = token_as_ident(reader.next().unwrap())?;
49 reader.expect_next(CSSToken::Colon)?;
50 let value = CSSValue::from_reader(reader)?;
51 declarations.push((property_name, value));
52 if let Token(CSSToken::CloseCurly, last_span) = reader.next().unwrap() {
53 return Ok(Self {
54 position: Some(first_span.union(&last_span)),
55 selectors,
56 declarations,
57 nested_rules,
58 });
59 }
60 }
61 }
62 let last_span = reader.expect_next(CSSToken::CloseCurly)?;
63 Ok(Self {
64 position: Some(first_span.union(&last_span)),
65 selectors,
66 declarations,
67 nested_rules,
68 })
69 }
70
71 fn to_string_from_buffer(
72 &self,
73 buf: &mut impl ToString,
74 settings: &ToStringSettings,
75 depth: u8,
76 ) {
77 for (idx, selector) in self.selectors.iter().enumerate() {
78 selector.to_string_from_buffer(buf, settings, depth);
79 if idx + 1 < self.selectors.len() {
80 if settings.minify {
81 buf.push(',');
82 } else {
83 buf.push_str(", ");
84 }
85 }
86 }
87 if !settings.minify {
88 buf.push(' ');
89 }
90 buf.push('{');
91 for (idx, (name, value)) in self.declarations.iter().enumerate() {
92 if !settings.minify {
93 buf.push_new_line();
94 buf.push_str(&settings.indent_with.repeat(depth as usize + 1));
95 }
96 buf.push_str(name);
97 buf.push(':');
98
99 if !settings.minify {
100 buf.push(' ');
101 }
102 value.to_string_from_buffer(buf, settings, depth);
103 buf.push(';');
104 if !settings.minify && idx == self.declarations.len() - 1 {
105 buf.push_new_line();
106 buf.push_str(&settings.indent_with.repeat(depth as usize));
107 }
108 }
109 buf.push('}');
110 }
111
112 fn get_position(&self) -> Option<&Span> {
113 self.position.as_ref()
114 }
115}