1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 use cg;
6 use quote::Tokens;
7 use syn::DeriveInput;
8 use synstructure;
9 use to_css::CssVariantAttrs;
10 
derive(input: DeriveInput) -> Tokens11 pub fn derive(input: DeriveInput) -> Tokens {
12     let name = &input.ident;
13     let s = synstructure::Structure::new(&input);
14 
15     let match_body = s.variants().iter().fold(quote!(), |match_body, variant| {
16         let bindings = variant.bindings();
17         assert!(
18             bindings.is_empty(),
19             "Parse is only supported for single-variant enums for now"
20         );
21 
22         let variant_attrs = cg::parse_variant_attrs::<CssVariantAttrs>(&variant.ast());
23         let identifier = cg::to_css_identifier(
24             &variant_attrs.keyword.unwrap_or(variant.ast().ident.as_ref().into()),
25         );
26         let ident = &variant.ast().ident;
27 
28         let mut body = quote! {
29             #match_body
30             #identifier => Ok(#name::#ident),
31         };
32 
33 
34         let aliases = match variant_attrs.aliases {
35             Some(aliases) => aliases,
36             None => return body,
37         };
38 
39         for alias in aliases.split(",") {
40             body = quote! {
41                 #body
42                 #alias => Ok(#name::#ident),
43             };
44         }
45 
46         body
47     });
48 
49     let parse_trait_impl = quote! {
50         impl ::parser::Parse for #name {
51             #[inline]
52             fn parse<'i, 't>(
53                 _: &::parser::ParserContext,
54                 input: &mut ::cssparser::Parser<'i, 't>,
55             ) -> Result<Self, ::style_traits::ParseError<'i>> {
56                 Self::parse(input)
57             }
58         }
59     };
60 
61     // TODO(emilio): It'd be nice to get rid of these, but that makes the
62     // conversion harder...
63     let methods_impl = quote! {
64         impl #name {
65             /// Parse this keyword.
66             #[inline]
67             pub fn parse<'i, 't>(
68                 input: &mut ::cssparser::Parser<'i, 't>,
69             ) -> Result<Self, ::style_traits::ParseError<'i>> {
70                 let location = input.current_source_location();
71                 let ident = input.expect_ident()?;
72                 Self::from_ident(ident.as_ref()).map_err(|()| {
73                     location.new_unexpected_token_error(
74                         ::cssparser::Token::Ident(ident.clone())
75                     )
76                 })
77             }
78 
79             /// Parse this keyword from a string slice.
80             #[inline]
81             pub fn from_ident(ident: &str) -> Result<Self, ()> {
82                 match_ignore_ascii_case! { ident,
83                     #match_body
84                     _ => Err(()),
85                 }
86             }
87         }
88     };
89 
90     quote! {
91         #parse_trait_impl
92         #methods_impl
93     }
94 }
95