castep_param_derive/
lib.rs1use 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 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 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 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}