castep_param_derive/
lib.rs

1use darling::{FromAttributes, FromDeriveInput};
2use proc_macro::{self, TokenStream};
3use quote::quote;
4use syn::{parse_macro_input, DataEnum, DeriveInput, Expr, Ident};
5
6#[derive(FromAttributes, Default)]
7#[darling(default, attributes(param_display))]
8struct ParamFieldOpt {
9    use_ref: Option<bool>,
10    display: Option<Expr>,
11}
12
13#[derive(FromDeriveInput, Default)]
14#[darling(default, attributes(param_display))]
15struct ParamOpt {
16    use_display: Option<bool>,
17}
18
19#[proc_macro_derive(ParamDisplay, attributes(param_display))]
20pub fn derive_param_display(input: TokenStream) -> TokenStream {
21    let input = parse_macro_input!(input as DeriveInput);
22    let ident = &input.ident;
23    let struct_opts = ParamOpt::from_derive_input(&input).expect("Wrong options");
24    let data = if let syn::Data::Struct(data) = &input.data {
25        data
26    } else {
27        unimplemented!()
28    };
29    let fields = data.fields.iter().map(|f| {
30        let name = f.ident.as_ref().unwrap();
31        let opts = ParamFieldOpt::from_attributes(&f.attrs).expect("Wrong option");
32        let field = match opts.use_ref {
33            Some(true) => quote! {
34                self.#name.as_ref()
35            },
36            Some(false) | None => quote! {
37                self.#name
38            },
39        };
40        let output_func = if let Some(true) = struct_opts.use_display {
41            quote! {
42                to_string()
43            }
44        } else {
45            match opts.display {
46                Some(e) => quote! {
47                    #e
48                },
49                None => quote! {
50                    output()
51                },
52            }
53        };
54        quote! {
55            #field.map(|v| v.#output_func)
56        }
57    });
58    let expanded = quote! {
59        impl std::fmt::Display for #ident {
60            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61                let output = [
62                    #(#fields, )*
63                ].into_iter().flatten().collect::<Vec<String>>().join("\n");
64                write!(f, "{output}")
65            }
66        }
67    };
68    expanded.into()
69}
70
71#[derive(FromDeriveInput, Default)]
72#[darling(default, attributes(keyword_display))]
73struct Opts {
74    field: String,
75    specified_fields: Option<bool>,
76    direct_display: Option<bool>,
77    display_format: Option<String>,
78    from: Option<Ident>,
79    value: Option<Ident>,
80    borrowed_value: Option<Ident>,
81    default_value: Option<Expr>,
82}
83
84#[derive(FromAttributes, Default)]
85#[darling(default, attributes(keyword_display))]
86struct EnumAttrs {
87    field: String,
88    display_format: Option<String>,
89}
90
91fn data_enum_display_impl(data_enum: &DataEnum, struct_ident: &Ident) -> proc_macro2::TokenStream {
92    let variants = data_enum.variants.iter().map(|v| {
93        let name = &v.ident;
94        let opts = EnumAttrs::from_attributes(&v.attrs).expect("Wrong attrs");
95        let display_format = if let Some(s) = opts.display_format {
96            quote!(#s)
97        } else {
98            quote!("{}")
99        };
100        match &v.fields {
101            // Don't expect this in enum
102            syn::Fields::Named(_) => unimplemented!(),
103            syn::Fields::Unnamed(_) => quote! {
104            #struct_ident::#name(t) => write!(f, #display_format, t)},
105            syn::Fields::Unit => quote! {#struct_ident::#name => write!(f, "{:?}", self)},
106        }
107    });
108    quote! {
109        impl std::fmt::Display for #struct_ident {
110            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
111                match self {
112                    #(#variants,)*
113                }
114            }
115        }
116    }
117}
118
119fn data_enum_field_impl(data_enum: &DataEnum, struct_ident: &Ident) -> proc_macro2::TokenStream {
120    let variants = data_enum.variants.iter().map(|v| {
121        let name = &v.ident;
122        let variant_expr = match v.fields {
123            // Don't expect this in enum
124            syn::Fields::Named(_) => unimplemented!(),
125            syn::Fields::Unnamed(_) => quote! {#struct_ident::#name(_)},
126            syn::Fields::Unit => quote! {#struct_ident::#name},
127        };
128        let opts = EnumAttrs::from_attributes(&v.attrs).expect("Wrong attrs");
129        let field = opts.field;
130        quote! {
131            #variant_expr => #field.to_string()
132        }
133    });
134    quote! {
135        fn field(&self) -> String {
136            match self {
137                #(#variants,)*
138            }
139        }
140    }
141}
142
143#[derive(FromAttributes, Default)]
144#[darling(default, attributes(keyword_display))]
145struct FieldAttrs {
146    is_option: Option<bool>,
147}
148
149#[proc_macro_derive(KeywordDisplayStruct, attributes(keyword_display))]
150pub fn derive_struct(input: TokenStream) -> TokenStream {
151    let input = parse_macro_input!(input);
152    let opts = Opts::from_derive_input(&input).expect("Wrong option");
153    let DeriveInput { ident, .. } = input;
154    let field = opts.field;
155    let field_text = quote! {
156        fn field(&self) -> String {
157            #field.to_string()
158        }
159    };
160    let fields = match &input.data {
161        syn::Data::Struct(data_struct) => &data_struct.fields,
162        syn::Data::Enum(_) => unimplemented!(),
163        syn::Data::Union(_) => unimplemented!(),
164    };
165    let formatted_fields = fields.iter().map(|f| {
166        let opts = FieldAttrs::from_attributes(&f.attrs).expect("Wrong attrs");
167        let ident = f
168            .ident
169            .as_ref()
170            .expect("Tuple struct does not have named field");
171        if let Some(true) = opts.is_option {
172            quote! {self.#ident.map(|v| v.output()).unwrap_or_default()}
173        } else {
174            quote! {self.#ident}
175        }
176    });
177    let expr = opts.display_format.unwrap_or("{}".to_string());
178    let display_impl = quote! {
179    use crate::param::KeywordDisplay;
180    impl std::fmt::Display for #ident {
181        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
182            write!(f, #expr, #(#formatted_fields), *)
183        }
184    }
185    };
186    let derive_from = match opts.from {
187        Some(x) => {
188            let field_values = fields.iter().map(|f| {
189                let opts = FieldAttrs::from_attributes(&f.attrs).expect("Wrong attrs");
190                let ident = f
191                    .ident
192                    .as_ref()
193                    .expect("Tuple struct does not have named field");
194                if let Some(true) = opts.is_option {
195                    quote! {#ident: Default::default()}
196                } else {
197                    quote! {#ident: value }
198                }
199            });
200            quote! {
201            impl From<#x> for #ident {
202                fn from(value: #x) -> #ident {
203                        #ident {
204                        #(#field_values), *
205                    }
206                }
207            }
208            }
209        }
210        None => quote! {},
211    };
212
213    let default = if let Some(x) = opts.default_value {
214        let field_values = fields.iter().map(|f| {
215            let opts = FieldAttrs::from_attributes(&f.attrs).expect("Wrong attrs");
216            let ident = f
217                .ident
218                .as_ref()
219                .expect("Tuple struct does not have named field");
220            if let Some(true) = opts.is_option {
221                quote! {#ident: Default::default()}
222            } else {
223                quote! {#ident: #x }
224            }
225        });
226        quote! {
227        impl Default for #ident {
228            fn default() -> #ident {
229                #ident {
230                    #(#field_values), *
231                }
232            }
233        }
234        }
235    } else {
236        quote! {}
237    };
238    let output = quote! {
239        impl crate::param::KeywordDisplay for #ident {
240            #field_text
241        }
242        #display_impl
243        #derive_from
244        #default
245    };
246    output.into()
247}
248
249#[proc_macro_derive(KeywordDisplay, attributes(keyword_display))]
250pub fn derive(input: TokenStream) -> TokenStream {
251    let input = parse_macro_input!(input);
252    let opts = Opts::from_derive_input(&input).expect("Wrong option");
253    let DeriveInput { ident, .. } = input;
254    let field = opts.field;
255    let field_text = match &input.data {
256        syn::Data::Struct(_) => {
257            quote! {
258                fn field(&self) -> String {
259                    #field.to_string()
260                }
261            }
262        }
263        syn::Data::Enum(data_enum) => {
264            if let Some(true) = opts.specified_fields {
265                data_enum_field_impl(data_enum, &ident)
266            } else {
267                quote! {
268                    fn field(&self) -> String {
269                        #field.to_string()
270                    }
271                }
272            }
273        }
274        syn::Data::Union(_) => unimplemented!(),
275    };
276
277    let from = match opts.from {
278        Some(x) => quote! {
279            impl From<#x> for #ident {
280                fn from(value: #x) -> Self {
281                    Self(value)
282                }
283            }
284        },
285        None => quote! {},
286    };
287
288    let value = match opts.value {
289        Some(x) => quote! {
290            impl #ident {
291                pub fn value(&self) -> #x {
292                    self.0
293                }
294            }
295        },
296        None => quote! {},
297    };
298
299    let borrowed_value = if let Some(x) = opts.borrowed_value {
300        quote! {
301            impl #ident {
302                pub fn value(&self) -> &#x {
303                    &self.0
304                }
305            }
306        }
307    } else {
308        quote! {}
309    };
310
311    let direct_display = if let Some(false) = opts.direct_display {
312        quote! {}
313    } else {
314        let expr = if let Some(expr) = opts.display_format {
315            quote! {#expr}
316        } else {
317            quote! {"{}"}
318        };
319        match &input.data {
320            syn::Data::Struct(_) => {
321                quote! {
322                impl std::fmt::Display for #ident {
323                    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
324                        write!(f, #expr, self.0)
325                    }
326                }
327                }
328            }
329            syn::Data::Enum(data_enum) => data_enum_display_impl(data_enum, &ident),
330            // We don't use union so far
331            syn::Data::Union(_) => unimplemented!(),
332        }
333    };
334    let default = if let syn::Data::Struct(data_struct) = &input.data {
335        if let syn::Fields::Unnamed(fields) = &data_struct.fields {
336            if let Some(x) = opts.default_value {
337                let values = fields.unnamed.iter().map(|_| quote! {#x});
338                quote! {
339                impl Default for #ident {
340                    fn default() -> #ident {
341                        #ident(#(#values),*)
342                    }
343                }
344                }
345            } else {
346                quote! {}
347            }
348        } else {
349            quote! {}
350        }
351    } else {
352        quote! {}
353    };
354
355    let output = quote! {
356        impl crate::param::KeywordDisplay for #ident {
357            #field_text
358        }
359        #from
360        #value
361        #borrowed_value
362        #direct_display
363        #default
364    };
365    output.into()
366}