git.delta.rocks / jrsonnet / refs/commits / a9a2382fcb8f

difftreelog

feat derive(Typed) fields aliases (#177)

Valery Klachkov2024-11-04parent: #e46de0a.patch.diff
in: master
* Support fields aliases

* Improve codegen

1 file changed

modifiedcrates/jrsonnet-macros/src/lib.rsdiffbeforeafterboth
96mod kw {96mod kw {
97 syn::custom_keyword!(fields);97 syn::custom_keyword!(fields);
98 syn::custom_keyword!(rename);98 syn::custom_keyword!(rename);
99 syn::custom_keyword!(alias);
99 syn::custom_keyword!(flatten);100 syn::custom_keyword!(flatten);
100 syn::custom_keyword!(add);101 syn::custom_keyword!(add);
101 syn::custom_keyword!(hide);102 syn::custom_keyword!(hide);
412#[allow(clippy::struct_excessive_bools)]413#[allow(clippy::struct_excessive_bools)]
413struct TypedAttr {414struct TypedAttr {
414 rename: Option<String>,415 rename: Option<String>,
416 aliases: Vec<String>,
415 flatten: bool,417 flatten: bool,
416 /// flatten(ok) strategy for flattened optionals418 /// flatten(ok) strategy for flattened optionals
417 /// field would be None in case of any parsing error (as in serde)419 /// field would be None in case of any parsing error (as in serde)
437 ));439 ));
438 }440 }
439 out.rename = Some(name.value());441 out.rename = Some(name.value());
440 } else if lookahead.peek(kw::flatten) {442 } else if lookahead.peek(kw::alias) {
443 input.parse::<kw::alias>()?;
444 input.parse::<Token![=]>()?;
445 let alias = input.parse::<LitStr>()?;
446 out.aliases.push(alias.value());
447 } else if lookahead.peek(kw::flatten) {
441 input.parse::<kw::flatten>()?;448 input.parse::<kw::flatten>()?;
442 out.flatten = true;449 out.flatten = true;
443 if input.peek(token::Paren) {450 if input.peek(token::Paren) {
538 })545 })
539 }546 }
547
540 fn expand_parse(&self) -> TokenStream {548 fn expand_parse(&self) -> TokenStream {
549 if self.is_option {
550 self.expand_parse_optional()
551 } else {
552 self.expand_parse_mandatory()
553 }
554 }
555
556 fn expand_parse_optional(&self) -> TokenStream {
541 let ident = &self.ident;557 let ident = &self.ident;
542 let ty = &self.ty;558 let ty = &self.ty;
559
560 // optional flatten is handled in same way as serde
543 if self.attr.flatten {561 if self.attr.flatten {
544 // optional flatten is handled in same way as serde
545 return if self.is_option {562 return quote! {
546 quote! {
547 #ident: <#ty as TypedObj>::parse(&obj).ok(),563 #ident: <#ty as TypedObj>::parse(&obj).ok(),
548 }564 };
549 } else {
550 quote! {
551 #ident: <#ty as TypedObj>::parse(&obj)?,
552 }
553 };
554 };565 }
555566
556 let name = self.name().unwrap();567 let name = self.name().unwrap();
557 let value = if self.is_option {568 let aliases = &self.attr.aliases;
569
558 quote! {570 quote! {
571 #ident: {
559 if let Some(value) = obj.get(#name.into())? {572 let __value = if let Some(__v) = obj.get(#name.into())? {
573 Some(__v)
560 Some(<#ty as Typed>::from_untyped(value)?)574 } #(else if let Some(__v) = obj.get(#aliases.into())? {
561 } else {575 Some(__v)
562 None576 })* else {
563 }577 None
578 };
579
580 __value.map(<#ty as Typed>::from_untyped).transpose()?
564 }581 },
565 } else {582 }
566 quote! {
567 <#ty as Typed>::from_untyped(obj.get(#name.into())?.ok_or_else(|| ErrorKind::NoSuchField(#name.into(), vec![]))?)?
568 }
569 };
570
571 quote! {
572 #ident: #value,
573 }
574 }583 }
584
585 fn expand_parse_mandatory(&self) -> TokenStream {
586 let ident = &self.ident;
587 let ty = &self.ty;
588
589 // optional flatten is handled in same way as serde
590 if self.attr.flatten {
591 return quote! {
592 #ident: <#ty as TypedObj>::parse(&obj)?,
593 };
594 }
595
596 let name = self.name().unwrap();
597 let aliases = &self.attr.aliases;
598
599 let error_text = if aliases.is_empty() {
600 // clippy does not understand name variable usage in quote! macro
601 #[allow(clippy::redundant_clone)]
602 name.clone()
603 } else {
604 format!("{name} (alias {})", aliases.join(", "))
605 };
606
607 quote! {
608 #ident: {
609 let __value = if let Some(__v) = obj.get(#name.into())? {
610 __v
611 } #(else if let Some(__v) = obj.get(#aliases.into())? {
612 __v
613 })* else {
614 return Err(ErrorKind::NoSuchField(#error_text.into(), vec![]).into());
615 };
616
617 <#ty as Typed>::from_untyped(__value)?
618 },
619 }
620 }
621
575 fn expand_serialize(&self) -> TokenStream {622 fn expand_serialize(&self) -> TokenStream {
576 let ident = &self.ident;623 let ident = &self.ident;