blob: a2fa9893cd92795169cfd1962aa5283f551f6a35 [file] [log] [blame]
use proc_macro2::TokenStream;
use syn::{Generics, Ident, Path, WherePredicate};
use ast::{Data, Fields};
use codegen::error::{ErrorCheck, ErrorDeclaration};
use codegen::field;
use codegen::{DefaultExpression, Field, FieldsGen, Variant};
use usage::{CollectTypeParams, IdentSet, Purpose};
#[derive(Debug)]
pub struct TraitImpl<'a> {
pub ident: &'a Ident,
pub generics: &'a Generics,
pub data: Data<Variant<'a>, Field<'a>>,
pub default: Option<DefaultExpression<'a>>,
pub map: Option<&'a Path>,
pub bound: Option<&'a [WherePredicate]>,
pub allow_unknown_fields: bool,
}
impl<'a> TraitImpl<'a> {
/// Get all declared type parameters.
pub fn declared_type_params(&self) -> IdentSet {
self.generics
.type_params()
.map(|tp| tp.ident.clone())
.collect()
}
/// Get the type parameters which are used by non-skipped fields.
pub fn used_type_params(&self) -> IdentSet {
self.type_params_matching(|f| !f.skip, |v| !v.skip)
}
/// Get the type parameters which are used by skipped fields.
pub fn skipped_type_params(&self) -> IdentSet {
self.type_params_matching(|f| f.skip, |v| v.skip)
}
fn type_params_matching<'b, F, V>(&'b self, field_filter: F, variant_filter: V) -> IdentSet
where
F: Fn(&&Field) -> bool,
V: Fn(&&Variant) -> bool,
{
let declared = self.declared_type_params();
match self.data {
Data::Struct(ref v) => self.type_params_in_fields(v, &field_filter, &declared),
Data::Enum(ref v) => {
v.iter()
.filter(variant_filter)
.fold(Default::default(), |mut state, variant| {
state.extend(self.type_params_in_fields(
&variant.data,
&field_filter,
&declared,
));
state
})
}
}
}
/// Get the type parameters of all fields in a set matching some filter
fn type_params_in_fields<'b, F>(
&'b self,
fields: &'b Fields<Field<'a>>,
field_filter: F,
declared: &IdentSet,
) -> IdentSet
where
F: Fn(&&'b Field) -> bool,
{
fields
.iter()
.filter(field_filter)
.collect_type_params_cloned(&Purpose::BoundImpl.into(), declared)
}
}
impl<'a> TraitImpl<'a> {
/// Gets the `let` declaration for errors accumulated during parsing.
pub fn declare_errors(&self) -> ErrorDeclaration {
ErrorDeclaration::default()
}
/// Gets the check which performs an early return if errors occurred during parsing.
pub fn check_errors(&self) -> ErrorCheck {
ErrorCheck::default()
}
/// Generate local variable declarations for all fields.
pub(in codegen) fn local_declarations(&self) -> TokenStream {
if let Data::Struct(ref vd) = self.data {
let vdr = vd.as_ref().map(Field::as_declaration);
let decls = vdr.fields.as_slice();
quote!(#(#decls)*)
} else {
quote!()
}
}
/// Generate immutable variable declarations for all fields.
pub(in codegen) fn immutable_declarations(&self) -> TokenStream {
if let Data::Struct(ref vd) = self.data {
let vdr = vd.as_ref().map(|f| field::Declaration::new(f, false));
let decls = vdr.fields.as_slice();
quote!(#(#decls)*)
} else {
quote!()
}
}
pub(in codegen) fn map_fn(&self) -> Option<TokenStream> {
self.map.as_ref().map(|path| quote!(.map(#path)))
}
/// Generate local variable declaration and initialization for instance from which missing fields will be taken.
pub(in codegen) fn fallback_decl(&self) -> TokenStream {
let default = self.default.as_ref().map(DefaultExpression::as_declaration);
quote!(#default)
}
pub fn require_fields(&self) -> TokenStream {
if let Data::Struct(ref vd) = self.data {
let check_nones = vd.as_ref().map(Field::as_presence_check);
let checks = check_nones.fields.as_slice();
quote!(#(#checks)*)
} else {
quote!()
}
}
pub(in codegen) fn initializers(&self) -> TokenStream {
self.make_field_ctx().initializers()
}
/// Generate the loop which walks meta items looking for property matches.
pub(in codegen) fn core_loop(&self) -> TokenStream {
self.make_field_ctx().core_loop()
}
fn make_field_ctx(&'a self) -> FieldsGen<'a> {
match self.data {
Data::Enum(_) => panic!("Core loop on enums isn't supported"),
Data::Struct(ref data) => FieldsGen::new(data, self.allow_unknown_fields),
}
}
}