delegare_derive/
lib.rs

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}