1use crate::values;
2use css_parse::{
3 Build, ComponentValues, Cursor, DeclarationValue, KindSet, Parser, Peek, Result as ParserResult, State, T,
4 keyword_set,
5};
6use csskit_derives::{Parse, ToCursors, ToSpan, Visitable};
7use std::{fmt::Debug, hash::Hash};
8
9include!(concat!(env!("OUT_DIR"), "/css_apply_properties.rs"));
11
12#[derive(Parse, ToSpan, ToCursors, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
13#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
14#[parse(state = State::Nested, stop = KindSet::RIGHT_CURLY_OR_SEMICOLON)]
15pub struct Custom<'a>(pub ComponentValues<'a>);
16
17#[derive(Parse, ToSpan, ToCursors, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
18#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
19#[parse(state = State::Nested, stop = KindSet::RIGHT_CURLY_OR_SEMICOLON)]
20pub struct Computed<'a>(pub ComponentValues<'a>);
21
22impl<'a> Peek<'a> for Computed<'a> {
23 fn peek(p: &Parser<'a>, c: Cursor) -> bool {
24 <T![Function]>::peek(p, c)
25 && matches!(
26 p.parse_str_lower(c),
27 "var"
28 | "calc" | "min"
29 | "max" | "clamp"
30 | "round" | "mod"
31 | "rem" | "sin" | "cos"
32 | "tan" | "asin"
33 | "atan" | "atan2"
34 | "pow" | "sqrt"
35 | "hypot" | "log"
36 | "exp" | "abs" | "sign"
37 )
38 }
39}
40
41#[derive(Parse, ToSpan, ToCursors, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
42#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
43#[parse(state = State::Nested, stop = KindSet::RIGHT_CURLY_OR_SEMICOLON)]
44pub struct Unknown<'a>(pub ComponentValues<'a>);
45
46macro_rules! style_value {
47 ( $( $name: ident: $ty: ident$(<$a: lifetime>)? = $str: tt,)+ ) => {
48 #[derive(ToSpan, ToCursors, Visitable, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
49 #[cfg_attr(feature = "serde", derive(serde::Serialize), serde(tag = "type", rename_all = "kebab-case"))]
50 #[visit]
51 pub enum StyleValue<'a> {
52 #[visit(skip)]
53 Initial(T![Ident]),
54 #[visit(skip)]
55 Inherit(T![Ident]),
56 #[visit(skip)]
57 Unset(T![Ident]),
58 #[visit(skip)]
59 Revert(T![Ident]),
60 #[visit(skip)]
61 RevertLayer(T![Ident]),
62 #[cfg_attr(feature = "serde", serde(untagged))]
63 Custom(Custom<'a>),
64 #[cfg_attr(feature = "serde", serde(untagged))]
65 Computed(Computed<'a>),
66 #[cfg_attr(feature = "serde", serde(untagged))]
67 Unknown(Unknown<'a>),
68 $(
69 #[cfg_attr(feature = "serde", serde(untagged))]
70 $name(values::$ty$(<$a>)?),
71 )+
72 }
73 }
74}
75
76apply_properties!(style_value);
77
78keyword_set!(pub enum CSSWideKeyword {
79 Initial: "initial",
80 Inherit: "inherit",
81 Unset: "unset",
82 Revert: "revert",
83 RevertLayer: "revert-layer",
84});
85
86macro_rules! define_property_id {
87 ( $( $name: ident: $ty: ident$(<$a: lifetime>)? = $str: tt,)+ ) => {
88 keyword_set!(pub enum PropertyId {
89 $($name: $str,)+
90 });
91 }
92}
93apply_properties!(define_property_id);
94
95impl<'a> DeclarationValue<'a> for StyleValue<'a> {
96 type ComputedValue = Computed<'a>;
97
98 fn valid_declaration_name(p: &Parser<'a>, c: Cursor) -> bool {
99 PropertyId::peek(p, c)
100 }
101
102 fn is_unknown(&self) -> bool {
103 matches!(self, Self::Unknown(_))
104 }
105
106 fn is_initial(&self) -> bool {
107 matches!(self, Self::Initial(_))
108 }
109
110 fn is_inherit(&self) -> bool {
111 matches!(self, Self::Inherit(_))
112 }
113
114 fn is_unset(&self) -> bool {
115 matches!(self, Self::Unset(_))
116 }
117
118 fn is_revert(&self) -> bool {
119 matches!(self, Self::Revert(_))
120 }
121
122 fn is_revert_layer(&self) -> bool {
123 matches!(self, Self::RevertLayer(_))
124 }
125
126 fn needs_computing(&self) -> bool {
127 matches!(self, Self::Computed(_))
128 }
129
130 fn parse_custom_declaration_value(p: &mut Parser<'a>, _name: Cursor) -> ParserResult<Self> {
131 p.parse::<Custom>().map(Self::Custom)
132 }
133
134 fn parse_computed_declaration_value(p: &mut Parser<'a>, _name: Cursor) -> ParserResult<Self> {
135 p.parse::<Computed>().map(Self::Computed)
136 }
137
138 fn parse_specified_declaration_value(p: &mut Parser<'a>, name: Cursor) -> ParserResult<Self> {
139 match p.parse_if_peek::<CSSWideKeyword>()? {
140 Some(CSSWideKeyword::Initial(ident)) => return Ok(Self::Initial(ident)),
141 Some(CSSWideKeyword::Inherit(ident)) => return Ok(Self::Inherit(ident)),
142 Some(CSSWideKeyword::Unset(ident)) => return Ok(Self::Unset(ident)),
143 Some(CSSWideKeyword::Revert(ident)) => return Ok(Self::Revert(ident)),
144 Some(CSSWideKeyword::RevertLayer(ident)) => return Ok(Self::RevertLayer(ident)),
145 None => {}
146 }
147 macro_rules! parse_declaration_value {
148 ( $( $name: ident: $ty: ident$(<$a: lifetime>)? = $str: tt,)+ ) => {
149 match PropertyId::build(p, name) {
150 $(PropertyId::$name(_) => p.parse::<values::$ty>().map(Self::$name),)+
151 }
152 }
153 }
154 apply_properties!(parse_declaration_value)
155 }
156
157 fn parse_unknown_declaration_value(p: &mut Parser<'a>, _name: Cursor) -> ParserResult<Self> {
158 p.parse::<Unknown>().map(Self::Unknown)
159 }
160}
161
162#[cfg(test)]
163mod tests {
164 use super::*;
165 use css_parse::{Declaration, assert_parse};
166
167 type Property<'a> = Declaration<'a, StyleValue<'a>>;
168
169 #[test]
170 fn size_test() {
171 assert_eq!(std::mem::size_of::<Property>(), 368);
172 assert_eq!(std::mem::size_of::<StyleValue>(), 296);
173 }
174
175 #[test]
176 fn test_writes() {
177 assert_parse!(Property, "width:inherit", Property { value: StyleValue::Inherit(_), .. });
178 assert_parse!(
179 Property,
180 "width:inherit!important",
181 Property { value: StyleValue::Inherit(_), important: Some(_), .. }
182 );
183 assert_parse!(Property, "width:revert;", Property { value: StyleValue::Revert(_), semicolon: Some(_), .. });
184 assert_parse!(Property, "width:var(--a)", Property { value: StyleValue::Computed(_), .. });
185
186 assert_parse!(Property, "float:none!important");
187 assert_parse!(Property, "width:1px");
188 assert_parse!(Property, "width:min(1px, 2px)");
189 assert_parse!(Property, "border:1px solid var(--red)");
190 assert_parse!(Property, "dunno:like whatever");
192 assert_parse!(Property, "rotate:1.21gw");
193 assert_parse!(Property, "_background:black");
194 assert_parse!(Property, "--custom:{foo:{bar};baz:(bing);}");
195 }
196}