double_derive/lib.rs
1mod double;
2mod double_trait;
3mod dummies;
4mod dummy_impl;
5
6use syn::{Error, Ident, ItemTrait, parse_macro_input};
7
8/// Generates a trait which replicates the original trait method for method. It does implement the
9/// original trait for each of its implementations, by means of forwarding the method calls. The
10/// utility comes from the fact that the generated trait has default implementations for each method
11/// using `unimplemented!()`, which makes it useful for testing purposes.
12///
13/// If a test requires an implementation of an original trait `Org` yet would only invoke one of its
14/// methods, implementing the mirrored method on an implementation of the generated trait `OrgDummy`
15/// is sufficient. The other methods would not be inovked in the test, so their default
16/// implementation using `unimplemented!()` would not be reached.
17///
18/// The argument passed to the attribute is used as the name of the generated trait.
19///
20/// * Existing default implementations are respected and not overridden.
21/// * Visibility of the generated trait is the same as the original trait.
22/// * `async` methods are supported
23/// * Methods returning `impl` Traits are not supported, with the exception of `impl Future`.
24/// * Generated double trait is implemented for `Dummy`.
25///
26/// # Example
27///
28/// Basic usage allows creating test stubs for traits, without worrying about implementing methods
29/// not called in test code
30///
31/// ```no_run
32/// use double_trait::double;
33///
34/// #[double(MyTraitDouble)]
35/// trait MyTrait {
36/// fn answer(&self) -> i32;
37///
38/// fn some_other_method(&self);
39/// }
40///
41/// struct MyStub;
42///
43/// impl MyTraitDouble for MyStub {
44/// fn answer(&self) -> i32 {
45/// 42
46/// }
47/// }
48///
49/// assert_eq!(42, MyTrait::answer(&MyStub));
50/// ```
51///
52/// Then interacting with the `async_trait` crate, make sure to put the `#[async_trait]` attribute
53/// on top.
54///
55/// ```no_run
56/// use double_trait::double;
57/// use async_trait::async_trait;
58///
59/// #[async_trait]
60/// #[double(MyTraitDouble)]
61/// trait MyTrait {
62/// async fn answer(&self) -> i32;
63/// }
64/// ```
65#[proc_macro_attribute]
66pub fn double(
67 attr: proc_macro::TokenStream,
68 item: proc_macro::TokenStream,
69) -> proc_macro::TokenStream {
70 let double_name = parse_macro_input!(attr as Ident);
71 let item = parse_macro_input!(item as ItemTrait);
72
73 let output = double::expand(double_name, item).unwrap_or_else(Error::into_compile_error);
74
75 proc_macro::TokenStream::from(output)
76}
77
78/// Generates a "dummy" implementation for each method in a trait using `unimplemented!()`. The main
79/// use case is to greate specialized test doubles for implementing the trait without worrying the
80/// need to explicitly implement methods, which are not invoked by the test.
81///
82/// * Existing default implementations are respected and not overridden.
83/// * `async` methods are supported
84/// * Methods returning `impl` Traits are not supported, with the exception of `impl Future` and
85/// `impl Iterator`.
86/// * Dummy implements the trait
87#[proc_macro_attribute]
88pub fn dummies(
89 _attr: proc_macro::TokenStream,
90 item: proc_macro::TokenStream,
91) -> proc_macro::TokenStream {
92 // let double_name = parse_macro_input!(attr as Ident);
93 let item = parse_macro_input!(item as ItemTrait);
94
95 let output = dummies::expand(item).unwrap_or_else(Error::into_compile_error);
96
97 proc_macro::TokenStream::from(output)
98}