generic_derive/
lib.rs

1#![allow(unused)]
2extern crate proc_macro;
3
4use quote::quote;
5use syn::{
6    Data,
7    DataStruct,
8    Fields,
9    Field,
10    Variant,
11    FieldsUnnamed,
12};
13use generic_core::into::*;
14use generic_core::value::*;
15
16
17
18///////////////////////////////////////////////////////////////////////////////
19// INTO-GENERIC ENTRYPOINT
20///////////////////////////////////////////////////////////////////////////////
21
22#[proc_macro_derive(IntoGeneric)]
23pub fn into_generic_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
24    let ast = syn::parse::<syn::DeriveInput>(input).expect("syn::parse failed");
25    let type_name = ast.ident.clone();
26    
27    match ast.data.clone() {
28        Data::Struct(DataStruct{fields: Fields::Named(named), ..}) => {
29            let fields = named.named.into_iter().collect::<Vec<_>>();
30            into_generic_struct(&type_name, &fields)
31        }
32        Data::Struct(DataStruct{fields: Fields::Unnamed(xs), ..}) => {
33            into_generic_tuple_struct(&type_name, xs)
34        }
35        Data::Struct(DataStruct{fields: Fields::Unit, ..}) => {
36            unimplemented!("Unit structs not yet supported")
37        }
38        Data::Enum(xs) => {
39            let variants = xs.variants
40                .into_iter()
41                .collect::<Vec<_>>();
42            into_generic_enum(&type_name, &variants)
43        }
44        Data::Union(_) => {
45            unimplemented!("Union types not yet supported")
46        }
47    }
48}
49
50
51///////////////////////////////////////////////////////////////////////////////
52// INTO-GENERIC - TUPLE-STRUCT
53///////////////////////////////////////////////////////////////////////////////
54
55fn into_generic_struct(type_name: &proc_macro2::Ident, fields: &[Field]) -> proc_macro::TokenStream {
56    let field_conversions = fields
57        .iter()
58        .map(|field| {
59            let field_ident = field.ident
60                .clone()
61                .expect("missing ident");
62            quote! {
63                let key = stringify!(#field_ident).to_owned();
64                let value = IntoGeneric::into_generic(&self.#field_ident);
65                map.insert(key, value);
66            }
67        })
68        .collect::<Vec<_>>();
69    let gen = quote! {
70        impl IntoGeneric for #type_name {
71            fn into_generic(&self) -> Value {
72                use generic_core::into::*;
73                use generic_core::value::*;
74
75                let mut map: HashMap<String, Value> = std::collections::HashMap::new();
76                #(#field_conversions)*
77                Value::Struct(Struct {
78                    type_name: String::from(stringify!(#type_name)),
79                    data: map,
80                })
81            }
82        }
83    };
84    gen.into()
85}
86
87
88///////////////////////////////////////////////////////////////////////////////
89// INTO-GENERIC - TUPLE-STRUCT
90///////////////////////////////////////////////////////////////////////////////
91
92fn into_generic_tuple_struct(type_name: &proc_macro2::Ident, fields: FieldsUnnamed) -> proc_macro::TokenStream {
93    let field_conversions = fields.unnamed
94        .iter()
95        .enumerate()
96        .map(|(ix, _)| {
97            let ix = syn::Index::from(ix);
98            quote!{
99                let value = IntoGeneric::into_generic(&self.#ix);
100                vec.push(value);
101            }
102        })
103        .collect::<Vec<_>>();
104    let gen = quote! {
105        impl IntoGeneric for #type_name {
106            fn into_generic(&self) -> Value {
107                use generic_core::into::*;
108                use generic_core::value::*;
109
110                let mut vec = Vec::<Value>::new();
111
112                #(#field_conversions)*
113                Value::TupleStruct(TupleStruct {
114                    type_name: String::from(stringify!(#type_name)),
115                    data: vec,
116                })
117            }
118        }
119    };
120    gen.into()
121}
122
123
124///////////////////////////////////////////////////////////////////////////////
125// INTO-GENERIC - ENUMS
126///////////////////////////////////////////////////////////////////////////////
127
128fn into_generic_enum(type_name: &proc_macro2::Ident, variants: &[Variant]) -> proc_macro::TokenStream {
129    let arms = variants
130        .iter()
131        .map(|var| {
132            let variant_name = &var.ident;
133            match var.fields.clone() {
134                Fields::Named(xs) => {
135                    let (ident_binders, to_generics) = xs.named
136                        .iter()
137                        .map(|x| {
138                            let ident = x.ident.clone().expect("todo - tuple structs");
139                            let binder = quote!{
140                                ref #ident,
141                            };
142                            let to_generic = quote!{
143                                let key = stringify!(#ident).to_owned();
144                                let value = IntoGeneric::into_generic(#ident);
145                                map.insert(key, value);
146                            };
147                            (binder, to_generic)
148                        })
149                        .unzip::<_, _, Vec<_>, Vec<_>>();
150                    quote! {
151                        #type_name::#variant_name{#(#ident_binders)*} => {
152                            let mut map = std::collections::HashMap::<String, Value>::new();
153
154                            #(#to_generics)*
155                            
156                            Value::Variant(Variant::StructVariant {
157                                type_name: stringify!(#type_name).to_owned(),
158                                variant_name: stringify!(#variant_name).to_owned(),
159                                data: map,
160                            })
161                        }
162                    }
163                },
164                Fields::Unnamed(xs) => {
165                    let (ident_binders, to_generics) = xs.unnamed
166                        .iter()
167                        .map(|_| {
168                            let ident = format!("id_{}", rand::random::<u16>());
169                            let ident = proc_macro2::Ident::new(&ident, proc_macro2::Span::call_site());
170                            let binder = quote!{
171                                ref #ident,
172                            };
173                            let to_generic = quote!{
174                                let value = IntoGeneric::into_generic(#ident);
175                                vec.push(value);
176                            };
177                            (binder, to_generic)
178                        })
179                        .unzip::<_, _, Vec<_>, Vec<_>>();
180                    quote! {
181                        #type_name::#variant_name(#(#ident_binders)*) => {
182                            let mut vec = Vec::<Value>::new();
183
184                            #(#to_generics)*
185                            
186                            Value::Variant(Variant::TupleVariant {
187                                type_name: stringify!(#type_name).to_owned(),
188                                variant_name: stringify!(#variant_name).to_owned(),
189                                data: vec,
190                            })
191                        }
192                    }
193                },
194                Fields::Unit => {
195                    quote! {
196                        #type_name::#variant_name => {
197                            Value::Variant(Variant::UnitVariant{
198                                type_name: stringify!(#type_name).to_owned(),
199                                variant_name: stringify!(#variant_name).to_owned(),
200                            })
201                        }
202                    }
203                },
204            }
205        });
206    let gen = quote! {
207        impl IntoGeneric for #type_name {
208            fn into_generic(&self) -> Value {
209                use generic_core::into::*;
210                use generic_core::value::*;
211
212                match self {
213                    #(#arms)*
214                }
215            }
216        }
217    };
218    gen.into()
219}
220