valu3_derive/
lib.rs

1extern crate proc_macro;
2use proc_macro::TokenStream;
3use quote::quote;
4use syn::{parse_macro_input, Data, DeriveInput, Fields, Generics, Variant};
5
6#[proc_macro_derive(ToValue)]
7pub fn to_value_derive(input: TokenStream) -> TokenStream {
8    let input = parse_macro_input!(input as DeriveInput);
9
10    let name = input.ident;
11    let generics = input.generics;
12
13    let to_value_impl = match input.data {
14        Data::Struct(data) => to_value_struct_impl(name, generics, data.fields),
15        Data::Enum(data) => to_value_enum_impl(name, generics, data.variants),
16        Data::Union(_) => panic!("ToValueBehavior cannot be derived for unions"),
17    };
18
19    let expanded = quote! {
20        #to_value_impl
21    };
22
23    TokenStream::from(expanded)
24}
25
26fn to_value_struct_impl(
27    name: syn::Ident,
28    generics: Generics,
29    fields: Fields,
30) -> proc_macro2::TokenStream {
31    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
32
33    let field_transforms = match fields {
34        Fields::Named(fields) => fields
35            .named
36            .iter()
37            .map(|field| {
38                let name = match field.ident.as_ref() {
39                    Some(name) => name,
40                    None => panic!("ToValueBehavior cannot be derived for unnamed fields"),
41                };
42                let field_name = format!("{}", name);
43                quote! {
44                    map.insert(#field_name.to_string(), self.#name.clone().into());
45                }
46            })
47            .collect::<Vec<_>>(),
48        Fields::Unnamed(fields) => fields
49            .unnamed
50            .iter()
51            .enumerate()
52            .map(|(index, _field)| {
53                let index = syn::Index::from(index);
54                quote! {
55                    Value::from(self.#index.clone())
56                }
57            })
58            .collect::<Vec<_>>(),
59        Fields::Unit => {
60            return quote! {
61                impl #impl_generics ToValueBehavior for  #name #ty_generics #where_clause {
62                    fn to_value(&self) -> Value {
63                        Value::Null
64                    }
65                }
66            }
67        }
68    };
69
70    quote! {
71        impl #impl_generics ToValueBehavior  for #name #ty_generics #where_clause {
72            fn to_value(&self) -> Value {
73                let mut map: std::collections::HashMap<String, Value>= std::collections::HashMap::new();
74                #(#field_transforms)*
75                Value::from(map)
76            }
77        }
78    }
79}
80
81fn to_value_enum_impl(
82    name: syn::Ident,
83    generics: Generics,
84    variants: syn::punctuated::Punctuated<Variant, syn::Token![,]>,
85) -> proc_macro2::TokenStream {
86    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
87
88    let variant_transforms = variants.iter().map(|variant| {
89        let variant_name = &variant.ident;
90        quote! {
91            #name::#variant_name => Value::from(stringify!(#variant_name)),
92        }
93    });
94
95    quote! {
96        impl #impl_generics ToValueBehavior for #name #ty_generics #where_clause {
97            fn to_value(&self) -> Value {
98                match self {
99                    #(#variant_transforms)*
100                }
101            }
102        }
103    }
104}
105
106#[proc_macro_derive(FromValue)]
107pub fn from_value_derive(input: TokenStream) -> TokenStream {
108    // Parse a `DeriveInput` AST from the input tokens.
109    let ast = parse_macro_input!(input as DeriveInput);
110
111    // Get the name and fields of the struct being derived.
112    let target_name = &ast.ident;
113    let target_generics = &ast.generics;
114    let (impl_generics, ty_generics, where_clause) = target_generics.split_for_impl();
115
116    match ast.data {
117        Data::Struct(data_struct) => {
118            // Define a new implementation of the `FromValueBehavior` trait for the struct.
119            let mut field_names = Vec::new();
120            let mut from_value_exprs = Vec::new();
121
122            if let Fields::Named(fields) = data_struct.fields {
123                for field in fields.named.iter() {
124                    let field_name = match field.ident.as_ref() {
125                        Some(name) => name,
126                        None => panic!(
127                            "Can only derive FromValueBehavior for a struct with named fields."
128                        ),
129                    };
130                    let field_type = &field.ty;
131
132                    field_names.push(field_name.clone());
133
134                    from_value_exprs.push(quote! {
135                        #field_name: {
136                            let item = match map.get(stringify!(#field_name)) {
137                                Some(item) => item.clone(),
138                                None => return None,
139                            };
140                            match <#field_type as FromValueBehavior>::from_value(item) {
141                                Some(item) => item,
142                                None => return None,
143                            }
144                        }
145                    });
146                }
147            } else {
148                panic!("Can only derive FromValueBehavior for a struct with named fields.");
149            }
150
151            let expanded = quote! {
152                impl #impl_generics FromValueBehavior for #target_name #ty_generics #where_clause {
153                    type Item = Self;
154
155                    fn from_value(value: Value) -> Option<Self> {
156                        if let Value::Object(map) = value {
157                            Some(
158                                Self {
159                                    #(#from_value_exprs),*
160                                }
161                            )
162                        } else {
163                            None
164                        }
165                    }
166                }
167            };
168
169            TokenStream::from(expanded)
170        }
171        Data::Enum(data_enum) => {
172            let variants = data_enum.variants;
173            let mut variant_names = Vec::new();
174
175            for variant in variants.iter() {
176                let variant_name = &variant.ident;
177                variant_names.push(variant_name.clone());
178            }
179
180            let expanded = quote! {
181                impl #impl_generics PrimitiveType for #target_name #ty_generics #where_clause {}
182
183                impl #impl_generics FromValueBehavior for #target_name #ty_generics #where_clause {
184                    type Item = Self;
185
186                    fn from_value(value: Value) -> Option<Self> {
187                        match value {
188                            Value::String(value) => {
189                                match value.as_str() {
190                                    #(
191                                        stringify!(#variant_names) => Some(#target_name::#variant_names),
192                                    )*
193                                    _ => None,
194                                }
195                            },
196                            _ => None,
197                        }
198                    }
199                }
200            };
201
202            TokenStream::from(expanded)
203        }
204        _ => panic!("Can only derive FromValueBehavior for a struct."),
205    }
206}
207
208#[proc_macro_derive(ToJson)]
209pub fn to_json_derive(input: TokenStream) -> TokenStream {
210    let ast = parse_macro_input!(input as DeriveInput);
211    let name = &ast.ident;
212    let generics = &ast.generics;
213    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
214
215    let gen = quote! {
216        impl #impl_generics ToJsonBehavior for #name #ty_generics #where_clause {
217            fn to_json(&self) -> String {
218                self.to_value().to_string()
219            }
220        }
221    };
222
223    gen.into()
224}
225
226#[proc_macro_derive(ToYaml)]
227pub fn to_yaml_derive(input: TokenStream) -> TokenStream {
228    let input = parse_macro_input!(input as DeriveInput);
229    let name = input.ident;
230    let generics = input.generics;
231    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
232
233    let expanded = quote! {
234        impl #impl_generics ToYamlBehavior for #name  #ty_generics #where_clause {
235            fn to_yaml(&self) -> String {
236                let value = self.to_value();
237                value.to_yaml()
238            }
239        }
240    };
241
242    TokenStream::from(expanded)
243}