livemod_derive/
lib.rs

1use proc_macro2::{Ident, Span, TokenStream};
2use quote::quote;
3use syn::{
4    parenthesized, parse::Parse, DeriveInput, Field, FieldsNamed, FieldsUnnamed, LitStr, Token,
5};
6
7#[proc_macro_derive(LiveMod, attributes(livemod))]
8pub fn livemod_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
9    let ast: DeriveInput = syn::parse(input).unwrap();
10
11    match ast.data {
12        syn::Data::Struct(st) => {
13            let struct_name = ast.ident;
14            let (
15                FieldsDerive {
16                    idents,
17                    default_values: _,
18                    representations,
19                    get_named_values,
20                    get_selves,
21                },
22                named,
23            ) = match st.fields {
24                syn::Fields::Named(fields) => (derive_fields_named(fields), true),
25                syn::Fields::Unnamed(fields) => (derive_fields_unnamed(fields), false),
26                syn::Fields::Unit => {
27                    let gen = quote! {
28                        compile_error!("Derive not supported on unit struct")
29                    };
30                    return gen.into();
31                }
32            };
33
34            let self_pattern = if named {
35                quote! { Self { #(#idents),* } }
36            } else {
37                quote! { Self ( #(#idents),* ) }
38            };
39
40            let gen = quote! {
41                #[automatically_derived]
42                impl ::livemod::LiveMod for #struct_name {
43                    fn repr_default(&self, target: ::livemod::ActionTarget) -> ::livemod::Namespaced<::livemod::Repr> {
44                        let #self_pattern = self;
45                        if let Some((field, field_target)) = target.strip_one_field() {
46                            match field {
47                                #(#get_named_values as &dyn ::livemod::LiveMod,)*
48                                _ => panic!("Unexpected value name!"),
49                            }.repr_default(field_target)
50                        } else {
51                            ::livemod::Namespaced::basic_structure_repr(
52                                stringify!(#struct_name),
53                                &[
54                                    #(#representations),*
55                                ],
56                            )
57                        }
58                    }
59
60                    fn accept(&mut self, target: ::livemod::ActionTarget, value: ::livemod::Parameter<::livemod::Value>) -> bool {
61                        let #self_pattern = self;
62                        if let Some((field, field_target)) = target.strip_one_field() {
63                            match field {
64                                #(#get_named_values as &mut dyn ::livemod::LiveMod,)*
65                                _ => panic!("Unexpected target!"),
66                            }.accept(field_target, value)
67                        } else {
68                            panic!("Unexpected value!")
69                        }
70                    }
71
72                    fn get_self(&self, target: ::livemod::ActionTarget) -> ::livemod::Parameter<::livemod::Value> {
73                        let #self_pattern = self;
74                        if let Some((field, field_target)) = target.strip_one_field() {
75                            match field {
76                                #(#get_named_values as &dyn ::livemod::LiveMod,)*
77                                _ => panic!("Unexpected value name!"),
78                            }.get_self(field_target)
79                        } else {
80                            ::livemod::Parameter::Namespaced(::livemod::Namespaced::basic_structure_value(&[
81                                #(#get_selves),*
82                            ]))
83                        }
84                    }
85                }
86            };
87            gen.into()
88        }
89        syn::Data::Enum(en) => {
90            let enum_name = ast.ident;
91
92            let mut variant_names = vec![];
93            let mut variant_fields = vec![];
94            let mut variant_get_named_values = vec![];
95            let mut variant_get_named_values_mut = vec![];
96            let mut variant_defaults = vec![];
97            let mut variant_get_selves = vec![];
98
99            for variant in en.variants {
100                let variant_name = variant.ident;
101                let variant_string = variant_name.to_string();
102                variant_names.push(variant_string.clone());
103                match variant.fields {
104                    syn::Fields::Named(fields) => {
105                        let FieldsDerive {
106                            idents,
107                            default_values,
108                            representations,
109                            get_named_values,
110                            get_selves,
111                        } = derive_fields_named(fields);
112                        let self_pattern = quote! {
113                            Self::#variant_name { #(#idents),* }
114                        };
115
116                        variant_fields
117                            .push(quote! { #self_pattern => vec![#(#representations),*] });
118                        variant_get_named_values.push(quote! { #self_pattern => match name { #(#get_named_values as &dyn ::livemod::LiveMod,)* _ => panic!("Unexpected value name!") } });
119                        variant_get_named_values_mut.push(quote! { #self_pattern => match name { #(#get_named_values as &mut dyn ::livemod::LiveMod,)* _ => panic!("Unexpected value name!") } });
120                        variant_defaults.push(quote! { #variant_string => Self::#variant_name { #(#idents: #default_values),* } });
121                        variant_get_selves.push(quote! {
122                            #self_pattern => ::livemod::Namespaced::new(
123                                vec![String::from("livemod"), String::from("enum")],
124                                <_ as ::std::iter::FromIterator<_>>::from_iter(::std::array::IntoIter::new([
125                                    (String::from("variant"), Parameter::String(String::from(#variant_string))),
126                                    (String::from("current"), Parameter::Namespaced(::livemod::Namespaced::fields_value(&[#(#get_selves),*]))),
127                                ]))
128                            )
129                        });
130                    }
131                    syn::Fields::Unnamed(fields) => {
132                        let FieldsDerive {
133                            idents,
134                            default_values,
135                            representations,
136                            get_named_values,
137                            get_selves,
138                        } = derive_fields_unnamed(fields);
139                        let self_pattern = quote! {
140                            Self::#variant_name ( #(#idents),* )
141                        };
142
143                        variant_fields
144                            .push(quote! { #self_pattern => vec![#(#representations),*] });
145                        variant_get_named_values.push(quote! { #self_pattern => match name { #(#get_named_values as &dyn ::livemod::LiveMod,)* _ => panic!("Unexpected value name!") } });
146                        variant_get_named_values_mut.push(quote! { #self_pattern => match name { #(#get_named_values as &mut dyn ::livemod::LiveMod,)* _ => panic!("Unexpected value name!") } });
147                        variant_defaults.push(quote! { #variant_string => Self::#variant_name ( #(#default_values),* ) });
148                        variant_get_selves.push(quote! {
149                            #self_pattern => ::livemod::Namespaced::new(
150                                vec![String::from("livemod"), String::from("enum")],
151                                <_ as ::std::iter::FromIterator<_>>::from_iter(::std::array::IntoIter::new([
152                                    (String::from("variant"), Parameter::String(String::from(#variant_string))),
153                                    (String::from("current"), Parameter::Namespaced(::livemod::Namespaced::fields_value(&[#(#get_selves),*]))),
154                                ]))
155                            )
156                        });
157                    }
158                    syn::Fields::Unit => {
159                        variant_fields.push(quote! { Self::#variant_name => vec![] });
160                        variant_get_named_values.push(
161                            quote! { Self::#variant_name => panic!("Unexpected value name!") },
162                        );
163                        variant_get_named_values_mut.push(
164                            quote! { Self::#variant_name => panic!("Unexpected value name!") },
165                        );
166                        variant_defaults.push(quote! { #variant_string => Self::#variant_name });
167                        variant_get_selves.push(quote! { Self::#variant_name => ::livemod::Namespaced::new(vec![String::from("livemod"), String::from("enum")], <_ as ::std::iter::FromIterator<_>>::from_iter(::std::array::IntoIter::new([(String::from("variant"), Parameter::String(String::from(#variant_string)))]))) });
168                    }
169                }
170            }
171
172            let gen = quote! {
173                #[automatically_derived]
174                impl ::livemod::LiveMod for #enum_name {
175                    fn repr_default(&self, target: ::livemod::ActionTarget) -> ::livemod::Namespaced<::livemod::Repr> {
176                        if let Some((name, field_target)) = target.strip_one_field() {
177                            match self {
178                                #(#variant_get_named_values ,)*
179                            }.repr_default(field_target)
180                        } else {
181                            ::livemod::Namespaced::new(
182                                vec![
183                                    String::from("livemod"),
184                                    String::from("enum"),
185                                ],
186                                <_ as ::std::iter::FromIterator<_>>::from_iter(::std::array::IntoIter::new([
187                                    (String::from("name"), Parameter::String(String::from(stringify!(#enum_name)))),
188                                    (
189                                        String::from("variants"),
190                                        Parameter::Namespaced(Namespaced::new(
191                                            vec![String::from("livemod"), String::from("variants")],
192                                            <_ as ::std::iter::FromIterator<_>>::from_iter(
193                                                ::std::array::IntoIter::new([
194                                                    #(#variant_names),*
195                                                ])
196                                                .enumerate()
197                                                .map(|(i, variant_name)| {
198                                                    (i.to_string(), Parameter::String(variant_name.to_string()))
199                                                })
200                                            ),
201                                        )),
202                                    ),
203                                    (
204                                        String::from("current"),
205                                        Parameter::Namespaced(Namespaced::new(
206                                            vec![String::from("livemod"), String::from("fields")],
207                                            //FIXME: If anybody can help me with the unneccesary heap allocation in here, please do so. I'm sick of macros.
208                                            <_ as ::std::iter::FromIterator<_>>::from_iter(match self {
209                                                #(#variant_fields ,)*
210                                            }.into_iter().map(|(s, n)| (s, ::livemod::Parameter::Namespaced(n))))
211                                        )),
212                                    )
213                                ]))
214                            )
215                        }
216                    }
217
218                    fn accept(&mut self, target: ::livemod::ActionTarget, value: ::livemod::Parameter<::livemod::Value>) -> bool {
219                        if let Some((name, field_target)) = target.strip_one_field() {
220                            if name == "variant" {
221                                let variant_name = value.as_string().unwrap();
222                                *self = match variant_name.as_str() {
223                                    #(#variant_defaults ,)*
224                                    name => panic!("Unknown variant name: {}", name)
225                                };
226                                true
227                            } else {
228                                if let Some((name, field_target)) = field_target.strip_one_field() {
229                                    match self {
230                                        #(#variant_get_named_values_mut ,)*
231                                    }.accept(field_target, value)
232                                } else {
233                                    unimplemented!()
234                                }
235                            }
236                        } else {
237                            unimplemented!()
238                        }
239                    }
240
241                    fn get_self(&self, target: ActionTarget) -> ::livemod::Parameter<::livemod::Value> {
242                        if let Some((name, field_target)) = target.strip_one_field() {
243                            match self {
244                                #(#variant_get_named_values ,)*
245                            }.get_self(field_target)
246                        } else {
247                            ::livemod::Parameter::Namespaced(match self {
248                                #(#variant_get_selves ,)*
249                            })
250                        }
251                    }
252                }
253            };
254            gen.into()
255        }
256        syn::Data::Union(_) => {
257            let gen = quote! {
258                compile_error!("Derive not supported on union")
259            };
260            gen.into()
261        }
262    }
263}
264
265struct FieldsDerive {
266    idents: Vec<Ident>,
267    default_values: Vec<TokenStream>,
268    representations: Vec<TokenStream>,
269    get_named_values: Vec<TokenStream>,
270    get_selves: Vec<TokenStream>,
271}
272
273struct FieldDerive {
274    ident: Ident,
275    default_value: TokenStream,
276    representation: Option<TokenStream>,
277    get_named_value: Option<TokenStream>,
278    get_self: Option<TokenStream>,
279}
280
281fn derive_fields_named(fields: FieldsNamed) -> FieldsDerive {
282    let iter = fields.named.into_iter().map(|field| {
283        let ident = field.ident.clone().unwrap();
284        let name = ident.to_string();
285        derive_field(ident, name, field)
286    });
287
288    let mut gen = FieldsDerive {
289        idents: Vec::new(),
290        default_values: Vec::new(),
291        representations: Vec::new(),
292        get_named_values: Vec::new(),
293        get_selves: Vec::new(),
294    };
295
296    for field in iter {
297        gen.idents.push(field.ident);
298        gen.default_values.push(field.default_value);
299        gen.representations.extend(field.representation);
300        gen.get_named_values.extend(field.get_named_value);
301        gen.get_selves.extend(field.get_self);
302    }
303
304    gen
305}
306
307fn derive_fields_unnamed(fields: FieldsUnnamed) -> FieldsDerive {
308    let iter = fields.unnamed.into_iter().enumerate().map(|(i, field)| {
309        let ident = Ident::new(&format!("__{}", i), Span::call_site());
310        let name = i.to_string();
311        derive_field(ident, name, field)
312    });
313
314    let mut gen = FieldsDerive {
315        idents: Vec::new(),
316        default_values: Vec::new(),
317        representations: Vec::new(),
318        get_named_values: Vec::new(),
319        get_selves: Vec::new(),
320    };
321
322    for field in iter {
323        gen.idents.push(field.ident);
324        gen.default_values.push(field.default_value);
325        gen.representations.extend(field.representation);
326        gen.get_named_values.extend(field.get_named_value);
327        gen.get_selves.extend(field.get_self);
328    }
329
330    gen
331}
332
333fn derive_field(ident: Ident, default_name: String, field: Field) -> FieldDerive {
334    let attrs = match field
335        .attrs
336        .into_iter()
337        .filter_map(|attr| {
338            if attr.path.is_ident("livemod") {
339                Some(syn::parse2(attr.tokens))
340            } else {
341                None
342            }
343        })
344        .collect::<Result<Vec<_>, _>>()
345    {
346        Ok(attrs) => attrs,
347        Err(error) => {
348            return FieldDerive {
349                ident,
350                default_value: error.to_compile_error(),
351                representation: None,
352                get_named_value: None,
353                get_self: None,
354            };
355        }
356    };
357
358    let default_value = if let Some(default) = attrs.iter().find_map(|attr| match attr {
359        Attr::Default(ts) => Some(ts),
360        _ => None,
361    }) {
362        default.clone()
363    } else {
364        quote! { ::std::default::Default::default() }
365    };
366
367    let name = if let Some(name) = attrs.iter().find_map(|attr| match attr {
368        Attr::Rename(name) => Some(name),
369        _ => None,
370    }) {
371        name.clone()
372    } else {
373        default_name
374    };
375
376    let (representation, get_named_value, get_self) = if attrs
377        .iter()
378        .any(|attr| matches!(attr, Attr::Skip))
379    {
380        (None, None, None)
381    } else {
382        let default_repr = quote! { ::livemod::DefaultRepr };
383        let repr_struct = attrs
384            .iter()
385            .find_map(|attr| match attr {
386                Attr::Repr(ts) => Some(ts),
387                _ => None,
388            })
389            .unwrap_or(&default_repr);
390        let representation = quote! {
391            (#name.to_owned(), ::livemod::LiveModRepr::repr(&#repr_struct, #ident))
392        };
393
394        let get_named_value = quote! { #name => #ident };
395        let get_self = quote! { (#name.to_owned(), ::livemod::LiveMod::get_self(#ident, ::livemod::ActionTarget::This)) };
396        (Some(representation), Some(get_named_value), Some(get_self))
397    };
398
399    FieldDerive {
400        ident,
401        default_value,
402        representation,
403        get_named_value,
404        get_self,
405    }
406}
407
408enum Attr {
409    Skip,
410    Rename(String),
411    Repr(TokenStream),
412    Default(TokenStream),
413}
414
415impl Parse for Attr {
416    fn parse(direct_input: syn::parse::ParseStream) -> syn::Result<Self> {
417        let input;
418        parenthesized!(input in direct_input);
419        let attr_type: Ident = input.parse()?;
420        if attr_type == "skip" {
421            if !input.is_empty() {
422                return Err(input.error("Expected end of attribute content"));
423            }
424            Ok(Attr::Skip)
425        } else if attr_type == "rename" {
426            input.parse::<Token![=]>()?;
427            let new_name: LitStr = input.parse()?;
428            Ok(Attr::Rename(new_name.value()))
429        } else if attr_type == "repr" {
430            input.parse::<Token![=]>()?;
431            Ok(Attr::Repr(input.parse()?))
432        } else if attr_type == "default" {
433            input.parse::<Token![=]>()?;
434            Ok(Attr::Default(input.parse()?))
435        } else {
436            Err(syn::Error::new(
437                attr_type.span(),
438                "Unrecognised attribute tag",
439            ))
440        }
441    }
442}