1use proc_macro::TokenStream;
2use quote::{format_ident, quote};
3use syn::{parse::Parse, parse_macro_input, DeriveInput, Ident, Lit, Meta, MetaList, Token, Type};
4
5struct SerdeCryptAttrStruct {
6 ident: Ident,
7 _punct: Token![=],
8 literal: Lit,
9}
10
11impl Parse for SerdeCryptAttrStruct {
12 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
13 Ok(SerdeCryptAttrStruct {
14 ident: input.parse()?,
15 _punct: input.parse()?,
16 literal: input.parse()?,
17 })
18 }
19}
20
21struct SerdeCryptTypes {
22 e: Type,
23 _punct: Token![,],
24 d: Type,
25}
26
27impl Parse for SerdeCryptTypes {
28 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
29 Ok(SerdeCryptTypes {
30 e: input.parse()?,
31 _punct: input.parse()?,
32 d: input.parse()?,
33 })
34 }
35}
36
37#[proc_macro_attribute]
38pub fn serde_crypt_gen(_meta: TokenStream, input: TokenStream) -> TokenStream {
39 let ast = parse_macro_input!(input as DeriveInput);
40 let vis = ast.vis;
41 let attrs = ast.attrs.iter().map(|e| quote! { #e }).reduce(|a, e| {
42 quote! {
43 #a
44 #e
45 }
46 });
47 let encrypted_ident = format_ident!("{}Encrypted", ast.ident);
48 let decrypted_ident = format_ident!("{}Decrypted", ast.ident);
49 let encrypted_fields = match &ast.data {
50 syn::Data::Struct(ref data_struct) => data_struct
51 .fields
52 .iter()
53 .map(|field| {
54 let ident = &field.ident;
55 let field_vis = &field.vis;
56 let mut replace = false;
57 let mut custom_types = false;
58 let mut ty = field.ty.clone();
59 let field_attrs = &field
60 .attrs
61 .iter()
62 .filter(|attr| {
63 if let Meta::List(MetaList { path, tokens, .. }) = &attr.meta {
64 let serde_tag: Result<SerdeCryptAttrStruct, _> =
65 syn::parse2(tokens.clone());
66 let crypt_types: Result<SerdeCryptTypes, _> =
67 syn::parse2(tokens.clone());
68 if serde_tag.is_ok() {
69 let tokens = serde_tag.unwrap();
70 let ident = tokens.ident.to_string();
71 let lit: String = match tokens.literal {
72 Lit::Str(val) => val.value(),
73 _ => return true,
74 };
75
76 if path.is_ident("serde") && ident == "with" && lit == "serde_crypt"
77 {
78 replace = true;
79 return false;
80 }
81 }
82 if crypt_types.is_ok() {
83 let tokens = crypt_types.unwrap();
84 let enc = tokens.e;
85 if path.is_ident("serde_crypt_types") {
86 custom_types = true;
87 replace = true;
88 ty = enc;
89 return false;
90 }
91 }
92 }
93 true
94 })
95 .map(|e| quote! {#e})
96 .reduce(|a, e| {
97 quote! {
98 #a
99 #e
100 }
101 });
102 if replace {
103 if custom_types {
104 quote! {
105 #field_attrs
106 #field_vis #ident: #ty
107 }
108 } else {
109 quote! {
110 #field_attrs
111 #field_vis #ident: String
112 }
113 }
114 } else {
115 quote! { #field }
116 }
117 })
118 .reduce(|a, e| {
119 return quote! {
120 #a,
121 #e
122 };
123 }),
124 _ => panic!("#[serde_crypt_gen] may only be used on structs"),
125 };
126 let decrypted_fields = match &ast.data {
127 syn::Data::Struct(ref data_struct) => data_struct
128 .fields
129 .iter()
130 .map(|field| {
131 let ident = &field.ident;
132 let field_vis = &field.vis;
133 let mut custom_types = false;
134 let mut ty = field.ty.clone();
135 let field_attrs = &field
136 .attrs
137 .iter()
138 .filter(|attr| {
139 if let Meta::List(MetaList { path, tokens, .. }) = &attr.meta {
140 let crypt_types: Result<SerdeCryptTypes, _> =
141 syn::parse2(tokens.clone());
142 if crypt_types.is_ok() {
143 let tokens = crypt_types.unwrap();
144 let dec = tokens.d;
145 if path.is_ident("serde_crypt_types") {
146 custom_types = true;
147 ty = dec;
148 return false;
149 }
150 }
151 }
152 true
153 })
154 .map(|e| quote! {#e})
155 .reduce(|a, e| {
156 quote! {
157 #a
158 #e
159 }
160 });
161 if custom_types {
162 quote! {
163 #field_attrs
164 #[serde(with = "serde_crypt")]
165 #field_vis #ident: #ty
166 }
167 } else {
168 quote! { #field }
169 }
170 })
171 .map(|e| quote! {#e})
172 .reduce(|a, e| {
173 quote! {
174 #a,
175 #e
176 }
177 }),
178 _ => panic!("#[serde_crypt_gen] may only be used on structs"),
179 };
180
181 let types = quote! {
182 #attrs
183 #vis struct #encrypted_ident {
184 #encrypted_fields
185 }
186
187 #attrs
188 #vis struct #decrypted_ident {
189 #decrypted_fields
190 }
191 };
192 types.into()
193}