xso: allow omission of namespace and name on extracts
This is a quality-of-life improvement, as it may save lots of typing in the common case (see the diff in parsers).
This commit is contained in:
parent
48855843d4
commit
df63c2a78f
9 changed files with 129 additions and 54 deletions
|
|
@ -12,6 +12,7 @@ use syn::{spanned::Spanned, *};
|
|||
|
||||
use crate::error_message::ParentRef;
|
||||
use crate::field::{FieldBuilderPart, FieldDef, FieldIteratorPart, FieldTempInit};
|
||||
use crate::meta::NamespaceRef;
|
||||
use crate::scope::{mangle_member, AsItemsScope, FromEventsScope};
|
||||
use crate::state::{AsItemsSubmachine, FromEventsSubmachine, State};
|
||||
use crate::types::{feed_fn, namespace_ty, ncnamestr_cow_ty, phantom_lifetime_ty};
|
||||
|
|
@ -55,7 +56,10 @@ impl Compound {
|
|||
}
|
||||
|
||||
/// Construct a compound from fields.
|
||||
pub(crate) fn from_fields(compound_fields: &Fields) -> Result<Self> {
|
||||
pub(crate) fn from_fields(
|
||||
compound_fields: &Fields,
|
||||
container_namespace: &NamespaceRef,
|
||||
) -> Result<Self> {
|
||||
Self::from_field_defs(compound_fields.iter().enumerate().map(|(i, field)| {
|
||||
let index = match i.try_into() {
|
||||
Ok(v) => v,
|
||||
|
|
@ -68,7 +72,7 @@ impl Compound {
|
|||
))
|
||||
}
|
||||
};
|
||||
FieldDef::from_field(field, index)
|
||||
FieldDef::from_field(field, index, container_namespace)
|
||||
}))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ struct NameVariant {
|
|||
|
||||
impl NameVariant {
|
||||
/// Construct a new name-selected variant from its declaration.
|
||||
fn new(decl: &Variant) -> Result<Self> {
|
||||
fn new(decl: &Variant, enum_namespace: &NamespaceRef) -> Result<Self> {
|
||||
// We destructure here so that we get informed when new fields are
|
||||
// added and can handle them, either by processing them or raising
|
||||
// an error if they are present.
|
||||
|
|
@ -59,7 +59,7 @@ impl NameVariant {
|
|||
Ok(Self {
|
||||
name,
|
||||
ident: decl.ident.clone(),
|
||||
inner: Compound::from_fields(&decl.fields)?,
|
||||
inner: Compound::from_fields(&decl.fields, enum_namespace)?,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -190,7 +190,7 @@ impl EnumDef {
|
|||
let mut variants = Vec::new();
|
||||
let mut seen_names = HashMap::new();
|
||||
for variant in variant_iter {
|
||||
let variant = NameVariant::new(variant)?;
|
||||
let variant = NameVariant::new(variant, &namespace)?;
|
||||
if let Some(other) = seen_names.get(&variant.name) {
|
||||
return Err(Error::new_spanned(
|
||||
variant.name,
|
||||
|
|
|
|||
|
|
@ -192,6 +192,28 @@ enum FieldKind {
|
|||
},
|
||||
}
|
||||
|
||||
fn default_name(span: Span, name: Option<NameRef>, field_ident: Option<&Ident>) -> Result<NameRef> {
|
||||
match name {
|
||||
Some(v) => Ok(v),
|
||||
None => match field_ident {
|
||||
None => Err(Error::new(
|
||||
span,
|
||||
"name must be explicitly specified with the `name` key on unnamed fields",
|
||||
)),
|
||||
Some(field_ident) => match NcName::try_from(field_ident.to_string()) {
|
||||
Ok(value) => Ok(NameRef::Literal {
|
||||
span: field_ident.span(),
|
||||
value,
|
||||
}),
|
||||
Err(e) => Err(Error::new(
|
||||
field_ident.span(),
|
||||
format!("invalid XML name: {}", e),
|
||||
)),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
impl FieldKind {
|
||||
/// Construct a new field implementation from the meta attributes.
|
||||
///
|
||||
|
|
@ -199,34 +221,22 @@ impl FieldKind {
|
|||
/// it is not specified explicitly.
|
||||
///
|
||||
/// `field_ty` is needed for type inferrence on extracted fields.
|
||||
fn from_meta(meta: XmlFieldMeta, field_ident: Option<&Ident>, field_ty: &Type) -> Result<Self> {
|
||||
///
|
||||
/// `container_namespace` is used in some cases to insert a default
|
||||
/// namespace.
|
||||
fn from_meta(
|
||||
meta: XmlFieldMeta,
|
||||
field_ident: Option<&Ident>,
|
||||
field_ty: &Type,
|
||||
container_namespace: &NamespaceRef,
|
||||
) -> Result<Self> {
|
||||
match meta {
|
||||
XmlFieldMeta::Attribute {
|
||||
span,
|
||||
qname: QNameRef { namespace, name },
|
||||
default_,
|
||||
} => {
|
||||
let xml_name = match name {
|
||||
Some(v) => v,
|
||||
None => match field_ident {
|
||||
None => return Err(Error::new(
|
||||
span,
|
||||
"attribute name must be explicitly specified using `#[xml(attribute = ..)] on unnamed fields",
|
||||
)),
|
||||
Some(field_ident) => match NcName::try_from(field_ident.to_string()) {
|
||||
Ok(value) => NameRef::Literal {
|
||||
span: field_ident.span(),
|
||||
value,
|
||||
},
|
||||
Err(e) => {
|
||||
return Err(Error::new(
|
||||
field_ident.span(),
|
||||
format!("invalid XML attribute name: {}", e),
|
||||
))
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
let xml_name = default_name(span, name, field_ident)?;
|
||||
|
||||
Ok(Self::Attribute {
|
||||
xml_name,
|
||||
|
|
@ -267,19 +277,8 @@ impl FieldKind {
|
|||
qname: QNameRef { namespace, name },
|
||||
fields,
|
||||
} => {
|
||||
let Some(xml_namespace) = namespace else {
|
||||
return Err(Error::new(
|
||||
span,
|
||||
"`#[xml(extract(..))]` must contain a `namespace` key.",
|
||||
));
|
||||
};
|
||||
|
||||
let Some(xml_name) = name else {
|
||||
return Err(Error::new(
|
||||
span,
|
||||
"`#[xml(extract(..))]` must contain a `name` key.",
|
||||
));
|
||||
};
|
||||
let xml_namespace = namespace.unwrap_or_else(|| container_namespace.clone());
|
||||
let xml_name = default_name(span, name, field_ident)?;
|
||||
|
||||
let field = {
|
||||
let mut fields = fields.into_iter();
|
||||
|
|
@ -301,7 +300,7 @@ impl FieldKind {
|
|||
};
|
||||
|
||||
let parts = Compound::from_field_defs(
|
||||
[FieldDef::from_extract(field, 0, field_ty)].into_iter(),
|
||||
[FieldDef::from_extract(field, 0, field_ty, &xml_namespace)].into_iter(),
|
||||
)?;
|
||||
|
||||
Ok(Self::Extract {
|
||||
|
|
@ -334,7 +333,11 @@ impl FieldDef {
|
|||
///
|
||||
/// The `index` must be the zero-based index of the field even for named
|
||||
/// fields.
|
||||
pub(crate) fn from_field(field: &syn::Field, index: u32) -> Result<Self> {
|
||||
pub(crate) fn from_field(
|
||||
field: &syn::Field,
|
||||
index: u32,
|
||||
container_namespace: &NamespaceRef,
|
||||
) -> Result<Self> {
|
||||
let field_span = field.span();
|
||||
let meta = XmlFieldMeta::parse_from_attributes(&field.attrs, &field_span)?;
|
||||
|
||||
|
|
@ -352,7 +355,7 @@ impl FieldDef {
|
|||
let ty = field.ty.clone();
|
||||
|
||||
Ok(Self {
|
||||
kind: FieldKind::from_meta(meta, ident, &ty)?,
|
||||
kind: FieldKind::from_meta(meta, ident, &ty, container_namespace)?,
|
||||
member,
|
||||
ty,
|
||||
})
|
||||
|
|
@ -362,12 +365,17 @@ impl FieldDef {
|
|||
///
|
||||
/// The `index` must be the zero-based index of the field even for named
|
||||
/// fields.
|
||||
pub(crate) fn from_extract(meta: XmlFieldMeta, index: u32, ty: &Type) -> Result<Self> {
|
||||
pub(crate) fn from_extract(
|
||||
meta: XmlFieldMeta,
|
||||
index: u32,
|
||||
ty: &Type,
|
||||
container_namespace: &NamespaceRef,
|
||||
) -> Result<Self> {
|
||||
let span = meta.span();
|
||||
Ok(Self {
|
||||
member: Member::Unnamed(Index { index, span }),
|
||||
ty: ty.clone(),
|
||||
kind: FieldKind::from_meta(meta, None, ty)?,
|
||||
kind: FieldKind::from_meta(meta, None, ty, container_namespace)?,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ macro_rules! reject_key {
|
|||
pub(crate) use reject_key;
|
||||
|
||||
/// Value for the `#[xml(namespace = ..)]` attribute.
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) enum NamespaceRef {
|
||||
/// The XML namespace is specified as a string literal.
|
||||
LitStr(LitStr),
|
||||
|
|
|
|||
|
|
@ -75,9 +75,9 @@ impl StructDef {
|
|||
};
|
||||
|
||||
Ok(Self {
|
||||
inner: Compound::from_fields(fields, &namespace)?,
|
||||
namespace,
|
||||
name,
|
||||
inner: Compound::from_fields(fields)?,
|
||||
target_ty_ident: ident.clone(),
|
||||
builder_ty_ident,
|
||||
item_iter_ty_ident,
|
||||
|
|
|
|||
Loading…
Reference in a new issue