1extern crate proc_macro;
2use proc_macro::TokenStream;
3use quote::{format_ident, quote};
4use syn::{
5 parse::Parse,
6 parse_macro_input, parse_str,
7 punctuated::Punctuated,
8 token::{Comma, Default},
9 Expr, FnArg, Ident, Signature, Token, Type, TypeParen,
10};
11
12fn get_names(in_sig: &Punctuated<FnArg, Comma>) -> Punctuated<Ident, Comma> {
13 let mut res = Punctuated::new();
14
15 in_sig
16 .iter()
17 .map(|f| match f {
18 FnArg::Typed(t) => match *t.pat.clone() {
19 syn::Pat::Ident(id) => id.ident,
20 _ => unimplemented!(),
21 },
22 FnArg::Receiver(_) => unimplemented!(),
23 })
24 .for_each(|f| res.push(f));
25
26 res
27}
28
29fn get_types(in_sig: &Punctuated<FnArg, Comma>) -> Punctuated<Type, Comma> {
30 let mut res = Punctuated::new();
31
32 in_sig
33 .iter()
34 .map(|f| match f {
35 FnArg::Typed(t) => *t.ty.clone(),
36 FnArg::Receiver(_) => unimplemented!(),
37 })
38 .for_each(|f| res.push(f));
39
40 res
41}
42
43#[proc_macro]
44pub fn mock(input: TokenStream) -> TokenStream {
45 let func = parse_macro_input!(input as Signature);
46 let static_mock_name = format_ident!(
47 "STATIC_MOCK_{}",
48 func.ident.to_string().to_ascii_uppercase()
49 );
50 let extern_name = func.ident;
51 let in_types = get_types(&func.inputs);
52 let in_names = get_names(&func.inputs);
53 let in_sig = func.inputs;
54 let out_sig: Type = match func.output {
55 syn::ReturnType::Default => parse_str("()").unwrap(),
56 syn::ReturnType::Type(_, t) => *t,
57 };
58
59 quote!(
60 {
61
62 ffi_mock::lazy_static! {
63 static ref #static_mock_name: std::sync::Mutex<ffi_mock::FunctionMockInner<(#in_types), #out_sig>> =
64 std::sync::Mutex::new(ffi_mock::FunctionMockInner::new());
65 }
66
67 #[no_mangle]
68 extern "C" fn #extern_name(#in_sig) -> #out_sig {
69 let mut ffi_mock_mutex = #static_mock_name.lock().unwrap();
70 ffi_mock_mutex.call_history.push( (#in_names) );
71 ffi_mock_mutex.get_next_return()
72 }
73 ffi_mock::FunctionMock::new(&#static_mock_name)
74 }
75 )
76 .into()
77}