Skip to main content

procss/ast/
tree_ruleset.rs

1// ┌───────────────────────────────────────────────────────────────────────────┐
2// │                                                                           │
3// │  ██████╗ ██████╗  ██████╗   Copyright (C) 2022, The Prospective Company   │
4// │  ██╔══██╗██╔══██╗██╔═══██╗                                                │
5// │  ██████╔╝██████╔╝██║   ██║  This file is part of the Procss library,      │
6// │  ██╔═══╝ ██╔══██╗██║   ██║  distributed under the terms of the            │
7// │  ██║     ██║  ██║╚██████╔╝  Apache License 2.0.  The full license can     │
8// │  ╚═╝     ╚═╝  ╚═╝ ╚═════╝   be found in the LICENSE file.                 │
9// │                                                                           │
10// └───────────────────────────────────────────────────────────────────────────┘
11
12use nom::branch::alt;
13use nom::bytes::complete::tag;
14use nom::combinator::peek;
15use nom::error::ParseError;
16use nom::multi::{many0, many1};
17use nom::sequence::{terminated, tuple};
18use nom::{IResult, Parser};
19
20use super::flat_ruleset::FlatRuleset;
21use super::ruleset::{QualNestedRuleset, QualRule, QualRuleset, Rule, Ruleset, SelectorRuleset};
22use super::selector::Selector;
23use super::token::{comment0, sep0};
24use crate::parser::*;
25use crate::render::*;
26use crate::transform::TransformCss;
27
28/// A tree node which expresses a recursive `T` over `Ruleset<T>`.  Using this
29/// struct in place of `Rule` allows nested CSS selectors that can be later
30/// flattened.
31#[derive(Clone, Debug, Eq, PartialEq, Hash)]
32pub enum TreeRule<'a> {
33    Rule(Rule<'a>),
34    Ruleset(TreeRuleset<'a>),
35}
36
37impl<'a> ParseCss<'a> for TreeRule<'a> {
38    fn parse<E: ParseError<&'a str>>(input: &'a str) -> IResult<&'a str, Self, E> {
39        let block = terminated(TreeRuleset::parse, sep0).map(TreeRule::Ruleset);
40        let rule = terminated(Rule::parse, sep0).map(TreeRule::Rule);
41        alt((block, rule))(input)
42    }
43}
44
45impl<'a> RenderCss for TreeRule<'a> {
46    fn render(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47        match self {
48            TreeRule::Rule(rule) => rule.render(f),
49            TreeRule::Ruleset(block) => block.render(f),
50        }
51    }
52}
53
54impl<'a> TransformCss<Rule<'a>> for TreeRule<'a> {
55    fn transform_each<F: FnMut(&mut Rule<'a>)>(&mut self, f: &mut F) {
56        match self {
57            TreeRule::Rule(rule) => f(rule),
58            TreeRule::Ruleset(ruleset) => ruleset.transform_each(f),
59        }
60    }
61}
62
63impl<'a> TransformCss<TreeRuleset<'a>> for TreeRule<'a> {
64    fn transform_each<F: FnMut(&mut TreeRuleset<'a>)>(&mut self, f: &mut F) {
65        match self {
66            TreeRule::Rule(_) => (),
67            TreeRule::Ruleset(ruleset) => ruleset.transform_each(f),
68        }
69    }
70}
71
72impl<'a> TransformCss<Vec<TreeRuleset<'a>>> for TreeRule<'a> {
73    fn transform_each<F: FnMut(&mut Vec<TreeRuleset<'a>>)>(&mut self, f: &mut F) {
74        match self {
75            TreeRule::Rule(_) => (),
76            TreeRule::Ruleset(ruleset) => ruleset.transform_each(f),
77        }
78    }
79}
80
81/// A nested recursive block, ala popular CSS tools and the CSS nesting
82/// proposal.
83///
84/// ```css
85/// div {
86///     color: green;
87///     &#my_elem {
88///         color: red;
89///     }
90///     .sub_elem {
91///         color: purple;
92///     }
93/// }
94/// ```
95pub type TreeRuleset<'a> = Ruleset<'a, TreeRule<'a>>;
96
97impl<'a> ParseCss<'a> for TreeRuleset<'a> {
98    fn parse<E: ParseError<&'a str>>(input: &'a str) -> IResult<&'a str, Self, E> {
99        if let Ok((input, _)) = peek::<_, _, E, _>(tag("@"))(input) {
100            let (input, qual_rule) = QualRule::parse(input)?;
101            if let Ok((input, _)) = tag::<_, _, E>(";")(input) {
102                Ok((input, Ruleset::QualRule(qual_rule)))
103            } else {
104                let (input, _) = tuple((tag("{"), sep0))(input)?;
105                let (input, rules) = many1(TreeRule::parse::<E>)(input)?;
106                let (input, _) = tuple((comment0, tag("}")))(input)?;
107                Ok((input, Ruleset::QualRuleset(QualRuleset(qual_rule, rules))))
108            }
109        } else {
110            let (input, selector_ruleset) = SelectorRuleset::parse(input)?;
111            Ok((input, Ruleset::SelectorRuleset(selector_ruleset)))
112        }
113    }
114}
115
116impl<'a> TransformCss<TreeRuleset<'a>> for TreeRuleset<'a> {
117    fn transform_each<F: FnMut(&mut TreeRuleset<'a>)>(&mut self, f: &mut F) {
118        f(self);
119        match self {
120            Ruleset::QualRule(_) => (),
121            Ruleset::QualRuleset(_) => (),
122            Ruleset::QualNestedRuleset(..) => (),
123            Ruleset::SelectorRuleset(ruleset) => {
124                for rule in ruleset.1.iter_mut() {
125                    rule.transform_each(f)
126                }
127            }
128        }
129    }
130}
131
132impl<'a> TreeRuleset<'a> {
133    /// Flatten into a `FlatRuleset`, replacing this struct's inner `TreeRule`
134    /// recursive type with a regular `Rule`, removing arbitrary nesting of
135    /// `SelectorRuleset` variants.
136    pub fn flatten_tree(&self) -> Vec<FlatRuleset<'a>> {
137        match self {
138            Ruleset::SelectorRuleset(ruleset) => ruleset.flatten_tree(),
139            Ruleset::QualRule(x) => vec![Ruleset::QualRule(x.clone())],
140            Ruleset::QualRuleset(rules) => {
141                let mut new_rules: Vec<Rule<'a>> = vec![];
142                let mut new_rulesets: Vec<FlatRuleset<'a>> = vec![];
143                for rule in rules.1.iter() {
144                    match rule {
145                        TreeRule::Rule(rule) => new_rules.push(rule.clone()),
146                        TreeRule::Ruleset(ruleset) => {
147                            let sub_rules = ruleset.flatten_tree().into_iter();
148                            new_rulesets.extend(sub_rules)
149                        }
150                    }
151                }
152
153                let mut ret = vec![];
154                if !new_rules.is_empty() {
155                    let ruleset = QualRuleset(rules.0.clone(), new_rules);
156                    ret.push(Ruleset::QualRuleset(ruleset));
157                }
158
159                if !new_rulesets.is_empty() {
160                    ret.push(Ruleset::QualNestedRuleset(QualNestedRuleset(
161                        rules.0.clone(),
162                        new_rulesets,
163                    )))
164                }
165
166                ret
167            }
168            Ruleset::QualNestedRuleset(ruleset) => {
169                vec![Ruleset::QualNestedRuleset(QualNestedRuleset(
170                    ruleset.0.clone(),
171                    ruleset.1.iter().flat_map(|x| x.flatten_tree()).collect(),
172                ))]
173            }
174        }
175    }
176}
177
178type TreeSelectorRuleset<'a> = SelectorRuleset<'a, TreeRule<'a>>;
179
180impl<'a> ParseCss<'a> for TreeSelectorRuleset<'a> {
181    fn parse<E: ParseError<&'a str>>(input: &'a str) -> IResult<&'a str, Self, E> {
182        let (input, selector) = Selector::parse(input)?;
183        let (input, _) = tuple((comment0, tag("{"), sep0))(input)?;
184        let (input, rules) = many0(TreeRule::parse)(input)?;
185        let (input, _) = tuple((comment0, tag("}")))(input)?;
186        Ok((input, SelectorRuleset(selector, rules)))
187    }
188}
189
190impl<'a> TransformCss<Vec<TreeRuleset<'a>>> for TreeRuleset<'a> {
191    fn transform_each<F: FnMut(&mut Vec<TreeRuleset<'a>>)>(&mut self, f: &mut F) {
192        match self {
193            Ruleset::SelectorRuleset(ruleset) => {
194                for rule in ruleset.1.iter_mut() {
195                    rule.transform_each(f)
196                }
197            }
198            Ruleset::QualRule(_) => (),
199            Ruleset::QualRuleset(_) => (),
200            Ruleset::QualNestedRuleset(ruleset) => {
201                for rule in ruleset.1.iter_mut() {
202                    rule.transform_each(f)
203                }
204            }
205        }
206    }
207}
208
209impl<'a> TreeSelectorRuleset<'a> {
210    /// Flatten a TreeRuleset's SelectorRuleset into a `FlatRuleset`m erging any
211    /// nested rulesets which are not allowed in the latter.
212    pub fn flatten_tree(&self) -> Vec<Ruleset<'a, Rule<'a>>> {
213        let mut new_rules: Vec<Rule<'a>> = vec![];
214        let mut new_rulesets: Vec<FlatRuleset<'a>> = vec![];
215        for rule in self.1.iter() {
216            match rule {
217                TreeRule::Rule(rule) => new_rules.push(rule.clone()),
218                TreeRule::Ruleset(ruleset) => {
219                    if !new_rules.is_empty() {
220                        let ruleset = SelectorRuleset(self.0.clone(), new_rules);
221                        new_rulesets.push(Ruleset::SelectorRuleset(ruleset));
222                        new_rules = vec![];
223                    }
224
225                    let sub_rules = ruleset
226                        .flatten_tree()
227                        .into_iter()
228                        .map(|ruleset| self.join(ruleset));
229                    new_rulesets.extend(sub_rules)
230                }
231            }
232        }
233
234        if !new_rules.is_empty() {
235            let ruleset = SelectorRuleset(self.0.clone(), new_rules);
236            new_rulesets.push(Ruleset::SelectorRuleset(ruleset));
237        }
238
239        new_rulesets
240    }
241
242    /// Join a new `Ruleset` as an extension of self's selector.
243    fn join(&self, rhs: Ruleset<'a, Rule<'a>>) -> Ruleset<'a, Rule<'a>> {
244        match rhs {
245            Ruleset::SelectorRuleset(inner_ruleset) => {
246                let joined_selector = self.0.join(&inner_ruleset.0);
247                Ruleset::SelectorRuleset(SelectorRuleset(joined_selector, inner_ruleset.1))
248            }
249            ruleset => ruleset,
250        }
251    }
252}