mirror of
https://gitlab.com/xmpp-rs/xmpp-rs.git
synced 2024-06-26 17:08:26 +02:00
parsers-macros: 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
32121f29a9
commit
3983e0d705
|
@ -350,41 +350,21 @@ The following field kinds are available:
|
||||||
|
|
||||||
### Extraction specification
|
### 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`
|
||||||
attribute from the child element.
|
- `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
|
Otherwise, the default is `String`, which is not going to work in many cases.
|
||||||
on named fields, it is required because the name cannot be inferred from
|
This limitation could be lifted, but we need a use case for it first :). So
|
||||||
the field identifier.
|
if you run into this file an issue please!
|
||||||
- `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>`.
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
pub use xmpp_parsers_macros::FromXml;
|
pub use xmpp_parsers_macros::FromXml;
|
||||||
|
|
|
@ -7,7 +7,7 @@ use syn::*;
|
||||||
|
|
||||||
use crate::compound::Compound;
|
use crate::compound::Compound;
|
||||||
use crate::error_message::{self, ParentRef};
|
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};
|
use super::{ChildMode, FieldDef, FieldNamespace, FieldParsePart};
|
||||||
|
|
||||||
|
@ -61,14 +61,14 @@ impl ExtractDef {
|
||||||
span: Span,
|
span: Span,
|
||||||
namespace: FieldNamespace,
|
namespace: FieldNamespace,
|
||||||
name: NameRef,
|
name: NameRef,
|
||||||
parts: Vec<ExtractMeta>,
|
parts: Vec<Box<XmlFieldMeta>>,
|
||||||
mut single_extract_type: Option<Type>,
|
mut single_extract_type: Option<Type>,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
if parts.len() != 1 {
|
if parts.len() != 1 {
|
||||||
single_extract_type = None;
|
single_extract_type = None;
|
||||||
}
|
}
|
||||||
let parts = Compound::new(parts.into_iter().enumerate().map(|(i, x)| {
|
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 {
|
Ok(Self {
|
||||||
namespace,
|
namespace,
|
||||||
|
@ -268,7 +268,7 @@ impl ChildField {
|
||||||
mode: ChildMode,
|
mode: ChildMode,
|
||||||
namespace: Option<NamespaceRef>,
|
namespace: Option<NamespaceRef>,
|
||||||
name: Option<NameRef>,
|
name: Option<NameRef>,
|
||||||
extract: Vec<ExtractMeta>,
|
extract: Vec<Box<XmlFieldMeta>>,
|
||||||
default_on_missing: Flag,
|
default_on_missing: Flag,
|
||||||
field_type: &Type,
|
field_type: &Type,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
|
|
|
@ -21,7 +21,7 @@ use quote::quote;
|
||||||
use syn::{spanned::Spanned, *};
|
use syn::{spanned::Spanned, *};
|
||||||
|
|
||||||
use crate::error_message::ParentRef;
|
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::attribute::AttributeField;
|
||||||
use self::child::ChildField;
|
use self::child::ChildField;
|
||||||
|
@ -156,26 +156,78 @@ impl FieldKind {
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<ExtractMeta> for FieldKind {
|
/// Construct a `FieldKind` by processing the options in the meta.
|
||||||
type Error = Error;
|
///
|
||||||
|
/// *Note*: Explicit type specifications are rejected by this function.
|
||||||
fn try_from(other: ExtractMeta) -> Result<Self> {
|
/// Those must have been taken out of the `meta` before calling this
|
||||||
match other {
|
/// function. The compile-time error message emitted by this function is
|
||||||
ExtractMeta::Attribute {
|
/// 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,
|
name,
|
||||||
ty: _, // this is processed in FieldDef::from_extract already
|
|
||||||
default_on_missing,
|
default_on_missing,
|
||||||
} => Ok(Self::Attribute(AttributeField {
|
codec,
|
||||||
name: name.into(),
|
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,
|
default_on_missing,
|
||||||
codec: None,
|
} => Ok(FieldKind::Child(ChildField::new(
|
||||||
})),
|
span,
|
||||||
ExtractMeta::Text {
|
mode,
|
||||||
ty: _, // this is processed in FieldDef::from_extract already
|
namespace,
|
||||||
} => Ok(Self::Text(TextField { codec: None })),
|
name,
|
||||||
ExtractMeta::Elements => Ok(Self::Elements(ElementsField { selector: None })),
|
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 {
|
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
|
/// This is used to build a [`crate::compound::Compound`] used to parse
|
||||||
/// the extract. As it would otherwise be an insane amount of duplication,
|
/// the extract. As it would otherwise be an insane amount of duplication,
|
||||||
|
@ -251,24 +304,20 @@ impl FieldDef {
|
||||||
/// type.
|
/// type.
|
||||||
fn from_extract(
|
fn from_extract(
|
||||||
span: Span,
|
span: Span,
|
||||||
mut extract: ExtractMeta,
|
mut extract: XmlFieldMeta,
|
||||||
index: u32,
|
index: u32,
|
||||||
single_extract_type: Option<Type>,
|
single_extract_type: Option<Type>,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let string_ty: Type =
|
let string_ty: Type =
|
||||||
parse_str("::std::string::String").expect("cannot construct string type");
|
parse_str("::std::string::String").expect("cannot construct string type");
|
||||||
let ty = match extract {
|
let ty = extract.determine_type();
|
||||||
ExtractMeta::Attribute { ref mut ty, .. } => ty.take(),
|
|
||||||
ExtractMeta::Text { ref mut ty, .. } => ty.take(),
|
|
||||||
ExtractMeta::Elements => Some(
|
|
||||||
parse_str("::std::vec::Vec<::xmpp_parsers_core::exports::minidom::Element>")
|
|
||||||
.expect("cannot construct elements type"),
|
|
||||||
),
|
|
||||||
};
|
|
||||||
let ty = ty
|
let ty = ty
|
||||||
.or(single_extract_type.map(try_unwrap_option_type))
|
.or(single_extract_type.map(try_unwrap_option_type))
|
||||||
.unwrap_or(string_ty);
|
.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 {
|
Ok(Self {
|
||||||
span,
|
span,
|
||||||
ident: Member::Unnamed(Index {
|
ident: Member::Unnamed(Index {
|
||||||
|
@ -311,7 +360,7 @@ impl FieldDef {
|
||||||
meta = Some((XmlFieldMeta::parse_from_attribute(attr)?, attr.span()));
|
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(
|
return Err(Error::new_spanned(
|
||||||
field,
|
field,
|
||||||
"exactly one #[xml(..)] attribute per field required.",
|
"exactly one #[xml(..)] attribute per field required.",
|
||||||
|
@ -326,53 +375,14 @@ impl FieldDef {
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
let kind = match meta {
|
if let Some(ty) = meta.take_type() {
|
||||||
XmlFieldMeta::Attribute {
|
return Err(Error::new_spanned(
|
||||||
name,
|
ty,
|
||||||
default_on_missing,
|
"specifying the type on struct or enum variant fields is redundant and not allowed",
|
||||||
codec,
|
));
|
||||||
} => FieldKind::Attribute(AttributeField::new(
|
}
|
||||||
&span,
|
|
||||||
field.ident.as_ref(),
|
let kind = FieldKind::new(&span, field.ident.as_ref(), &field.ty, meta)?;
|
||||||
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,
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
span,
|
span,
|
||||||
|
|
|
@ -142,8 +142,8 @@ impl<'a> MetaParse for ParseNodeFilter<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a `Vec<ExtractMeta>` from a meta.
|
/// Parse a `Vec<Box<XmlFieldMeta>>` from a meta.
|
||||||
struct ParseExtracts<'a>(&'a mut Vec<ExtractMeta>);
|
struct ParseExtracts<'a>(&'a mut Vec<Box<XmlFieldMeta>>);
|
||||||
|
|
||||||
impl<'a> MetaParse for ParseExtracts<'a> {
|
impl<'a> MetaParse for ParseExtracts<'a> {
|
||||||
fn name(&self) -> &'static str {
|
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<()> {
|
fn force_parse_at_meta<'x>(&mut self, meta: meta::ParseNestedMeta<'x>) -> Result<()> {
|
||||||
meta.parse_nested_meta(|meta| {
|
meta.parse_nested_meta(|meta| {
|
||||||
self.0.push(ExtractMeta::parse_from_meta(meta)?);
|
self.0.push(Box::new(XmlFieldMeta::parse_from_meta(meta)?));
|
||||||
Ok(())
|
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.
|
/// Mode for destructured child collection.
|
||||||
#[cfg_attr(feature = "debug", derive(Debug))]
|
#[cfg_attr(feature = "debug", derive(Debug))]
|
||||||
pub(crate) enum ChildMode {
|
pub(crate) enum ChildMode {
|
||||||
|
@ -753,6 +677,9 @@ pub(crate) enum XmlFieldMeta {
|
||||||
|
|
||||||
/// Contents of the `codec = ..` option.
|
/// Contents of the `codec = ..` option.
|
||||||
codec: Option<Type>,
|
codec: Option<Type>,
|
||||||
|
|
||||||
|
/// Contents of the `type = ..` option.
|
||||||
|
ty: Option<Type>,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Maps the field to a destructured XML child element.
|
/// Maps the field to a destructured XML child element.
|
||||||
|
@ -774,7 +701,7 @@ pub(crate) enum XmlFieldMeta {
|
||||||
name: Option<NameRef>,
|
name: Option<NameRef>,
|
||||||
|
|
||||||
/// Contents of the `extract(..)` option.
|
/// Contents of the `extract(..)` option.
|
||||||
extract: Vec<ExtractMeta>,
|
extract: Vec<Box<XmlFieldMeta>>,
|
||||||
|
|
||||||
/// Presence of the `default` flag.
|
/// Presence of the `default` flag.
|
||||||
default_on_missing: Flag,
|
default_on_missing: Flag,
|
||||||
|
@ -793,6 +720,9 @@ pub(crate) enum XmlFieldMeta {
|
||||||
Text {
|
Text {
|
||||||
/// Contents of the `codec = ..` option.
|
/// Contents of the `codec = ..` option.
|
||||||
codec: Option<Type>,
|
codec: Option<Type>,
|
||||||
|
|
||||||
|
/// Contents of the `type = ..` option.
|
||||||
|
ty: Option<Type>,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Maps the field to a subset of XML child elements, without
|
/// Maps the field to a subset of XML child elements, without
|
||||||
|
@ -840,6 +770,9 @@ pub(crate) enum XmlFieldMeta {
|
||||||
Ignore,
|
Ignore,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static VALID_FIELD_XML_OPTIONS: &'static str =
|
||||||
|
"attribute, child, children, namespace, text, elements, element, flag, ignore";
|
||||||
|
|
||||||
impl XmlFieldMeta {
|
impl XmlFieldMeta {
|
||||||
/// Processes a `#[xml(attribute)]` meta, creating a [`Self::Attribute`]
|
/// Processes a `#[xml(attribute)]` meta, creating a [`Self::Attribute`]
|
||||||
/// variant.
|
/// variant.
|
||||||
|
@ -851,11 +784,11 @@ impl XmlFieldMeta {
|
||||||
"namespaced attributes are not supported yet...",
|
"namespaced attributes are not supported yet...",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
meta.reject_type("type = .. is not allowed on fields (only on extracts)")?;
|
|
||||||
Ok(Self::Attribute {
|
Ok(Self::Attribute {
|
||||||
name: meta.name,
|
name: meta.name,
|
||||||
default_on_missing: meta.default_on_missing,
|
default_on_missing: meta.default_on_missing,
|
||||||
codec: meta.codec,
|
codec: meta.codec,
|
||||||
|
ty: meta.ty,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -866,13 +799,13 @@ impl XmlFieldMeta {
|
||||||
) -> Result<(
|
) -> Result<(
|
||||||
Option<NamespaceRef>,
|
Option<NamespaceRef>,
|
||||||
Option<NameRef>,
|
Option<NameRef>,
|
||||||
Vec<ExtractMeta>,
|
Vec<Box<XmlFieldMeta>>,
|
||||||
Flag,
|
Flag,
|
||||||
)> {
|
)> {
|
||||||
if meta.input.peek(token::Paren) {
|
if meta.input.peek(token::Paren) {
|
||||||
let mut name: Option<NameRef> = None;
|
let mut name: Option<NameRef> = None;
|
||||||
let mut namespace: Option<NamespaceRef> = 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;
|
let mut default_on_missing = Flag::Absent;
|
||||||
meta.parse_nested_meta(|meta| {
|
meta.parse_nested_meta(|meta| {
|
||||||
parse_meta!(
|
parse_meta!(
|
||||||
|
@ -928,13 +861,18 @@ impl XmlFieldMeta {
|
||||||
/// variant.
|
/// variant.
|
||||||
fn text_from_meta(meta: meta::ParseNestedMeta<'_>) -> Result<Self> {
|
fn text_from_meta(meta: meta::ParseNestedMeta<'_>) -> Result<Self> {
|
||||||
let mut codec: Option<Type> = None;
|
let mut codec: Option<Type> = None;
|
||||||
|
let mut ty: Option<Type> = None;
|
||||||
if meta.input.peek(token::Paren) {
|
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
|
#[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| {
|
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`]
|
/// Processes a `#[xml(element)]` meta, creating a [`Self::Element`]
|
||||||
|
@ -942,7 +880,7 @@ impl XmlFieldMeta {
|
||||||
fn element_from_meta(meta: meta::ParseNestedMeta<'_>) -> Result<Self> {
|
fn element_from_meta(meta: meta::ParseNestedMeta<'_>) -> Result<Self> {
|
||||||
let meta = AttributeMeta::parse_from_meta(meta)?;
|
let meta = AttributeMeta::parse_from_meta(meta)?;
|
||||||
meta.reject_codec("codec = .. is not allowed on #[xml(element)]")?;
|
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 {
|
Ok(Self::Element {
|
||||||
name: meta.name,
|
name: meta.name,
|
||||||
namespace: meta.namespace,
|
namespace: meta.namespace,
|
||||||
|
@ -955,7 +893,7 @@ impl XmlFieldMeta {
|
||||||
fn elements_from_meta(meta: meta::ParseNestedMeta<'_>) -> Result<Self> {
|
fn elements_from_meta(meta: meta::ParseNestedMeta<'_>) -> Result<Self> {
|
||||||
let meta = AttributeMeta::parse_from_meta(meta)?;
|
let meta = AttributeMeta::parse_from_meta(meta)?;
|
||||||
meta.reject_codec("codec = .. is not allowed on #[xml(elements)]")?;
|
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)")?;
|
meta.reject_default("default cannot be used on #[xml(elements)] (it is implied, the default is the empty container)")?;
|
||||||
Ok(Self::Elements {
|
Ok(Self::Elements {
|
||||||
name: meta.name,
|
name: meta.name,
|
||||||
|
@ -966,7 +904,7 @@ impl XmlFieldMeta {
|
||||||
/// Processes a `#[xml(flag)]` meta, creating a [`Self::Flag`] variant.
|
/// Processes a `#[xml(flag)]` meta, creating a [`Self::Flag`] variant.
|
||||||
fn flag_from_meta(meta: meta::ParseNestedMeta<'_>) -> Result<Self> {
|
fn flag_from_meta(meta: meta::ParseNestedMeta<'_>) -> Result<Self> {
|
||||||
let meta = AttributeMeta::parse_from_meta(meta)?;
|
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 {
|
if let Flag::Present(default_on_missing) = meta.default_on_missing {
|
||||||
return Err(syn::Error::new(
|
return Err(syn::Error::new(
|
||||||
default_on_missing,
|
default_on_missing,
|
||||||
|
@ -985,6 +923,36 @@ impl XmlFieldMeta {
|
||||||
Ok(Self::Ignore)
|
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.
|
/// Parse an `#[xml(..)]` attribute.
|
||||||
///
|
///
|
||||||
/// This switches based on the first identifier within the `#[xml(..)]`
|
/// 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
|
/// Undefined options or options with incompatible values are rejected
|
||||||
/// with an appropriate compile-time error.
|
/// with an appropriate compile-time error.
|
||||||
pub(crate) fn parse_from_attribute(attr: &Attribute) -> Result<Self> {
|
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;
|
let mut result: Option<Self> = None;
|
||||||
|
|
||||||
attr.parse_nested_meta(|meta| {
|
attr.parse_nested_meta(|meta| {
|
||||||
|
@ -1012,42 +977,8 @@ impl XmlFieldMeta {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if meta.path.is_ident("attribute") {
|
result = Some(Self::parse_from_meta(meta)?);
|
||||||
result = Some(Self::attribute_from_meta(meta)?);
|
Ok(())
|
||||||
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
|
|
||||||
),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
if let Some(result) = result {
|
if let Some(result) = result {
|
||||||
|
@ -1056,10 +987,60 @@ impl XmlFieldMeta {
|
||||||
Err(Error::new_spanned(
|
Err(Error::new_spanned(
|
||||||
attr,
|
attr,
|
||||||
format!(
|
format!(
|
||||||
"missing field options. specify at least one of {}.",
|
"missing field options. specify at one of {}.",
|
||||||
VALID_OPTIONS
|
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<::xmpp_parsers_core::exports::minidom::Element>")
|
||||||
|
.expect("cannot construct elements type"),
|
||||||
|
),
|
||||||
|
Self::Element { .. } => Some(
|
||||||
|
parse_str("::xmpp_parsers_core::exports::minidom::Element")
|
||||||
|
.expect("cannot construct element type"),
|
||||||
|
),
|
||||||
|
Self::Flag { .. } => Some(parse_str("bool").expect("cannot construct element type")),
|
||||||
|
Self::Ignore => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user