1use quote::quote;
2use syn::{Error, Ident, ItemTrait, parse_macro_input};
3
4mod double_trait;
5mod trait_impl;
6
7use self::{double_trait::double_trait, trait_impl::trait_impl};
8
9#[proc_macro_attribute]
27pub fn double(
28 attr: proc_macro::TokenStream,
29 item: proc_macro::TokenStream,
30) -> proc_macro::TokenStream {
31 let double_name = parse_macro_input!(attr as Ident);
32 let item = parse_macro_input!(item as ItemTrait);
33
34 let output = expand(double_name, item).unwrap_or_else(Error::into_compile_error);
35
36 proc_macro::TokenStream::from(output)
37}
38
39fn expand(double_trait_name: Ident, org_trait: ItemTrait) -> syn::Result<proc_macro2::TokenStream> {
43 let double_trait = double_trait(double_trait_name.clone(), org_trait.clone())?;
44 let trait_impl = trait_impl(double_trait_name.clone(), org_trait.clone());
45
46 let token_stream = quote! {
53 #org_trait
54
55 #double_trait
56
57 #trait_impl
58
59 impl #double_trait_name for double_trait::Dummy{}
60 };
61 Ok(token_stream)
62}
63
64#[cfg(test)]
65mod tests {
66
67 use super::{Ident, expand};
68 use quote::quote;
69 use syn::{ItemTrait, parse2};
70
71 #[test]
72 fn generate_double_trait() {
73 let (attr, item) = given(quote! { MyTraitDummy }, quote! { trait MyTrait {} });
74
75 let output = expand(attr, item).unwrap();
76
77 let expected = quote! {
78 trait MyTrait {}
79
80 trait MyTraitDummy {}
81
82 impl<T> MyTrait for T where T: MyTraitDummy {}
83
84 impl MyTraitDummy for double_trait::Dummy {}
85 };
86 assert_eq!(expected.to_string(), output.to_string());
87 }
88
89 #[test]
90 fn forward_visibility() {
91 let (attr, item) = given(quote! { MyTraitDummy }, quote! { pub trait MyTrait {} });
93
94 let output = expand(attr, item).unwrap();
96
97 let expected = quote! {
99 pub trait MyTrait {}
100
101 pub trait MyTraitDummy {}
102
103 impl<T> MyTrait for T where T: MyTraitDummy {}
104
105 impl MyTraitDummy for double_trait::Dummy {}
106 };
107 assert_eq!(expected.to_string(), output.to_string());
108 }
109
110 #[test]
111 fn forward_method() {
112 let (attr, item) = given(
114 quote! { MyTraitDummy },
115 quote! {
116 trait MyTrait {
117 fn foobar(&self);
118 }
119 },
120 );
121
122 let output = expand(attr, item).unwrap();
124
125 let expected = quote! {
127 trait MyTrait {
128 fn foobar(&self);
129 }
130
131 trait MyTraitDummy {
132 fn foobar (&self) { unimplemented!() }
133 }
134
135 impl<T> MyTrait for T where T: MyTraitDummy {
136 fn foobar(&self) { <Self as MyTraitDummy>::foobar(self,) }
137 }
138
139 impl MyTraitDummy for double_trait::Dummy {}
140 };
141 assert_eq!(expected.to_string(), output.to_string());
142 }
143
144 #[test]
145 fn respect_existing_default_impl() {
146 let (attr, item) = given(
148 quote! { MyTraitDummy },
149 quote! {
150 pub trait MyTrait {
151 fn foobar() { println!("Hello Default!") }
152 }
153 },
154 );
155
156 let output = expand(attr, item).unwrap();
158
159 let expected = quote! {
161 pub trait MyTrait {
162 fn foobar() { println!("Hello Default!") }
163 }
164
165 pub trait MyTraitDummy {}
166
167 impl<T> MyTrait for T where T: MyTraitDummy {
168 fn foobar() { <Self as MyTraitDummy>::foobar() }
169 }
170
171 impl MyTraitDummy for double_trait::Dummy {}
172 };
173 assert_eq!(expected.to_string(), output.to_string());
174 }
175
176 #[test]
177 fn forward_async_method() {
178 let (attr, item) = given(
180 quote! { MyTraitDummy },
181 quote! {
182 trait MyTrait {
183 async fn foobar(&self);
184 }
185 },
186 );
187
188 let output = expand(attr, item).unwrap();
190
191 let expected = quote! {
193 trait MyTrait {
194 async fn foobar(&self);
195 }
196
197 trait MyTraitDummy {
198 async fn foobar (&self) { unimplemented!() }
199 }
200
201 impl<T> MyTrait for T where T: MyTraitDummy {
202 async fn foobar(&self) { <Self as MyTraitDummy>::foobar(self,).await }
203 }
204
205 impl MyTraitDummy for double_trait::Dummy {}
206 };
207 assert_eq!(expected.to_string(), output.to_string());
208 }
209
210 fn given(attr: proc_macro2::TokenStream, item: proc_macro2::TokenStream) -> (Ident, ItemTrait) {
211 let attr: Ident = parse2(attr).unwrap();
212 let item: ItemTrait = parse2(item).unwrap();
213 (attr, item)
214 }
215}