Auto merge of #18208 - servo:derive-all-the-things, r=emilio

Use darling and derive more things

<!-- Reviewable:start -->
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/18208)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2017-08-24 04:05:03 -05:00 committed by GitHub
commit d3c2017b23
10 changed files with 95 additions and 106 deletions

View file

@ -787,40 +787,6 @@ impl ToAnimatedZero for Visibility {
}
}
/// https://drafts.csswg.org/css-transitions/#animtype-length
impl Animate for VerticalAlign {
#[inline]
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
match (self, other) {
(
&VerticalAlign::LengthOrPercentage(ref this),
&VerticalAlign::LengthOrPercentage(ref other),
) => {
Ok(VerticalAlign::LengthOrPercentage(
this.animate(other, procedure)?
))
},
_ => Err(()),
}
}
}
impl ComputeSquaredDistance for VerticalAlign {
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
match (self, other) {
(&VerticalAlign::LengthOrPercentage(ref this), &VerticalAlign::LengthOrPercentage(ref other)) => {
this.compute_squared_distance(other)
},
_ => {
// FIXME(nox): Should this return `Ok(SquaredDistance::Value(0.))`
// if `self` and `other` are the same keyword value?
Err(())
},
}
}
}
impl ToAnimatedZero for VerticalAlign {
#[inline]
fn to_animated_zero(&self) -> Result<Self, ()> { Err(()) }
@ -2459,24 +2425,6 @@ impl ToAnimatedZero for TransformList {
}
}
impl<A, B> ToAnimatedZero for Either<A, B>
where
A: ToAnimatedZero,
B: ToAnimatedZero,
{
#[inline]
fn to_animated_zero(&self) -> Result<Self, ()> {
match *self {
Either::First(ref first) => {
Ok(Either::First(first.to_animated_zero()?))
},
Either::Second(ref second) => {
Ok(Either::Second(second.to_animated_zero()?))
},
}
}
}
/// Animated SVGPaint
pub type IntermediateSVGPaint = SVGPaint<AnimatedRGBA, ComputedUrl>;

View file

@ -370,11 +370,12 @@ ${helpers.single_keyword("position", "static absolute relative fixed",
/// The keywords are the same, and the `LengthOrPercentage` is computed
/// here.
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, PartialEq)]
pub enum T {
% for keyword in vertical_align_keywords:
${to_rust_ident(keyword)},
#[animation(error)] // only animatable as a length
${to_rust_ident(keyword)},
% endfor
LengthOrPercentage(computed::LengthOrPercentage),
}

View file

@ -69,7 +69,7 @@ impl Parse for Impossible {
/// A struct representing one of two kinds of values.
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Animate, Clone, ComputeSquaredDistance, Copy, HasViewportPercentage)]
#[derive(PartialEq, ToAnimatedValue, ToComputedValue, ToCss)]
#[derive(PartialEq, ToAnimatedValue, ToAnimatedZero, ToComputedValue, ToCss)]
pub enum Either<A, B> {
/// The first value.
First(A),

View file

@ -10,6 +10,7 @@ path = "lib.rs"
proc-macro = true
[dependencies]
darling = "0.2"
quote = "0.3.15"
syn = { version = "0.11", features = ["visit"] }
synstructure = "0.5.2"

View file

@ -14,7 +14,13 @@ pub fn derive(input: syn::DeriveInput) -> quote::Tokens {
let variants = cg::variants(&input);
let mut match_body = quote!();
match_body.append_all(variants.iter().map(|variant| {
let mut append_error_clause = variants.len() > 1;
match_body.append_all(variants.iter().flat_map(|variant| {
let attrs = cg::parse_variant_attrs::<AnimateAttrs>(variant);
if attrs.error {
append_error_clause = true;
return None;
}
let name = cg::variant_ctor(&input, variant);
let (this_pattern, this_info) = cg::ref_pattern(&name, variant, "this");
let (other_pattern, other_info) = cg::ref_pattern(&name, variant, "other");
@ -29,15 +35,15 @@ pub fn derive(input: syn::DeriveInput) -> quote::Tokens {
let #result = ::values::animated::Animate::animate(#this, #other, procedure)?;
}
}));
quote! {
Some(quote! {
(&#this_pattern, &#other_pattern) => {
#computations
Ok(#result_value)
}
}
})
}));
if variants.len() > 1 {
if append_error_clause {
match_body = quote! { #match_body, _ => Err(()), };
}
@ -57,3 +63,9 @@ pub fn derive(input: syn::DeriveInput) -> quote::Tokens {
}
}
}
#[derive(Default, FromVariant)]
#[darling(attributes(animate), default)]
pub struct AnimateAttrs {
pub error: bool,
}

View file

@ -2,6 +2,7 @@
* 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 darling::FromVariant;
use quote::Tokens;
use std::borrow::Cow;
use std::iter;
@ -136,6 +137,16 @@ where
}
}
pub fn parse_variant_attrs<A>(variant: &Variant) -> A
where
A: FromVariant,
{
match A::from_variant(variant) {
Ok(attrs) => attrs,
Err(e) => panic!("failed to parse attributes: {}", e),
}
}
pub fn ref_pattern<'a>(
name: &Ident,
variant: &'a Variant,

View file

@ -2,6 +2,7 @@
* 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 animate::AnimateAttrs;
use cg;
use quote;
use syn;
@ -14,7 +15,13 @@ pub fn derive(input: syn::DeriveInput) -> quote::Tokens {
let variants = cg::variants(&input);
let mut match_body = quote!();
let mut append_error_clause = variants.len() > 1;
match_body.append_all(variants.iter().map(|variant| {
let attrs = cg::parse_variant_attrs::<AnimateAttrs>(variant);
if attrs.error {
append_error_clause = true;
return None;
}
let name = cg::variant_ctor(&input, variant);
let (this_pattern, this_info) = cg::ref_pattern(&name, &variant, "this");
let (other_pattern, other_info) = cg::ref_pattern(&name, &variant, "other");
@ -32,14 +39,14 @@ pub fn derive(input: syn::DeriveInput) -> quote::Tokens {
}), "+");
sum
};
quote! {
Some(quote! {
(&#this_pattern, &#other_pattern) => {
Ok(#sum)
}
}
})
}));
if variants.len() > 1 {
if append_error_clause {
match_body = quote! { #match_body, _ => Err(()), };
}

View file

@ -2,6 +2,7 @@
* 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/. */
#[macro_use] extern crate darling;
extern crate proc_macro;
#[macro_use] extern crate quote;
extern crate syn;
@ -18,13 +19,13 @@ mod to_animated_zero;
mod to_computed_value;
mod to_css;
#[proc_macro_derive(Animate)]
#[proc_macro_derive(Animate, attributes(animation))]
pub fn derive_animate(stream: TokenStream) -> TokenStream {
let input = syn::parse_derive_input(&stream.to_string()).unwrap();
animate::derive(input).to_string().parse().unwrap()
}
#[proc_macro_derive(ComputeSquaredDistance)]
#[proc_macro_derive(ComputeSquaredDistance, attributes(animation))]
pub fn derive_compute_squared_distance(stream: TokenStream) -> TokenStream {
let input = syn::parse_derive_input(&stream.to_string()).unwrap();
compute_squared_distance::derive(input).to_string().parse().unwrap()

View file

@ -16,47 +16,8 @@ pub fn derive(input: syn::DeriveInput) -> quote::Tokens {
let style = synstructure::BindStyle::Ref.into();
let match_body = synstructure::each_variant(&input, &style, |bindings, variant| {
let mut identifier = to_css_identifier(variant.ident.as_ref());
let mut css_attrs = variant.attrs.iter().filter(|attr| attr.name() == "css");
let (is_function, use_comma) = css_attrs.next().map_or((false, false), |attr| {
match attr.value {
syn::MetaItem::List(ref ident, ref items) if ident.as_ref() == "css" => {
let mut nested = items.iter();
let mut is_function = false;
let mut use_comma = false;
for attr in nested.by_ref() {
match *attr {
syn::NestedMetaItem::MetaItem(syn::MetaItem::Word(ref ident)) => {
match ident.as_ref() {
"function" => {
if is_function {
panic!("repeated `#[css(function)]` attribute");
}
is_function = true;
},
"comma" => {
if use_comma {
panic!("repeated `#[css(comma)]` attribute");
}
use_comma = true;
},
_ => panic!("only `#[css(function | comma)]` is supported for now"),
}
},
_ => panic!("only `#[css(<ident...>)]` is supported for now"),
}
}
if nested.next().is_some() {
panic!("only `#[css()]` or `#[css(<ident>)]` is supported for now")
}
(is_function, use_comma)
},
_ => panic!("only `#[css(...)]` is supported for now"),
}
});
if css_attrs.next().is_some() {
panic!("only a single `#[css(...)]` attribute is supported for now");
}
let separator = if use_comma { ", " } else { " " };
let css_attrs = cg::parse_variant_attrs::<CssAttrs>(variant);
let separator = if css_attrs.comma { ", " } else { " " };
let mut expr = if !bindings.is_empty() {
let mut expr = quote! {};
for binding in bindings {
@ -80,7 +41,7 @@ pub fn derive(input: syn::DeriveInput) -> quote::Tokens {
::std::fmt::Write::write_str(dest, #identifier)
}
};
if is_function {
if css_attrs.function {
identifier.push_str("(");
expr = quote! {
::std::fmt::Write::write_str(dest, #identifier)?;
@ -107,6 +68,13 @@ pub fn derive(input: syn::DeriveInput) -> quote::Tokens {
}
}
#[derive(Default, FromVariant)]
#[darling(attributes(css), default)]
struct CssAttrs {
function: bool,
comma: bool,
}
/// Transforms "FooBar" to "foo-bar".
///
/// If the first Camel segment is "Moz" or "Webkit", the result string