1use convert_case::{Casing, Case};
2use proc_macro2::{Span, Ident, TokenStream};
3use quote::{quote, TokenStreamExt, format_ident};
4use syn_v2::{DeriveInput, Data, spanned::Spanned, Field, parse_macro_input};
5
6use derive_attribute_utils::{TryFromMeta, Syn2, ArgResult, Error, ErrorMsg::{*, self}, SynVersion, Concat, GetSpan, AttributeName, Attribute, CustomArgFromMeta, CustomArg};
7
8#[proc_macro_derive(Attribute, attributes(attr))]
9pub fn derive_attribute(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
10
11 let ast = parse_macro_input!(input as DeriveInput);
12
13 let maybe_output = attempt_derive_attr(ast);
14 let output =
15 match maybe_output {
16 Ok(output) => output,
17 Err(errors) => {
18 let compile_errors = errors.into_iter().map(|e| e.to_compile_error());
19 quote!(#(#compile_errors)*)
20 }
21 };
22
23 output.into()
24}
25fn attempt_derive_attr(ast: DeriveInput) -> Result<TokenStream, Vec<syn_v2::Error>> {
26 let mut all_errors = vec![];
27
28 let maybe_container_attr = AttributeAttribute::from_attrs(ast.ident.span(), ast.attrs)?;
29
30 let struct_data =
31 match ast.data {
32 Data::Struct(struct_date) => struct_date,
33 _ => {
34 all_errors.push(syn_v2::Error::new(ast.ident.span(), "Invalid body expected struct"));
35 return Err(all_errors)
36 }
37 };
38
39 let mut builder = AttributeTraitBuilder::new(ast.ident, maybe_container_attr);
40
41
42
43 for field in struct_data.fields {
44 let field_attr =
45 match AttributeAttribute::from_attrs(field.ident.span(), field.attrs.clone()) {
46 Ok(attr) => attr,
47 Err(ref mut errors) => {
48 all_errors.append(errors);
49 continue;
50 }
51 };
52
53 builder.check_field(field, field_attr);
54
55 }
56
57 let output = builder.build();
58
59 match all_errors.len() {
60 0 => Ok(output),
61 _ => Err(all_errors)
62 }
63}
64
65
66
67#[proc_macro_derive(List, attributes(attr))]
68pub fn derive_list(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
69
70 let ast = parse_macro_input!(input as DeriveInput);
71
72 let maybe_output = attempt_derive_list(ast);
73 let output =
74 match maybe_output {
75 Ok(output) => output,
76 Err(errors) => {
77 let compile_errors = errors.into_iter().map(|e| e.to_compile_error());
78 quote!(#(#compile_errors)*)
79 }
80 };
81
82 output.into()
83}
84fn attempt_derive_list(ast: DeriveInput) -> Result<TokenStream, Vec<syn_v2::Error>> {
85 let mut all_errors = vec![];
86
87 let struct_data =
88 match ast.data {
89 Data::Struct(struct_date) => struct_date,
90 _ => {
91 all_errors.push(syn_v2::Error::new(ast.ident.span(), "Invalid body expected struct"));
92 return Err(all_errors)
93 }
94 };
95
96 let mut builder = ListTraitBuilder::new(ast.ident);
97
98
99
100 for field in struct_data.fields {
101 let field_attr =
102 match AttributeAttribute::from_attrs(field.ident.span(), field.attrs.clone()) {
103 Ok(attr) => attr,
104 Err(ref mut errors) => {
105 all_errors.append(errors);
106 continue;
107 }
108 };
109
110 builder.check_field(field, field_attr);
111
112 }
113
114 let output = builder.build();
115
116 match all_errors.len() {
117 0 => Ok(output),
118 _ => Err(all_errors)
119 }
120}
121
122
123
124
125
126
127
128
129
130struct BuilderParts {
131 builder_name: Ident,
132 field_declaration: TokenStream,
133 field_expansion: TokenStream,
134 concat_parts: TokenStream
135}
136impl BuilderParts {
137 fn new(struct_name: &Ident) -> Self {
138 Self {
139 builder_name: format_ident!("{struct_name}Builder"),
140 field_declaration: TokenStream::new(),
141 field_expansion: TokenStream::new(),
142 concat_parts: TokenStream::new()
143 }
144 }
145 fn generate_builder(self) -> (TokenStream, Ident) {
146 let Self { builder_name, field_declaration, field_expansion, concat_parts } = self;
147
148 let declaration =
149 quote!{
150 struct #builder_name<V: SynVersion> {
151 #field_declaration
152 }
153 impl<V: SynVersion> #builder_name<V> {
154 fn new(location: Span) -> Self {
155 Self {
156 #field_expansion
157 }
158 }
159 }
160 impl<V: SynVersion> Concat for #builder_name<V> {
161 const NO_DUPLICATES: bool = false;
162
163 fn concat(&mut self, other: Self) {
164 #concat_parts
165 }
166 }
167 };
168
169 (declaration, builder_name)
170 }
171}
172
173
174struct Validation {
175 validate_arguments: TokenStream,
176 expansion: TokenStream
177}
178impl Validation {
179 fn new() -> Self {
180 Self {
181 validate_arguments: TokenStream::new(),
182 expansion: TokenStream::new()
183 }
184 }
185}
186
187struct TryFrom {
188 match_branches: TokenStream
189}
190impl TryFrom {
191 fn new() -> Self {
192 Self {
193 match_branches: TokenStream::new()
194 }
195 }
196}
197
198struct MacroBase {
199 struct_name: Ident,
200 builder_parts: BuilderParts,
201 try_from: TryFrom,
202 validation: Validation,
203}
204impl MacroBase {
205 fn new(struct_name: Ident) -> Self {
206 Self {
207 struct_name: struct_name.clone(),
208 builder_parts: BuilderParts::new(&struct_name),
209 try_from: TryFrom::new(),
210 validation: Validation::new()
211 }
212 }
213
214 fn check_field(&mut self, field: Field, attribute: AttributeAttribute) {
215 let Self { builder_parts, try_from, validation, ..} = self;
216
217 let field_name = field.ident.unwrap();
218 let field_type = field.ty;
219
220 {
221 let field_decl = quote!{ #field_name: ArgResult<<#field_type as TryFromMeta<V>>::InitialType>, };
222 builder_parts.field_declaration.append_all(field_decl);
223 }
224
225 {
226 let field_expansion = quote!{ #field_name: ArgResult::new(location), };
227 builder_parts.field_expansion.append_all(field_expansion);
228 }
229
230 {
231 let concat_part = quote!{self.#field_name.concat(other.#field_name);};
232 builder_parts.concat_parts.append_all(concat_part);
233 }
234
235 let field_name_str =
236 match attribute.name {
237 Some(name) => name,
238 None => field_name.to_string()
239 };
240 {
241 let branch =
242 quote!{
243 #field_name_str => {
244 let value = <#field_type as TryFromMeta<V>>::try_from_meta(arg);
245 builder.#field_name.concat(value);
246 }
247 };
248 try_from.match_branches.append_all(branch);
249 }
250
251 let field_type_str = field_name.to_string();
252 {
253 let normal_validation =
254 quote!{
255 let mut #field_name = <#field_type as TryFromMeta<V>>::validate(builder.#field_name, #field_type_str);
256 if let Err(ref mut errors) = #field_name {
257 state.errors.append(errors);
258 }
259 };
260
261 let validate_field =
262 match attribute.default {
263 Some(arg) => {
264 let x =
265 match arg.0 {
266 Default::UseSelfDefault => quote!{ <#field_type as Default>::default() },
267 Default::ChooseDefault(path) => quote![ #path() ]
268 };
269
270 quote!{
271 let mut #field_name =
272 match builder.#field_name.value.is_none() && builder.#field_name.found_with_errors() == false {
273 true => Ok(#x),
274 false => {
275 #normal_validation
276
277 #field_name
278 }
279 };
280 }
281 }
282 None => normal_validation
283 };
284 validation.validate_arguments.append_all(validate_field);
285 }
286
287 {
288 let field_error = format!("failed to deserialize '{field_name_str}'");
289 let field_expansion = quote!{ #field_name: #field_name.expect(#field_error), };
290 validation.expansion.append_all(field_expansion);
291 }
292
293 }
294}
295
296struct AttributeTraitBuilder {
297 container_attr: AttributeAttribute,
298 base: MacroBase
299}
300impl AttributeTraitBuilder {
301 fn new(struct_name: Ident, container_attr: AttributeAttribute) -> Self {
302 Self {
303 container_attr,
304 base: MacroBase::new(struct_name)
305 }
306 }
307 fn check_field(&mut self, field: Field, attribute: AttributeAttribute) {
308 self.base.check_field(field, attribute);
309 }
310
311 fn build(self) -> TokenStream {
312 let Self {
313 container_attr,
314 base:
315 MacroBase {
316 struct_name,
317 builder_parts,
318 try_from,
319 validation
320 }
321 } = self;
322
323 let set_default =
324 match container_attr.default {
325 Some(CustomArg(Default::UseSelfDefault)) => quote!{ return Ok(<Self as Default>::default()) },
326 Some(CustomArg(Default::ChooseDefault(path))) => quote!{ return Ok(#path()) },
327 None => quote!()
328 };
329
330 let (builder_decl, builder_name) = builder_parts.generate_builder();
331
332
333 let mut all_attribute_impls = TokenStream::new();
334 #[cfg(feature = "syn_1")]
335 {
336 let attr_impl =
337 quote!{
338 use derive_attribute::Syn1;
339 impl Attribute<Syn1> for #struct_name {}
340 };
341 all_attribute_impls.append_all(attr_impl);
342 }
343 #[cfg(feature = "syn_2")]
344 {
345 let attr_impl =
346 quote!{
347 use derive_attribute::Syn2;
348 impl Attribute<Syn2> for #struct_name {}
349 };
350 all_attribute_impls.append_all(attr_impl);
351 }
352
353
354 let name =
355 match container_attr.name {
356 Some(name) => name,
357 None => struct_name.to_string().to_case(Case::Snake)
358 };
359
360 let try_from_fn = generate_try_from_meta(format_ident!("deserialize_attr_args"), &builder_name, try_from);
361 let validation_fn = generate_validate(validation, set_default, format_ident!("MissingAttribute"));
362
363 quote!{
364 const _: () = {
365 use derive_attribute::{AttributeName, TryFromMeta, Attribute, GetSpan, Concat, Error, ErrorMsg::*, SynVersion, ArgResult, reexports::proc_macro2::Span};
366
367 impl AttributeName for #struct_name {
368 const NAME: &'static str = #name;
369 }
370
371 #all_attribute_impls
372
373 #builder_decl
374
375 impl<V: SynVersion> TryFromMeta<V> for #struct_name {
376 type InitialType = #builder_name<V>;
377
378 type Metadata = V::Attribute;
379
380 #try_from_fn
381
382 #validation_fn
383 }
384
385 };
386 }
387 }
388
389}
390
391
392struct ListTraitBuilder {
393 base: MacroBase
394}
395impl ListTraitBuilder {
396 fn new(struct_name: Ident) -> Self {
397 Self {
398 base: MacroBase::new(struct_name)
399 }
400 }
401 fn check_field(&mut self, field: Field, attribute: AttributeAttribute) {
402 self.base.check_field(field, attribute);
403 }
404 fn build(self) -> TokenStream {
405 let Self {
406 base:
407 MacroBase {
408 struct_name,
409 builder_parts,
410 try_from,
411 validation
412 }
413 } = self;
414
415 let (builder_decl, builder_name) = builder_parts.generate_builder();
416
417 let try_from_fn = generate_try_from_meta(format_ident!("deserialize_list_args"), &builder_name, try_from);
418 let validation_fn = generate_validate(validation, quote!(), format_ident!("MissingArg"));
419
420 quote!{
421 const _: () = {
422 use derive_attribute::{AttributeName, TryFromMeta, Attribute, GetSpan, Concat, Error, ErrorMsg::*, SynVersion, ArgResult, reexports::proc_macro2::Span};
423
424
425 #builder_decl
426
427 impl<V: SynVersion> TryFromMeta<V> for #struct_name {
428 type InitialType = #builder_name<V>;
429
430 type Metadata = V::ArgMeta;
431
432 #try_from_fn
433
434 #validation_fn
435 }
436
437 };
438 }
439
440 }
441
442}
443
444
445
446fn generate_try_from_meta(deserialize_args: Ident, builder_name: &Ident, try_from: TryFrom) -> TokenStream {
447 let TryFrom { match_branches } = try_from;
448 quote!{
449 fn try_from_meta(arg_meta: Self::Metadata) -> ArgResult<Self::InitialType> {
450 let mut result = ArgResult::new(arg_meta.get_span());
451
452 let mut builder = #builder_name::new(arg_meta.get_span());
453
454 let attribute_args =
455 match V::#deserialize_args(&arg_meta) {
456 Some(args) => args,
457 None => {
458 result.add_error(InvalidType { expected: "list" });
459 return result
460 }
461 };
462
463
464 for arg in attribute_args {
465 let key = V::deserialize_key(&arg).expect("key failed");
466 match key.as_str() {
467 #match_branches
468
469 _ => result.errors.push(Error::new(arg.get_span(), InvalidArg))
470 }
471 }
472 result.add_value(builder);
473 result
474 }
475 }
476}
477
478fn generate_validate(validate: Validation, set_default: TokenStream, error_type: Ident) -> TokenStream {
479 let Validation { validate_arguments, expansion } = validate;
480 quote!{
481 fn validate(state: ArgResult<Self::InitialType>, arg_name: &'static str) -> Result<Self, Vec<Error>> {
482 let mut state = state;
483
484 if state.value.is_none() && state.found_with_errors() == false {
485 #set_default
486 }
487
488 let mut builder =
495 match state.found_with_errors() {
496 true => return Err(state.errors),
497 false if state.value.is_none() => {
498 state.add_error(#error_type(arg_name));
499 return Err(state.errors);
500 }
501 false => state.value.unwrap()
502 };
503
504 #validate_arguments
505
506
507 match state.errors.len() {
508 0 => Ok(Self { #expansion }),
509 _ => Err(state.errors)
510 }
511 }
512 }
513}
514
515
516
517#[derive(Debug, Default)]
518struct AttributeAttribute {
519 name: Option<String>,
520 default: Option<CustomArg<Default>>,
521}
522
523struct AttributeAttributeBuilder<V: SynVersion> {
524 name: ArgResult<<Option<String> as TryFromMeta<V>>::InitialType>,
525 default: ArgResult<<Option<CustomArg<Default>> as TryFromMeta<V>>::InitialType>,
526}
527impl<V: SynVersion> AttributeAttributeBuilder<V> {
528 fn new(location: Span) -> Self {
529 Self {
530 name: ArgResult::new(location),
531 default: ArgResult::new(location),
532 }
533 }
534}
535impl<V: SynVersion> Concat for AttributeAttributeBuilder<V> {
536 const NO_DUPLICATES: bool = false;
537 fn concat(&mut self, other: Self) {
538 self.name.concat(other.name);
539 self.default.concat(other.default);
540 }
541}
542
543
544impl Attribute<Syn2> for AttributeAttribute {}
545impl AttributeName for AttributeAttribute {
546 const NAME: &'static str = "attr";
547}
548impl<V: SynVersion> TryFromMeta<V> for AttributeAttribute {
549 type InitialType = AttributeAttributeBuilder<V>;
550
551 type Metadata = V::Attribute;
552 fn try_from_meta(arg_meta: Self::Metadata) -> ArgResult<Self::InitialType> {
553 let mut result = ArgResult::new(arg_meta.get_span());
554
555 let mut builder = AttributeAttributeBuilder::new(arg_meta.get_span());
556
557 let attribute_args =
558 match V::deserialize_attr_args(&arg_meta) {
559 Some(args) => args,
560 None => {
561 result.add_error(InvalidType { expected: "list" });
562 return result
563 }
564 };
565
566
567 for arg in attribute_args {
568 let key = V::deserialize_key(&arg).expect("key failed");
569
570 match key.as_str() {
571 "name" => {
572 let value = <Option<String> as TryFromMeta<V>>::try_from_meta(arg);
573 builder.name.concat(value);
574 }
575 "default" => {
576 let value = <Option<CustomArg<Default>> as TryFromMeta<V>>::try_from_meta(arg);
577 builder.default.concat(value);
578 }
579
580 _ => result.errors.push(Error::new(arg.get_span(), InvalidArg))
581 };
582
583 }
584 result.add_value(builder);
585 result
586 }
587
588 fn validate(state: ArgResult<Self::InitialType>, arg_name: &'static str) -> Result<Self, Vec<Error>> {
589 let mut state = state;
590
591 if state.value.is_none() && state.found_with_errors() == false {
592 return Ok(Self::default())
593 }
594
595 let builder =
596 match state.value {
597 Some(value) => value,
598 None => return Err(vec![Error::new(state.location, MissingAttribute(arg_name))])
599 };
600
601 let mut maybe_name = <Option<String> as TryFromMeta<V>>::validate(builder.name, "name");
602 if let Err(ref mut errors) = maybe_name {
603 state.errors.append(errors);
604 }
605
606 let mut maybe_default = <Option<CustomArg<Default>> as TryFromMeta<V>>::validate(builder.default, "default");
607 if let Err(ref mut errors) = maybe_default {
608 state.errors.append(errors);
609 }
610
611 match state.errors.len() {
612 0 => Ok(Self { name: maybe_name.expect("name failed"), default: maybe_default.expect("default failed") }),
613 _ => Err(state.errors)
614 }
615 }
616}
617
618#[derive(Debug)]
619enum Default {
620 UseSelfDefault,
621 ChooseDefault(Ident)
622}
623impl<V: SynVersion> CustomArgFromMeta<V> for Default {
624 fn try_from_meta(meta: V::ArgMeta) -> Result<Self, ErrorMsg> {
625 let maybe_bool = V::deserialize_bool(&meta);
626 let maybe_path = V::deserialize_string(&meta);
627
628 match (maybe_bool, maybe_path) {
629 (Some(is_default), _) if is_default => Ok(Self::UseSelfDefault),
630 (_, Some(path)) => Ok(Self::ChooseDefault(format_ident!("{path}"))),
631 _ => Err(InvalidType { expected: "boolean or path string" })
632 }
633 }
634}