1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
use quote::quote;
use syn::parse::Parse;

#[proc_macro_attribute]
pub fn box_dyn(
    args: proc_macro::TokenStream,
    input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
    // let mut input: syn::DeriveInput = syn::parse2(input.into()).unwrap();
    let additional_bounds = syn::parse_macro_input!(args with syn::punctuated::Punctuated::<syn::Path, syn::Token![,]>::parse_terminated);
    // let args_parsed: syn::punctuated::Punctuated<syn::Path, syn::Token![,]>::parse_terminated =
    //     syn::parse2(input.into()).unwrap();
    // let args_parsed = syn::punctuated::Punctuated::<syn::Path, syn::Token![,]>::parse_terminated
    //     .parse2(args)
    //     .unwrap();
    let trait_item: syn::ItemTrait = syn::parse2(input.into()).unwrap();
    let trait_name = &trait_item.ident;
    let trait_generics = &trait_item.generics;
    // let trait_with_generics = quote! { #trait_name };
    // generics
    // dbg!(trait_name.to_string());

    let trait_items: Vec<_> = trait_item
        .items
        .clone()
        .into_iter()
        .filter_map(|mut item| {
            match item {
                // An associated constant within the definition of a trait.
                syn::TraitItem::Const(ref mut val) => {
                    // default
                    // syn::TraitItemConst
                    val.default = todo!();
                    Some(item)
                    // Some(val.into_token_stream())
                }
                // An associated function within the definition of a trait.
                syn::TraitItem::Fn(ref mut func) => {
                    // syn::TraitItemFn
                    let func_name = &func.sig.ident;
                    // dbg!(&func_name);
                    let receiver = func
                        .sig
                        .inputs
                        .iter()
                        .find_map(|arg| match arg {
                            syn::FnArg::Receiver(ty) => Some(ty.clone()),
                            _ => None,
                        })
                        .expect("trait functions need receiver type");
                    let self_typ =
                        match (receiver.reference.is_some(), receiver.mutability.is_some()) {
                            (true, true) => quote! { self.as_mut() },
                            (true, false) => quote! { self.as_ref() },
                            (false, _) => quote! { self },
                        };
                    // pub reference: Option<(Token![&], Option<Lifetime>)>,
                    // pub mutability: Option<Token![mut]>,

                    let param_names: Vec<_> = func
                        .sig
                        .inputs
                        .iter()
                        .filter_map(|arg| match arg {
                            syn::FnArg::Typed(ty) => Some(ty.pat.clone()),
                            _ => None,
                        })
                        .collect();

                    // dbg!(&trait_name);
                    // dbg!(&func_name);
                    // dbg!(&self_typ);
                    // dbg!(&param_names
                    //     .iter()
                    //     .map(|p| quote! { #p })
                    //     .collect::<Vec<_>>());

                    // println!(
                    //     "{}",
                    //     pretty_print(quote! {{
                    //         #trait_name::#func_name(#self_typ, #(#param_names),*)
                    //     }})
                    // );
                    func.default = Some(
                        syn::parse2::<syn::Block>(quote! {{
                            #trait_name::#func_name(#self_typ, #(#param_names),*)
                        }})
                        .unwrap(),
                    );
                    Some(item)
                    // Some(func)
                    // pub attrs: Vec<Attribute>,
                    // pub sig: Signature,
                    // pub default: Option<Block>,
                    //
                }

                // An associated type within the definition of a trait.
                syn::TraitItem::Type(typ) => {
                    // syn::TraitItemType
                    typ.default = todo!();
                    Some(item)
                }
                // A macro invocation within the definition of a trait.
                // syn::TraitItemMacro
                // syn::TraitItem::Macro(_) => None,
                // Tokens within the definition of a trait not interpreted by Syn.
                // syn::TokenStream
                // syn::TraitItem::Verbatim(_) => None,
                _ => None,
            }
        })
        .collect();

    let t = quote! { __BoxDynT };
    let trait_where_predicates = &trait_generics
        .where_clause
        .as_ref()
        .map(|clause| &clause.predicates);
    let trait_generic_params = &trait_generics.params;

    let t_bounds: Vec<_> = [quote! { #trait_name #trait_generics }]
        .into_iter()
        .chain(additional_bounds.into_iter().map(|b| quote! { #b }))
        .collect();

    let out = quote! {
        #trait_item

        impl<#t, #trait_generic_params> #trait_name #trait_generics for Box<#t>
        where
            #t: #(#t_bounds)+*,
            #trait_where_predicates
        {
            #(#trait_items)*
        }
    };
    println!("{}", pretty_print(&out));
    out.into()
}

#[allow(dead_code)]
fn pretty_print<T>(input: T) -> String
where
    T: quote::ToTokens,
{
    let file: syn::File = syn::parse2(quote! {
        fn main() {
            #input
        }
    })
    .unwrap();
    prettyplease::unparse(&file)
}