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
use proc_macro::TokenStream; use proc_macro2::TokenStream as TokenStream2; use quote::quote; use syn::{ parse_macro_input, punctuated::Punctuated, token::Comma, Data, DeriveInput, Field, Fields, }; #[proc_macro_derive(Serialize)] pub fn derive_serialize(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); let struct_name = input.ident; let struct_fields = get_struct_fields(&input.data); let it = struct_fields.iter().map(|field| { let name = &field.ident; let keyword = to_edn_keyword(format!("{}", quote! {#name})); quote! { format!("{} {}, ", #keyword, self.#name.serialize()) } }); let expanded = quote! { impl edn_rs::Serialize for #struct_name { fn serialize(self) -> String { let mut s = String::new(); s.push_str("{ "); #(s.push_str(&#it);)* s.push_str("}"); s } } }; expanded.into() } #[proc_macro_derive(Deserialize)] pub fn derive_deserialize(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); let struct_name = input.ident; let struct_fields = get_struct_fields(&input.data); let deserialized_fields = generate_field_deserialization(&struct_fields); let expanded = quote! { impl edn_rs::Deserialize for #struct_name { fn deserialize(edn: &edn_rs::Edn) -> Result<Self, edn_rs::EdnError> { Ok(Self { #deserialized_fields }) } } }; expanded.into() } fn get_struct_fields(data: &Data) -> &Punctuated<Field, Comma> { match *data { Data::Struct(ref data) => match data.fields { Fields::Named(ref fields) => &fields.named, _ => unimplemented!(), }, _ => unimplemented!(), } } fn to_edn_keyword(field_name: String) -> String { let mut keyword = field_name .replace("___", "/") .replace("__", ".") .replace("_", "-"); keyword.insert(0, ':'); keyword } #[test] fn test_to_edn_keyword() { assert_eq!(to_edn_keyword("name".to_string()), ":name"); assert_eq!(to_edn_keyword("crux__db___id".to_string()), ":crux.db/id"); assert_eq!( to_edn_keyword("account___amount".to_string()), ":account/amount" ); assert_eq!(to_edn_keyword("tx___tx_time".to_string()), ":tx/tx-time"); } fn generate_field_deserialization(fields: &Punctuated<Field, Comma>) -> TokenStream2 { fields .iter() .map(|f| { let name = &f.ident; let keyword = to_edn_keyword(format!("{}", quote! {#name})); quote! { #name: edn_rs::Deserialize::deserialize(&edn[#keyword])?, } }) .collect() }