1use quote::quote;
2use syn::{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]
21pub fn double(
22 attr: proc_macro::TokenStream,
23 item: proc_macro::TokenStream,
24) -> proc_macro::TokenStream {
25 let double_name = parse_macro_input!(attr as Ident);
26 let item = parse_macro_input!(item as ItemTrait);
27
28 let output = double_impl(double_name, item);
29
30 proc_macro::TokenStream::from(output)
31}
32
33fn double_impl(double_trait_name: Ident, org_trait: ItemTrait) -> proc_macro2::TokenStream {
37 let double_trait = double_trait(double_trait_name.clone(), org_trait.clone());
38 let trait_impl = trait_impl(double_trait_name, org_trait.clone());
39
40 quote! {
47 #org_trait
48
49 #double_trait
50
51 #trait_impl
52 }
53}
54
55#[cfg(test)]
56mod tests {
57
58 use super::{Ident, double_impl};
59 use quote::quote;
60 use syn::{ItemTrait, parse2};
61
62 #[test]
63 fn generate_double_trait() {
64 let (attr, item) = given(quote! { MyTraitDummy }, quote! { trait MyTrait {} });
65
66 let output = double_impl(attr, item);
67
68 let expected = quote! {
69 trait MyTrait {}
70
71 trait MyTraitDummy {}
72
73 impl<T> MyTrait for T where T: MyTraitDummy {}
74 };
75 assert_eq!(expected.to_string(), output.to_string());
76 }
77
78 #[test]
79 fn forward_visibility() {
80 let (attr, item) = given(quote! { MyTraitDummy }, quote! { pub trait MyTrait {} });
82
83 let output = double_impl(attr, item);
85
86 let expected = quote! {
88 pub trait MyTrait {}
89
90 pub trait MyTraitDummy {}
91
92 impl<T> MyTrait for T where T: MyTraitDummy {}
93 };
94 assert_eq!(expected.to_string(), output.to_string());
95 }
96
97 #[test]
98 fn forward_method() {
99 let (attr, item) = given(
101 quote! { MyTraitDummy },
102 quote! {
103 trait MyTrait {
104 fn foobar(&self);
105 }
106 },
107 );
108
109 let output = double_impl(attr, item);
111
112 let expected = quote! {
114 trait MyTrait {
115 fn foobar(&self);
116 }
117
118 trait MyTraitDummy {
119 fn foobar (&self) { unimplemented!() }
120 }
121
122 impl<T> MyTrait for T where T: MyTraitDummy {
123 fn foobar(&self) { MyTraitDummy::foobar(self,) }
124 }
125 };
126 assert_eq!(expected.to_string(), output.to_string());
127 }
128
129 #[test]
130 fn respect_existing_default_impl() {
131 let (attr, item) = given(
133 quote! { MyTraitDummy },
134 quote! {
135 pub trait MyTrait {
136 fn foobar() { println!("Hello Default!") }
137 }
138 },
139 );
140
141 let output = double_impl(attr, item);
143
144 let expected = quote! {
146 pub trait MyTrait {
147 fn foobar() { println!("Hello Default!") }
148 }
149
150 pub trait MyTraitDummy {}
151
152 impl<T> MyTrait for T where T: MyTraitDummy {
153 fn foobar() { MyTraitDummy::foobar() }
154 }
155 };
156 assert_eq!(expected.to_string(), output.to_string());
157 }
158
159 fn given(attr: proc_macro2::TokenStream, item: proc_macro2::TokenStream) -> (Ident, ItemTrait) {
160 let attr: Ident = parse2(attr).unwrap();
161 let item: ItemTrait = parse2(item).unwrap();
162 (attr, item)
163 }
164}