difftreelog
feat derive(Typed) fields aliases (#177)
in: master
* Support fields aliases * Improve codegen
1 file changed
crates/jrsonnet-macros/src/lib.rsdiffbeforeafterboth96mod 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 optionals417 /// 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 }547540 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 }555556 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;559560 // optional flatten is handled in same way as serde543 if self.attr.flatten {561 if self.attr.flatten {544 // optional flatten is handled in same way as serde545 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 }555566556 let name = self.name().unwrap();567 let name = self.name().unwrap();557 let value = if self.is_option {568 let aliases = &self.attr.aliases;569558 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 None578 };579580 __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 };570571 quote! {572 #ident: #value,573 }574 }583 }584585 fn expand_parse_mandatory(&self) -> TokenStream {586 let ident = &self.ident;587 let ty = &self.ty;588589 // optional flatten is handled in same way as serde590 if self.attr.flatten {591 return quote! {592 #ident: <#ty as TypedObj>::parse(&obj)?,593 };594 }595596 let name = self.name().unwrap();597 let aliases = &self.attr.aliases;598599 let error_text = if aliases.is_empty() {600 // clippy does not understand name variable usage in quote! macro601 #[allow(clippy::redundant_clone)]602 name.clone()603 } else {604 format!("{name} (alias {})", aliases.join(", "))605 };606607 quote! {608 #ident: {609 let __value = if let Some(__v) = obj.get(#name.into())? {610 __v611 } #(else if let Some(__v) = obj.get(#aliases.into())? {612 __v613 })* else {614 return Err(ErrorKind::NoSuchField(#error_text.into(), vec![]).into());615 };616617 <#ty as Typed>::from_untyped(__value)?618 },619 }620 }621575 fn expand_serialize(&self) -> TokenStream {622 fn expand_serialize(&self) -> TokenStream {576 let ident = &self.ident;623 let ident = &self.ident;