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, attributes(attr))]
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 let ast = parse_macro_input!(input as DeriveInput);
110
111 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 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 v if v == 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}