lewp_css/domain/
css_rules.rs

1// This file is part of css. It is subject to the license terms in the COPYRIGHT file found in the top-level directory of this distribution and at https://raw.githubusercontent.com/lemonrock/css/master/COPYRIGHT. No part of predicator, including this file, may be copied, modified, propagated, or distributed except according to the terms contained in the COPYRIGHT file.
2// Copyright © 2017 The developers of css. See the COPYRIGHT file in the top-level directory of this distribution and at https://raw.githubusercontent.com/lemonrock/css/master/COPYRIGHT.
3
4use {
5    super::{
6        at_rules::VendorPrefixedAtRule,
7        CssRule::{self},
8        HasCssRules,
9        RulesMutateError::{self},
10    },
11    cssparser::ToCss,
12    std::fmt,
13};
14
15/// A list of CSS rules.
16#[derive(Default, Debug, Clone)]
17pub struct CssRules(pub Vec<CssRule>);
18
19impl ToCss for CssRules {
20    fn to_css<W: fmt::Write>(&self, dest: &mut W) -> fmt::Result {
21        for cssRule in self.0.iter() {
22            cssRule.to_css(dest)?;
23        }
24
25        Ok(())
26    }
27}
28
29impl HasCssRules for CssRules {
30    #[inline(always)]
31    fn css_rules(&self) -> &CssRules {
32        self
33    }
34
35    #[inline(always)]
36    fn css_rules_mut(&mut self) -> &mut CssRules {
37        self
38    }
39
40    #[inline(always)]
41    fn css_rules_slice(&self) -> &[CssRule] {
42        &self.0[..]
43    }
44
45    #[inline(always)]
46    fn css_rules_vec(&self) -> &Vec<CssRule> {
47        &self.0
48    }
49
50    #[inline(always)]
51    fn css_rules_vec_mut(&mut self) -> &mut Vec<CssRule> {
52        &mut self.0
53    }
54}
55
56impl CssRules {
57    /// Allows vendor prefixing of at-rules
58    #[inline(always)]
59    pub fn vendor_prefix_at_rules<
60        AtRule: VendorPrefixedAtRule,
61        CssRuleMatcher: Fn(&CssRule) -> Option<&AtRule>,
62        VendorPrefixer: Fn(usize, &AtRule) -> Vec<CssRule>,
63    >(
64        &mut self,
65        remove_unprefixed_at_rule: bool,
66        css_rule_matcher: CssRuleMatcher,
67        vendor_prefixer: VendorPrefixer,
68    ) {
69        let mut index = 0;
70        while index < self.0.len() {
71            let newCssRulesToInsert = match css_rule_matcher(unsafe {
72                self.0.get_unchecked(index)
73            }) {
74                None => None,
75                Some(atRule) => {
76                    if atRule.isNotVendorPrefixed() {
77                        Some(vendor_prefixer(index, atRule))
78                    } else {
79                        None
80                    }
81                }
82            };
83
84            index += if let Some(mut newCssRulesToInsert) = newCssRulesToInsert
85            {
86                let indexIncrement = newCssRulesToInsert.len();
87
88                // TODO: Inefficient
89                for newCssRuleToInsert in newCssRulesToInsert.drain(..) {
90                    self.css_rules_vec_mut().insert(index, newCssRuleToInsert);
91                }
92                if remove_unprefixed_at_rule {
93                    self.css_rules_vec_mut().remove(index + indexIncrement);
94                    indexIncrement
95                } else {
96                    indexIncrement + 1
97                }
98            } else {
99                1
100            };
101        }
102    }
103
104    /// Whether this CSS rules is empty.
105    pub fn is_empty(&self) -> bool {
106        self.0.is_empty()
107    }
108
109    /// Returns whether all the rules in this list are namespace or import rules.
110    fn only_namespace_or_import(&self) -> bool {
111        use self::CssRule::*;
112
113        self.0.iter().all(|r| match *r {
114            Namespace(..) | Import(..) => true,
115            _ => false,
116        })
117    }
118
119    /// <https://drafts.csswg.org/cssom/#remove-a-css-rule>
120    pub fn remove_rule(
121        &mut self,
122        index: usize,
123    ) -> Result<(), RulesMutateError> {
124        use self::{CssRule::Namespace, RulesMutateError::*};
125
126        // Step 1, 2
127        if index >= self.0.len() {
128            return Err(IndexSize);
129        }
130
131        {
132            // Step 3
133            let rule = &self.0[index];
134
135            // Step 4
136            if let Namespace(..) = *rule {
137                if !self.only_namespace_or_import() {
138                    return Err(InvalidState);
139                }
140            }
141        }
142
143        // Step 5, 6
144        self.0.remove(index);
145
146        Ok(())
147    }
148}