1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#![forbid(unsafe_code)]
use quote::quote;
use syn::spanned::Spanned;
extern crate proc_macro;
#[proc_macro_attribute]
pub fn unimock(
_attr: proc_macro::TokenStream,
input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let item_trait = syn::parse_macro_input!(input as syn::ItemTrait);
let trait_ident = &item_trait.ident;
let impl_attributes = item_trait
.attrs
.iter()
.filter_map(|attribute| match attribute.style {
syn::AttrStyle::Outer => {
if let Some(last_segment) = attribute.path.segments.last() {
if last_segment.ident == "async_trait" {
Some(quote! { #[async_trait::async_trait ]})
} else {
None
}
} else {
None
}
}
syn::AttrStyle::Inner(_) => None,
});
let mock_ident = quote::format_ident!("Mock{}", item_trait.ident);
let trait_name_literal = syn::LitStr::new(&format!("{trait_ident}"), trait_ident.span());
let method_impls = item_trait.items.iter().filter_map(|item| match item {
syn::TraitItem::Method(method) => {
Some(impl_method(method, &mock_ident, &trait_name_literal))
}
_ => None,
});
let output = quote! {
#[::mockall::automock]
#item_trait
#(#impl_attributes)*
impl #trait_ident for ::unimock::Unimock {
#(#method_impls)*
}
};
proc_macro::TokenStream::from(output)
}
fn impl_method(
method: &syn::TraitItemMethod,
mock_ident: &syn::Ident,
trait_name_literal: &syn::LitStr,
) -> proc_macro2::TokenStream {
let sig = &method.sig;
let method_ident = &sig.ident;
let parameters = sig.inputs.iter().filter_map(|fn_arg| match fn_arg {
syn::FnArg::Receiver(_) => None,
syn::FnArg::Typed(pat_type) => match pat_type.pat.as_ref() {
syn::Pat::Ident(ident) => Some(quote! { #ident }),
_ => {
Some(syn::Error::new(pat_type.span(), "Unprocessable argument").to_compile_error())
}
},
});
let dot_await = sig.asyncness.map(|_| quote! { .await });
quote! {
#sig {
self.get::<#mock_ident>(#trait_name_literal).#method_ident(#(#parameters),*) #dot_await
}
}
}