lightningcss/rules/
property.rs1use super::Location;
4#[cfg(feature = "visitor")]
5use crate::visitor::Visit;
6use crate::{
7 error::{ParserError, PrinterError},
8 printer::Printer,
9 properties::custom::TokenList,
10 traits::{Parse, ToCss},
11 values::{
12 ident::DashedIdent,
13 syntax::{ParsedComponent, SyntaxString},
14 },
15};
16use cssparser::*;
17
18#[derive(Debug, PartialEq, Clone)]
20#[cfg_attr(feature = "visitor", derive(Visit))]
21#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
22#[cfg_attr(
23 feature = "serde",
24 derive(serde::Serialize, serde::Deserialize),
25 serde(rename_all = "camelCase")
26)]
27#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
28pub struct PropertyRule<'i> {
29 #[cfg_attr(feature = "serde", serde(borrow))]
31 pub name: DashedIdent<'i>,
32 #[cfg_attr(feature = "visitor", skip_visit)]
34 pub syntax: SyntaxString,
35 #[cfg_attr(feature = "visitor", skip_visit)]
37 pub inherits: bool,
38 #[cfg_attr(feature = "visitor", skip_visit)]
40 pub initial_value: Option<ParsedComponent<'i>>,
41 #[cfg_attr(feature = "visitor", skip_visit)]
43 pub loc: Location,
44}
45
46impl<'i> PropertyRule<'i> {
47 pub(crate) fn parse<'t>(
48 name: DashedIdent<'i>,
49 input: &mut Parser<'i, 't>,
50 loc: Location,
51 ) -> Result<Self, ParseError<'i, ParserError<'i>>> {
52 let mut parser = PropertyRuleDeclarationParser {
53 syntax: None,
54 inherits: None,
55 initial_value: None,
56 };
57
58 let mut decl_parser = RuleBodyParser::new(input, &mut parser);
59 while let Some(decl) = decl_parser.next() {
60 match decl {
61 Ok(()) => {}
62 Err((e, _)) => return Err(e),
63 }
64 }
65
66 let parser = decl_parser.parser;
68 let syntax = parser
69 .syntax
70 .clone()
71 .ok_or(decl_parser.input.new_custom_error(ParserError::AtRuleBodyInvalid))?;
72 let inherits = parser
73 .inherits
74 .clone()
75 .ok_or(decl_parser.input.new_custom_error(ParserError::AtRuleBodyInvalid))?;
76
77 let initial_value = match syntax {
79 SyntaxString::Universal => match parser.initial_value {
80 None => None,
81 Some(val) => {
82 let mut input = ParserInput::new(val);
83 let mut parser = Parser::new(&mut input);
84
85 if parser.is_exhausted() {
86 Some(ParsedComponent::TokenList(TokenList(vec![])))
87 } else {
88 Some(syntax.parse_value(&mut parser)?)
89 }
90 }
91 },
92 _ => {
93 let val = parser
94 .initial_value
95 .ok_or(input.new_custom_error(ParserError::AtRuleBodyInvalid))?;
96 let mut input = ParserInput::new(val);
97 let mut parser = Parser::new(&mut input);
98 Some(syntax.parse_value(&mut parser)?)
99 }
100 };
101
102 return Ok(PropertyRule {
103 name,
104 syntax,
105 inherits,
106 initial_value,
107 loc,
108 });
109 }
110}
111
112impl<'i> ToCss for PropertyRule<'i> {
113 fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
114 where
115 W: std::fmt::Write,
116 {
117 #[cfg(feature = "sourcemap")]
118 dest.add_mapping(self.loc);
119 dest.write_str("@property ")?;
120 self.name.to_css(dest)?;
121 dest.whitespace()?;
122 dest.write_char('{')?;
123 dest.indent();
124 dest.newline()?;
125
126 dest.write_str("syntax:")?;
127 dest.whitespace()?;
128 self.syntax.to_css(dest)?;
129 dest.write_char(';')?;
130 dest.newline()?;
131
132 dest.write_str("inherits:")?;
133 dest.whitespace()?;
134 match self.inherits {
135 true => dest.write_str("true")?,
136 false => dest.write_str("false")?,
137 }
138
139 if let Some(initial_value) = &self.initial_value {
140 dest.write_char(';')?;
141 dest.newline()?;
142
143 dest.write_str("initial-value:")?;
144 dest.whitespace()?;
145 initial_value.to_css(dest)?;
146
147 if !dest.minify {
148 dest.write_char(';')?;
149 }
150 }
151
152 dest.dedent();
153 dest.newline()?;
154 dest.write_char('}')
155 }
156}
157
158pub(crate) struct PropertyRuleDeclarationParser<'i> {
159 syntax: Option<SyntaxString>,
160 inherits: Option<bool>,
161 initial_value: Option<&'i str>,
162}
163
164impl<'i> cssparser::DeclarationParser<'i> for PropertyRuleDeclarationParser<'i> {
165 type Declaration = ();
166 type Error = ParserError<'i>;
167
168 fn parse_value<'t>(
169 &mut self,
170 name: CowRcStr<'i>,
171 input: &mut cssparser::Parser<'i, 't>,
172 ) -> Result<Self::Declaration, cssparser::ParseError<'i, Self::Error>> {
173 match_ignore_ascii_case! { &name,
174 "syntax" => {
175 let syntax = SyntaxString::parse(input)?;
176 self.syntax = Some(syntax);
177 },
178 "inherits" => {
179 let location = input.current_source_location();
180 let ident = input.expect_ident()?;
181 let inherits = match_ignore_ascii_case! {&*ident,
182 "true" => true,
183 "false" => false,
184 _ => return Err(location.new_unexpected_token_error(
185 cssparser::Token::Ident(ident.clone())
186 ))
187 };
188 self.inherits = Some(inherits);
189 },
190 "initial-value" => {
191 let start = input.position();
193 while input.next().is_ok() {}
194 let initial_value = input.slice_from(start);
195 self.initial_value = Some(initial_value);
196 },
197 _ => return Err(input.new_custom_error(ParserError::InvalidDeclaration))
198 }
199
200 return Ok(());
201 }
202}
203
204impl<'i> AtRuleParser<'i> for PropertyRuleDeclarationParser<'i> {
206 type Prelude = ();
207 type AtRule = ();
208 type Error = ParserError<'i>;
209}
210
211impl<'i> QualifiedRuleParser<'i> for PropertyRuleDeclarationParser<'i> {
212 type Prelude = ();
213 type QualifiedRule = ();
214 type Error = ParserError<'i>;
215}
216
217impl<'i> RuleBodyItemParser<'i, (), ParserError<'i>> for PropertyRuleDeclarationParser<'i> {
218 fn parse_qualified(&self) -> bool {
219 false
220 }
221
222 fn parse_declarations(&self) -> bool {
223 true
224 }
225}