// vim: tw=80 use proc_macro2::Span; use quote::{ToTokens, format_ident, quote}; use std::{
collections::hash_map::DefaultHasher,
hash::{Hash, Hasher}
}; use syn::{
*,
spanned::Spanned
};
pub(crate) struct MockTrait { pub attrs: Vec<Attribute>, pub consts: Vec<ImplItemConst>, pub generics: Generics, pub methods: Vec<MockFunction>, /// Internally-used name of the trait used. pub ss_name: Ident, /// Fully-qualified name of the trait pub trait_path: Path, /// Path on which the trait is implemented. Usually will be the same as /// structname, but might include concrete generic parameters.
self_path: PathSegment, pub types: Vec<ImplItemType>, pub unsafety: Option<Token![unsafe]>
}
impl MockTrait { fn ss_name_priv(trait_path: &Path) -> Ident { let path_args = &trait_path.segments.last().unwrap().arguments; if path_args.is_empty() { // Skip the hashing step for easie debugging of generated code
format_ident!("{}", trait_path.segments.last().unwrap().ident)
} else { // Hash the path args to permit mocking structs that implement // multiple traits distinguished only by their path args letmut hasher = DefaultHasher::new();
path_args.hash(&mut hasher);
format_ident!("{}_{}", trait_path.segments.last().unwrap().ident,
hasher.finish())
}
}
pubfn ss_name(&self) -> &Ident {
&self.ss_name
}
/// Create a new MockTrait /// /// # Arguments /// * `structname` - name of the struct that implements this trait /// * `struct_generics` - Generics of the parent structure /// * `impl_` - Mockable ItemImpl for a trait /// * `vis` - Visibility of the struct pubfn new(structname: &Ident,
struct_generics: &Generics,
impl_: ItemImpl,
vis: &Visibility) -> Self
{ letmut consts = Vec::new(); letmut methods = Vec::new(); letmut types = Vec::new(); let trait_path = iflet Some((_, path, _)) = impl_.trait_ {
path
} else {
compile_error(impl_.span(), "impl block must implement a trait");
Path::from(format_ident!("__mockall_invalid"))
}; let ss_name = MockTrait::ss_name_priv(&trait_path); let self_path = match *impl_.self_ty { Type::Path(mut type_path) =>
type_path.path.segments.pop().unwrap().into_value(),
x => {
compile_error(x.span(), "mockall_derive only supports mocking traits and structs");
PathSegment::from(Ident::new("", Span::call_site()))
}
};
for ii in impl_.items.into_iter() { match ii {
ImplItem::Const(iic) => {
consts.push(iic);
},
ImplItem::Fn(iif) => { let mf = mock_function::Builder::new(&iif.sig, vis)
.attrs(&iif.attrs)
.levels(2)
.call_levels(0)
.struct_(structname)
.struct_generics(struct_generics)
.trait_(&ss_name)
.build();
methods.push(mf);
},
ImplItem::Type(iit) => {
types.push(iit);
},
_ => {
compile_error(ii.span(), "This impl item is not yet supported by MockAll");
}
}
}
MockTrait {
attrs: impl_.attrs,
consts,
generics: impl_.generics,
methods,
ss_name,
trait_path,
self_path,
types,
unsafety: impl_.unsafety
}
}
/// Generate code for the trait implementation on the mock struct /// /// # Arguments /// /// * `modname`: Name of the parent struct's private module // Supplying modname is an unfortunately hack. Ideally MockTrait // wouldn't need to know that. pubfn trait_impl(&self, modname: &Ident) -> impl ToTokens { let trait_impl_attrs = &self.attrs; let impl_attrs = AttrFormatter::new(&self.attrs)
.async_trait(false)
.doc(false)
.format(); let (ig, _tg, wc) = self.generics.split_for_impl(); let consts = &self.consts; let path_args = &self.self_path.arguments; let calls = self.methods.iter()
.map(|meth| meth.call(Some(modname)))
.collect::<Vec<_>>(); let contexts = self.methods.iter()
.filter(|meth| meth.is_static())
.map(|meth| meth.context_fn(Some(modname)))
.collect::<Vec<_>>(); let expects = self.methods.iter()
.filter(|meth| !meth.is_static())
.map(|meth| { if meth.is_method_generic() { // Specific impls with generic methods are TODO.
meth.expect(modname, None)
} else {
meth.expect(modname, Some(path_args))
}
}).collect::<Vec<_>>(); let trait_path = &self.trait_path; let self_path = &self.self_path; let types = &self.types; let unsafety = &self.unsafety;
quote!( #(#trait_impl_attrs)* #unsafetyimpl#ig#trait_pathfor#self_path#wc { #(#consts)* #(#types)* #(#calls)*
} #(#impl_attrs)* impl#ig#self_path#wc { #(#expects)* #(#contexts)*
}
)
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.