css_ast/types/
repeat_style.rs

1use crate::diagnostics;
2use css_parse::{Build, Cursor, Parse, Parser, Peek, Result as ParserResult, T, keyword_set};
3use csskit_derives::{ToCursors, ToSpan, Visitable};
4
5/// <https://drafts.csswg.org/css-backgrounds-4/#background-repeat>
6///
7/// ```text,ignore
8/// <repeat-style> = repeat-x | repeat-y | <repetition>{1,2}
9/// ```
10#[derive(ToCursors, ToSpan, Visitable, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
11#[cfg_attr(feature = "serde", derive(serde::Serialize), serde(rename_all = "kebab-case"))]
12#[visit(self)]
13pub enum RepeatStyle {
14	RepeatX(T![Ident]),
15	RepeatY(T![Ident]),
16	Repetition(Repetition, Option<Repetition>),
17}
18
19impl<'a> Peek<'a> for RepeatStyle {
20	fn peek(p: &Parser<'a>, c: Cursor) -> bool {
21		<Repetition>::peek(p, c) || (<T![Ident]>::peek(p, c) && matches!(p.parse_str_lower(c), "repeat-x" | "repeat-y"))
22	}
23}
24
25impl<'a> Parse<'a> for RepeatStyle {
26	fn parse(p: &mut Parser<'a>) -> ParserResult<Self> {
27		let ident = p.parse::<T![Ident]>()?;
28		let c: Cursor = ident.into();
29		match p.parse_str_lower(c) {
30			"repeat-x" => Ok(Self::RepeatX(<T![Ident]>::build(p, c))),
31			"repeat-y" => Ok(Self::RepeatY(<T![Ident]>::build(p, c))),
32			_ if <Repetition>::peek(p, c) => {
33				let first = Repetition::build(p, c);
34				let second = p.parse_if_peek::<Repetition>()?;
35				Ok(Self::Repetition(first, second))
36			}
37			_ => {
38				let source_cursor = p.to_source_cursor(c);
39				Err(diagnostics::UnexpectedIdent(source_cursor.to_string(), c))?
40			}
41		}
42	}
43}
44
45keyword_set!(
46	/// https://drafts.csswg.org/css-backgrounds-4/#typedef-repetition
47	///
48	/// ```text,ignore
49	/// <repetition> = repeat | space | round | no-repeat
50	/// ```
51	#[derive(Visitable)]
52	#[visit(skip)]
53	pub enum Repetition {
54		Repeat: "repeat",
55		Space: "space",
56		Round: "round",
57		NoRepeat: "no-repeat"
58	}
59);