1use crate::cg;
6use crate::to_css::{CssBitflagAttrs, CssVariantAttrs};
7use proc_macro2::{Span, TokenStream};
8use quote::TokenStreamExt;
9use syn::{self, DeriveInput, Ident, Path};
10use synstructure::{Structure, VariantInfo};
11
12#[derive(Default, FromVariant)]
13#[darling(attributes(parse), default)]
14pub struct ParseVariantAttrs {
15 pub aliases: Option<String>,
16 pub condition: Option<Path>,
17 pub parse_fn: Option<Path>,
18}
19
20#[derive(Default, FromField)]
21#[darling(attributes(parse), default)]
22pub struct ParseFieldAttrs {
23 field_bound: bool,
24}
25
26fn parse_bitflags(bitflags: &CssBitflagAttrs) -> TokenStream {
27 let mut match_arms = TokenStream::new();
28 for (rust_name, css_name) in bitflags.single_flags() {
29 let rust_ident = Ident::new(&rust_name, Span::call_site());
30 match_arms.append_all(quote! {
31 #css_name if result.is_empty() => {
32 single_flag = true;
33 Self::#rust_ident
34 },
35 });
36 }
37
38 for (rust_name, css_name) in bitflags.mixed_flags() {
39 let rust_ident = Ident::new(&rust_name, Span::call_site());
40 match_arms.append_all(quote! {
41 #css_name => Self::#rust_ident,
42 });
43 }
44
45 let mut validate_condition = quote! { !result.is_empty() };
46 if let Some(ref function) = bitflags.validate_mixed {
47 validate_condition.append_all(quote! {
48 && #function(&mut result)
49 });
50 }
51
52 quote! {
58 let mut result = Self::empty();
59 loop {
60 let mut single_flag = false;
61 let flag: Result<_, style_traits::ParseError<'i>> = input.try_parse(|input| {
62 Ok(try_match_ident_ignore_ascii_case! { input,
63 #match_arms
64 })
65 });
66
67 let flag = match flag {
68 Ok(flag) => flag,
69 Err(..) => break,
70 };
71
72 if single_flag {
73 return Ok(flag);
74 }
75
76 if result.intersects(flag) {
77 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
78 }
79
80 result.insert(flag);
81 }
82 if #validate_condition {
83 Ok(result)
84 } else {
85 Err(input.new_custom_error(style_traits::StyleParseErrorKind::UnspecifiedError))
86 }
87 }
88}
89
90fn parse_non_keyword_variant(
91 where_clause: &mut Option<syn::WhereClause>,
92 variant: &VariantInfo,
93 variant_attrs: &CssVariantAttrs,
94 parse_attrs: &ParseVariantAttrs,
95 skip_try: bool,
96) -> TokenStream {
97 let bindings = variant.bindings();
98 assert!(parse_attrs.aliases.is_none());
99 assert!(variant_attrs.function.is_none());
100 assert!(variant_attrs.keyword.is_none());
101 assert_eq!(
102 bindings.len(),
103 1,
104 "We only support deriving parse for simple variants"
105 );
106 let binding_ast = &bindings[0].ast();
107 let ty = &binding_ast.ty;
108
109 if let Some(ref bitflags) = variant_attrs.bitflags {
110 assert!(skip_try, "Should be the only variant");
111 assert!(parse_attrs.parse_fn.is_none(), "should not be needed");
112 assert!(
113 parse_attrs.condition.is_none(),
114 "Should be the only variant"
115 );
116 assert!(where_clause.is_none(), "Generic bitflags?");
117 return parse_bitflags(bitflags);
118 }
119
120 let field_attrs = cg::parse_field_attrs::<ParseFieldAttrs>(binding_ast);
121
122 if field_attrs.field_bound {
123 cg::add_predicate(where_clause, parse_quote!(#ty: crate::parser::Parse));
124 }
125
126 let mut parse = if let Some(ref parse_fn) = parse_attrs.parse_fn {
127 quote! { #parse_fn(context, input) }
128 } else {
129 quote! { <#ty as crate::parser::Parse>::parse(context, input) }
130 };
131
132 let variant_name = &variant.ast().ident;
133 let variant_name = match variant.prefix {
134 Some(p) => quote! { #p::#variant_name },
135 None => quote! { #variant_name },
136 };
137
138 parse = if skip_try {
139 quote! {
140 let v = #parse?;
141 return Ok(#variant_name(v));
142 }
143 } else {
144 quote! {
145 if let Ok(v) = input.try_parse(|input| #parse) {
146 return Ok(#variant_name(v));
147 }
148 }
149 };
150
151 if let Some(ref condition) = parse_attrs.condition {
152 parse = quote! {
153 if #condition(context) {
154 #parse
155 }
156 };
157
158 if skip_try {
159 parse = quote! {
162 #parse
163 Err(input.new_custom_error(style_traits::StyleParseErrorKind::UnspecifiedError))
164 };
165 }
166 }
167
168 parse
169}
170
171pub fn derive(mut input: DeriveInput) -> TokenStream {
172 let mut where_clause = input.generics.where_clause.take();
173 for param in input.generics.type_params() {
174 cg::add_predicate(
175 &mut where_clause,
176 parse_quote!(#param: crate::parser::Parse),
177 );
178 }
179
180 let name = &input.ident;
181 let s = Structure::new(&input);
182
183 let mut saw_condition = false;
184 let mut match_keywords = quote! {};
185 let mut non_keywords = vec![];
186
187 let mut effective_variants = 0;
188 for variant in s.variants().iter() {
189 let css_variant_attrs = cg::parse_variant_attrs_from_ast::<CssVariantAttrs>(&variant.ast());
190 if css_variant_attrs.skip {
191 continue;
192 }
193 effective_variants += 1;
194
195 let parse_attrs = cg::parse_variant_attrs_from_ast::<ParseVariantAttrs>(&variant.ast());
196
197 saw_condition |= parse_attrs.condition.is_some();
198
199 if !variant.bindings().is_empty() {
200 non_keywords.push((variant, css_variant_attrs, parse_attrs));
201 continue;
202 }
203
204 assert!(parse_attrs.parse_fn.is_none());
205
206 let identifier = cg::to_css_identifier(
207 &css_variant_attrs
208 .keyword
209 .unwrap_or_else(|| variant.ast().ident.to_string()),
210 );
211 let ident = &variant.ast().ident;
212
213 let condition = match parse_attrs.condition {
214 Some(ref p) => quote! { if #p(context) },
215 None => quote! {},
216 };
217
218 match_keywords.extend(quote! {
219 #identifier #condition => Ok(#name::#ident),
220 });
221
222 let aliases = match parse_attrs.aliases {
223 Some(aliases) => aliases,
224 None => continue,
225 };
226
227 for alias in aliases.split(',') {
228 match_keywords.extend(quote! {
229 #alias #condition => Ok(#name::#ident),
230 });
231 }
232 }
233
234 let needs_context = saw_condition || !non_keywords.is_empty();
235
236 let context_ident = if needs_context {
237 quote! { context }
238 } else {
239 quote! { _ }
240 };
241
242 let has_keywords = non_keywords.len() != effective_variants;
243
244 let mut parse_non_keywords = quote! {};
245 for (i, (variant, css_attrs, parse_attrs)) in non_keywords.iter().enumerate() {
246 let skip_try = !has_keywords && i == non_keywords.len() - 1;
247 let parse_variant =
248 parse_non_keyword_variant(&mut where_clause, variant, css_attrs, parse_attrs, skip_try);
249 parse_non_keywords.extend(parse_variant);
250 }
251
252 let parse_body = if needs_context {
253 let parse_keywords = if has_keywords {
254 quote! {
255 let location = input.current_source_location();
256 let ident = input.expect_ident()?;
257 match_ignore_ascii_case! { &ident,
258 #match_keywords
259 _ => Err(location.new_unexpected_token_error(
260 cssparser::Token::Ident(ident.clone())
261 ))
262 }
263 }
264 } else {
265 quote! {}
266 };
267
268 quote! {
269 #parse_non_keywords
270 #parse_keywords
271 }
272 } else {
273 quote! { Self::parse(input) }
274 };
275
276 let has_non_keywords = !non_keywords.is_empty();
277
278 input.generics.where_clause = where_clause;
279 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
280
281 let parse_trait_impl = quote! {
282 impl #impl_generics crate::parser::Parse for #name #ty_generics #where_clause {
283 #[inline]
284 fn parse<'i, 't>(
285 #context_ident: &crate::parser::ParserContext,
286 input: &mut cssparser::Parser<'i, 't>,
287 ) -> Result<Self, style_traits::ParseError<'i>> {
288 #parse_body
289 }
290 }
291 };
292
293 if needs_context {
294 return parse_trait_impl;
295 }
296
297 assert!(!has_non_keywords);
298
299 let methods_impl = quote! {
302 impl #name {
303 #[inline]
305 pub fn parse<'i, 't>(
306 input: &mut cssparser::Parser<'i, 't>,
307 ) -> Result<Self, style_traits::ParseError<'i>> {
308 let location = input.current_source_location();
309 let ident = input.expect_ident()?;
310 Self::from_ident(ident.as_ref()).map_err(|()| {
311 location.new_unexpected_token_error(
312 cssparser::Token::Ident(ident.clone())
313 )
314 })
315 }
316
317 #[inline]
319 pub fn from_ident(ident: &str) -> Result<Self, ()> {
320 match_ignore_ascii_case! { ident,
321 #match_keywords
322 _ => Err(()),
323 }
324 }
325 }
326 };
327
328 quote! {
329 #parse_trait_impl
330 #methods_impl
331 }
332}