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
extern crate proc_macro;

use proc_macro2::TokenStream;
use quote::quote;
use syn::{
    Data, DeriveInput, Fields, Generics, GenericParam, Ident,
    parse_macro_input, parse_quote,
};

#[proc_macro_derive(AsTuple)]
pub fn derive_as_tuple(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    let name = input.ident;
    let mut generics = input.generics.clone();
    generics.params.push(parse_quote!('__as_tuple));
    let generics = add_bounds(generics);
    let (impl_generics, _, _) = generics.split_for_impl();
    let (_, ty_generics, where_clause) = input.generics.split_for_impl();
    let tuple_type = tuple_gen(&input.data, TupleOptions::TypeSig { mutable: false });
    let tuple_type_mut = tuple_gen(&input.data, TupleOptions::TypeSig { mutable: true });
    let destructure_expr = destructure(&name, &input.data, false);
    let destructure_expr_mut = destructure(&name, &input.data, true);
    let expanded = quote! {
        impl #impl_generics ::as_tuple::AsTuple<'__as_tuple> for #name #ty_generics #where_clause {
            type Tuple = #tuple_type;
            type TupleMut = #tuple_type_mut;

            fn as_tuple(&'__as_tuple self) -> Self::Tuple {
                #destructure_expr
            }

            fn as_tuple_mut(&'__as_tuple mut self) -> Self::TupleMut {
                #destructure_expr_mut
            }
        }
    };
    //panic!("{}", expanded.to_string());
    proc_macro::TokenStream::from(expanded)
}

fn add_bounds(mut generics: Generics) -> Generics {
    for param in &mut generics.params {
        match *param {
            GenericParam::Type(ref mut type_param) => {
                type_param.bounds.push(parse_quote!('__as_tuple));
            },
            GenericParam::Lifetime(ref mut lifetime_param) => {
                lifetime_param.bounds.push(parse_quote!('__as_tuple));
            },
            _ => (),
        }
    }
    generics
}

#[derive(Debug)]
enum TupleOptions {
    TypeSig { mutable: bool },
    Binding,
    Ref { mutable: bool },
}

fn tuple_gen(data: &Data, options: TupleOptions) -> TokenStream {
    match *data {
        Data::Struct(ref data) => {
            match data.fields {
                Fields::Named(ref fields) => {
                    let recurse = fields
                        .named
                        .iter()
                        .map(|f| match options {
                            TupleOptions::TypeSig { mutable } => if !mutable {
                                let ty = &f.ty;
                                quote! { &'__as_tuple #ty }
                            } else {
                                let ty = &f.ty;
                                quote! { &'__as_tuple mut #ty }
                            },
                            TupleOptions::Binding => {
                                let name = f.ident.as_ref().unwrap();
                                quote! { #name }
                            },
                            _ => unimplemented!(),
                        });
                    quote! { (#(#recurse),*) }
                },
                Fields::Unnamed(ref fields) => {
                    let recurse = fields
                        .unnamed
                        .iter()
                        .enumerate()
                        .map(|(i, f)| match options {
                            TupleOptions::TypeSig { mutable } => if !mutable {
                                let ty = &f.ty;
                                quote! { &'__as_tuple #ty }
                            } else {
                                let ty = &f.ty;
                                quote! { &'__as_tuple mut #ty }
                            },
                            TupleOptions::Binding => {
                                quote! { field_#i }
                            },
                            TupleOptions::Ref { mutable } => if !mutable {
                                quote! { &self.#i }
                            } else {
                                quote! { &mut self.#i }
                            },
                        });
                    quote! { (#(#recurse),*) }
                },
                Fields::Unit => quote!{ () },
            }
        },
        Data::Enum(_) | Data::Union(_) => unimplemented!(),
    }
}

fn destructure(name: &Ident, data_root: &Data, mutable: bool) -> TokenStream {
    match *data_root {
        Data::Struct(ref data) => {
            match data.fields {
                Fields::Named(ref fields) => {
                    let recurse = fields.named.iter().map(|f| if !mutable {
                        let name = f.ident.as_ref().unwrap();
                        quote! { ref #name }
                    } else {
                        let name = f.ident.as_ref().unwrap();
                        quote! { ref mut #name }
                    });
                    let tuple_bind = tuple_gen(data_root, TupleOptions::Binding);
                    quote! {
                        let #name { #(#recurse),* } = self;
                        #tuple_bind
                    }
                },
                Fields::Unnamed(_) => {
                    let tuple = tuple_gen(data_root, TupleOptions::Ref { mutable });
                    quote! { #tuple }
                },
                Fields::Unit => quote!{ () },
            }
        },
        Data::Enum(_) | Data::Union(_) => unimplemented!(),
    }
}