forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			140 lines
		
	
	
	
		
			4.1 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			140 lines
		
	
	
	
		
			4.1 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| use cg;
 | |
| use quote::Tokens;
 | |
| use syn::{DeriveInput, Path};
 | |
| use synstructure;
 | |
| use to_css::CssVariantAttrs;
 | |
| 
 | |
| #[darling(attributes(parse), default)]
 | |
| #[derive(Default, FromVariant)]
 | |
| pub struct ParseVariantAttrs {
 | |
|     pub aliases: Option<String>,
 | |
|     pub condition: Option<Path>,
 | |
| }
 | |
| 
 | |
| pub fn derive(input: DeriveInput) -> Tokens {
 | |
|     let name = &input.ident;
 | |
|     let s = synstructure::Structure::new(&input);
 | |
| 
 | |
|     let mut saw_condition = false;
 | |
|     let match_body = s.variants().iter().fold(quote!(), |match_body, variant| {
 | |
|         let bindings = variant.bindings();
 | |
|         assert!(
 | |
|             bindings.is_empty(),
 | |
|             "Parse is only supported for single-variant enums for now"
 | |
|         );
 | |
| 
 | |
|         let css_variant_attrs =
 | |
|             cg::parse_variant_attrs_from_ast::<CssVariantAttrs>(&variant.ast());
 | |
|         let parse_attrs =
 | |
|             cg::parse_variant_attrs_from_ast::<ParseVariantAttrs>(&variant.ast());
 | |
|         if css_variant_attrs.skip {
 | |
|             return match_body;
 | |
|         }
 | |
| 
 | |
|         let identifier = cg::to_css_identifier(
 | |
|             &css_variant_attrs.keyword.unwrap_or(variant.ast().ident.as_ref().into()),
 | |
|         );
 | |
|         let ident = &variant.ast().ident;
 | |
| 
 | |
|         saw_condition |= parse_attrs.condition.is_some();
 | |
|         let condition = match parse_attrs.condition {
 | |
|             Some(ref p) => quote! { if #p(context) },
 | |
|             None => quote! { },
 | |
|         };
 | |
| 
 | |
|         let mut body = quote! {
 | |
|             #match_body
 | |
|             #identifier #condition => Ok(#name::#ident),
 | |
|         };
 | |
| 
 | |
|         let aliases = match parse_attrs.aliases {
 | |
|             Some(aliases) => aliases,
 | |
|             None => return body,
 | |
|         };
 | |
| 
 | |
|         for alias in aliases.split(",") {
 | |
|             body = quote! {
 | |
|                 #body
 | |
|                 #alias #condition => Ok(#name::#ident),
 | |
|             };
 | |
|         }
 | |
| 
 | |
|         body
 | |
|     });
 | |
| 
 | |
|     let context_ident = if saw_condition {
 | |
|         quote! { context }
 | |
|     } else {
 | |
|         quote! { _ }
 | |
|     };
 | |
| 
 | |
|     let parse_body = if saw_condition {
 | |
|         quote! {
 | |
|             let location = input.current_source_location();
 | |
|             let ident = input.expect_ident()?;
 | |
|             match_ignore_ascii_case! { &ident,
 | |
|                 #match_body
 | |
|                 _ => Err(location.new_unexpected_token_error(
 | |
|                     ::cssparser::Token::Ident(ident.clone())
 | |
|                 ))
 | |
|             }
 | |
|         }
 | |
|     } else {
 | |
|         quote! { Self::parse(input) }
 | |
|     };
 | |
| 
 | |
| 
 | |
|     let parse_trait_impl = quote! {
 | |
|         impl ::parser::Parse for #name {
 | |
|             #[inline]
 | |
|             fn parse<'i, 't>(
 | |
|                 #context_ident: &::parser::ParserContext,
 | |
|                 input: &mut ::cssparser::Parser<'i, 't>,
 | |
|             ) -> Result<Self, ::style_traits::ParseError<'i>> {
 | |
|                 #parse_body
 | |
|             }
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     if saw_condition {
 | |
|         return parse_trait_impl;
 | |
|     }
 | |
| 
 | |
|     // TODO(emilio): It'd be nice to get rid of these, but that makes the
 | |
|     // conversion harder...
 | |
|     let methods_impl = quote! {
 | |
|         impl #name {
 | |
|             /// Parse this keyword.
 | |
|             #[inline]
 | |
|             pub fn parse<'i, 't>(
 | |
|                 input: &mut ::cssparser::Parser<'i, 't>,
 | |
|             ) -> Result<Self, ::style_traits::ParseError<'i>> {
 | |
|                 let location = input.current_source_location();
 | |
|                 let ident = input.expect_ident()?;
 | |
|                 Self::from_ident(ident.as_ref()).map_err(|()| {
 | |
|                     location.new_unexpected_token_error(
 | |
|                         ::cssparser::Token::Ident(ident.clone())
 | |
|                     )
 | |
|                 })
 | |
|             }
 | |
| 
 | |
|             /// Parse this keyword from a string slice.
 | |
|             #[inline]
 | |
|             pub fn from_ident(ident: &str) -> Result<Self, ()> {
 | |
|                 match_ignore_ascii_case! { ident,
 | |
|                     #match_body
 | |
|                     _ => Err(()),
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     quote! {
 | |
|         #parse_trait_impl
 | |
|         #methods_impl
 | |
|     }
 | |
| }
 | 
