1use crate::{CssAtomSet, CssDiagnostic, SelectorList, StyleValue, UnknownAtRule, UnknownQualifiedRule, rules};
2use css_parse::{
3 BadDeclaration, Cursor, Diagnostic, Parse, Parser, QualifiedRule, Result as ParserResult, RuleVariants,
4};
5use csskit_derives::{Parse, Peek, ToCursors, ToSpan};
6
7#[derive(Parse, Peek, ToSpan, ToCursors, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
14#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
15#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
16pub struct StyleRule<'a>(pub QualifiedRule<'a, SelectorList<'a>, StyleValue<'a>, NestedGroupRule<'a>>);
17
18macro_rules! apply_rules {
20 ($macro: ident) => {
21 $macro! {
22 Container(ContainerRule<'a>): "container",
23 Layer(LayerRule<'a>): "layer",
24 Media(MediaRule<'a>): "media",
25 Scope(ScopeRule): "scope",
26 Supports(SupportsRule<'a>): "supports",
27 }
28 };
29}
30
31macro_rules! nested_group_rule {
32 ( $(
33 $name: ident($ty: ident$(<$a: lifetime>)?): $str: pat,
34 )+ ) => {
35 #[allow(clippy::large_enum_variant)] #[derive(ToSpan, ToCursors, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
38 #[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable))]
39 #[cfg_attr(feature = "serde", derive(serde::Serialize), serde(untagged))]
40 pub enum NestedGroupRule<'a> {
41 $(
42 $name(rules::$ty$(<$a>)?),
43 )+
44 UnknownAt(UnknownAtRule<'a>),
45 Style(StyleRule<'a>),
46 Unknown(UnknownQualifiedRule<'a>),
47 BadDeclaration(BadDeclaration<'a>),
48 }
49 }
50}
51apply_rules!(nested_group_rule);
52
53impl<'a> RuleVariants<'a> for NestedGroupRule<'a> {
54 fn parse_at_rule<I>(p: &mut Parser<'a, I>, name: Cursor) -> ParserResult<Self>
55 where
56 I: Iterator<Item = Cursor> + Clone,
57 {
58 macro_rules! parse_rule {
59 ( $(
60 $name: ident($ty: ident$(<$a: lifetime>)?): $str: pat,
61 )+ ) => {
62 match p.to_atom::<CssAtomSet>(name) {
63 $(CssAtomSet::$name => p.parse::<rules::$ty>().map(Self::$name),)+
64 _ => Err(Diagnostic::new(name.into(), Diagnostic::unexpected_at_rule))?,
65 }
66 }
67 }
68 apply_rules!(parse_rule)
69 }
70
71 fn parse_unknown_at_rule<I>(p: &mut Parser<'a, I>, _name: Cursor) -> ParserResult<Self>
72 where
73 I: Iterator<Item = Cursor> + Clone,
74 {
75 p.parse::<UnknownAtRule>().map(Self::UnknownAt)
76 }
77
78 fn parse_qualified_rule<I>(p: &mut Parser<'a, I>, _name: Cursor) -> ParserResult<Self>
79 where
80 I: Iterator<Item = Cursor> + Clone,
81 {
82 p.parse::<StyleRule>().map(Self::Style)
83 }
84
85 fn parse_unknown_qualified_rule<I>(p: &mut Parser<'a, I>, _name: Cursor) -> ParserResult<Self>
86 where
87 I: Iterator<Item = Cursor> + Clone,
88 {
89 p.parse::<UnknownQualifiedRule>().map(Self::Unknown)
90 }
91
92 fn bad_declaration(b: BadDeclaration<'a>) -> Option<Self> {
93 Some(Self::BadDeclaration(b))
94 }
95}
96
97impl<'a> Parse<'a> for NestedGroupRule<'a> {
98 fn parse<I>(p: &mut Parser<'a, I>) -> ParserResult<Self>
99 where
100 I: Iterator<Item = Cursor> + Clone,
101 {
102 Self::parse_rule_variants(p)
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109 use crate::CssAtomSet;
110 use css_parse::assert_parse;
111
112 #[test]
113 fn size_test() {
114 assert_eq!(std::mem::size_of::<StyleRule>(), 128);
115 }
116
117 #[test]
118 fn test_writes() {
119 assert_parse!(CssAtomSet::ATOMS, StyleRule, "body{}");
120 assert_parse!(CssAtomSet::ATOMS, StyleRule, "body,body{}");
121 assert_parse!(CssAtomSet::ATOMS, StyleRule, "body{width:1px;}");
122 assert_parse!(CssAtomSet::ATOMS, StyleRule, "body{opacity:0;}");
123 assert_parse!(CssAtomSet::ATOMS, StyleRule, ".foo *{}");
124 assert_parse!(CssAtomSet::ATOMS, StyleRule, ":nth-child(1){opacity:0;}");
125 assert_parse!(CssAtomSet::ATOMS, StyleRule, ".foo{--bar:(baz);}");
126 assert_parse!(CssAtomSet::ATOMS, StyleRule, ".foo{width: calc(1px + (var(--foo)) + 1px);}");
127 assert_parse!(CssAtomSet::ATOMS, StyleRule, ".foo{--bar:1}");
128 assert_parse!(CssAtomSet::ATOMS, StyleRule, ":root{--custom:{width:0;height:0;};}");
129 assert_parse!(CssAtomSet::ATOMS, StyleRule, ":root{a;b{}}");
131 assert_parse!(CssAtomSet::ATOMS, StyleRule, ":root{$(var)-size: 100%;}");
133 }
134}