xso-proc: add support for optional children

This commit is contained in:
Jonas Schäfer 2024-06-30 09:21:46 +02:00
commit 01336802b4
6 changed files with 133 additions and 11 deletions

View file

@ -152,7 +152,11 @@ enum FieldKind {
},
/// The field maps to a child
Child,
Child {
// Flag indicating whether the value should be defaulted if the
// child is absent.
default_: Flag,
},
}
impl FieldKind {
@ -199,7 +203,7 @@ impl FieldKind {
XmlFieldMeta::Text { codec } => Ok(Self::Text { codec }),
XmlFieldMeta::Child => Ok(Self::Child),
XmlFieldMeta::Child { default_ } => Ok(Self::Child { default_ }),
}
}
}
@ -341,7 +345,7 @@ impl FieldDef {
})
}
FieldKind::Child => {
FieldKind::Child { ref default_ } => {
let FromEventsScope {
ref substate_result,
..
@ -353,6 +357,18 @@ impl FieldDef {
let from_events = from_events_fn(self.ty.clone());
let from_xml_builder = from_xml_builder_ty(self.ty.clone());
let on_absent = match default_ {
Flag::Absent => quote! {
return ::core::result::Result::Err(::xso::error::Error::Other(#missing_msg).into())
},
Flag::Present(_) => {
let default_ = default_fn(self.ty.clone());
quote! {
#default_()
}
}
};
Ok(FieldBuilderPart::Nested {
value: FieldTempInit {
init: quote! { ::std::option::Option::None },
@ -368,7 +384,7 @@ impl FieldDef {
finalize: quote! {
match #field_access {
::std::option::Option::Some(value) => value,
::std::option::Option::None => return ::core::result::Result::Err(::xso::error::Error::Other(#missing_msg).into()),
::std::option::Option::None => #on_absent,
}
},
})
@ -426,7 +442,7 @@ impl FieldDef {
Ok(FieldIteratorPart::Text { generator })
}
FieldKind::Child => {
FieldKind::Child { default_: _ } => {
let AsItemsScope { ref lifetime, .. } = scope;
let as_xml_iter = as_xml_iter_fn(self.ty.clone());

View file

@ -318,7 +318,10 @@ pub(crate) enum XmlFieldMeta {
},
/// `#[xml(child)`
Child,
Child {
/// The `default` flag.
default_: Flag,
},
}
impl XmlFieldMeta {
@ -424,8 +427,26 @@ impl XmlFieldMeta {
}
/// Parse a `#[xml(child)]` meta.
fn child_from_meta(_: ParseNestedMeta<'_>) -> Result<Self> {
Ok(Self::Child)
fn child_from_meta(meta: ParseNestedMeta<'_>) -> Result<Self> {
if meta.input.peek(syn::token::Paren) {
let mut default_ = Flag::Absent;
meta.parse_nested_meta(|meta| {
if meta.path.is_ident("default") {
if default_.is_set() {
return Err(Error::new_spanned(meta.path, "duplicate `default` key"));
}
default_ = (&meta.path).into();
Ok(())
} else {
Err(Error::new_spanned(meta.path, "unsupported key"))
}
})?;
Ok(Self::Child { default_ })
} else {
Ok(Self::Child {
default_: Flag::Absent,
})
}
}
/// Parse [`Self`] from a nestd meta, switching on the identifier