battler_wamp_values_proc_macro/
lib.rs1extern crate proc_macro;
2
3use itertools::Itertools;
4use proc_macro::TokenStream;
5use proc_macro2::Span;
6use quote::quote;
7use syn::{
8 Error,
9 Field,
10 Ident,
11 Index,
12 ItemStruct,
13 Meta,
14 Path,
15 Type,
16 parse::{
17 Parse,
18 ParseStream,
19 },
20 parse_macro_input,
21};
22
23#[derive(Default)]
24enum DefaultAttr {
25 #[default]
26 False,
27 True,
28 Path(Path),
29}
30
31impl DefaultAttr {
32 pub fn can_be_default(&self) -> bool {
33 match self {
34 Self::False => false,
35 _ => true,
36 }
37 }
38}
39
40#[derive(Default)]
41struct InputFieldAttrs {
42 default: DefaultAttr,
43 skip_serializing_if: Option<Path>,
44}
45
46fn parse_input_field_attrs(field: &Field) -> syn::Result<InputFieldAttrs> {
47 let attr = field.attrs.iter().find(|attr| {
48 if let Meta::List(list) = &attr.meta {
49 if list.path.is_ident("battler_wamp_values") {
50 return true;
51 }
52 }
53 false
54 });
55 let attr = match attr {
56 Some(attr) => attr,
57 None => return Ok(InputFieldAttrs::default()),
58 };
59
60 let mut default = DefaultAttr::False;
61 let mut skip_serializing_if = None;
62 attr.parse_nested_meta(|meta| {
63 if meta.path.is_ident("default") {
64 default = match meta.value() {
65 Ok(value) => DefaultAttr::Path(value.parse::<Path>()?),
66 Err(_) => DefaultAttr::True,
67 };
68 }
69 if meta.path.is_ident("skip_serializing_if") {
70 let value = meta.value()?;
71 skip_serializing_if = Some(value.parse::<Path>()?);
72 }
73 Ok(())
74 })?;
75 Ok(InputFieldAttrs {
76 default,
77 skip_serializing_if,
78 })
79}
80
81struct InputField {
82 ident: Option<Ident>,
83 ty: Type,
84 attrs: InputFieldAttrs,
85}
86
87struct ListInput {
88 ident: Ident,
89 fields: Vec<InputField>,
90}
91
92impl Parse for ListInput {
93 fn parse(input: ParseStream) -> syn::Result<Self> {
94 let call_site = Span::call_site();
95 let input = match ItemStruct::parse(input) {
96 Ok(item) => item,
97 Err(_) => return Err(Error::new(call_site, "input must be a struct")),
98 };
99 let ident = input.ident;
100 let mut defaulted = false;
101 let fields = input
102 .fields
103 .into_iter()
104 .map(|field| {
105 let attrs = parse_input_field_attrs(&field)?;
106 if attrs.default.can_be_default() {
107 defaulted = true;
108 } else if defaulted {
109 return Err(Error::new(
110 call_site,
111 "fields after a default field must also have a default",
112 ));
113 }
114 Ok(InputField {
115 ident: field.ident,
116 ty: field.ty,
117 attrs,
118 })
119 })
120 .collect::<Result<Vec<_>, Error>>()?;
121 Ok(Self { ident, fields })
122 }
123}
124
125#[proc_macro_derive(WampList, attributes(battler_wamp_values))]
129pub fn derive_wamp_list(input: TokenStream) -> TokenStream {
130 let input = parse_macro_input!(input as ListInput);
131 let call_site = Span::call_site();
132
133 let ident = input.ident;
134
135 let (field_serializers, field_deserializers, field_identifiers): (Vec<_>, Vec<_>, Vec<_>) = input.fields.iter().enumerate().map(|(i, field)| {
136 let accessor = match &field.ident {
137 Some(ident) => quote!(self.#ident),
138 None => { let i = Index::from(i); quote!(self.#i) },
139 };
140 let ty = &field.ty;
141 let field_name = field.ident.clone().unwrap_or(Ident::new(&format!("field_{i}"), call_site));
142 let serialize_check = match &field.attrs.skip_serializing_if {
143 Some(skip_serializing_if) => Some(quote! {
144 if #skip_serializing_if(&#accessor) {
145 return Ok(battler_wamp_values::Value::List(list));
146 }
147 }),
148 None => None,
149 };
150 let if_empty = match &field.attrs.default {
151 DefaultAttr::False => quote!(return Err(battler_wamp_values::WampDeserializeError::new(std::fmt::format(format_args!("list member {} of {} is missing", std::stringify!(#field_name), std::stringify!(#ident)))))),
152 DefaultAttr::True => quote!(<#ty as Default>::default()),
153 DefaultAttr::Path(path) => quote!(#path()),
154 };
155 (
156 quote! {
157 #serialize_check
158 match battler_wamp_values::WampSerialize::wamp_serialize(#accessor) {
159 Ok(val) => list.push(val),
160 Err(err) => return Err(err.annotate(std::fmt::format(format_args!("failed to serialize list member {} of {}", std::stringify!(#field_name), std::stringify!(#ident))))),
161 }
162 },
163 quote! {
164 let #field_name = match list.get_mut(#i) {
165 Some(val) => {
166 let mut out = battler_wamp_values::Value::Bool(false);
167 std::mem::swap(val, &mut out);
168 Some(out)
169 }
170 None => None,
171 };
172 let #field_name = match #field_name {
173 Some(val) => match battler_wamp_values::WampDeserialize::wamp_deserialize(val) {
174 Ok(val) => val,
175 Err(err) => return Err(err.annotate(std::fmt::format(format_args!("failed to deserialize list member {} of {}", std::stringify!(#field_name), std::stringify!(#ident)))))
176 },
177 None => #if_empty,
178 };
179 },
180 quote!(#field_name)
181 )
182 }).multiunzip();
183
184 let serialize = quote! {
185 impl battler_wamp_values::WampSerialize for #ident {
186 fn wamp_serialize(self) -> core::result::Result<battler_wamp_values::Value, battler_wamp_values::WampSerializeError> {
187 let mut list = battler_wamp_values::List::default();
188 #(#field_serializers)*
189 Ok(battler_wamp_values::Value::List(list))
190 }
191 }
192 };
193
194 let named = input.fields.is_empty() || input.fields.iter().any(|field| field.ident.is_some());
195 let struct_constructor = if named {
196 quote!(#ident { #(#field_identifiers,)* })
197 } else {
198 quote!(#ident(#(#field_identifiers,)*))
199 };
200 let deserialize = quote! {
201 impl battler_wamp_values::WampDeserialize for #ident {
202 fn wamp_deserialize(value: battler_wamp_values::Value) -> core::result::Result<Self, battler_wamp_values::WampDeserializeError> {
203 let mut list = match value {
204 battler_wamp_values::Value::List(list) => list,
205 _ => return Err(battler_wamp_values::WampDeserializeError::new("value must be a list")),
206 };
207 #(#field_deserializers)*
208 Ok(#struct_constructor)
209 }
210 }
211 };
212
213 quote! {
214 #serialize
215 #deserialize
216 }
217 .into()
218}
219
220struct DictionaryInput {
221 ident: Ident,
222 fields: Vec<InputField>,
223}
224
225impl Parse for DictionaryInput {
226 fn parse(input: ParseStream) -> syn::Result<Self> {
227 let call_site = Span::call_site();
228 let input = match ItemStruct::parse(input) {
229 Ok(item) => item,
230 Err(_) => return Err(Error::new(call_site, "input must be a struct")),
231 };
232 let ident = input.ident;
233 let fields = input
234 .fields
235 .into_iter()
236 .map(|field| {
237 let attrs = parse_input_field_attrs(&field)?;
238 Ok(InputField {
239 ident: field.ident,
240 ty: field.ty,
241 attrs,
242 })
243 })
244 .collect::<Result<Vec<_>, Error>>()?;
245 Ok(Self { ident, fields })
246 }
247}
248
249#[proc_macro_derive(WampDictionary, attributes(battler_wamp_values))]
253pub fn derive_wamp_dictionary(input: TokenStream) -> TokenStream {
254 let input = parse_macro_input!(input as DictionaryInput);
255 let call_site = Span::call_site();
256
257 let ident = input.ident;
258
259 let (field_serializers, field_deserializers, field_identifiers): (Vec<_>, Vec<_>, Vec<_>) = input.fields.iter().enumerate().map(|(i, field)| {
260 let accessor = match &field.ident {
261 Some(ident) => quote!(self.#ident),
262 None => { let i = Index::from(i); quote!(self.#i) },
263 };
264 let ty = &field.ty;
265 let field_name = field.ident.clone().unwrap_or(Ident::new(&format!("field_{i}"), call_site));
266 let serialize_check = match &field.attrs.skip_serializing_if {
267 Some(skip_serializing_if) => quote!(!#skip_serializing_if(&#accessor)),
268 None => quote!(true),
269 };
270 let if_empty = match &field.attrs.default {
271 DefaultAttr::False => quote!(return Err(battler_wamp_values::WampDeserializeError::new(std::fmt::format(format_args!("dictionary member {} of {} is missing", std::stringify!(#field_name), std::stringify!(#ident)))))),
272 DefaultAttr::True => quote!(<#ty as Default>::default()),
273 DefaultAttr::Path(path) => quote!(#path()),
274 };
275 (
276 quote! {
277 if #serialize_check {
278 match battler_wamp_values::WampSerialize::wamp_serialize(#accessor) {
279 Ok(val) => dict.insert(stringify!(#field_name).to_owned(), val),
280 Err(err) => return Err(err.annotate(std::fmt::format(format_args!("failed to serialize dictionary member {} of {}", std::stringify!(#field_name), std::stringify!(#ident))))),
281 };
282 }
283 },
284 quote! {
285 let #field_name = match dict.get_mut(stringify!(#field_name)) {
286 Some(val) => {
287 let mut out = battler_wamp_values::Value::Bool(false);
288 std::mem::swap(val, &mut out);
289 Some(out)
290 }
291 None => None,
292 };
293 let #field_name = match #field_name {
294 Some(val) => match battler_wamp_values::WampDeserialize::wamp_deserialize(val) {
295 Ok(val) => val,
296 Err(err) => return Err(err.annotate(std::fmt::format(format_args!("failed to deserialize dictionary member {} of {}", std::stringify!(#field_name), std::stringify!(#ident)))))
297 },
298 None => #if_empty,
299 };
300 },
301 quote!(#field_name)
302 )
303 }).multiunzip();
304
305 let serialize = quote! {
306 impl battler_wamp_values::WampSerialize for #ident {
307 fn wamp_serialize(self) -> core::result::Result<battler_wamp_values::Value, battler_wamp_values::WampSerializeError> {
308 let mut dict = battler_wamp_values::Dictionary::default();
309 #(#field_serializers)*
310 Ok(battler_wamp_values::Value::Dictionary(dict))
311 }
312 }
313 };
314
315 let named = input.fields.is_empty() || input.fields.iter().any(|field| field.ident.is_some());
316 let struct_constructor = if named {
317 quote!(#ident { #(#field_identifiers,)* })
318 } else {
319 quote!(#ident(#(#field_identifiers,)*))
320 };
321 let deserialize = quote! {
322 impl battler_wamp_values::WampDeserialize for #ident {
323 fn wamp_deserialize(value: battler_wamp_values::Value) -> core::result::Result<Self, battler_wamp_values::WampDeserializeError> {
324 let mut dict = match value {
325 battler_wamp_values::Value::Dictionary(dict) => dict,
326 _ => return Err(battler_wamp_values::WampDeserializeError::new("value must be a list")),
327 };
328 #(#field_deserializers)*
329 Ok(#struct_constructor)
330 }
331 }
332 };
333
334 quote! {
335 #serialize
336 #deserialize
337 }
338 .into()
339}