1extern crate proc_macro;
2
3mod create;
4mod methods;
5mod self_type;
6
7use darling::{export::NestedMeta, FromMeta};
8use proc_macro::TokenStream;
9use quote::quote;
10
11#[proc_macro_attribute]
12pub fn create(args: TokenStream, original: TokenStream) -> TokenStream {
13 let original = syn::parse_macro_input!(original as syn::ItemStruct);
14
15 let args = match NestedMeta::parse_meta_list(args.into())
16 .map_err(darling::Error::from)
17 .and_then(|v| create::Args::from_list(&v))
18 {
19 Ok(v) => v,
20 Err(e) => return e.write_errors().into(),
21 };
22
23 let mockable = create::Mockable::new(original, args);
24
25 TokenStream::from(mockable)
26}
27
28#[proc_macro_attribute]
29pub fn methods(args: TokenStream, original: TokenStream) -> TokenStream {
30 let original = syn::parse_macro_input!(original as syn::ItemImpl);
31
32 let args = match NestedMeta::parse_meta_list(args.into())
33 .map_err(darling::Error::from)
34 .and_then(|v| methods::Args::from_list(&v))
35 {
36 Ok(v) => v,
37 Err(e) => return e.write_errors().into(),
38 };
39
40 match methods::Mockable::new(original, args) {
41 Ok(mockable) => TokenStream::from(mockable),
42 Err(e) => e.write_errors().into(),
43 }
44}
45
46#[proc_macro]
47pub fn when(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
48 match syn::parse_macro_input!(input as syn::Expr) {
49 syn::Expr::Field(syn::ExprField {
50 base,
51 member: syn::Member::Named(ident),
52 ..
53 }) => {
54 let when = quote::format_ident!("_when_{}", ident);
55 TokenStream::from(quote!( { #base.#when() }))
56 }
57 syn::Expr::MethodCall(syn::ExprMethodCall {
58 receiver,
59 method,
60 args,
61 turbofish,
62 ..
63 }) => {
64 let when = quote::format_ident!("_when_{}", method);
65
66 let args = args
67 .into_iter()
68 .map(expr_to_matcher)
69 .collect::<Result<Vec<_>, _>>();
70
71 match args {
72 Err(e) => e.write_errors().into(),
73 Ok(args) if args.is_empty() => { TokenStream::from(quote!({ #receiver.#when #turbofish() }))}
74 Ok(args) => { TokenStream::from(quote!({ #receiver.#when #turbofish().with_args((#(#args,)*)) }))}
75 }
76 }
77 expr => darling::Error::custom("faux::when! only accepts arguments in the format of: `when!(receiver.method)` or `receiver.method(args...)`")
78 .with_span(&expr)
79 .write_errors()
80 .into(),
81 }
82}
83
84use quote::ToTokens;
85
86fn ref_matcher_maybe(
87 expr: &syn::Expr,
88 left: &syn::Expr,
89 matcher: impl FnOnce() -> darling::Result<proc_macro2::TokenStream>,
90) -> darling::Result<proc_macro2::TokenStream> {
91 match left {
92 syn::Expr::Infer(_) => matcher(),
93 syn::Expr::Unary(syn::ExprUnary {
94 op: syn::UnOp::Deref(_),
95 expr,
96 ..
97 }) => {
98 let matcher = matcher()?;
99 Ok(quote! { faux::matcher::ArgMatcher::<#expr>::into_ref_matcher(#matcher) })
100 }
101 _ => Ok(quote! { faux::matcher::eq(#expr) }),
102 }
103}
104
105fn expr_to_matcher(expr: syn::Expr) -> darling::Result<proc_macro2::TokenStream> {
106 match &expr {
107 syn::Expr::Infer(_) => Ok(quote! { faux::matcher::any() }),
108 syn::Expr::Assign(syn::ExprAssign { left, right, .. }) => {
109 ref_matcher_maybe(&expr, left, || Ok(right.to_token_stream()))
110 }
111 syn::Expr::Binary(syn::ExprBinary {
112 left, op, right, ..
113 }) => ref_matcher_maybe(&expr, left, || match op {
114 syn::BinOp::Eq(_) => Ok(quote! { faux::matcher::eq_against(#right) }),
115 _ => Err(darling::Error::custom(format!(
116 "faux:when! does not handle argument matchers with syntax: '{}'",
117 expr.to_token_stream()
118 ))
119 .with_span(&expr)),
120 }),
121 arg => Ok(quote! { faux::matcher::eq(#arg) }),
122 }
123}