1use proc_macro::TokenStream;
2use quote::{quote, ToTokens};
3use syn::{
4 parse::{Parse, ParseStream}, parse_macro_input, parse_quote, punctuated::Punctuated, spanned::Spanned, token::Comma, Field, FnArg, GenericParam, Ident, ItemStruct, ItemTrait, Meta, Token, TraitItem, TraitItemFn, Type, TypeParamBound, WhereClause
5};
6
7#[proc_macro_attribute]
8pub fn delegate(_attr: TokenStream, input: TokenStream) -> TokenStream {
9 let item_trait = parse_macro_input!(input as ItemTrait);
10
11 let delegation_functions: Vec<_> = item_trait
12 .items
13 .iter()
14 .map(|trait_item| match trait_item {
15 syn::TraitItem::Fn(f) => {
16 generate_delegation_function(f).unwrap_or_else(|| trait_item.clone())
17 }
18 item => item.clone(),
19 })
20 .collect();
21
22 let mut where_clause = item_trait
23 .generics
24 .where_clause
25 .clone()
26 .map(|v| v.predicates).unwrap_or_else(|| parse_quote!());
27
28 let generic_params_in_impl = &item_trait.generics.params;
29 for param in generic_params_in_impl.iter() {
30 if let GenericParam::Type(ty) = param {
31 let ident = &ty.ident;
32 where_clause.push(parse_quote!(#ident: '__delegate_lifetime));
33 }
34 }
35
36 let mut generic_params = item_trait.generics.params.clone();
37
38 let name = &item_trait.ident;
39 let delegate_generic: Ident = parse_quote!(__DelegateImpl);
40 detach_bounds_from_generic(&mut generic_params);
41 let mut delegate_generic_bound: Punctuated<GenericParam, Comma> = parse_quote! {
42 #delegate_generic: delegare::Delegatable<'__delegate_lifetime, &'__delegate_lifetime dyn #name<#generic_params>>,
43 };
44 let delegate_generic_param = match delegate_generic_bound.first_mut().unwrap() {
45 GenericParam::Type(value) => {
46 value
47 },
48 _ => unreachable!()
49 };
50
51 for bound in item_trait.supertraits.iter() {
52 delegate_generic_param.bounds.push(bound.clone());
53 }
54 quote! {
55 #item_trait
56
57 impl<'__delegate_lifetime: 'static, #delegate_generic, #generic_params_in_impl> #name<#generic_params> for #delegate_generic
58 where
59 #delegate_generic_bound
60 #delegate_generic::Target: #name<#generic_params>,
61 #where_clause
62 {
63 #(#delegation_functions)*
64 }
65 }
66 .into()
67}
68
69fn generate_delegation_function(f: &TraitItemFn) -> Option<TraitItem> {
70 let sig = &f.sig;
71 let name = &sig.ident;
72 let first_argument = sig.inputs.first()?;
73 let delegate_fn_ident = delegate_fn_ident(first_argument)?;
74 let inputs_variables: Vec<_> = sig
75 .inputs
76 .iter()
77 .filter_map(|input| match input {
78 FnArg::Typed(typed) => Some(Ident::new(
79 typed.pat.to_token_stream().to_string().as_str(),
80 typed.pat.span(),
81 )),
82 _ => None,
83 })
84 .collect();
85 Some(TraitItem::Fn(parse_quote! {
86 #[inline(always)]
87 #sig {
88 self.#delegate_fn_ident().#name(#(#inputs_variables),*)
89 }
90 }))
91}
92
93fn delegate_fn_ident(first_argument: &FnArg) -> Option<Ident> {
94 if let syn::FnArg::Receiver(reciever) = first_argument {
95 match (reciever.reference.is_some(), reciever.mutability.is_some()) {
96 (false, _) => parse_quote! {delegate_owned},
97 (true, true) => parse_quote! {delegate_mut},
98 (true, false) => parse_quote! {delegate_ref},
99 }
100 } else {
101 None
102 }
103}
104
105#[proc_macro_derive(Delegate, attributes(to))]
106pub fn derive_delegate(input: TokenStream) -> TokenStream {
107 let item_struct = parse_macro_input!(input as ItemStruct);
108 let impls: Vec<_> = item_struct
109 .fields
110 .iter()
111 .filter_map(|field| generate_delegatable_for_field(&item_struct, field))
112 .collect();
113 quote!(#(#(#impls)*)*).into()
114}
115
116fn generate_delegatable_for_field(
117 item_struct: &ItemStruct,
118 field: &Field,
119) -> Option<Vec<proc_macro2::TokenStream>> {
120 field.attrs.iter().find_map(|attr| {
121 let list = match &attr.meta {
122 Meta::List(list) => list,
123 _ => return None,
124 };
125 if !list.path.is_ident("to") {
126 return None;
127 }
128 let struct_name = &item_struct.ident;
129 let struct_generic_params_in_impl = &item_struct.generics.params;
130 let mut struct_generic_params = struct_generic_params_in_impl.clone();
131 detach_bounds_from_generic(&mut struct_generic_params);
132 let struct_where_clause = &item_struct.generics.where_clause;
133 let trait_names = attr.parse_args::<CommaSeparatedTypes>().unwrap().types;
134 let field_name = &field.ident;
135 let field_type = &field.ty;
136 let impls: Vec<_> = trait_names
137 .iter()
138 .map(|trait_type| {
139 quote! {
140 impl<'__delegate_lifetime: 'static, #struct_generic_params_in_impl>
141 delegare::Delegatable<'__delegate_lifetime, &'__delegate_lifetime dyn #trait_type>
142 for #struct_name<#struct_generic_params> #struct_where_clause {
143 type Target = #field_type;
144
145 fn delegate_mut(&mut self) -> &mut Self::Target {
146 &mut self.#field_name
147 }
148
149 fn delegate_ref(&self) -> &Self::Target {
150 &self.#field_name
151 }
152
153 fn delegate_owned(self) -> Self::Target {
154 self.#field_name
155 }
156 }
157 }
158 })
159 .collect();
160 Some(impls)
161 })
162}
163
164struct CommaSeparatedTypes {
165 types: Punctuated<Type, Token![,]>,
166}
167
168impl Parse for CommaSeparatedTypes {
169 fn parse(input: ParseStream) -> Result<Self, syn::Error> {
170 Ok(CommaSeparatedTypes {
171 types: input.parse_terminated(Type::parse, Token![,])?,
172 })
173 }
174}
175
176
177fn detach_bounds_from_generic( params: &mut Punctuated<GenericParam, Comma>) {
178 for param in params.iter_mut() {
179 match param {
180 GenericParam::Lifetime(value) => {
181 value.bounds.clear()
182 },
183 GenericParam::Type(value) => {
184 value.bounds.clear()
185 },
186 _ => {}
187 }
188 };
189}