Skip to main content

double_derive/
lib.rs

1mod double_trait;
2mod dummies;
3mod dummy_impl;
4
5use syn::{Error, ItemTrait, parse_macro_input};
6
7/// Generates a "dummy" implementation for each method in a trait using `unimplemented!()`. The main
8/// use case is to greate specialized test doubles for implementing the trait without worrying the
9/// need to explicitly implement methods, which are not invoked by the test.
10///
11/// * Existing default implementations are respected and not overridden.
12/// * `async` methods are supported
13/// * Methods returning `impl` Traits are not supported, with the exception of `impl Future` and
14///   `impl Iterator`. One way to deal with this, is to give them an explicit default implementation
15///   in the test case. E.g.,
16///
17///   ```
18///   # trait Answer {}
19///   # struct DummyAnswer;
20///   # impl Answer for DummyAnswer {}
21///
22///   #[cfg_attr(test, double_trait::dummies)]
23///   trait MyTrait {
24///     #[cfg(not(test))]
25///     fn answer(&self) -> impl Answer;
26///
27///     // `dummies` can not interfere a type for `impl Answer`, so we provide a default impl here.
28///     #[cfg(test)]
29///     fn answer(&self) -> impl Answer {
30///         DummyAnswer
31///     }
32///
33///     // ... other methods ...
34///   }
35///   ```
36/// * Dummy implements the trait
37#[proc_macro_attribute]
38pub fn dummies(
39    _attr: proc_macro::TokenStream,
40    item: proc_macro::TokenStream,
41) -> proc_macro::TokenStream {
42    let item = parse_macro_input!(item as ItemTrait);
43
44    let output = dummies::expand(item).unwrap_or_else(Error::into_compile_error);
45
46    proc_macro::TokenStream::from(output)
47}