lewp_css/domain/at_rules/counter_style/
ranges.rs

1use {
2    crate::{
3        parsers::{Parse, ParserContext},
4        CustomParseError,
5    },
6    cssparser::{
7        BasicParseError,
8        BasicParseErrorKind,
9        ParseError,
10        Parser,
11        ToCss,
12        Token::*,
13    },
14    std::fmt,
15};
16
17// 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.
18// 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.
19
20/// <https://drafts.csswg.org/css-counter-styles/#counter-style-range>
21///
22/// Empty Vec represents 'auto'
23#[derive(Clone, Debug)]
24pub struct Ranges(pub Vec<::std::ops::Range<Option<i32>>>);
25
26impl Parse for Ranges {
27    fn parse<'i, 't>(
28        _context: &ParserContext,
29        input: &mut Parser<'i, 't>,
30    ) -> Result<Self, ParseError<'i, CustomParseError<'i>>> {
31        if input
32            .r#try(|input| input.expect_ident_matching("auto"))
33            .is_ok()
34        {
35            Ok(Self::empty())
36        } else {
37            input
38                .parse_comma_separated(|input| {
39                    let opt_start = Self::parse_bound(input)?;
40                    let opt_end = Self::parse_bound(input)?;
41                    if let (Some(start), Some(end)) = (opt_start, opt_end) {
42                        if start > end {
43                            return Err(ParseError::from(
44                                CustomParseError::CounterStyleRangesCanNotHaveStartGreaterThanEnd(
45                                    start, end,
46                                ),
47                            ));
48                        }
49                    }
50                    Ok(opt_start..opt_end)
51                })
52                .map(Ranges)
53        }
54    }
55}
56
57impl ToCss for Ranges {
58    fn to_css<W: fmt::Write>(&self, dest: &mut W) -> fmt::Result {
59        let mut iter = self.0.iter();
60        if let Some(first) = iter.next() {
61            Self::range_to_css(first, dest)?;
62            for item in iter {
63                dest.write_str(",")?;
64                Self::range_to_css(item, dest)?;
65            }
66            Ok(())
67        } else {
68            dest.write_str("auto")
69        }
70    }
71}
72
73impl Ranges {
74    #[inline(always)]
75    pub fn empty() -> Self {
76        Ranges(Vec::new())
77    }
78
79    #[inline(always)]
80    pub fn is_auto(&self) -> bool {
81        self.0.is_empty()
82    }
83
84    fn parse_bound<'i, 't>(
85        input: &mut Parser<'i, 't>,
86    ) -> Result<Option<i32>, ParseError<'i, CustomParseError<'i>>> {
87        match input.next() {
88            Ok(&Number {
89                int_value: Some(v), ..
90            }) => Ok(Some(v)),
91
92            Ok(&Ident(ref ident)) if ident.eq_ignore_ascii_case("infinite") => {
93                Ok(None)
94            }
95
96            Ok(token) => Err(ParseError::from(BasicParseError {
97                kind: BasicParseErrorKind::UnexpectedToken(token.clone()),
98                location: input.state().source_location(),
99            })),
100
101            Err(error) => Err(error.into()),
102        }
103    }
104
105    fn range_to_css<W: fmt::Write>(
106        range: &::std::ops::Range<Option<i32>>,
107        dest: &mut W,
108    ) -> fmt::Result {
109        Self::bound_to_css(range.start, dest)?;
110        dest.write_char(' ')?;
111        Self::bound_to_css(range.end, dest)
112    }
113
114    fn bound_to_css<W: fmt::Write>(
115        range: Option<i32>,
116        dest: &mut W,
117    ) -> fmt::Result {
118        if let Some(finite) = range {
119            finite.to_css(dest)
120        } else {
121            dest.write_str("infinite")
122        }
123    }
124}