зеркало из https://github.com/mozilla/gecko-dev.git
141 строка
4.1 KiB
Rust
141 строка
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
|
|
}
|
|
}
|