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

Introduce style_derive::cg

<!-- 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/18200)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2017-08-23 10:01:15 -05:00 committed by GitHub
commit e629f8da7b
9 changed files with 289 additions and 483 deletions

View file

@ -2,56 +2,29 @@
* 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;
use std::borrow::Cow;
use syn;
use synstructure;
pub fn derive(input: syn::DeriveInput) -> quote::Tokens {
let name = &input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let mut where_clause = where_clause.clone();
for param in &input.generics.ty_params {
where_clause.predicates.push(where_predicate(syn::Ty::Path(None, param.ident.clone().into())))
}
let trait_path = &["values", "animated", "Animate"];
let (impl_generics, ty_generics, mut where_clause) =
cg::trait_parts(&input, trait_path);
let variants = variants(&input);
let variants = cg::variants(&input);
let mut match_body = quote!();
match_body.append_all(variants.iter().map(|variant| {
let name = match input.body {
syn::Body::Struct(_) => Cow::Borrowed(&input.ident),
syn::Body::Enum(_) => {
Cow::Owned(syn::Ident::from(format!("{}::{}", input.ident, variant.ident)))
},
};
let (this_pattern, this_info) = synstructure::match_pattern(
&name,
&variant.data,
&synstructure::BindOpts::with_prefix(
synstructure::BindStyle::Ref,
"this".to_owned(),
),
);
let (other_pattern, other_info) = synstructure::match_pattern(
&name,
&variant.data,
&synstructure::BindOpts::with_prefix(
synstructure::BindStyle::Ref,
"other".to_owned(),
),
);
let (result_value, result_info) = synstructure::match_pattern(
&name,
&variant.data,
&synstructure::BindOpts::with_prefix(
synstructure::BindStyle::Move,
"result".to_owned(),
),
);
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");
let (result_value, result_info) = cg::value(&name, variant, "result");
let mut computations = quote!();
let iter = result_info.iter().zip(this_info.iter().zip(&other_info));
computations.append_all(iter.map(|(result, (this, other))| {
where_clause.predicates.push(where_predicate(this.field.ty.clone()));
where_clause.predicates.push(
cg::where_predicate(this.field.ty.clone(), trait_path),
);
quote! {
let #result = ::values::animated::Animate::animate(#this, #other, procedure)?;
}
@ -84,40 +57,3 @@ pub fn derive(input: syn::DeriveInput) -> quote::Tokens {
}
}
}
fn variants(input: &syn::DeriveInput) -> Cow<[syn::Variant]> {
match input.body {
syn::Body::Enum(ref variants) => (&**variants).into(),
syn::Body::Struct(ref data) => {
vec![syn::Variant {
ident: input.ident.clone(),
attrs: input.attrs.clone(),
data: data.clone(),
discriminant: None,
}].into()
},
}
}
fn where_predicate(ty: syn::Ty) -> syn::WherePredicate {
syn::WherePredicate::BoundPredicate(
syn::WhereBoundPredicate {
bound_lifetimes: vec![],
bounded_ty: ty,
bounds: vec![syn::TyParamBound::Trait(
syn::PolyTraitRef {
bound_lifetimes: vec![],
trait_ref: syn::Path {
global: true,
segments: vec![
"values".into(),
"animated".into(),
"Animate".into(),
],
},
},
syn::TraitBoundModifier::None,
)],
},
)
}

View file

@ -0,0 +1,219 @@
/* 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 quote::Tokens;
use std::borrow::Cow;
use std::iter;
use syn::{AngleBracketedParameterData, Body, DeriveInput, Ident, ImplGenerics};
use syn::{Path, PathParameters, PathSegment, PolyTraitRef, QSelf};
use syn::{TraitBoundModifier, Ty, TyGenerics, TyParam, TyParamBound, TypeBinding};
use syn::{Variant, WhereBoundPredicate, WhereClause, WherePredicate};
use syn::visit::{self, Visitor};
use synstructure::{self, BindOpts, BindStyle, BindingInfo};
pub fn fmap_match<F>(
input: &DeriveInput,
bind_style: BindStyle,
mut f: F,
) -> Tokens
where
F: FnMut(BindingInfo) -> Tokens,
{
synstructure::each_variant(input, &bind_style.into(), |fields, variant| {
let name = variant_ctor(input, variant);
let (mapped, mapped_fields) = value(&name, variant, "mapped");
let fields_pairs = fields.into_iter().zip(mapped_fields);
let mut computations = quote!();
computations.append_all(fields_pairs.map(|(field, mapped_field)| {
let expr = f(field);
quote! { let #mapped_field = #expr; }
}));
computations.append(mapped);
Some(computations)
})
}
pub fn fmap_trait_parts<'a>(
input: &'a DeriveInput,
trait_path: &[&str],
trait_output: &str,
) -> (ImplGenerics<'a>, TyGenerics<'a>, WhereClause, Path) {
let (impl_generics, ty_generics, where_clause) = trait_parts(input, trait_path);
let output_ty = PathSegment {
ident: input.ident.clone(),
parameters: PathParameters::AngleBracketed(AngleBracketedParameterData {
lifetimes: input.generics.lifetimes.iter().map(|l| l.lifetime.clone()).collect(),
types: input.generics.ty_params.iter().map(|ty| {
Ty::Path(
Some(QSelf {
ty: Box::new(Ty::Path(None, ty.ident.clone().into())),
position: trait_path.len(),
}),
path(trait_path.iter().chain(iter::once(&trait_output))),
)
}).collect(),
.. Default::default()
}),
}.into();
(impl_generics, ty_generics, where_clause, output_ty)
}
fn fmap_trait_where_predicate(
bounded_ty: Ty,
trait_path: &[&str],
trait_output: Option<(&str, Ty)>,
) -> WherePredicate {
WherePredicate::BoundPredicate(WhereBoundPredicate {
bound_lifetimes: vec![],
bounded_ty,
bounds: vec![TyParamBound::Trait(
PolyTraitRef {
bound_lifetimes: vec![],
trait_ref: fmap_trait_ref(trait_path, trait_output),
},
TraitBoundModifier::None
)],
})
}
fn fmap_trait_ref(path: &[&str], output: Option<(&str, Ty)>) -> Path {
let (name, parent) = path.split_last().unwrap();
let last_segment = PathSegment {
ident: (*name).into(),
parameters: PathParameters::AngleBracketed(
AngleBracketedParameterData {
bindings: output.into_iter().map(|(param, ty)| {
TypeBinding { ident: param.into(), ty }
}).collect(),
.. Default::default()
}
)
};
Path {
global: true,
segments: {
parent
.iter()
.cloned()
.map(Into::into)
.chain(iter::once(last_segment))
.collect()
},
}
}
pub fn is_parameterized(ty: &Ty, params: &[TyParam]) -> bool {
struct IsParameterized<'a> {
params: &'a [TyParam],
has_free: bool,
}
impl<'a> Visitor for IsParameterized<'a> {
fn visit_path(&mut self, path: &Path) {
if !path.global && path.segments.len() == 1 {
if self.params.iter().any(|param| param.ident == path.segments[0].ident) {
self.has_free = true;
}
}
visit::walk_path(self, path);
}
}
let mut visitor = IsParameterized { params: params, has_free: false };
visitor.visit_ty(ty);
visitor.has_free
}
pub fn path<S>(segments: S) -> Path
where
S: IntoIterator,
<S as IntoIterator>::Item: AsRef<str>,
{
Path {
global: true,
segments: segments.into_iter().map(|s| s.as_ref().into()).collect(),
}
}
pub fn ref_pattern<'a>(
name: &Ident,
variant: &'a Variant,
prefix: &str,
) -> (Tokens, Vec<BindingInfo<'a>>) {
synstructure::match_pattern(
&name,
&variant.data,
&BindOpts::with_prefix(BindStyle::Ref, prefix.to_owned()),
)
}
pub fn trait_parts<'a>(
input: &'a DeriveInput,
trait_path: &[&str],
) -> (ImplGenerics<'a>, TyGenerics<'a>, WhereClause) {
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let mut where_clause = where_clause.clone();
for param in &input.generics.ty_params {
where_clause.predicates.push(fmap_trait_where_predicate(
Ty::Path(None, param.ident.clone().into()),
trait_path,
None,
));
}
(impl_generics, ty_generics, where_clause)
}
pub fn value<'a>(
name: &Ident,
variant: &'a Variant,
prefix: &str,
) -> (Tokens, Vec<BindingInfo<'a>>) {
synstructure::match_pattern(
&name,
&variant.data,
&BindOpts::with_prefix(BindStyle::Move, prefix.to_owned()),
)
}
pub fn variant_ctor<'a>(
input: &'a DeriveInput,
variant: &Variant,
) -> Cow<'a, Ident> {
match input.body {
Body::Struct(_) => Cow::Borrowed(&input.ident),
Body::Enum(_) => {
Cow::Owned(Ident::from(
format!("{}::{}", input.ident, variant.ident),
))
},
}
}
pub fn variants(input: &DeriveInput) -> Cow<[Variant]> {
match input.body {
Body::Enum(ref variants) => (&**variants).into(),
Body::Struct(ref data) => {
vec![Variant {
ident: input.ident.clone(),
attrs: input.attrs.clone(),
data: data.clone(),
discriminant: None,
}].into()
},
}
}
pub fn where_predicate(ty: Ty, segments: &[&str]) -> WherePredicate {
WherePredicate::BoundPredicate(WhereBoundPredicate {
bound_lifetimes: vec![],
bounded_ty: ty,
bounds: vec![TyParamBound::Trait(
PolyTraitRef {
bound_lifetimes: vec![],
trait_ref: path(segments),
},
TraitBoundModifier::None,
)],
})
}

View file

@ -2,50 +2,30 @@
* 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;
use std::borrow::Cow;
use syn;
use synstructure;
pub fn derive(input: syn::DeriveInput) -> quote::Tokens {
let name = &input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let mut where_clause = where_clause.clone();
for param in &input.generics.ty_params {
where_clause.predicates.push(where_predicate(syn::Ty::Path(None, param.ident.clone().into())))
}
let trait_path = &["values", "distance", "ComputeSquaredDistance"];
let (impl_generics, ty_generics, mut where_clause) =
cg::trait_parts(&input, trait_path);
let variants = variants(&input);
let variants = cg::variants(&input);
let mut match_body = quote!();
match_body.append_all(variants.iter().map(|variant| {
let name = match input.body {
syn::Body::Struct(_) => Cow::Borrowed(&input.ident),
syn::Body::Enum(_) => {
Cow::Owned(syn::Ident::from(format!("{}::{}", input.ident, variant.ident)))
},
};
let (this_pattern, this_info) = synstructure::match_pattern(
&name,
&variant.data,
&synstructure::BindOpts::with_prefix(
synstructure::BindStyle::Ref,
"this".to_owned(),
),
);
let (other_pattern, other_info) = synstructure::match_pattern(
&name,
&variant.data,
&synstructure::BindOpts::with_prefix(
synstructure::BindStyle::Ref,
"other".to_owned(),
),
);
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");
let sum = if this_info.is_empty() {
quote! { ::values::distance::SquaredDistance::Value(0.) }
} else {
let mut sum = quote!();
sum.append_separated(this_info.iter().zip(&other_info).map(|(this, other)| {
where_clause.predicates.push(where_predicate(this.field.ty.clone()));
where_clause.predicates.push(
cg::where_predicate(this.field.ty.clone(), trait_path),
);
quote! {
::values::distance::ComputeSquaredDistance::compute_squared_distance(#this, #other)?
}
@ -78,40 +58,3 @@ pub fn derive(input: syn::DeriveInput) -> quote::Tokens {
}
}
}
fn variants(input: &syn::DeriveInput) -> Cow<[syn::Variant]> {
match input.body {
syn::Body::Enum(ref variants) => (&**variants).into(),
syn::Body::Struct(ref data) => {
vec![syn::Variant {
ident: input.ident.clone(),
attrs: input.attrs.clone(),
data: data.clone(),
discriminant: None,
}].into()
},
}
}
fn where_predicate(ty: syn::Ty) -> syn::WherePredicate {
syn::WherePredicate::BoundPredicate(
syn::WhereBoundPredicate {
bound_lifetimes: vec![],
bounded_ty: ty,
bounds: vec![syn::TyParamBound::Trait(
syn::PolyTraitRef {
bound_lifetimes: vec![],
trait_ref: syn::Path {
global: true,
segments: vec![
"values".into(),
"distance".into(),
"ComputeSquaredDistance".into(),
],
},
},
syn::TraitBoundModifier::None,
)],
},
)
}

View file

@ -2,17 +2,16 @@
* 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;
use syn;
use synstructure;
pub fn derive(input: syn::DeriveInput) -> quote::Tokens {
let name = &input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let mut where_clause = where_clause.clone();
for param in &input.generics.ty_params {
where_clause.predicates.push(where_predicate(syn::Ty::Path(None, param.ident.clone().into())))
}
let trait_path = &["style_traits", "HasViewportPercentage"];
let (impl_generics, ty_generics, mut where_clause) =
cg::trait_parts(&input, trait_path);
let style = synstructure::BindStyle::Ref.into();
let match_body = synstructure::each_variant(&input, &style, |bindings, _| {
@ -22,7 +21,9 @@ pub fn derive(input: syn::DeriveInput) -> quote::Tokens {
};
let mut expr = quote!(::style_traits::HasViewportPercentage::has_viewport_percentage(#first));
for binding in rest {
where_clause.predicates.push(where_predicate(binding.field.ty.clone()));
where_clause.predicates.push(
cg::where_predicate(binding.field.ty.clone(), trait_path),
);
expr = quote!(#expr || ::style_traits::HasViewportPercentage::has_viewport_percentage(#binding));
}
Some(expr)
@ -40,17 +41,3 @@ pub fn derive(input: syn::DeriveInput) -> quote::Tokens {
}
}
}
fn where_predicate(ty: syn::Ty) -> syn::WherePredicate {
syn::WherePredicate::BoundPredicate(syn::WhereBoundPredicate {
bound_lifetimes: vec![],
bounded_ty: ty,
bounds: vec![syn::TyParamBound::Trait(
syn::PolyTraitRef {
bound_lifetimes: vec![],
trait_ref: syn::parse_path("::style_traits::HasViewportPercentage").unwrap(),
},
syn::TraitBoundModifier::None
)],
})
}

View file

@ -10,6 +10,7 @@ extern crate synstructure;
use proc_macro::TokenStream;
mod animate;
mod cg;
mod compute_squared_distance;
mod has_viewport_percentage;
mod to_animated_value;

View file

@ -2,49 +2,24 @@
* 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;
use syn;
use synstructure;
use synstructure::BindStyle;
pub fn derive(input: syn::DeriveInput) -> quote::Tokens {
let name = &input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let mut where_clause = where_clause.clone();
for param in &input.generics.ty_params {
where_clause.predicates.push(where_predicate(syn::Ty::Path(None, param.ident.clone().into()), None));
}
let (impl_generics, ty_generics, where_clause, animated_value_type) =
cg::fmap_trait_parts(
&input,
&["values", "animated", "ToAnimatedValue"],
"AnimatedValue",
);
let animated_value_type = syn::Path::from(syn::PathSegment {
ident: name.clone(),
parameters: syn::PathParameters::AngleBracketed(syn::AngleBracketedParameterData {
lifetimes: input.generics.lifetimes.iter().map(|l| {
l.lifetime.clone()
}).collect(),
types: input.generics.ty_params.iter().map(|ty| {
syn::Ty::Path(
Some(syn::QSelf {
ty: Box::new(syn::Ty::Path(None, ty.ident.clone().into())),
position: 3,
}),
syn::Path {
global: true,
segments: vec![
"values".into(),
"animated".into(),
"ToAnimatedValue".into(),
"AnimatedValue".into(),
],
},
)
}).collect(),
.. Default::default()
}),
});
let to_body = match_body(&input, |field| {
let to_body = cg::fmap_match(&input, BindStyle::Move, |field| {
quote!(::values::animated::ToAnimatedValue::to_animated_value(#field))
});
let from_body = match_body(&input, |field| {
let from_body = cg::fmap_match(&input, BindStyle::Move, |field| {
quote!(::values::animated::ToAnimatedValue::from_animated_value(#field))
});
@ -69,73 +44,3 @@ pub fn derive(input: syn::DeriveInput) -> quote::Tokens {
}
}
}
fn match_body<F>(input: &syn::DeriveInput, f: F) -> quote::Tokens
where
F: Fn(&synstructure::BindingInfo) -> quote::Tokens,
{
let by_value = synstructure::BindStyle::Move.into();
synstructure::each_variant(&input, &by_value, |fields, variant| {
let name = if let syn::Body::Enum(_) = input.body {
format!("{}::{}", input.ident, variant.ident).into()
} else {
variant.ident.clone()
};
let (animated_value, computed_fields) = synstructure::match_pattern(&name, &variant.data, &by_value);
let fields_pairs = fields.iter().zip(computed_fields.iter());
let mut computations = quote!();
computations.append_all(fields_pairs.map(|(field, computed_field)| {
let expr = f(field);
quote!(let #computed_field = #expr;)
}));
Some(quote!(
#computations
#animated_value
))
})
}
/// `#ty: ::values::animated::ToAnimatedValue<AnimatedValue = #animated_value,>`
fn where_predicate(ty: syn::Ty, animated_value: Option<syn::Ty>) -> syn::WherePredicate {
syn::WherePredicate::BoundPredicate(syn::WhereBoundPredicate {
bound_lifetimes: vec![],
bounded_ty: ty,
bounds: vec![syn::TyParamBound::Trait(
syn::PolyTraitRef {
bound_lifetimes: vec![],
trait_ref: trait_ref(animated_value),
},
syn::TraitBoundModifier::None
)],
})
}
/// `::values::animated::ToAnimatedValue<AnimatedValue = #animated_value,>`
fn trait_ref(animated_value: Option<syn::Ty>) -> syn::Path {
syn::Path {
global: true,
segments: vec![
"values".into(),
"animated".into(),
syn::PathSegment {
ident: "ToAnimatedValue".into(),
parameters: syn::PathParameters::AngleBracketed(
syn::AngleBracketedParameterData {
bindings: trait_bindings(animated_value),
.. Default::default()
}
),
}
],
}
}
/// `AnimatedValue = #animated_value,`
fn trait_bindings(animated_value: Option<syn::Ty>) -> Vec<syn::TypeBinding> {
animated_value.into_iter().map(|ty| {
syn::TypeBinding {
ident: "AnimatedValue".into(),
ty: ty,
}
}).collect()
}

View file

@ -2,78 +2,31 @@
* 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;
use syn;
use synstructure;
use synstructure::BindStyle;
pub fn derive(input: syn::DeriveInput) -> quote::Tokens {
let name = &input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let mut where_clause = where_clause.clone();
for param in &input.generics.ty_params {
where_clause.predicates.push(
where_predicate(syn::Ty::Path(None, param.ident.clone().into())),
);
}
let (impl_generics, ty_generics, where_clause) = cg::trait_parts(
&input,
&["values", "animated", "ToAnimatedZero"],
);
let to_body = match_body(&input);
let to_body = cg::fmap_match(&input, BindStyle::Ref, |field| {
quote! { ::values::animated::ToAnimatedZero::to_animated_zero(#field)? }
});
quote! {
impl #impl_generics ::values::animated::ToAnimatedZero for #name #ty_generics #where_clause {
#[allow(unused_variables)]
#[inline]
fn to_animated_zero(&self) -> Result<Self, ()> {
match *self {
Ok(match *self {
#to_body
}
})
}
}
}
}
fn match_body(input: &syn::DeriveInput) -> quote::Tokens {
synstructure::each_variant(&input, &synstructure::BindStyle::Ref.into(), |fields, variant| {
let name = if let syn::Body::Enum(_) = input.body {
format!("{}::{}", input.ident, variant.ident).into()
} else {
variant.ident.clone()
};
let (zero, computed_fields) = synstructure::match_pattern(
&name,
&variant.data,
&synstructure::BindStyle::Move.into(),
);
let fields_pairs = fields.iter().zip(computed_fields.iter());
let mut computations = quote!();
computations.append_all(fields_pairs.map(|(field, computed_field)| {
quote! {
let #computed_field = ::values::animated::ToAnimatedZero::to_animated_zero(#field)?;
}
}));
Some(quote!(
#computations
Ok(#zero)
))
})
}
fn where_predicate(ty: syn::Ty) -> syn::WherePredicate {
syn::WherePredicate::BoundPredicate(syn::WhereBoundPredicate {
bound_lifetimes: vec![],
bounded_ty: ty,
bounds: vec![syn::TyParamBound::Trait(
syn::PolyTraitRef {
bound_lifetimes: vec![],
trait_ref: syn::Path {
global: true,
segments: vec![
"values".into(),
"animated".into(),
"ToAnimatedZero".into(),
],
},
},
syn::TraitBoundModifier::None,
)],
})
}

View file

@ -2,49 +2,24 @@
* 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;
use syn;
use synstructure;
use synstructure::BindStyle;
pub fn derive(input: syn::DeriveInput) -> quote::Tokens {
let name = &input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let mut where_clause = where_clause.clone();
for param in &input.generics.ty_params {
where_clause.predicates.push(where_predicate(syn::Ty::Path(None, param.ident.clone().into()), None));
}
let (impl_generics, ty_generics, where_clause, computed_value_type) =
cg::fmap_trait_parts(
&input,
&["values", "computed", "ToComputedValue"],
"ComputedValue",
);
let computed_value_type = syn::Path::from(syn::PathSegment {
ident: name.clone(),
parameters: syn::PathParameters::AngleBracketed(syn::AngleBracketedParameterData {
lifetimes: input.generics.lifetimes.iter().map(|l| {
l.lifetime.clone()
}).collect(),
types: input.generics.ty_params.iter().map(|ty| {
syn::Ty::Path(
Some(syn::QSelf {
ty: Box::new(syn::Ty::Path(None, ty.ident.clone().into())),
position: 3,
}),
syn::Path {
global: true,
segments: vec![
"values".into(),
"computed".into(),
"ToComputedValue".into(),
"ComputedValue".into(),
],
},
)
}).collect(),
.. Default::default()
}),
});
let to_body = match_body(&input, |field| {
let to_body = cg::fmap_match(&input, BindStyle::Ref, |field| {
quote!(::values::computed::ToComputedValue::to_computed_value(#field, context))
});
let from_body = match_body(&input, |field| {
let from_body = cg::fmap_match(&input, BindStyle::Ref, |field| {
quote!(::values::computed::ToComputedValue::from_computed_value(#field))
});
@ -69,74 +44,3 @@ pub fn derive(input: syn::DeriveInput) -> quote::Tokens {
}
}
}
fn match_body<F>(input: &syn::DeriveInput, f: F) -> quote::Tokens
where F: Fn(&synstructure::BindingInfo) -> quote::Tokens,
{
let by_ref = synstructure::BindStyle::Ref.into();
let by_value = synstructure::BindStyle::Move.into();
synstructure::each_variant(&input, &by_ref, |fields, variant| {
let name = if let syn::Body::Enum(_) = input.body {
format!("{}::{}", input.ident, variant.ident).into()
} else {
variant.ident.clone()
};
let (computed_value, computed_fields) = synstructure::match_pattern(&name, &variant.data, &by_value);
let fields_pairs = fields.iter().zip(computed_fields.iter());
let mut computations = quote!();
computations.append_all(fields_pairs.map(|(field, computed_field)| {
let expr = f(field);
quote!(let #computed_field = #expr;)
}));
Some(quote!(
#computations
#computed_value
))
})
}
/// `#ty: ::values::computed::ToComputedValue<ComputedValue = #computed_value,>`
fn where_predicate(ty: syn::Ty, computed_value: Option<syn::Ty>) -> syn::WherePredicate {
syn::WherePredicate::BoundPredicate(syn::WhereBoundPredicate {
bound_lifetimes: vec![],
bounded_ty: ty,
bounds: vec![syn::TyParamBound::Trait(
syn::PolyTraitRef {
bound_lifetimes: vec![],
trait_ref: trait_ref(computed_value),
},
syn::TraitBoundModifier::None
)],
})
}
/// `::values::computed::ToComputedValue<ComputedValue = #computed_value,>`
fn trait_ref(computed_value: Option<syn::Ty>) -> syn::Path {
syn::Path {
global: true,
segments: vec![
"values".into(),
"computed".into(),
syn::PathSegment {
ident: "ToComputedValue".into(),
parameters: syn::PathParameters::AngleBracketed(
syn::AngleBracketedParameterData {
bindings: trait_bindings(computed_value),
.. Default::default()
}
),
}
],
}
}
/// `ComputedValue = #computed_value,`
fn trait_bindings(computed_value: Option<syn::Ty>) -> Vec<syn::TypeBinding> {
computed_value.into_iter().map(|ty| {
syn::TypeBinding {
ident: "ComputedValue".into(),
ty: ty,
}
}).collect()
}

View file

@ -2,17 +2,16 @@
* 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;
use syn;
use synstructure;
pub fn derive(input: syn::DeriveInput) -> quote::Tokens {
let name = &input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let mut where_clause = where_clause.clone();
for param in &input.generics.ty_params {
where_clause.predicates.push(where_predicate(syn::Ty::Path(None, param.ident.clone().into())))
}
let trait_path = &["style_traits", "ToCss"];
let (impl_generics, ty_generics, mut where_clause) =
cg::trait_parts(&input, trait_path);
let style = synstructure::BindStyle::Ref.into();
let match_body = synstructure::each_variant(&input, &style, |bindings, variant| {
@ -61,8 +60,10 @@ pub fn derive(input: syn::DeriveInput) -> quote::Tokens {
let mut expr = if !bindings.is_empty() {
let mut expr = quote! {};
for binding in bindings {
if has_free_params(&binding.field.ty, &input.generics.ty_params) {
where_clause.predicates.push(where_predicate(binding.field.ty.clone()));
if cg::is_parameterized(&binding.field.ty, &input.generics.ty_params) {
where_clause.predicates.push(
cg::where_predicate(binding.field.ty.clone(), trait_path),
);
}
expr = quote! {
#expr
@ -106,49 +107,6 @@ pub fn derive(input: syn::DeriveInput) -> quote::Tokens {
}
}
/// Returns whether `ty` is parameterized by any parameter from `params`.
fn has_free_params(ty: &syn::Ty, params: &[syn::TyParam]) -> bool {
use syn::visit::Visitor;
struct HasFreeParams<'a> {
params: &'a [syn::TyParam],
has_free: bool,
}
impl<'a> Visitor for HasFreeParams<'a> {
fn visit_path(&mut self, path: &syn::Path) {
if !path.global && path.segments.len() == 1 {
if self.params.iter().any(|param| param.ident == path.segments[0].ident) {
self.has_free = true;
}
}
syn::visit::walk_path(self, path);
}
}
let mut visitor = HasFreeParams { params: params, has_free: false };
visitor.visit_ty(ty);
visitor.has_free
}
/// `#ty: ::style_traits::ToCss`
fn where_predicate(ty: syn::Ty) -> syn::WherePredicate {
syn::WherePredicate::BoundPredicate(syn::WhereBoundPredicate {
bound_lifetimes: vec![],
bounded_ty: ty,
bounds: vec![syn::TyParamBound::Trait(
syn::PolyTraitRef {
bound_lifetimes: vec![],
trait_ref: syn::Path {
global: true,
segments: vec!["style_traits".into(), "ToCss".into()],
},
},
syn::TraitBoundModifier::None
)],
})
}
/// Transforms "FooBar" to "foo-bar".
///
/// If the first Camel segment is "Moz" or "Webkit", the result string