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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
extern crate proc_macro;
mod create;
mod methods;
mod self_type;
use darling::FromMeta;
use proc_macro::TokenStream;
use quote::quote;
#[proc_macro_attribute]
pub fn create(args: TokenStream, original: TokenStream) -> TokenStream {
let original = syn::parse_macro_input!(original as syn::ItemStruct);
let args = syn::parse_macro_input!(args as syn::AttributeArgs);
let args = match create::Args::from_list(&args) {
Ok(v) => v,
Err(e) => {
return e.write_errors().into();
}
};
let mockable = create::Mockable::new(original, args);
TokenStream::from(mockable)
}
#[proc_macro_attribute]
pub fn methods(args: TokenStream, original: TokenStream) -> TokenStream {
let original = syn::parse_macro_input!(original as syn::ItemImpl);
let args = syn::parse_macro_input!(args as syn::AttributeArgs);
let args = match methods::Args::from_list(&args) {
Ok(v) => v,
Err(e) => {
return e.write_errors().into();
}
};
match methods::Mockable::new(original, args) {
Ok(mockable) => TokenStream::from(mockable),
Err(e) => e.write_errors().into(),
}
}
#[proc_macro]
pub fn when(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
match syn::parse_macro_input!(input as syn::Expr) {
syn::Expr::Field(syn::ExprField {
base,
member: syn::Member::Named(ident),
..
}) => {
let when = quote::format_ident!("_when_{}", ident);
TokenStream::from(quote!( { #base.#when() }))
}
syn::Expr::MethodCall(syn::ExprMethodCall {
receiver,
method,
args,
..
}) => {
let when = quote::format_ident!("_when_{}", method);
let args = args
.into_iter()
.map(expr_to_matcher)
.collect::<Result<Vec<_>, _>>();
match args {
Err(e) => e.write_errors().into(),
Ok(args) => TokenStream::from(quote!({ #receiver.#when().with_args((#(#args,)*)) }))
}
}
expr => darling::Error::custom("faux::when! only accepts arguments in the format of: `when!(receiver.method)` or `receiver.method(args...)`")
.with_span(&expr)
.write_errors()
.into(),
}
}
use quote::ToTokens;
fn ref_matcher_maybe(
expr: &syn::Expr,
left: &syn::Expr,
matcher: impl FnOnce() -> darling::Result<proc_macro2::TokenStream>,
) -> darling::Result<proc_macro2::TokenStream> {
match left {
syn::Expr::Verbatim(t) if t.to_string() == "_" => matcher(),
syn::Expr::Unary(syn::ExprUnary {
op: syn::UnOp::Deref(_),
expr,
..
}) => {
let matcher = matcher()?;
Ok(quote! { faux::matcher::ArgMatcher::<#expr>::into_ref_matcher(#matcher) })
}
_ => Ok(quote! { faux::matcher::eq(#expr) }),
}
}
fn expr_to_matcher(expr: syn::Expr) -> darling::Result<proc_macro2::TokenStream> {
match &expr {
syn::Expr::Verbatim(t) if t.to_string() == "_" => Ok(quote! { faux::matcher::any() }),
syn::Expr::Assign(syn::ExprAssign { left, right, .. }) => {
ref_matcher_maybe(&expr, left, || Ok(right.to_token_stream()))
}
syn::Expr::Binary(syn::ExprBinary {
left, op, right, ..
}) => ref_matcher_maybe(&expr, left, || match op {
syn::BinOp::Eq(_) => Ok(quote! { faux::matcher::eq_against(#right) }),
_ => Err(darling::Error::custom(format!(
"faux:when! does not handle argument matchers with syntax: '{}'",
expr.to_token_stream()
))
.with_span(&expr)),
}),
arg => Ok(quote! { faux::matcher::eq(#arg) }),
}
}