lewp_css/domain/at_rules/counter_style/
ranges.rs1use {
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#[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}