xso: add support for extracting into collections
This commit is contained in:
parent
5590d707fb
commit
8732ca9a28
4 changed files with 135 additions and 4 deletions
|
|
@ -249,7 +249,23 @@ impl FieldKind {
|
|||
})
|
||||
}
|
||||
|
||||
XmlFieldMeta::Text { span: _, codec } => Ok(Self::Text { codec }),
|
||||
XmlFieldMeta::Text {
|
||||
span: _,
|
||||
codec,
|
||||
type_,
|
||||
} => {
|
||||
// This would've been taken via `XmlFieldMeta::take_type` if
|
||||
// this field was within an extract where a `type_` is legal
|
||||
// to have.
|
||||
if let Some(type_) = type_ {
|
||||
return Err(Error::new_spanned(
|
||||
type_,
|
||||
"specifying `type_` on fields inside structs and enum variants is redundant and not allowed."
|
||||
));
|
||||
}
|
||||
|
||||
Ok(Self::Text { codec })
|
||||
}
|
||||
|
||||
XmlFieldMeta::Child {
|
||||
span: _,
|
||||
|
|
@ -280,12 +296,13 @@ impl FieldKind {
|
|||
XmlFieldMeta::Extract {
|
||||
span,
|
||||
qname: QNameRef { namespace, name },
|
||||
amount,
|
||||
fields,
|
||||
} => {
|
||||
let xml_namespace = namespace.unwrap_or_else(|| container_namespace.clone());
|
||||
let xml_name = default_name(span, name, field_ident)?;
|
||||
|
||||
let field = {
|
||||
let mut field = {
|
||||
let mut fields = fields.into_iter();
|
||||
let Some(field) = fields.next() else {
|
||||
return Err(Error::new(
|
||||
|
|
@ -304,13 +321,28 @@ impl FieldKind {
|
|||
field
|
||||
};
|
||||
|
||||
let amount = amount.unwrap_or(AmountConstraint::FixedSingle(Span::call_site()));
|
||||
let field_ty = match field.take_type() {
|
||||
Some(v) => v,
|
||||
None => match amount {
|
||||
// Only allow inferrence for single values: inferrence
|
||||
// for collections will always be wrong.
|
||||
AmountConstraint::FixedSingle(_) => field_ty.clone(),
|
||||
_ => {
|
||||
return Err(Error::new(
|
||||
field.span(),
|
||||
"extracted field must specify a type explicitly when extracting into a collection."
|
||||
));
|
||||
}
|
||||
},
|
||||
};
|
||||
let parts = Compound::from_field_defs(
|
||||
[FieldDef::from_extract(field, 0, field_ty, &xml_namespace)].into_iter(),
|
||||
[FieldDef::from_extract(field, 0, &field_ty, &xml_namespace)].into_iter(),
|
||||
)?;
|
||||
|
||||
Ok(Self::Child {
|
||||
default_: Flag::Absent,
|
||||
amount: AmountConstraint::FixedSingle(Span::call_site()),
|
||||
amount,
|
||||
extract: Some(ExtractDef {
|
||||
xml_namespace,
|
||||
xml_name,
|
||||
|
|
|
|||
|
|
@ -645,6 +645,9 @@ pub(crate) enum XmlFieldMeta {
|
|||
|
||||
/// The path to the optional codec type.
|
||||
codec: Option<Expr>,
|
||||
|
||||
/// An explicit type override, only usable within extracts.
|
||||
type_: Option<Type>,
|
||||
},
|
||||
|
||||
/// `#[xml(child)`
|
||||
|
|
@ -671,6 +674,9 @@ pub(crate) enum XmlFieldMeta {
|
|||
/// The namespace/name keys.
|
||||
qname: QNameRef,
|
||||
|
||||
/// The `n` flag.
|
||||
amount: Option<AmountConstraint>,
|
||||
|
||||
/// The `fields` nested meta.
|
||||
fields: Vec<XmlFieldMeta>,
|
||||
},
|
||||
|
|
@ -751,10 +757,12 @@ impl XmlFieldMeta {
|
|||
}
|
||||
Ok(Self::Text {
|
||||
span: meta.path.span(),
|
||||
type_: None,
|
||||
codec: Some(codec),
|
||||
})
|
||||
} else if meta.input.peek(syn::token::Paren) {
|
||||
let mut codec: Option<Expr> = None;
|
||||
let mut type_: Option<Type> = None;
|
||||
meta.parse_nested_meta(|meta| {
|
||||
if meta.path.is_ident("codec") {
|
||||
if codec.is_some() {
|
||||
|
|
@ -773,17 +781,25 @@ impl XmlFieldMeta {
|
|||
}
|
||||
codec = Some(new_codec);
|
||||
Ok(())
|
||||
} else if meta.path.is_ident("type_") {
|
||||
if type_.is_some() {
|
||||
return Err(Error::new_spanned(meta.path, "duplicate `type_` key"));
|
||||
}
|
||||
type_ = Some(meta.value()?.parse()?);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::new_spanned(meta.path, "unsupported key"))
|
||||
}
|
||||
})?;
|
||||
Ok(Self::Text {
|
||||
span: meta.path.span(),
|
||||
type_,
|
||||
codec,
|
||||
})
|
||||
} else {
|
||||
Ok(Self::Text {
|
||||
span: meta.path.span(),
|
||||
type_: None,
|
||||
codec: None,
|
||||
})
|
||||
}
|
||||
|
|
@ -829,6 +845,7 @@ impl XmlFieldMeta {
|
|||
fn extract_from_meta(meta: ParseNestedMeta<'_>) -> Result<Self> {
|
||||
let mut qname = QNameRef::default();
|
||||
let mut fields = None;
|
||||
let mut amount = None;
|
||||
meta.parse_nested_meta(|meta| {
|
||||
if meta.path.is_ident("fields") {
|
||||
if let Some((fields_span, _)) = fields.as_ref() {
|
||||
|
|
@ -843,6 +860,12 @@ impl XmlFieldMeta {
|
|||
})?;
|
||||
fields = Some((meta.path.span(), new_fields));
|
||||
Ok(())
|
||||
} else if meta.path.is_ident("n") {
|
||||
if amount.is_some() {
|
||||
return Err(Error::new_spanned(meta.path, "duplicate `n` key"));
|
||||
}
|
||||
amount = Some(meta.value()?.parse()?);
|
||||
Ok(())
|
||||
} else {
|
||||
match qname.parse_incremental_from_meta(meta)? {
|
||||
None => Ok(()),
|
||||
|
|
@ -855,6 +878,7 @@ impl XmlFieldMeta {
|
|||
span: meta.path.span(),
|
||||
qname,
|
||||
fields,
|
||||
amount,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -952,4 +976,12 @@ impl XmlFieldMeta {
|
|||
Self::Extract { ref span, .. } => *span,
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract an explicit type specification if it exists.
|
||||
pub(crate) fn take_type(&mut self) -> Option<Type> {
|
||||
match self {
|
||||
Self::Text { ref mut type_, .. } => type_.take(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue