mirror of https://gitlab.com/xmpp-rs/xmpp-rs.git
xso_proc: merge ExtractMeta into XmlFieldMeta
This transforms the `extract(..)` spec into something extremely close to a real struct type def, with the full flexibility that gives.
This commit is contained in:
parent
89ce34e729
commit
cb51dbf215
|
@ -7,7 +7,7 @@ use syn::*;
|
|||
|
||||
use crate::compound::Compound;
|
||||
use crate::error_message::{self, ParentRef};
|
||||
use crate::meta::{ExtractMeta, Flag, NameRef, NamespaceRef};
|
||||
use crate::meta::{Flag, NameRef, NamespaceRef, XmlFieldMeta};
|
||||
|
||||
use super::{ChildMode, FieldDef, FieldNamespace, FieldParsePart};
|
||||
|
||||
|
@ -61,14 +61,14 @@ impl ExtractDef {
|
|||
span: Span,
|
||||
namespace: FieldNamespace,
|
||||
name: NameRef,
|
||||
parts: Vec<ExtractMeta>,
|
||||
parts: Vec<Box<XmlFieldMeta>>,
|
||||
mut single_extract_type: Option<Type>,
|
||||
) -> Result<Self> {
|
||||
if parts.len() != 1 {
|
||||
single_extract_type = None;
|
||||
}
|
||||
let parts = Compound::new(parts.into_iter().enumerate().map(|(i, x)| {
|
||||
FieldDef::from_extract(span.clone(), x, i as u32, single_extract_type.take())
|
||||
FieldDef::from_extract(span.clone(), *x, i as u32, single_extract_type.take())
|
||||
}))?;
|
||||
Ok(Self {
|
||||
namespace,
|
||||
|
@ -268,7 +268,7 @@ impl ChildField {
|
|||
mode: ChildMode,
|
||||
namespace: Option<NamespaceRef>,
|
||||
name: Option<NameRef>,
|
||||
extract: Vec<ExtractMeta>,
|
||||
extract: Vec<Box<XmlFieldMeta>>,
|
||||
default_on_missing: Flag,
|
||||
field_type: &Type,
|
||||
) -> Result<Self> {
|
||||
|
|
|
@ -21,7 +21,7 @@ use quote::quote;
|
|||
use syn::{spanned::Spanned, *};
|
||||
|
||||
use crate::error_message::ParentRef;
|
||||
use crate::meta::{ChildMode, ExtractMeta, NamespaceRef, StaticNamespace, XmlFieldMeta};
|
||||
use crate::meta::{ChildMode, NamespaceRef, StaticNamespace, XmlFieldMeta};
|
||||
|
||||
use self::attribute::AttributeField;
|
||||
use self::child::ChildField;
|
||||
|
@ -156,26 +156,78 @@ impl FieldKind {
|
|||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<ExtractMeta> for FieldKind {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(other: ExtractMeta) -> Result<Self> {
|
||||
match other {
|
||||
ExtractMeta::Attribute {
|
||||
/// Construct a `FieldKind` by processing the options in the meta.
|
||||
///
|
||||
/// *Note*: Explicit type specifications are rejected by this function.
|
||||
/// Those must have been taken out of the `meta` before calling this
|
||||
/// function. The compile-time error message emitted by this function is
|
||||
/// not very useful in this case, so if you do not want to support
|
||||
/// explicit types, you should take them out at the call site and emit
|
||||
/// a more useful error there.
|
||||
fn new(
|
||||
span: &Span,
|
||||
field_ident: Option<&Ident>,
|
||||
field_ty: &Type,
|
||||
meta: XmlFieldMeta,
|
||||
) -> Result<Self> {
|
||||
match meta {
|
||||
XmlFieldMeta::Attribute {
|
||||
name,
|
||||
ty: _, // this is processed in FieldDef::from_extract already
|
||||
default_on_missing,
|
||||
} => Ok(Self::Attribute(AttributeField {
|
||||
name: name.into(),
|
||||
codec,
|
||||
ty,
|
||||
} => {
|
||||
if let Some(ty) = ty {
|
||||
return Err(Error::new_spanned(ty, "cannot set attribute type here"));
|
||||
};
|
||||
Ok(FieldKind::Attribute(AttributeField::new(
|
||||
span,
|
||||
field_ident,
|
||||
name,
|
||||
default_on_missing,
|
||||
codec,
|
||||
)?))
|
||||
}
|
||||
XmlFieldMeta::Child {
|
||||
mode,
|
||||
namespace,
|
||||
name,
|
||||
extract,
|
||||
default_on_missing,
|
||||
codec: None,
|
||||
})),
|
||||
ExtractMeta::Text {
|
||||
ty: _, // this is processed in FieldDef::from_extract already
|
||||
} => Ok(Self::Text(TextField { codec: None })),
|
||||
ExtractMeta::Elements => Ok(Self::Elements(ElementsField { selector: None })),
|
||||
} => Ok(FieldKind::Child(ChildField::new(
|
||||
span,
|
||||
mode,
|
||||
namespace,
|
||||
name,
|
||||
extract,
|
||||
default_on_missing,
|
||||
field_ty,
|
||||
)?)),
|
||||
XmlFieldMeta::Text { codec, ty } => {
|
||||
if let Some(ty) = ty {
|
||||
return Err(Error::new_spanned(ty, "cannot set text type here"));
|
||||
};
|
||||
Ok(FieldKind::Text(TextField::new(span, codec)?))
|
||||
}
|
||||
XmlFieldMeta::Namespace => Ok(FieldKind::Namespace(NamespaceField::new())),
|
||||
XmlFieldMeta::Element {
|
||||
namespace,
|
||||
name,
|
||||
default_on_missing,
|
||||
} => Ok(FieldKind::Element(ElementField::new(
|
||||
span,
|
||||
namespace,
|
||||
name,
|
||||
default_on_missing,
|
||||
)?)),
|
||||
XmlFieldMeta::Elements { namespace, name } => Ok(FieldKind::Elements(
|
||||
ElementsField::new(span, namespace, name)?,
|
||||
)),
|
||||
XmlFieldMeta::Flag { namespace, name } => {
|
||||
Ok(FieldKind::Flag(FlagField::new(span, namespace, name)?))
|
||||
}
|
||||
XmlFieldMeta::Ignore => Ok(FieldKind::Ignore),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -228,7 +280,8 @@ fn try_unwrap_option_type(ty: Type) -> Type {
|
|||
}
|
||||
|
||||
impl FieldDef {
|
||||
/// Generate a [`FieldDef`] from an [`ExtractMeta`] specification.
|
||||
/// Generate a [`FieldDef`] as extract from an [`XmlFieldMeta`]
|
||||
/// specification.
|
||||
///
|
||||
/// This is used to build a [`crate::compound::Compound`] used to parse
|
||||
/// the extract. As it would otherwise be an insane amount of duplication,
|
||||
|
@ -251,24 +304,20 @@ impl FieldDef {
|
|||
/// type.
|
||||
fn from_extract(
|
||||
span: Span,
|
||||
mut extract: ExtractMeta,
|
||||
mut extract: XmlFieldMeta,
|
||||
index: u32,
|
||||
single_extract_type: Option<Type>,
|
||||
) -> Result<Self> {
|
||||
let string_ty: Type =
|
||||
parse_str("::std::string::String").expect("cannot construct string type");
|
||||
let ty = match extract {
|
||||
ExtractMeta::Attribute { ref mut ty, .. } => ty.take(),
|
||||
ExtractMeta::Text { ref mut ty, .. } => ty.take(),
|
||||
ExtractMeta::Elements => Some(
|
||||
parse_str("::std::vec::Vec<::xso::exports::minidom::Element>")
|
||||
.expect("cannot construct elements type"),
|
||||
),
|
||||
};
|
||||
let ty = extract.determine_type();
|
||||
let ty = ty
|
||||
.or(single_extract_type.map(try_unwrap_option_type))
|
||||
.unwrap_or(string_ty);
|
||||
let kind = extract.try_into()?;
|
||||
let kind = FieldKind::new(
|
||||
&span, None, // field_ident is always none here, we use unnamed fields.
|
||||
&ty, extract,
|
||||
)?;
|
||||
Ok(Self {
|
||||
span,
|
||||
ident: Member::Unnamed(Index {
|
||||
|
@ -311,7 +360,7 @@ impl FieldDef {
|
|||
meta = Some((XmlFieldMeta::parse_from_attribute(attr)?, attr.span()));
|
||||
}
|
||||
|
||||
let Some((meta, span)) = meta else {
|
||||
let Some((mut meta, span)) = meta else {
|
||||
return Err(Error::new_spanned(
|
||||
field,
|
||||
"exactly one #[xml(..)] attribute per field required.",
|
||||
|
@ -326,53 +375,14 @@ impl FieldDef {
|
|||
}),
|
||||
};
|
||||
|
||||
let kind = match meta {
|
||||
XmlFieldMeta::Attribute {
|
||||
name,
|
||||
default_on_missing,
|
||||
codec,
|
||||
} => FieldKind::Attribute(AttributeField::new(
|
||||
&span,
|
||||
field.ident.as_ref(),
|
||||
name,
|
||||
default_on_missing,
|
||||
codec,
|
||||
)?),
|
||||
XmlFieldMeta::Child {
|
||||
mode,
|
||||
namespace,
|
||||
name,
|
||||
extract,
|
||||
default_on_missing,
|
||||
} => FieldKind::Child(ChildField::new(
|
||||
&span,
|
||||
mode,
|
||||
namespace,
|
||||
name,
|
||||
extract,
|
||||
default_on_missing,
|
||||
&field.ty,
|
||||
)?),
|
||||
XmlFieldMeta::Text { codec } => FieldKind::Text(TextField::new(&span, codec)?),
|
||||
XmlFieldMeta::Namespace => FieldKind::Namespace(NamespaceField::new()),
|
||||
XmlFieldMeta::Element {
|
||||
namespace,
|
||||
name,
|
||||
default_on_missing,
|
||||
} => FieldKind::Element(ElementField::new(
|
||||
&span,
|
||||
namespace,
|
||||
name,
|
||||
default_on_missing,
|
||||
)?),
|
||||
XmlFieldMeta::Elements { namespace, name } => {
|
||||
FieldKind::Elements(ElementsField::new(&span, namespace, name)?)
|
||||
}
|
||||
XmlFieldMeta::Flag { namespace, name } => {
|
||||
FieldKind::Flag(FlagField::new(&span, namespace, name)?)
|
||||
}
|
||||
XmlFieldMeta::Ignore => FieldKind::Ignore,
|
||||
};
|
||||
if let Some(ty) = meta.take_type() {
|
||||
return Err(Error::new_spanned(
|
||||
ty,
|
||||
"specifying the type on struct or enum variant fields is redundant and not allowed",
|
||||
));
|
||||
}
|
||||
|
||||
let kind = FieldKind::new(&span, field.ident.as_ref(), &field.ty, meta)?;
|
||||
|
||||
Ok(Self {
|
||||
span,
|
||||
|
|
|
@ -142,8 +142,8 @@ impl<'a> MetaParse for ParseNodeFilter<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Parse a `Vec<ExtractMeta>` from a meta.
|
||||
struct ParseExtracts<'a>(&'a mut Vec<ExtractMeta>);
|
||||
/// Parse a `Vec<Box<XmlFieldMeta>>` from a meta.
|
||||
struct ParseExtracts<'a>(&'a mut Vec<Box<XmlFieldMeta>>);
|
||||
|
||||
impl<'a> MetaParse for ParseExtracts<'a> {
|
||||
fn name(&self) -> &'static str {
|
||||
|
@ -152,7 +152,7 @@ impl<'a> MetaParse for ParseExtracts<'a> {
|
|||
|
||||
fn force_parse_at_meta<'x>(&mut self, meta: meta::ParseNestedMeta<'x>) -> Result<()> {
|
||||
meta.parse_nested_meta(|meta| {
|
||||
self.0.push(ExtractMeta::parse_from_meta(meta)?);
|
||||
self.0.push(Box::new(XmlFieldMeta::parse_from_meta(meta)?));
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
@ -645,82 +645,6 @@ impl AttributeMeta {
|
|||
}
|
||||
}
|
||||
|
||||
/// A single extraction part inside an `#[xml(..(.., extract(..)))]`
|
||||
/// attribute.
|
||||
#[cfg_attr(feature = "debug", derive(Debug))]
|
||||
pub(crate) enum ExtractMeta {
|
||||
/// XML attribute extraction.
|
||||
///
|
||||
/// Maps to `extract(.., attribute, ..)`,
|
||||
/// `extract(.., attribute = .., ..)`, or
|
||||
/// `extract(.., attribute(..), ..)`.
|
||||
Attribute {
|
||||
name: NameRef,
|
||||
default_on_missing: Flag,
|
||||
ty: Option<Type>,
|
||||
},
|
||||
|
||||
/// XML text extraction.
|
||||
///
|
||||
/// Maps to `extract(.., text, ..)`.
|
||||
Text { ty: Option<Type> },
|
||||
|
||||
/// XML child element extraction.
|
||||
///
|
||||
/// Maps to `extract(.., elements, ..)`.
|
||||
Elements,
|
||||
}
|
||||
|
||||
impl ExtractMeta {
|
||||
/// Parse a single extraction spec from the given `meta`.
|
||||
///
|
||||
/// See the enum variants for accepted syntaxes.
|
||||
fn parse_from_meta(meta: meta::ParseNestedMeta<'_>) -> Result<Self> {
|
||||
if meta.path.is_ident("text") {
|
||||
if meta.input.peek(token::Paren) {
|
||||
let mut ty: Option<Type> = None;
|
||||
#[rustfmt::skip] // rustfmt transforms the code so that the attribute inside parse_meta! is on an expression which is not allowed
|
||||
meta.parse_nested_meta(|meta| {
|
||||
parse_meta!(meta, ParseValue("type", &mut ty),)
|
||||
})?;
|
||||
Ok(Self::Text { ty })
|
||||
} else {
|
||||
Ok(Self::Text { ty: None })
|
||||
}
|
||||
} else if meta.path.is_ident("attribute") {
|
||||
let path = meta.path.clone();
|
||||
let meta = AttributeMeta::parse_from_meta(meta)?;
|
||||
let Some(name) = meta.name else {
|
||||
return Err(syn::Error::new_spanned(path, "attribute name must be specified in extract(), e.g. extract(attribute = \"name\")"));
|
||||
};
|
||||
if let Some(namespace) = meta.namespace {
|
||||
return Err(Error::new_spanned(
|
||||
namespace,
|
||||
"namespaced attributes are not supported yet...",
|
||||
));
|
||||
}
|
||||
Ok(Self::Attribute {
|
||||
name,
|
||||
ty: meta.ty,
|
||||
default_on_missing: meta.default_on_missing,
|
||||
})
|
||||
} else if meta.path.is_ident("elements") {
|
||||
if meta.input.peek(token::Paren) {
|
||||
return Err(syn::Error::new_spanned(
|
||||
meta.path,
|
||||
"arguments to `collect` inside #[xml(..(extract(..)))] are not supported.",
|
||||
));
|
||||
}
|
||||
Ok(Self::Elements)
|
||||
} else {
|
||||
return Err(syn::Error::new_spanned(
|
||||
meta.path,
|
||||
"unsupported extract spec",
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Mode for destructured child collection.
|
||||
#[cfg_attr(feature = "debug", derive(Debug))]
|
||||
pub(crate) enum ChildMode {
|
||||
|
@ -753,6 +677,9 @@ pub(crate) enum XmlFieldMeta {
|
|||
|
||||
/// Contents of the `codec = ..` option.
|
||||
codec: Option<Type>,
|
||||
|
||||
/// Contents of the `type = ..` option.
|
||||
ty: Option<Type>,
|
||||
},
|
||||
|
||||
/// Maps the field to a destructured XML child element.
|
||||
|
@ -774,7 +701,7 @@ pub(crate) enum XmlFieldMeta {
|
|||
name: Option<NameRef>,
|
||||
|
||||
/// Contents of the `extract(..)` option.
|
||||
extract: Vec<ExtractMeta>,
|
||||
extract: Vec<Box<XmlFieldMeta>>,
|
||||
|
||||
/// Presence of the `default` flag.
|
||||
default_on_missing: Flag,
|
||||
|
@ -793,6 +720,9 @@ pub(crate) enum XmlFieldMeta {
|
|||
Text {
|
||||
/// Contents of the `codec = ..` option.
|
||||
codec: Option<Type>,
|
||||
|
||||
/// Contents of the `type = ..` option.
|
||||
ty: Option<Type>,
|
||||
},
|
||||
|
||||
/// Maps the field to a subset of XML child elements, without
|
||||
|
@ -840,6 +770,9 @@ pub(crate) enum XmlFieldMeta {
|
|||
Ignore,
|
||||
}
|
||||
|
||||
static VALID_FIELD_XML_OPTIONS: &'static str =
|
||||
"attribute, child, children, namespace, text, elements, element, flag, ignore";
|
||||
|
||||
impl XmlFieldMeta {
|
||||
/// Processes a `#[xml(attribute)]` meta, creating a [`Self::Attribute`]
|
||||
/// variant.
|
||||
|
@ -851,11 +784,11 @@ impl XmlFieldMeta {
|
|||
"namespaced attributes are not supported yet...",
|
||||
));
|
||||
}
|
||||
meta.reject_type("type = .. is not allowed on fields (only on extracts)")?;
|
||||
Ok(Self::Attribute {
|
||||
name: meta.name,
|
||||
default_on_missing: meta.default_on_missing,
|
||||
codec: meta.codec,
|
||||
ty: meta.ty,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -866,13 +799,13 @@ impl XmlFieldMeta {
|
|||
) -> Result<(
|
||||
Option<NamespaceRef>,
|
||||
Option<NameRef>,
|
||||
Vec<ExtractMeta>,
|
||||
Vec<Box<XmlFieldMeta>>,
|
||||
Flag,
|
||||
)> {
|
||||
if meta.input.peek(token::Paren) {
|
||||
let mut name: Option<NameRef> = None;
|
||||
let mut namespace: Option<NamespaceRef> = None;
|
||||
let mut extract: Vec<ExtractMeta> = Vec::new();
|
||||
let mut extract: Vec<Box<XmlFieldMeta>> = Vec::new();
|
||||
let mut default_on_missing = Flag::Absent;
|
||||
meta.parse_nested_meta(|meta| {
|
||||
parse_meta!(
|
||||
|
@ -928,13 +861,18 @@ impl XmlFieldMeta {
|
|||
/// variant.
|
||||
fn text_from_meta(meta: meta::ParseNestedMeta<'_>) -> Result<Self> {
|
||||
let mut codec: Option<Type> = None;
|
||||
let mut ty: Option<Type> = None;
|
||||
if meta.input.peek(token::Paren) {
|
||||
#[rustfmt::skip] // rustfmt transforms the code so that the attribute inside parse_meta! is on an expression which is not allowed
|
||||
meta.parse_nested_meta(|meta| {
|
||||
parse_meta!(meta, ParseValue("codec", &mut codec),)
|
||||
parse_meta!(
|
||||
meta,
|
||||
ParseValue("codec", &mut codec),
|
||||
ParseValue("type", &mut ty),
|
||||
)
|
||||
})?;
|
||||
}
|
||||
Ok(Self::Text { codec })
|
||||
Ok(Self::Text { codec, ty })
|
||||
}
|
||||
|
||||
/// Processes a `#[xml(element)]` meta, creating a [`Self::Element`]
|
||||
|
@ -942,7 +880,7 @@ impl XmlFieldMeta {
|
|||
fn element_from_meta(meta: meta::ParseNestedMeta<'_>) -> Result<Self> {
|
||||
let meta = AttributeMeta::parse_from_meta(meta)?;
|
||||
meta.reject_codec("codec = .. is not allowed on #[xml(element)]")?;
|
||||
meta.reject_type("type = .. is not allowed on fields (only on extracts)")?;
|
||||
meta.reject_type("type = .. is not allowed on #[xml(element)]")?;
|
||||
Ok(Self::Element {
|
||||
name: meta.name,
|
||||
namespace: meta.namespace,
|
||||
|
@ -955,7 +893,7 @@ impl XmlFieldMeta {
|
|||
fn elements_from_meta(meta: meta::ParseNestedMeta<'_>) -> Result<Self> {
|
||||
let meta = AttributeMeta::parse_from_meta(meta)?;
|
||||
meta.reject_codec("codec = .. is not allowed on #[xml(elements)]")?;
|
||||
meta.reject_type("type = .. is not allowed on fields (only on extracts)")?;
|
||||
meta.reject_type("type = .. is not allowed on #[xml(elements)]")?;
|
||||
meta.reject_default("default cannot be used on #[xml(elements)] (it is implied, the default is the empty container)")?;
|
||||
Ok(Self::Elements {
|
||||
name: meta.name,
|
||||
|
@ -966,7 +904,7 @@ impl XmlFieldMeta {
|
|||
/// Processes a `#[xml(flag)]` meta, creating a [`Self::Flag`] variant.
|
||||
fn flag_from_meta(meta: meta::ParseNestedMeta<'_>) -> Result<Self> {
|
||||
let meta = AttributeMeta::parse_from_meta(meta)?;
|
||||
meta.reject_type("type = .. is not allowed on fields (only on extracts)")?;
|
||||
meta.reject_type("type = .. is not allowed on #[xml(flag)]")?;
|
||||
if let Flag::Present(default_on_missing) = meta.default_on_missing {
|
||||
return Err(syn::Error::new(
|
||||
default_on_missing,
|
||||
|
@ -985,6 +923,36 @@ impl XmlFieldMeta {
|
|||
Ok(Self::Ignore)
|
||||
}
|
||||
|
||||
fn parse_from_meta(meta: meta::ParseNestedMeta<'_>) -> Result<Self> {
|
||||
if meta.path.is_ident("attribute") {
|
||||
Self::attribute_from_meta(meta)
|
||||
} else if meta.path.is_ident("child") {
|
||||
Self::child_from_meta(meta)
|
||||
} else if meta.path.is_ident("children") {
|
||||
Self::children_from_meta(meta)
|
||||
} else if meta.path.is_ident("namespace") {
|
||||
Self::namespace_from_meta(meta)
|
||||
} else if meta.path.is_ident("text") {
|
||||
Self::text_from_meta(meta)
|
||||
} else if meta.path.is_ident("elements") {
|
||||
Self::elements_from_meta(meta)
|
||||
} else if meta.path.is_ident("element") {
|
||||
Self::element_from_meta(meta)
|
||||
} else if meta.path.is_ident("flag") {
|
||||
Self::flag_from_meta(meta)
|
||||
} else if meta.path.is_ident("ignore") {
|
||||
Self::ignore_from_meta(meta)
|
||||
} else {
|
||||
Err(Error::new_spanned(
|
||||
meta.path,
|
||||
format!(
|
||||
"unsupported field option. supported options: {}.",
|
||||
VALID_FIELD_XML_OPTIONS
|
||||
),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse an `#[xml(..)]` attribute.
|
||||
///
|
||||
/// This switches based on the first identifier within the `#[xml(..)]`
|
||||
|
@ -999,9 +967,6 @@ impl XmlFieldMeta {
|
|||
/// Undefined options or options with incompatible values are rejected
|
||||
/// with an appropriate compile-time error.
|
||||
pub(crate) fn parse_from_attribute(attr: &Attribute) -> Result<Self> {
|
||||
static VALID_OPTIONS: &'static str =
|
||||
"attribute, child, children, namespace, text, elements, element, flag";
|
||||
|
||||
let mut result: Option<Self> = None;
|
||||
|
||||
attr.parse_nested_meta(|meta| {
|
||||
|
@ -1012,42 +977,8 @@ impl XmlFieldMeta {
|
|||
));
|
||||
}
|
||||
|
||||
if meta.path.is_ident("attribute") {
|
||||
result = Some(Self::attribute_from_meta(meta)?);
|
||||
Ok(())
|
||||
} else if meta.path.is_ident("child") {
|
||||
result = Some(Self::child_from_meta(meta)?);
|
||||
Ok(())
|
||||
} else if meta.path.is_ident("children") {
|
||||
result = Some(Self::children_from_meta(meta)?);
|
||||
Ok(())
|
||||
} else if meta.path.is_ident("namespace") {
|
||||
result = Some(Self::namespace_from_meta(meta)?);
|
||||
Ok(())
|
||||
} else if meta.path.is_ident("text") {
|
||||
result = Some(Self::text_from_meta(meta)?);
|
||||
Ok(())
|
||||
} else if meta.path.is_ident("elements") {
|
||||
result = Some(Self::elements_from_meta(meta)?);
|
||||
Ok(())
|
||||
} else if meta.path.is_ident("element") {
|
||||
result = Some(Self::element_from_meta(meta)?);
|
||||
Ok(())
|
||||
} else if meta.path.is_ident("flag") {
|
||||
result = Some(Self::flag_from_meta(meta)?);
|
||||
Ok(())
|
||||
} else if meta.path.is_ident("ignore") {
|
||||
result = Some(Self::ignore_from_meta(meta)?);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::new_spanned(
|
||||
meta.path,
|
||||
format!(
|
||||
"unsupported field option. supported options: {}.",
|
||||
VALID_OPTIONS
|
||||
),
|
||||
))
|
||||
}
|
||||
result = Some(Self::parse_from_meta(meta)?);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
if let Some(result) = result {
|
||||
|
@ -1056,10 +987,60 @@ impl XmlFieldMeta {
|
|||
Err(Error::new_spanned(
|
||||
attr,
|
||||
format!(
|
||||
"missing field options. specify at least one of {}.",
|
||||
VALID_OPTIONS
|
||||
"missing field options. specify at one of {}.",
|
||||
VALID_FIELD_XML_OPTIONS
|
||||
),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// Take the explicit type specification out of this meta and return it,
|
||||
/// if any.
|
||||
///
|
||||
/// This function *does not recurse* into nested `extract(..)` specs.
|
||||
pub(crate) fn take_type(&mut self) -> Option<Type> {
|
||||
match self {
|
||||
XmlFieldMeta::Attribute { ref mut ty, .. } => ty.take(),
|
||||
XmlFieldMeta::Text { ref mut ty, .. } => ty.take(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Take the explicit type specification out of this meta and return it,
|
||||
/// if any.
|
||||
///
|
||||
/// This function **does recurse** into nested `extract(..)` specs and
|
||||
/// returns types for kinds with known type.
|
||||
pub(crate) fn determine_type(&mut self) -> Option<Type> {
|
||||
match self {
|
||||
Self::Attribute { ref mut ty, .. } => ty.take(),
|
||||
Self::Child {
|
||||
ref mut extract,
|
||||
mode: ChildMode::Single,
|
||||
..
|
||||
} => {
|
||||
if extract.len() == 1 {
|
||||
extract[0].take_type()
|
||||
} else {
|
||||
None // cannot be determined from meta alone
|
||||
}
|
||||
}
|
||||
Self::Child {
|
||||
mode: ChildMode::Collection,
|
||||
..
|
||||
} => None, // cannot be determined from meta alone
|
||||
Self::Namespace => None, // cannot be determined from meta alone
|
||||
Self::Text { ref mut ty, .. } => ty.take(),
|
||||
Self::Elements { .. } => Some(
|
||||
parse_str("::std::vec::Vec<::xso::exports::minidom::Element>")
|
||||
.expect("cannot construct elements type"),
|
||||
),
|
||||
Self::Element { .. } => Some(
|
||||
parse_str("::xso::exports::minidom::Element")
|
||||
.expect("cannot construct element type"),
|
||||
),
|
||||
Self::Flag { .. } => Some(parse_str("bool").expect("cannot construct element type")),
|
||||
Self::Ignore => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -350,41 +350,21 @@ The following field kinds are available:
|
|||
|
||||
### Extraction specification
|
||||
|
||||
Inside `extract(..)`, the following parts can be specified:
|
||||
Inside `extract(..)`, there must be a list of type annotations as used on
|
||||
fields. All annotations can be used which can also be used on fields. Because
|
||||
here there is no possibility to do any inferrence on the field type, the
|
||||
following field attributes support an additional, optional `type` argument:
|
||||
|
||||
- `attribute = ..` (specifying the name), `attribute(..)`: Extract an
|
||||
attribute from the child element.
|
||||
- `attribute`
|
||||
- `text`
|
||||
|
||||
Valid options inside `attribute(..)` are:
|
||||
If the `extract(..)` contains exactly one part and the type of the extract
|
||||
is not specified on that one part, it is assumed to be equal to the type of
|
||||
the field the extract is used on.
|
||||
|
||||
- `name = ..` (required): The XML name of the attribute to extract. Unlike
|
||||
on named fields, it is required because the name cannot be inferred from
|
||||
the field identifier.
|
||||
- `type = ..`: The type to use for the attribute.
|
||||
|
||||
If the extract is used on a `#[xml(child)]` field and it consists of only
|
||||
one part, then the type defaults to the type of the field minus a single
|
||||
layer of `Option<..>` wrapping, if any (i.e.: if you have a field with
|
||||
type `Option<Foo>`, the defaulted type is `Foo`).
|
||||
|
||||
If the extract is used on a `#[xml(children)]` field or consists of more
|
||||
than one part, the type is defaulted to [`String`].
|
||||
|
||||
- `text`, `text(..)`: Extract the child's text contents.
|
||||
|
||||
Valid options inside `text(..)` are:
|
||||
|
||||
- `type = ..`: The type to use for the text.
|
||||
|
||||
If the extract is used on a `#[xml(child)]` field and it consists of only
|
||||
one part, then the type defaults to the type of the field minus a single
|
||||
layer of `Option<..>` wrapping, if any (i.e.: if you have a field with
|
||||
type `Option<Foo>`, the defaulted type is `Foo`).
|
||||
|
||||
If the extract is used on a `#[xml(children)]` field or consists of more
|
||||
than one part, the type is defaulted to [`String`].
|
||||
|
||||
- `elements`: Extract the child's child elements as `Vec<Element>`.
|
||||
Otherwise, the default is `String`, which is not going to work in many cases.
|
||||
This limitation could be lifted, but we need a use case for it first :). So
|
||||
if you run into this file an issue please!
|
||||
|
||||
*/
|
||||
pub use xso_proc::FromXml;
|
||||
|
|
Loading…
Reference in New Issue