1use std::{collections::BTreeSet, iter};
4
5use proc_macro2::{Span, TokenStream};
6use quote::{quote, ToTokens};
7use syn::{
8 ext::IdentExt as _,
9 parse::{Parse, ParseStream},
10 spanned::Spanned as _,
11 token,
12};
13
14use crate::{
15 ext::{Data as _, Ident as _},
16 parse::{
17 attrs::{
18 dedup,
19 field::TryMerge as _,
20 kind,
21 validate::{rule, Validate as _},
22 },
23 err,
24 ext::ParseBuffer as _,
25 },
26 ParseAttrs, Required, Spanning,
27};
28
29const TRAIT_NAME: &str = "ParseAttrs";
31
32const ATTR_NAME: &str = "parse";
34
35pub fn derive(input: syn::DeriveInput) -> syn::Result<TokenStream> {
42 if !matches!(&input.data, syn::Data::Struct(_)) {
43 return Err(syn::Error::new_spanned(
44 input,
45 format!("only structs can derive {TRAIT_NAME}"),
46 ));
47 }
48
49 let out = Definition {
50 ty: input.ident,
51 generics: input.generics,
52 fields: input
53 .data
54 .named_fields()?
55 .into_iter()
56 .map(Field::try_from)
57 .collect::<syn::Result<Vec<_>>>()?,
58 };
59
60 let impl_syn_parse = out.impl_syn_parse();
61 let impl_parse_attrs = out.impl_parse_attrs();
62 Ok(quote! {
63 #impl_syn_parse
64 #impl_parse_attrs
65 })
66}
67
68#[derive(Debug)]
71struct Definition {
72 ty: syn::Ident,
76
77 generics: syn::Generics,
79
80 fields: Vec<Field>,
82}
83
84impl Definition {
85 #[must_use]
87 fn impl_syn_parse(&self) -> TokenStream {
88 let parse_arms = self.fields.iter().map(|f| {
89 let field = &f.ident;
90 let ty = &f.ty;
91 let kind = f.kind;
92 let dedup = f.dedup;
93 let arg_lits = &f.names;
94
95 let val_ty = quote! {
96 <#ty as ::synthez::field::Container<_>>::Value
97 };
98
99 let code = match kind {
100 Kind::Ident => quote! {
101 <#ty as ::synthez::parse::attrs::field::TryApply<
102 _, #kind, #dedup,
103 >>::try_apply(&mut out.#field, input.parse::<#val_ty>()?)?;
104 },
105 Kind::Nested => quote! {
106 ::synthez::ParseBufferExt::skip_any_ident(input)?;
107 let inner;
108 let _ = ::synthez::syn::parenthesized!(inner in input);
109 <#ty as ::synthez::parse::attrs::field::TryApply<
110 _, #kind, #dedup,
111 >>::try_apply(
112 &mut out.#field,
113 ::synthez::Spanning::new(inner.parse()?, &ident),
114 )?;
115 },
116 Kind::Value(spaced) => {
117 let method = syn::Ident::new_on_call_site(if spaced {
118 "parse_maybe_wrapped_and_punctuated"
119 } else {
120 "parse_eq_or_wrapped_and_punctuated"
121 });
122
123 quote! {
124 ::synthez::ParseBufferExt::skip_any_ident(input)?;
125 for v in ::synthez::ParseBufferExt::#method::<
126 #val_ty,
127 ::synthez::syn::token::Paren,
128 ::synthez::syn::token::Comma,
129 >(input)? {
130 <#ty as ::synthez::parse::attrs::field::TryApply<
131 _, #kind, #dedup,
132 >>::try_apply(&mut out.#field, v)?;
133 }
134 }
135 }
136 Kind::Map => quote! {
137 ::synthez::ParseBufferExt::skip_any_ident(input)?;
138 let k = input.parse()?;
139 input.parse::<::synthez::syn::token::Eq>()?;
140 let v = input.parse()?;
141 <#ty as ::synthez::parse::attrs::field::TryApply<
142 (_, _), #kind, #dedup,
143 >>::try_apply(&mut out.#field, (k, v))?;
144 },
145 };
146
147 quote! {
148 #( #arg_lits )|* => { #code },
149 }
150 });
151
152 let ty = &self.ty;
153 let (impl_generics, ty_generics, where_clause) =
154 self.generics.split_for_impl();
155
156 quote! {
157 #[automatically_derived]
158 impl #impl_generics ::synthez::syn::parse::Parse
159 for #ty #ty_generics
160 #where_clause
161 {
162 fn parse(
163 input: ::synthez::syn::parse::ParseStream<'_>,
164 ) -> ::synthez::syn::Result<Self> {
165 let mut out =
166 <#ty #ty_generics as ::std::default::Default>
167 ::default();
168 while !input.is_empty() {
169 let ident =
170 ::synthez::ParseBufferExt::parse_any_ident(
171 &input.fork(),
172 )?;
173 match ident.to_string().as_str() {
174 #( #parse_arms )*
175 name => {
176 return Err(::synthez::parse::err::
177 unknown_attr_arg(&ident, name));
178 },
179 }
180 if ::synthez::ParseBufferExt::try_parse::<
181 ::synthez::syn::token::Comma,
182 >(input)?.is_none() && !input.is_empty() {
183 return Err(::synthez::parse::err::
184 expected_followed_by_comma(&ident));
185 }
186 }
187 Ok(out)
188 }
189 }
190 }
191 }
192
193 #[must_use]
195 fn impl_parse_attrs(&self) -> TokenStream {
196 let ty = &self.ty;
197 let (impl_generics, ty_generics, where_clause) =
198 self.generics.split_for_impl();
199
200 let try_merge_fields = self.fields.iter().map(Field::gen_merge);
201
202 let validate_provided_fields =
203 self.fields.iter().map(Field::gen_validate_provided);
204 let validate_nested_fields =
205 self.fields.iter().filter_map(Field::gen_validate_nested);
206 let validate_custom_fields = self.fields.iter().flat_map(|f| {
207 let field = &f.ident;
208 f.validators.iter().map(move |validator| {
209 quote! {
210 #validator(&self.#field)?;
211 }
212 })
213 });
214
215 let fallback_nested_fields =
216 self.fields.iter().filter_map(Field::gen_fallback_nested);
217 let fallback_custom_fields = self.fields.iter().flat_map(|f| {
218 let field = &f.ident;
219 f.fallbacks.iter().map(move |fallback| {
220 quote! {
221 #fallback(&mut self.#field, attrs)?;
222 }
223 })
224 });
225
226 quote! {
227 #[automatically_derived]
228 impl #impl_generics ::synthez::parse::Attrs for #ty #ty_generics
229 #where_clause
230 {
231 fn try_merge(
232 mut self,
233 another: Self,
234 ) -> ::synthez::syn::Result<Self> {
235 #( #try_merge_fields )*
236 Ok(self)
237 }
238
239 fn validate(
240 &self,
241 attr_name: &str,
242 item_span: ::synthez::proc_macro2::Span,
243 ) -> ::synthez::syn::Result<()> {
244 #( #validate_provided_fields )*
245 #( #validate_nested_fields )*
246 #( #validate_custom_fields )*
247 Ok(())
248 }
249
250 fn fallback(
251 &mut self,
252 attrs: &[::synthez::syn::Attribute],
253 ) -> ::synthez::syn::Result<()> {
254 #( #fallback_nested_fields )*
255 #( #fallback_custom_fields )*
256 Ok(())
257 }
258 }
259 }
260 }
261}
262
263#[derive(Debug)]
265struct Field {
266 ident: syn::Ident,
271
272 ty: syn::Type,
276
277 kind: Kind,
279
280 dedup: Dedup,
283
284 names: Vec<String>,
287
288 validators: Vec<syn::Expr>,
291
292 fallbacks: Vec<syn::Expr>,
295}
296
297impl TryFrom<syn::Field> for Field {
298 type Error = syn::Error;
299
300 fn try_from(field: syn::Field) -> syn::Result<Self> {
301 let attrs = FieldAttrs::parse_attrs(ATTR_NAME, &field)?;
302
303 let field_span = field.span();
304 let ident = field.ident.ok_or_else(move || {
305 syn::Error::new(field_span, "Named field expected")
306 })?;
307
308 let mut names = if attrs.args.is_empty() {
309 iter::once(ident.unraw()).collect()
310 } else {
311 attrs.args
312 };
313 names.try_merge_self::<kind::Value, dedup::Unique>(attrs.aliases)?;
314
315 Ok(Self {
316 ident,
317 ty: field.ty,
318 kind: **attrs.kind,
319 dedup: attrs.dedup.as_deref().copied().unwrap_or_default(),
320 names: names.into_iter().map(|n| n.to_string()).collect(),
321 validators: attrs.validators,
322 fallbacks: attrs.fallbacks,
323 })
324 }
325}
326
327impl Field {
328 #[must_use]
330 fn gen_merge(&self) -> TokenStream {
331 let field = &self.ident;
332 let ty = &self.ty;
333 let kind = self.kind;
334 let dedup = self.dedup;
335
336 quote! {
337 <#ty as ::synthez::parse::attrs::field::TryApplySelf<
338 _, #kind, #dedup,
339 >>::try_apply_self(&mut self.#field, another.#field)?;
340 }
341 }
342
343 #[must_use]
345 fn gen_validate_provided(&self) -> TokenStream {
346 let field = &self.ident;
347 let ty = &self.ty;
348
349 let names_len = self.names.len();
350 let arg_names = if names_len > 1 {
351 format!(
352 "either `{}` or `{}`",
353 self.names[..(names_len - 1)].join("`, `"),
354 self.names[names_len - 1],
355 )
356 } else {
357 format!("`{}`", self.names[0])
358 };
359 let err_msg =
360 format!("{arg_names} argument of `#[{{}}]` attribute {{}}");
361
362 quote! {
363 if let Err(e) = <#ty as ::synthez::parse::attrs::Validation<
364 ::synthez::parse::attrs::validate::rule::Provided,
365 >>::validation(&self.#field) {
366 return Err(::synthez::syn::Error::new(
367 item_span,
368 format!(#err_msg, attr_name, e),
369 ));
370 }
371 }
372 }
373
374 #[must_use]
377 fn gen_validate_nested(&self) -> Option<TokenStream> {
378 if self.kind != Kind::Nested {
379 return None;
380 }
381
382 let field = &self.ident;
383 let attr_fmt = format!("{{}}({})", self.names[0]);
384
385 Some(quote! {
386 for v in &self.#field {
387 ::synthez::parse::Attrs::validate(
388 &**v,
389 &format!(#attr_fmt, attr_name),
390 ::synthez::spanned::IntoSpan::into_span(v),
391 )?;
392 }
393 })
394 }
395
396 #[must_use]
399 fn gen_fallback_nested(&self) -> Option<TokenStream> {
400 if self.kind != Kind::Nested {
401 return None;
402 }
403
404 let field = &self.ident;
405 let ty = &self.ty;
406
407 Some(quote! {
408 if !<#ty as ::synthez::field::Container<_>>::is_empty(
409 &self.#field,
410 ) {
411 for v in &mut self.#field {
412 ::synthez::parse::Attrs::fallback(&mut **v, attrs)?;
413 }
414 }
415 })
416 }
417}
418
419#[derive(Debug, Default)]
422struct FieldAttrs {
423 kind: Required<Spanning<Kind>>,
426
427 args: BTreeSet<syn::Ident>,
433
434 aliases: BTreeSet<syn::Ident>,
440
441 dedup: Option<Spanning<Dedup>>,
447
448 validators: Vec<syn::Expr>,
451
452 fallbacks: Vec<syn::Expr>,
456}
457
458impl Parse for FieldAttrs {
459 fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
460 let mut out = Self::default();
461 while !input.is_empty() {
462 let ident = input.fork().parse_any_ident()?;
463 match ident.to_string().as_str() {
464 "ident" | "nested" | "value" | "map" => {
465 out.kind.try_merge::<kind::Ident, dedup::Unique>(
466 input.parse::<Spanning<Kind>>()?,
467 )?;
468 }
469 "arg" | "args" => {
470 input.skip_any_ident()?;
471 for val in input.parse_eq_or_wrapped_and_punctuated::<
472 syn::Ident, token::Paren, token::Comma,
473 >()? {
474 out.args.try_merge::<kind::Value, dedup::Unique>(val)?;
475 }
476 }
477 "alias" | "aliases" => {
478 input.skip_any_ident()?;
479 for v in input.parse_eq_or_wrapped_and_punctuated::<
480 syn::Ident, token::Paren, token::Comma,
481 >()? {
482 out.aliases.try_merge::<kind::Value, dedup::Unique>(v)?;
483 }
484 }
485 "dedup" => {
486 input.skip_any_ident()?;
487 for val in input.parse_eq_or_wrapped_and_punctuated::<
488 Spanning<Dedup>, token::Paren, token::Comma,
489 >()? {
490 out.dedup.try_merge::<kind::Value, dedup::Unique>(val)?;
491 }
492 }
493 "validate" => {
494 input.skip_any_ident()?;
495 for v in input.parse_eq_or_wrapped_and_punctuated::<
496 syn::Expr, token::Paren, token::Comma,
497 >()? {
498 out.validators.try_merge::<
499 kind::Value, dedup::Unique,
500 >(v)?;
501 }
502 }
503 "fallbacks" | "fallback" => {
504 input.skip_any_ident()?;
505 for v in input.parse_eq_or_wrapped_and_punctuated::<
506 syn::Expr, token::Paren, token::Comma,
507 >()? {
508 out.fallbacks.try_merge::<
509 kind::Value, dedup::Unique,
510 >(v)?;
511 }
512 }
513 name => {
514 return Err(err::unknown_attr_arg(&ident, name));
515 }
516 }
517 if input.try_parse::<token::Comma>()?.is_none() && !input.is_empty()
518 {
519 return Err(err::expected_followed_by_comma(&ident));
520 }
521 }
522 Ok(out)
523 }
524}
525
526impl ParseAttrs for FieldAttrs {
527 fn try_merge(mut self, another: Self) -> syn::Result<Self> {
528 self.kind.try_merge_self::<kind::Value, dedup::Unique>(another.kind)?;
529 self.args.try_merge_self::<kind::Value, dedup::Unique>(another.args)?;
530 self.aliases
531 .try_merge_self::<kind::Value, dedup::Unique>(another.aliases)?;
532 self.dedup
533 .try_merge_self::<kind::Value, dedup::Unique>(another.dedup)?;
534 self.validators
535 .try_merge_self::<kind::Value, dedup::Unique>(another.validators)?;
536 self.fallbacks
537 .try_merge_self::<kind::Value, dedup::Unique>(another.fallbacks)?;
538 Ok(self)
539 }
540
541 fn validate(&self, attr_name: &str, item_span: Span) -> syn::Result<()> {
542 if self.kind.validate::<rule::Provided>().is_err() {
543 return Err(syn::Error::new(
544 item_span,
545 format!(
546 "either `ident`, `value` or `map` argument of \
547 `#[{attr_name}]` attribute is expected",
548 ),
549 ));
550 }
551 Ok(())
552 }
553}
554
555#[derive(Clone, Copy, Debug, Eq, PartialEq)]
557enum Kind {
558 Ident,
562
563 Nested,
565
566 Value(bool),
573
574 Map,
578}
579
580impl Parse for Spanning<Kind> {
581 fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
582 let ident = input.parse::<syn::Ident>()?;
583 Ok(Self::new(
584 match ident.to_string().as_str() {
585 "ident" => Kind::Ident,
586 "nested" => Kind::Nested,
587 "value" => {
588 if input.is_next::<token::Paren>() {
589 let inner;
590 _ = syn::parenthesized!(inner in input);
591 let inner = inner.parse::<syn::Ident>()?;
592 let val = inner.to_string();
593 if val != "spaced" {
594 return Err(syn::Error::new_spanned(
595 inner,
596 format!("invalid value setting: {val} "),
597 ));
598 }
599 Kind::Value(true)
600 } else {
601 Kind::Value(false)
602 }
603 }
604 "map" => Kind::Map,
605 val => {
606 return Err(syn::Error::new_spanned(
607 ident,
608 format!("invalid kind value: {val} "),
609 ));
610 }
611 },
612 &ident,
613 ))
614 }
615}
616
617impl ToTokens for Kind {
618 fn to_tokens(&self, out: &mut TokenStream) {
619 let variant = syn::Ident::new_on_call_site(match self {
620 Self::Ident => "Ident",
621 Self::Nested => "Nested",
622 Self::Value(_) => "Value",
623 Self::Map => "Map",
624 });
625 (quote! {
626 ::synthez::parse::attrs::kind::#variant
627 })
628 .to_tokens(out);
629 }
630}
631
632#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
634enum Dedup {
635 #[default]
637 Unique,
638
639 First,
641
642 Last,
644}
645
646impl Parse for Spanning<Dedup> {
647 fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
648 let ident = input.parse::<syn::Ident>()?;
649 Ok(Self::new(
650 match ident.to_string().as_str() {
651 "unique" => Dedup::Unique,
652 "first" => Dedup::First,
653 "last" => Dedup::Last,
654 val => {
655 return Err(syn::Error::new_spanned(
656 ident,
657 format!("invalid dedup value: {val} "),
658 ));
659 }
660 },
661 &ident,
662 ))
663 }
664}
665
666impl ToTokens for Dedup {
667 fn to_tokens(&self, out: &mut TokenStream) {
668 let variant = syn::Ident::new_on_call_site(match self {
669 Self::Unique => "Unique",
670 Self::First => "First",
671 Self::Last => "Last",
672 });
673 (quote! {
674 ::synthez::parse::attrs::dedup::#variant
675 })
676 .to_tokens(out);
677 }
678}