box_dyn/
lib.rs

1use quote::{quote, ToTokens};
2use syn::punctuated::Punctuated;
3
4#[proc_macro_attribute]
5pub fn box_dyn(
6    args: proc_macro::TokenStream,
7    input: proc_macro::TokenStream,
8) -> proc_macro::TokenStream {
9    // let mut input: syn::DeriveInput = syn::parse2(input.into()).unwrap();
10    // let additional_bounds = syn::parse_macro_input!(args with syn::punctuated::Punctuated::<syn::Path, syn::Token![,]>::parse_terminated);
11    // $crate::parse::Parser::parse($parser, $tokenstream)
12    let additional_bounds_comma_separated = syn::parse::Parser::parse(
13        <Punctuated<syn::TraitBound, syn::Token![,]>>::parse_terminated,
14        args.clone().into(),
15    )
16    .map(|bounds| bounds.into_iter().collect());
17    // dbg!(additional_bounds_comma_separated.is_ok());
18
19    let additional_bounds_plus_separated = syn::parse::Parser::parse(
20        <Punctuated<syn::TraitBound, syn::Token![+]>>::parse_terminated,
21        args.clone().into(),
22    )
23    .map(|bounds| bounds.into_iter().collect());
24    // dbg!(additional_bounds_plus_separated.is_ok());
25
26    let additional_bounds: Vec<syn::TraitBound> = additional_bounds_plus_separated
27        .or(additional_bounds_comma_separated)
28        .unwrap();
29
30    // dbg!(additional_bounds
31    //     .iter()
32    //     .map(|b| quote! { #b })
33    //     .collect::<Vec<_>>());
34
35    // .unwrap();
36    // let additional_bounds = syn::parse_macro_input!(args with ;
37    // Punctuated<GenericParam, Token![,]>
38    // let args_parsed: syn::punctuated::Punctuated<syn::Path, syn::Token![,]>::parse_terminated =
39    //     syn::parse2(input.into()).unwrap();
40    // let args_parsed = syn::punctuated::Punctuated::<syn::Path, syn::Token![,]>::parse_terminated
41    //     .parse2(args)
42    //     .unwrap();
43    let trait_item: syn::ItemTrait = syn::parse2(input.into()).unwrap();
44    let trait_name = &trait_item.ident;
45    let trait_generics = &trait_item.generics;
46    // let trait_with_generics = quote! { #trait_name };
47    // generics
48    // dbg!(trait_name.to_string());
49
50    let trait_items: Vec<_> = trait_item
51        .items
52        .clone()
53        .into_iter()
54        .filter_map(|mut item| {
55            match item {
56                // An associated constant within the definition of a trait.
57                syn::TraitItem::Const(ref mut val) => {
58                    // default
59                    // syn::TraitItemConst
60                    val.default = todo!();
61                    Some(item)
62                    // Some(val.into_token_stream())
63                }
64                // An associated function within the definition of a trait.
65                syn::TraitItem::Fn(ref mut func) => {
66                    // syn::TraitItemFn
67                    let func_name = &func.sig.ident;
68                    // dbg!(&func_name);
69                    let receiver = func
70                        .sig
71                        .inputs
72                        .iter()
73                        .find_map(|arg| match arg {
74                            syn::FnArg::Receiver(ty) => Some(ty.clone()),
75                            _ => None,
76                        })
77                        .expect("trait functions need receiver type");
78                    let self_typ =
79                        match (receiver.reference.is_some(), receiver.mutability.is_some()) {
80                            (true, true) => quote! { self.as_mut() },
81                            (true, false) => quote! { self.as_ref() },
82                            (false, _) => quote! { self },
83                        };
84                    // pub reference: Option<(Token![&], Option<Lifetime>)>,
85                    // pub mutability: Option<Token![mut]>,
86
87                    let param_names: Vec<_> = func
88                        .sig
89                        .inputs
90                        .iter()
91                        .filter_map(|arg| match arg {
92                            syn::FnArg::Typed(ty) => Some(ty.pat.clone()),
93                            _ => None,
94                        })
95                        .collect();
96
97                    // dbg!(&trait_name);
98                    // dbg!(&func_name);
99                    // dbg!(&self_typ);
100                    // dbg!(&param_names
101                    //     .iter()
102                    //     .map(|p| quote! { #p })
103                    //     .collect::<Vec<_>>());
104
105                    // println!(
106                    //     "{}",
107                    //     pretty_print(quote! {{
108                    //         #trait_name::#func_name(#self_typ, #(#param_names),*)
109                    //     }})
110                    // );
111                    func.default = Some(
112                        syn::parse2::<syn::Block>(quote! {{
113                            #trait_name::#func_name(#self_typ, #(#param_names),*)
114                        }})
115                        .unwrap(),
116                    );
117                    Some(item)
118                    // Some(func)
119                    // pub attrs: Vec<Attribute>,
120                    // pub sig: Signature,
121                    // pub default: Option<Block>,
122                    //
123                }
124
125                // An associated type within the definition of a trait.
126                syn::TraitItem::Type(typ) => {
127                    // syn::TraitItemType
128                    typ.default = todo!();
129                    Some(item)
130                }
131                // A macro invocation within the definition of a trait.
132                // syn::TraitItemMacro
133                // syn::TraitItem::Macro(_) => None,
134                // Tokens within the definition of a trait not interpreted by Syn.
135                // syn::TokenStream
136                // syn::TraitItem::Verbatim(_) => None,
137                _ => None,
138            }
139        })
140        .collect();
141
142    let t = quote! { __BoxDynT };
143    let trait_where_predicates = &trait_generics
144        .where_clause
145        .as_ref()
146        .map(|clause| &clause.predicates);
147    let trait_generic_params = &trait_generics.params;
148
149    let t_bounds: Vec<_> = [quote! { #trait_name #trait_generics }]
150        .into_iter()
151        .chain(additional_bounds.into_iter().map(|b| quote! { #b }))
152        .collect();
153
154    let out = quote! {
155        #trait_item
156
157        impl<#t, #trait_generic_params> #trait_name #trait_generics for Box<#t>
158        where
159            #t: #(#t_bounds)+*,
160            #trait_where_predicates
161        {
162            #(#trait_items)*
163        }
164    };
165    println!("{}", pretty_print(&out));
166    out.into()
167}
168
169#[allow(dead_code)]
170fn pretty_print<T>(input: T) -> String
171where
172    T: quote::ToTokens,
173{
174    let file: syn::File = syn::parse2(quote! {
175        fn main() {
176            #input
177        }
178    })
179    .unwrap();
180    prettyplease::unparse(&file)
181}