minetest_protocol_derive/
lib.rs

1extern crate proc_macro2;
2extern crate quote;
3extern crate syn;
4
5use proc_macro2::Ident;
6use proc_macro2::Literal;
7use proc_macro2::TokenStream;
8use quote::quote;
9use quote::quote_spanned;
10use quote::ToTokens;
11use syn::parse_macro_input;
12use syn::punctuated::Punctuated;
13use syn::spanned::Spanned;
14use syn::Data;
15use syn::DeriveInput;
16use syn::Field;
17use syn::Generics;
18use syn::Index;
19use syn::Type;
20use syn::TypeParam;
21
22#[proc_macro_derive(MinetestSerialize, attributes(wrap))]
23pub fn minetest_serialize(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
24    let input = parse_macro_input!(input as DeriveInput);
25    let name = input.ident;
26    let serialize_body = make_serialize_body(&name, &input.data);
27
28    // The struct must include Serialize in the bounds of any type
29    // that need to be serializable.
30    let impl_generic = input.generics.to_token_stream();
31    let name_generic = strip_generic_bounds(&input.generics).to_token_stream();
32    let where_generic = input.generics.where_clause;
33
34    let expanded = quote! {
35        impl #impl_generic Serialize for #name #name_generic #where_generic {
36            type Input = Self;
37            fn serialize<S: Serializer>(value: &Self::Input, ser: &mut S) -> SerializeResult {
38                #serialize_body
39                Ok(())
40            }
41        }
42    };
43    proc_macro::TokenStream::from(expanded)
44}
45
46#[proc_macro_derive(MinetestDeserialize, attributes(wrap))]
47pub fn minetest_deserialize(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
48    let input = parse_macro_input!(input as DeriveInput);
49    let name = input.ident;
50    let deserialize_body = make_deserialize_body(&name, &input.data);
51
52    // The struct must include Deserialize in the bounds of any type
53    // that need to be serializable.
54    let impl_generic = input.generics.to_token_stream();
55    let name_generic = strip_generic_bounds(&input.generics).to_token_stream();
56    let where_generic = input.generics.where_clause;
57
58    let expanded = quote! {
59        impl #impl_generic Deserialize for #name #name_generic #where_generic {
60            type Output = Self;
61            fn deserialize(deser: &mut Deserializer) -> DeserializeResult<Self> {
62                #deserialize_body
63            }
64        }
65    };
66    proc_macro::TokenStream::from(expanded)
67}
68
69fn get_wrapped_type(f: &Field) -> Type {
70    let mut ty = f.ty.clone();
71    for attr in f.attrs.iter() {
72        if attr.path.is_ident("wrap") {
73            ty = attr.parse_args::<Type>().unwrap();
74        }
75    }
76    ty
77}
78
79/// For struct, fields are serialized/deserialized in order.
80/// For enum, tags are assumed u8, consecutive, starting with 0.
81fn make_serialize_body(input_name: &Ident, data: &Data) -> TokenStream {
82    match *data {
83        syn::Data::Struct(ref data) => match data.fields {
84            syn::Fields::Named(ref fields) => {
85                let recurse = fields.named.iter().map(|f| {
86                    let name = &f.ident;
87                    let ty = get_wrapped_type(f);
88                    quote_spanned! {f.span() =>
89                        <#ty as Serialize>::serialize(&value.#name, ser)?;
90                    }
91                });
92                quote! {
93                    #(#recurse)*
94                }
95            }
96            syn::Fields::Unnamed(ref fields) => {
97                let recurse = fields.unnamed.iter().enumerate().map(|(i, f)| {
98                    let index = Index::from(i);
99                    let ty = get_wrapped_type(f);
100                    quote_spanned! {f.span() =>
101                        <#ty as Serialize>::serialize(&value.#index, ser)?;
102                    }
103                });
104                quote! {
105                    #(#recurse)*
106                }
107            }
108            syn::Fields::Unit => {
109                quote! {}
110            }
111        },
112        syn::Data::Enum(ref body) => {
113            let recurse = body.variants.iter().enumerate().map(|(i, v)| {
114                if !v.fields.is_empty() {
115                    quote_spanned! {v.span() =>
116                        compile_error!("Cannot handle fields yet");
117                    }
118                } else if v.discriminant.is_some() {
119                    quote_spanned! {v.span() =>
120                        compile_error!("Cannot handle discrimiant yet");
121                    }
122                } else {
123                    let id = &v.ident;
124                    let i = Literal::u8_unsuffixed(i as u8);
125                    quote_spanned! {v.span() =>
126                        #id => #i,
127                    }
128                }
129            });
130            quote! {
131                    use #input_name::*;
132                    let tag = match value {
133                        #(#recurse)*
134                    };
135                    u8::serialize(&tag, ser)?;
136            }
137        }
138        syn::Data::Union(_) => unimplemented!(),
139    }
140}
141
142fn make_deserialize_body(input_name: &Ident, data: &Data) -> TokenStream {
143    match *data {
144        syn::Data::Struct(ref data) => {
145            let inner = match data.fields {
146                syn::Fields::Named(ref fields) => {
147                    let recurse = fields.named.iter().map(|f| {
148                        let name = &f.ident;
149                        let ty = get_wrapped_type(f);
150                        quote_spanned! {f.span() =>
151                            #name: <#ty as Deserialize>::deserialize(deser)?,
152                        }
153                    });
154                    quote! {
155                        #(#recurse)*
156                    }
157                }
158                syn::Fields::Unnamed(ref fields) => {
159                    let recurse = fields.unnamed.iter().enumerate().map(|(i, f)| {
160                        let index = Index::from(i);
161                        let ty = get_wrapped_type(f);
162                        quote_spanned! {f.span() =>
163                            #index: <#ty as Deserialize>::deserialize(deser)?,
164                        }
165                    });
166                    quote! {
167                        #(#recurse)*
168                    }
169                }
170                syn::Fields::Unit => {
171                    quote! {}
172                }
173            };
174            quote! {
175                Ok(Self {
176                    #inner
177                })
178            }
179        }
180        syn::Data::Enum(ref body) => {
181            let recurse = body.variants.iter().enumerate().map(|(i, v)| {
182                if !v.fields.is_empty() {
183                    quote_spanned! {v.span() =>
184                        compile_error!("Cannot handle fields yet");
185                    }
186                } else if v.discriminant.is_some() {
187                    quote_spanned! {v.span() =>
188                        compile_error!("Cannot handle discrimiant yet");
189                    }
190                } else {
191                    let id = &v.ident;
192                    let i = Literal::u8_unsuffixed(i as u8);
193                    quote_spanned! {v.span() =>
194                        #i => #id,
195
196                    }
197                }
198            });
199
200            let input_name_str = Literal::string(&input_name.to_string());
201            quote! {
202                    use #input_name::*;
203                    let tag = u8::deserialize(deser)?;
204                    Ok(match tag {
205                        #(#recurse)*
206                        _ => bail!("Invalid {} tag: {}", #input_name_str, tag),
207                    })
208            }
209        }
210        syn::Data::Union(_) => unimplemented!(),
211    }
212}
213
214/// Converts <T: Trait, S: Trait2> into <T, S>
215fn strip_generic_bounds(input: &Generics) -> Generics {
216    let input = input.clone();
217    Generics {
218        lt_token: input.lt_token,
219        params: {
220            let mut params = input.params.clone();
221            params.iter_mut().for_each(|v| {
222                *v = match v.clone() {
223                    syn::GenericParam::Type(v) => syn::GenericParam::Type(TypeParam {
224                        attrs: Vec::new(),
225                        ident: v.ident.clone(),
226                        colon_token: None,
227                        bounds: Punctuated::new(),
228                        eq_token: None,
229                        default: None,
230                    }),
231                    any => any,
232                }
233            });
234            params
235        },
236        gt_token: input.gt_token,
237        where_clause: None,
238    }
239}