pub mod binding_implementer;
mod type_param_mapper;
mod mock_struct_implementer;
mod trait_implementer;
mod behaviour;
use syn;
use quote;
use ::generate::binding_implementer::*;
use ::generate::behaviour::*;
use ::generate::type_param_mapper::*;
use ::generate::mock_struct_implementer::*;
use ::generate::trait_implementer::*;
use data::*;
pub fn handle_generate_mocks() -> Vec<quote::Tokens> {
let mockable_traits = acquire!(MOCKABLE_TRAITS);
let mut requested_mocks = acquire!(REQUESTED_MOCKS);
let mut given_statements = acquire!(GIVEN_STATEMENTS);
let mut expect_statements = acquire!(EXPECT_STATEMENTS);
let mut bindings = acquire!(BINDINGS);
let mut tokens = implement_bindings(&bindings);
tokens.extend(implement_argmatcher());
tokens.extend(implement_given_behaviour());
tokens.extend(implement_expect_behaviour());
for requested_mock in requested_mocks.iter() {
let inst_traits = requested_mock.traits.iter().map(|trait_ty| create_instantiated_traits(trait_ty, &mockable_traits)).collect::<Vec<_>>();
tokens.extend(handle_generate_mock(requested_mock.maybe_type_name.as_ref().expect("Internal error: requested mock has no type name"),
&requested_mock.attributes,
&inst_traits,
&given_statements,
&expect_statements));
}
bindings.clear();
requested_mocks.clear();
given_statements.clear();
expect_statements.clear();
tokens
}
fn create_instantiated_traits(trait_path: &syn::Path, mockable_traits: &MockableTraits)
-> InstantiatedTrait {
let trait_info = mockable_traits
.get(&strip_generics(trait_path.clone()))
.expect(&format!("All mocked traits must be defined using 'mockable!': `{}` not found in {}",
quote!(#trait_path).to_string(),
mockable_traits.keys().map(|k| quote!(#k).to_string()).collect::<Vec<_>>().join(", ")));
let mut mapper = TypeParamMapper::new();
{
let generics: &syn::Generics = &trait_info.generics;
let instantiated_params = extract_parameterized_types_from_trait_use(trait_path);
for (param, instantiated) in generics.ty_params.iter().zip(instantiated_params) {
mapper.add_mapping(param.ident.clone(), instantiated);
}
}
InstantiatedTrait {
trait_ty: trait_path.clone(),
info: trait_info.clone(),
mapper: mapper
}
}
fn strip_generics(mut path_with_generics: syn::Path) -> syn::Path {
let mut segment = path_with_generics.segments.pop().unwrap();
segment.parameters = syn::PathParameters::none();
path_with_generics.segments.push(segment);
path_with_generics
}
fn handle_generate_mock(mock_type_name: &syn::Ident,
attributes: &[syn::Attribute],
requested_traits: &[InstantiatedTrait],
given_statements: &GivenStatements,
expect_statements: &ExpectStatements
) -> Vec<quote::Tokens> {
let mock_implementer = MockStructImplementer::for_(mock_type_name, attributes);
let mut mock = mock_implementer.implement();
let empty_given = Vec::new();
let empty_expect = Vec::new();
for inst_trait in requested_traits {
let given_statements_for_trait = given_statements.get(&inst_trait.trait_ty)
.unwrap_or(&empty_given);
let expect_statements_for_trait = expect_statements.get(&inst_trait.trait_ty)
.unwrap_or(&empty_expect);
mock.push(TraitImplementer::for_(mock_type_name,
inst_trait,
given_statements_for_trait,
expect_statements_for_trait
).implement());
}
mock
}
fn extract_parameterized_types_from_trait_use(trait_ty: &syn::Path) -> Vec<syn::Ty> {
match trait_ty.segments[0].parameters {
syn::PathParameters::AngleBracketed(ref data) => data.types.clone(),
_ => panic!("Type parameter extraction only works for angle-bracketed types.")
}
}
fn implement_argmatcher() -> Vec<quote::Tokens> {
let argmatcher_trait = quote! {
pub trait ArgMatcher<'a, T:'a> {
fn match_args(&self, actual: &'a T) -> bool;
}
};
let argmatcher_impl = quote! {
impl<'a, T:'a, F> ArgMatcher<'a,T> for F
where F: Fn(&'a T) -> bool {
fn match_args(&self, actual: &'a T) -> bool {
self(actual)
}
}
};
if cfg!(feature = "galvanic_assert_integration") {
let matcher_argmatcher_impl = quote! {
impl<'a, T:'a> ArgMatcher<'a,T> for Box<::galvanic_assert::Matcher<'a, T> + 'a> {
fn match_args(&self, actual: &'a T) -> bool {
self.check(actual).into()
}
}
};
vec![argmatcher_trait, argmatcher_impl, matcher_argmatcher_impl]
} else { vec![argmatcher_trait, argmatcher_impl] }
}
pub fn typed_arguments_for_method_sig(signature: &syn::MethodSig, mapper: &TypeParamMapper) -> Vec<quote::Tokens> {
let mut arg_idx = 1;
signature.decl.inputs.iter().map(|arg| {
let arg_name = syn::Ident::from(format!("arg{}", arg_idx));
match arg {
&syn::FnArg::Captured(_, ref ty) => {
let inst_ty = mapper.instantiate_from_ty(ty);
arg_idx += 1;
quote!(#arg_name: #inst_ty)
},
&syn::FnArg::Ignored(ref ty) => {
let inst_ty = mapper.instantiate_from_ty(ty);
arg_idx += 1;
quote!(#arg_name: #inst_ty)
}
_ => quote!(#arg)
}}).collect::<Vec<_>>()
}
#[derive(Clone,Debug)]
pub struct InstantiatedTrait {
trait_ty: syn::Path,
info: TraitInfo,
mapper: TypeParamMapper,
}