lbr_prelude_derive/
lib.rs1use proc_macro::TokenStream;
2use quote::{format_ident, quote};
3use syn::{self, parse_macro_input, DeriveInput};
4
5#[proc_macro_derive(Json)]
16pub fn derive_json_fn(input: TokenStream) -> TokenStream {
17 let ast = parse_macro_input!(input as DeriveInput);
18
19 let ident = &ast.ident;
20
21 let (to_json_impl, from_json_impl) = match &ast.data {
22 syn::Data::Struct(data_struct) => match &data_struct.fields {
23 syn::Fields::Named(fields_named) => impl_struct(ident, fields_named),
24 syn::Fields::Unnamed(fields_unnamed) => {
25 if fields_unnamed.unnamed.len() == 1 {
26 impl_newtype()
27 } else {
28 impl_tuple(ident, fields_unnamed)
29 }
30 }
31 syn::Fields::Unit => unimplemented!("Units are unsupported"),
32 },
33 syn::Data::Enum(data_enum) => impl_enum(ident, &data_enum.variants),
34 syn::Data::Union(_data_union) => unimplemented!("Unions are unsupported"),
35 };
36
37 let (impl_generics, ty_generics, where_clause) = &ast.generics.split_for_impl();
38
39 let expanded = quote! {
40 impl #impl_generics lbr_prelude::json::Json for #ident #ty_generics #where_clause {
41
42 #to_json_impl
43 #from_json_impl
44 }
45 };
46
47 TokenStream::from(expanded)
48}
49
50fn impl_struct(
53 ident: &syn::Ident,
54 fields_named: &syn::FieldsNamed,
55) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) {
56 let ident_str = ident.to_string();
57 let named = &fields_named.named;
58
59 let dict_insert = named.iter().map(|field| {
61 let key = &field.ident;
62 let key_str = key.as_ref().unwrap().to_string();
63 quote! {
64 dict.insert(#key_str.to_owned(), self.#key.to_json());
65 }
66 });
67
68 let to_json_impl = quote! {
69 fn to_json(&self) -> serde_json::Value {
70 let mut dict = serde_json::Map::new();
71 #(#dict_insert)*
72
73 serde_json::Value::Object(dict)
74 }
75 };
76
77 let dict_get = named.iter().map(|field| {
79 let key = &field.ident;
80 let key_str = key.as_ref().unwrap().to_string();
81 quote! {
82 let #key = dict
83 .get(#key_str)
84 .ok_or(lbr_prelude::error::Error::UnexpectedFieldName {
85 wanted: #key_str.to_owned(),
86 got: dict.keys().cloned().collect(),
87 parser: #ident_str.to_owned(),
88 })
89 .and_then(lbr_prelude::json::Json::from_json)?;
90 }
91 });
92
93 let keys = named.iter().map(|field| &field.ident);
94
95 let from_json_impl = quote! {
96 fn from_json(value: &serde_json::Value) -> std::result::Result<Self, lbr_prelude::error::Error> {
97 match value {
98 serde_json::Value::Object(dict) => {
99 #(#dict_get)*
100
101 Ok(Self {
102 #(#keys,)*
103 })
104 }
105 _ => Err(lbr_prelude::error::Error::UnexpectedJsonType {
106 wanted: lbr_prelude::error::JsonType::Object,
107 got: lbr_prelude::error::JsonType::from(value),
108 parser: #ident_str.to_owned(),
109 }),
110 }
111 }
112 };
113
114 (to_json_impl, from_json_impl)
115}
116
117fn impl_tuple(
120 ident: &syn::Ident,
121 fields_unnamed: &syn::FieldsUnnamed,
122) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) {
123 let ident_str = ident.to_string();
124
125 let arity = fields_unnamed.unnamed.len();
126 let to_json_indices = (0..arity).map(syn::Index::from);
127 let from_json_indices = to_json_indices.clone();
128
129 let to_json_impl = quote! {
130 fn to_json(&self) -> serde_json::Value {
131 serde_json::Value::Array(vec![
132 #(self.#to_json_indices.to_json(),)*
133 ])
134 }
135 };
136
137 let from_json_impl = quote! {
138 fn from_json(value: &serde_json::Value) -> std::result::Result<Self, lbr_prelude::error::Error> {
139 Vec::from_json(value).and_then(|vec: Vec<serde_json::Value>| {
140 if vec.len() == #arity {
141 Ok(Self(
142 #(Json::from_json(&vec[#from_json_indices])?,)*
143 ))
144 } else {
145 Err(lbr_prelude::error::Error::UnexpectedArrayLength {
146 wanted: #arity,
147 got: vec.len(),
148 parser: #ident_str.to_owned(),
149 })
150 }
151 })
152 }
153 };
154
155 (to_json_impl, from_json_impl)
156}
157
158fn impl_newtype() -> (proc_macro2::TokenStream, proc_macro2::TokenStream) {
162 let to_json_impl = quote! {
163 fn to_json(&self) -> serde_json::Value {
164 self.0.to_json()
165 }
166 };
167
168 let from_json_impl = quote! {
169 fn from_json(value: &serde_json::Value) -> std::result::Result<Self, lbr_prelude::error::Error> {
170 Ok(Self(lbr_prelude::json::Json::from_json(value)?))
171 }
172 };
173
174 (to_json_impl, from_json_impl)
175}
176
177fn impl_enum(
180 ident: &syn::Ident,
181 variants: &syn::punctuated::Punctuated<syn::Variant, syn::token::Comma>,
182) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) {
183 let ident_str = ident.to_string();
184 let to_json_match_arms = variants.iter().map(|variant| {
186 let variant_ident = &variant.ident;
187 let variant_str = variant.ident.to_string();
188
189 match &variant.fields {
190 syn::Fields::Named(_fields_named) => {
191 unimplemented!("Enums with named fields are unsupported.")
192 }
193 syn::Fields::Unnamed(fields_unnamed) => {
194 let arity = fields_unnamed.unnamed.len();
195 let fields = (0..arity).map(|i| format_ident!("f{}", i));
196 let fields_2 = fields.clone();
197
198 quote! {
199 #ident::#variant_ident( #(#fields),* ) =>
200 lbr_prelude::json::json_constructor(#variant_str, &vec![
201 #(#fields_2.to_json(),)*
202 ]),
203 }
204 }
205 syn::Fields::Unit => quote! {
206 #ident::#variant_ident =>
207 lbr_prelude::json::json_constructor(#variant_str, &Vec::with_capacity(0)),
208 },
209 }
210 });
211
212 let to_json_impl = quote! {
213 fn to_json(&self) -> serde_json::Value {
214 match self {
215 #(#to_json_match_arms)*
216 }
217 }
218 };
219
220 let from_json_match_arms = variants.iter().map(|variant| {
222 let variant_ident = &variant.ident;
223 let variant_str = variant.ident.to_string();
224
225 match &variant.fields {
226 syn::Fields::Named(_fields_named) => {
227 unimplemented!("Enums with named fields are unsupported.")
228 }
229
230 syn::Fields::Unnamed(fields_unnamed) => {
231 let arity = fields_unnamed.unnamed.len();
232 let fields = (0..arity).map(syn::Index::from);
233
234 quote! {
235 (
236 #variant_str,
237 Box::new(|ctor_fields| {
238 if ctor_fields.len() == #arity {
239 Ok(#ident::#variant_ident(
240 #(Json::from_json(&ctor_fields[#fields])?),*
241 ))
242 } else {
243 Err(lbr_prelude::error::Error::UnexpectedArrayLength {
244 wanted: #arity,
245 got: ctor_fields.len(),
246 parser: #ident_str.to_owned(),
247 })
248 }
249 })
250 )
251 }
252 }
253
254 syn::Fields::Unit => quote! {
255 (
256 #variant_str,
257 Box::new(|ctor_fields| match &ctor_fields[..] {
258 [] => Ok(#ident::#variant),
259 _ => Err(lbr_prelude::error::Error::UnexpectedArrayLength {
260 wanted: 0,
261 got: ctor_fields.len(),
262 parser: #ident_str.to_owned(),
263 })
264 })
265 )
266
267 },
268 }
269 });
270
271 let from_json_impl = quote! {
272 fn from_json(value: &serde_json::Value) -> std::result::Result<Self, lbr_prelude::error::Error> {
273 lbr_prelude::json::case_json_constructor(#ident_str, vec![
274 #(#from_json_match_arms),*
275 ],
276 value)
277 }
278 };
279
280 (to_json_impl, from_json_impl)
281}