lewp_css/domain/at_rules/counter_style/
counter_style_at_rule.rs

1use {
2    super::System::{self, *},
3    crate::{
4        domain::{at_rules::counter_style::*, CounterStyleIdent},
5        parsers::{CounterStyleAtRuleParser, ParserContext},
6        CustomParseError,
7    },
8    cssparser::{DeclarationListParser, ParseError, Parser, ToCss},
9    std::{borrow::Cow, fmt},
10};
11
12// 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.
13// 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.
14
15/// An @counter-style rule
16#[derive(Clone, Debug)]
17pub struct CounterStyleAtRule {
18    pub name: CounterStyleIdent,
19
20    /// <https://drafts.csswg.org/css-counter-styles/#counter-style-system>
21    pub system: Option<System>,
22
23    /// <https://drafts.csswg.org/css-counter-styles/#counter-style-negative>
24    pub negative: Option<Negative>,
25
26    /// <https://drafts.csswg.org/css-counter-styles/#counter-style-prefix>
27    pub prefix: Option<Symbol>,
28
29    /// <https://drafts.csswg.org/css-counter-styles/#counter-style-suffix>
30    pub suffix: Option<Symbol>,
31
32    /// <https://drafts.csswg.org/css-counter-styles/#counter-style-range>
33    pub range: Option<Ranges>,
34
35    /// <https://drafts.csswg.org/css-counter-styles/#counter-style-pad>
36    pub pad: Option<Pad>,
37
38    /// <https://drafts.csswg.org/css-counter-styles/#counter-style-fallback>
39    pub fallback: Option<Fallback>,
40
41    /// <https://drafts.csswg.org/css-counter-styles/#descdef-counter-style-symbols>
42    pub symbols: Option<Symbols>,
43
44    /// <https://drafts.csswg.org/css-counter-styles/#descdef-counter-style-additive-symbols>
45    pub additive_symbols: Option<AdditiveSymbols>,
46
47    /// <https://drafts.csswg.org/css-counter-styles/#counter-style-speak-as>
48    pub speak_as: Option<SpeakAs>,
49}
50
51impl ToCss for CounterStyleAtRule {
52    fn to_css<W: fmt::Write>(&self, dest: &mut W) -> fmt::Result {
53        #[inline(always)]
54        fn write<W: fmt::Write, T: ToCss>(
55            afterFirst: &mut bool,
56            dest: &mut W,
57            name: &str,
58            value: &Option<T>,
59        ) -> fmt::Result {
60            if let &Some(ref value) = value {
61                if *afterFirst {
62                    dest.write_char(';')?;
63                } else {
64                    *afterFirst = true;
65                }
66                dest.write_str(name)?;
67                dest.write_char(':')?;
68                value.to_css(dest)
69            } else {
70                Ok(())
71            }
72        }
73
74        dest.write_str("@counter-style ")?;
75        self.name.to_css(dest)?;
76        dest.write_char('{')?;
77        let mut afterFirst = false;
78        write(&mut afterFirst, dest, "system", &self.system)?;
79        write(&mut afterFirst, dest, "negative", &self.negative)?;
80        write(&mut afterFirst, dest, "prefix", &self.prefix)?;
81        write(&mut afterFirst, dest, "suffix", &self.suffix)?;
82        write(&mut afterFirst, dest, "range", &self.range)?;
83        write(&mut afterFirst, dest, "pad", &self.pad)?;
84        write(&mut afterFirst, dest, "fallback", &self.fallback)?;
85        write(&mut afterFirst, dest, "symbols", &self.symbols)?;
86        write(
87            &mut afterFirst,
88            dest,
89            "additive-symbols",
90            &self.additive_symbols,
91        )?;
92        write(&mut afterFirst, dest, "speak-as", &self.speak_as)?;
93        dest.write_char('}')
94    }
95}
96
97impl CounterStyleAtRule {
98    #[inline(always)]
99    fn empty(name: CounterStyleIdent) -> Self {
100        Self {
101            name,
102            system: None,
103            negative: None,
104            prefix: None,
105            suffix: None,
106            range: None,
107            pad: None,
108            fallback: None,
109            symbols: None,
110            additive_symbols: None,
111            speak_as: None,
112        }
113    }
114
115    /// Get the name of the counter style rule.
116    #[inline(always)]
117    pub fn name(&self) -> &CounterStyleIdent {
118        &self.name
119    }
120
121    /// <https://drafts.csswg.org/css-counter-styles/#counter-style-system>
122    #[inline(always)]
123    pub fn system(&self) -> Cow<System> {
124        if let Some(ref value) = self.system {
125            Cow::Borrowed(value)
126        } else {
127            Cow::Owned(System::Symbolic)
128        }
129    }
130
131    /// <https://drafts.csswg.org/css-counter-styles/#counter-style-negative>
132    #[inline(always)]
133    pub fn negative(&self) -> Cow<Negative> {
134        if let Some(ref value) = self.negative {
135            Cow::Borrowed(value)
136        } else {
137            Cow::Owned(Negative(Symbol::String("-".to_owned()), None))
138        }
139    }
140
141    /// <https://drafts.csswg.org/css-counter-styles/#counter-style-prefix>
142    #[inline(always)]
143    pub fn prefix(&self) -> Cow<Symbol> {
144        if let Some(ref value) = self.prefix {
145            Cow::Borrowed(value)
146        } else {
147            Cow::Owned(Symbol::String("".to_owned()))
148        }
149    }
150
151    /// <https://drafts.csswg.org/css-counter-styles/#counter-style-suffix>
152    #[inline(always)]
153    pub fn suffix(&self) -> Cow<Symbol> {
154        if let Some(ref value) = self.suffix {
155            Cow::Borrowed(value)
156        } else {
157            Cow::Owned(Symbol::String(". ".to_owned()))
158        }
159    }
160
161    /// <https://drafts.csswg.org/css-counter-styles/#counter-style-range>
162    #[inline(always)]
163    pub fn range(&self) -> Cow<Ranges> {
164        if let Some(ref value) = self.range {
165            Cow::Borrowed(value)
166        } else {
167            Cow::Owned(Ranges::empty())
168        }
169    }
170
171    /// <https://drafts.csswg.org/css-counter-styles/#counter-style-pad>
172    #[inline(always)]
173    pub fn pad(&self) -> Cow<Pad> {
174        if let Some(ref value) = self.pad {
175            Cow::Borrowed(value)
176        } else {
177            Cow::Owned(Pad(0, Symbol::String("".to_owned())))
178        }
179    }
180
181    /// <https://drafts.csswg.org/css-counter-styles/#counter-style-fallback>
182    #[inline(always)]
183    pub fn fallback(&self) -> Cow<Fallback> {
184        if let Some(ref value) = self.fallback {
185            Cow::Borrowed(value)
186        } else {
187            Cow::Owned(Fallback(CounterStyleIdent::decimal))
188        }
189    }
190
191    /// <https://drafts.csswg.org/css-counter-styles/#descdef-counter-style-symbols>
192    #[inline(always)]
193    pub fn symbols(&self) -> Option<&Symbols> {
194        self.symbols.as_ref()
195    }
196
197    /// <https://drafts.csswg.org/css-counter-styles/#descdef-counter-style-additive-symbols>
198    #[inline(always)]
199    pub fn additive_symbols(&self) -> Option<&AdditiveSymbols> {
200        self.additive_symbols.as_ref()
201    }
202
203    /// <https://drafts.csswg.org/css-counter-styles/#counter-style-speak-as>
204    #[inline(always)]
205    pub fn speak_as(&self) -> Cow<SpeakAs> {
206        if let Some(ref value) = self.speak_as {
207            Cow::Borrowed(value)
208        } else {
209            Cow::Owned(SpeakAs::Auto)
210        }
211    }
212
213    /// Parse the body (inside `{}`) of an @counter-style rule
214    pub(crate) fn parse_body<'i, 't>(
215        name: CounterStyleIdent,
216        context: &ParserContext,
217        input: &mut Parser<'i, 't>,
218    ) -> Result<CounterStyleAtRule, ParseError<'i, CustomParseError<'i>>> {
219        let mut rule = CounterStyleAtRule::empty(name);
220
221        {
222            let parser = CounterStyleAtRuleParser {
223                context,
224                rule: &mut rule,
225            };
226            let iterator = DeclarationListParser::new(input, parser);
227            for declaration in iterator {
228                if declaration.is_err() {
229                    return Err(declaration.unwrap_err().0);
230                }
231            }
232        }
233
234        match *rule.system() {
235            ref system @ Cyclic
236            | ref system @ Fixed { .. }
237            | ref system @ Symbolic
238            | ref system @ Alphabetic
239            | ref system @ Numeric
240                if rule.symbols.is_none() =>
241            {
242                return Err(ParseError::from(
243                    CustomParseError::InvalidCounterStyleWithoutSymbols(system.clone()),
244                ))
245            }
246
247            ref system @ Alphabetic | ref system @ Numeric
248                if rule.symbols().unwrap().0.len() < 2 =>
249            {
250                return Err(ParseError::from(
251                    CustomParseError::InvalidCounterStyleNotEnoughSymbols(system.clone()),
252                ))
253            }
254
255            Additive if rule.additive_symbols.is_none() => {
256                return Err(ParseError::from(
257                    CustomParseError::InvalidCounterStyleWithoutAdditiveSymbols,
258                ))
259            }
260
261            Extends(_) if rule.symbols.is_some() => {
262                return Err(ParseError::from(
263                    CustomParseError::InvalidCounterStyleExtendsWithSymbols,
264                ))
265            }
266
267            Extends(_) if rule.additive_symbols.is_some() => {
268                return Err(ParseError::from(
269                    CustomParseError::InvalidCounterStyleExtendsWithAdditiveSymbols,
270                ))
271            }
272
273            _ => {}
274        };
275
276        Ok(rule)
277    }
278}