mirror of https://gitlab.com/xmpp-rs/xmpp-rs.git
xso_proc: introduce Field trait and object
This simplifies the code of FieldDef and makes it more extensible for future ideas.
This commit is contained in:
parent
56e78a8197
commit
06732ca721
|
@ -1175,3 +1175,32 @@ fn element_codec_roundtrip() {
|
|||
other => panic!("unexpected result: {:?}", other),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(FromXml, IntoXml, PartialEq, Clone, Debug)]
|
||||
#[xml(namespace = self::TEST_NS1, name = "child-switched", child = self::TEST_NS2)]
|
||||
pub enum ChildSwitchedEnum {
|
||||
#[xml(name = "variant-1")]
|
||||
Variant1 {
|
||||
#[xml(text)]
|
||||
data: String,
|
||||
},
|
||||
#[xml(name = "variant-2")]
|
||||
Variant2 {
|
||||
#[xml(attribute)]
|
||||
data: String,
|
||||
},
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn child_switched_enum_roundtrip_1() {
|
||||
crate::util::test::roundtrip_full::<ChildSwitchedEnum>(
|
||||
"<child-switched xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'><variant-1 xmlns='urn:uuid:9a1f4eab-1cfd-464c-a16a-282877cd516f'>some data</variant-1></child-switched>",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn child_switched_enum_roundtrip_2() {
|
||||
crate::util::test::roundtrip_full::<ChildSwitchedEnum>(
|
||||
"<child-switched xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'><variant-2 xmlns='urn:uuid:9a1f4eab-1cfd-464c-a16a-282877cd516f' data='other data'/></child-switched>",
|
||||
);
|
||||
}
|
||||
|
|
|
@ -17,9 +17,5 @@ proc-macro = true
|
|||
|
||||
[dependencies]
|
||||
quote = "^1"
|
||||
syn = { version = "^2", features = ["full"] }
|
||||
syn = { version = "^2", features = ["full", "extra-traits"] }
|
||||
proc-macro2 = "^1"
|
||||
|
||||
[features]
|
||||
debug = ["syn/extra-traits"]
|
||||
default = ["debug"]
|
||||
|
|
|
@ -70,7 +70,7 @@ fn default_on_unknown_attribute(p: Option<Ident>) -> Expr {
|
|||
/// This struct is used to generate the parsing/serialisation loop, but most
|
||||
/// notably it is *not* responsible for matching the incoming element; that
|
||||
/// is the responsibility of the caller of the corresponding functions.
|
||||
#[cfg_attr(feature = "debug", derive(Debug))]
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Compound {
|
||||
/// The fields, in declaration order.
|
||||
fields: Vec<FieldDef>,
|
||||
|
@ -122,7 +122,7 @@ impl Compound {
|
|||
text_field = Some(field.ident.span());
|
||||
}
|
||||
|
||||
if field.kind.is_collect_wildcard() {
|
||||
if field.kind.is_child_wildcard() {
|
||||
if collect_wildcard_field.is_some() {
|
||||
return Err(Error::new_spanned(
|
||||
field.ident,
|
||||
|
@ -195,15 +195,15 @@ impl Compound {
|
|||
/// can be used to ignore attributes which have been used in element
|
||||
/// matching (e.g. enum discriminators).
|
||||
pub(crate) fn build_try_from_element(
|
||||
self,
|
||||
&self,
|
||||
container_name: &ParentRef,
|
||||
container_namespace_expr: &Expr,
|
||||
residual: &Ident,
|
||||
forgive_attributes: &[&str],
|
||||
) -> Result<TokenStream> {
|
||||
let readable_name = container_name.to_string();
|
||||
let on_unknown_child = self.on_unknown_child;
|
||||
let on_unknown_attribute = self.on_unknown_attribute;
|
||||
let on_unknown_child = &self.on_unknown_child;
|
||||
let on_unknown_attribute = &self.on_unknown_attribute;
|
||||
|
||||
let mut init = quote! {};
|
||||
let mut tupinit = quote! {};
|
||||
|
@ -221,7 +221,7 @@ impl Compound {
|
|||
};
|
||||
let mut had_fallback: bool = false;
|
||||
|
||||
for field in self.fields {
|
||||
for field in self.fields.iter() {
|
||||
let ident = field.ident.clone();
|
||||
let FieldParsePart {
|
||||
tempinit: field_tempinit,
|
||||
|
@ -311,14 +311,14 @@ impl Compound {
|
|||
/// Note that the field referenced by [`Self::namespace_field`] is not
|
||||
/// accessed by this function.
|
||||
pub(crate) fn build_into_element(
|
||||
self,
|
||||
&self,
|
||||
container_name: &ParentRef,
|
||||
container_namespace_expr: &Expr,
|
||||
builder: &Ident,
|
||||
mut access_field: impl FnMut(Member) -> Expr,
|
||||
) -> Result<TokenStream> {
|
||||
let mut build = quote! {};
|
||||
for field in self.fields {
|
||||
for field in self.fields.iter() {
|
||||
let field_build = field.build_into_element(
|
||||
container_name,
|
||||
container_namespace_expr,
|
||||
|
|
|
@ -57,7 +57,7 @@ fn build_ident_mapping<I: Iterator<Item = Member>>(
|
|||
///
|
||||
/// The caller of the respective methods decides what string the variant
|
||||
/// matches against and how that match is carried out.
|
||||
#[cfg_attr(feature = "debug", derive(Debug))]
|
||||
#[derive(Debug)]
|
||||
struct StrMatchedVariant {
|
||||
/// The string to match against.
|
||||
value: LitStr,
|
||||
|
@ -193,7 +193,7 @@ impl StrMatchedVariant {
|
|||
}
|
||||
|
||||
/// Variant of an enum where each variant has completely independent matching.
|
||||
#[cfg_attr(feature = "debug", derive(Debug))]
|
||||
#[derive(Debug)]
|
||||
struct DynamicVariant {
|
||||
/// The identifier of the enum variant.
|
||||
ident: Ident,
|
||||
|
@ -248,7 +248,7 @@ impl DynamicVariant {
|
|||
}
|
||||
|
||||
/// An enum which switches on the XML element's name, with a fixed namespace.
|
||||
#[cfg_attr(feature = "debug", derive(Debug))]
|
||||
#[derive(Debug)]
|
||||
struct XmlNameSwitched {
|
||||
/// The exhaustive flag, if set on the enum.
|
||||
///
|
||||
|
@ -450,7 +450,7 @@ impl XmlNameSwitched {
|
|||
}
|
||||
|
||||
/// An enum which switches on the value of an attribute of the XML element.
|
||||
#[cfg_attr(feature = "debug", derive(Debug))]
|
||||
#[derive(Debug)]
|
||||
struct XmlAttributeSwitched {
|
||||
/// The namespace the enum's XML element resides in.
|
||||
namespace: StaticNamespace,
|
||||
|
@ -678,7 +678,7 @@ impl XmlAttributeSwitched {
|
|||
}
|
||||
|
||||
/// An enum where each variant has completely independent matching.
|
||||
#[cfg_attr(feature = "debug", derive(Debug))]
|
||||
#[derive(Debug)]
|
||||
struct Dynamic {
|
||||
/// Variants of the enum.
|
||||
variants: Vec<DynamicVariant>,
|
||||
|
@ -785,7 +785,7 @@ impl Dynamic {
|
|||
|
||||
/// Inner part of [`EnumDef`], supporting the different styles of
|
||||
/// enumerations.
|
||||
#[cfg_attr(feature = "debug", derive(Debug))]
|
||||
#[derive(Debug)]
|
||||
enum EnumInner {
|
||||
/// Enum item where the variants switch on the XML element's name.
|
||||
XmlNameSwitched(XmlNameSwitched),
|
||||
|
@ -840,7 +840,7 @@ impl EnumInner {
|
|||
}
|
||||
|
||||
/// Represent an enum.
|
||||
#[cfg_attr(feature = "debug", derive(Debug))]
|
||||
#[derive(Debug)]
|
||||
struct EnumDef {
|
||||
/// The `validate` value, if set on the enum.
|
||||
///
|
||||
|
|
|
@ -9,8 +9,7 @@ use syn::{spanned::Spanned, Member, Path};
|
|||
///
|
||||
/// This reference can be converted to a hopefully-useful human-readable
|
||||
/// string via [`std::fmt::Display`].
|
||||
#[derive(Clone)]
|
||||
#[cfg_attr(feature = "debug", derive(Debug))]
|
||||
#[derive(Clone, Debug)]
|
||||
pub(super) enum ParentRef {
|
||||
/// The parent is addressable by a path, e.g. a struct type or enum
|
||||
/// variant.
|
||||
|
|
|
@ -7,12 +7,12 @@ use syn::{spanned::Spanned, *};
|
|||
use crate::error_message::{self, ParentRef};
|
||||
use crate::meta::{FlagOr, Name, NameRef};
|
||||
|
||||
use super::FieldParsePart;
|
||||
use super::{Field, FieldParsePart};
|
||||
|
||||
/// A field parsed from an XML attribute.
|
||||
///
|
||||
/// Maps to `#[xml(attribute)]`.
|
||||
#[cfg_attr(feature = "debug", derive(Debug))]
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct AttributeField {
|
||||
/// The XML name of the attribute.
|
||||
///
|
||||
|
@ -71,21 +71,19 @@ impl AttributeField {
|
|||
codec,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct the code necessary to parse the attribute from a
|
||||
/// `minidom::Element` into a field.
|
||||
///
|
||||
/// `ty` must be the field's type. The other arguments are currently
|
||||
/// ignored.
|
||||
pub(super) fn build_try_from_element(
|
||||
self,
|
||||
name: &ParentRef,
|
||||
impl Field for AttributeField {
|
||||
fn build_try_from_element(
|
||||
&self,
|
||||
container_name: &ParentRef,
|
||||
_container_namespace_expr: &Expr,
|
||||
_tempname: Ident,
|
||||
ident: Member,
|
||||
ty: Type,
|
||||
member: &Member,
|
||||
ty: &Type,
|
||||
) -> Result<FieldParsePart> {
|
||||
let missing_msg = error_message::on_missing_attribute(name, &ident);
|
||||
let name = self.name;
|
||||
let missing_msg = error_message::on_missing_attribute(container_name, &member);
|
||||
let name = &self.name;
|
||||
let on_missing = match self.default_ {
|
||||
FlagOr::Absent => {
|
||||
quote! {
|
||||
|
@ -101,14 +99,14 @@ impl AttributeField {
|
|||
#ty_default()
|
||||
}
|
||||
}
|
||||
FlagOr::Value { value, .. } => {
|
||||
FlagOr::Value { ref value, .. } => {
|
||||
quote! {
|
||||
#value()
|
||||
}
|
||||
}
|
||||
};
|
||||
let decode = match self.codec {
|
||||
Some(codec_ty) => {
|
||||
Some(ref codec_ty) => {
|
||||
let codec_ty_decode = quote_spanned! {codec_ty.span()=> <#codec_ty as ::xso::TextCodec::<#ty>>::decode};
|
||||
quote! {
|
||||
residual.attr(#name).map(|value| {
|
||||
|
@ -140,29 +138,29 @@ impl AttributeField {
|
|||
})
|
||||
}
|
||||
|
||||
/// Construct an expression which consumes the ident `builder`, which must
|
||||
/// be a minidom `Builder`, and returns it, modified in such a way that it
|
||||
/// contains the field's data.
|
||||
///
|
||||
/// - `ident` must be an expression to consume the field's data. It is
|
||||
/// evaluated exactly once.
|
||||
/// - `ty` must be the field's type.
|
||||
pub(super) fn build_into_element(self, ident: Expr, ty: Type) -> Result<TokenStream> {
|
||||
fn build_into_element(
|
||||
&self,
|
||||
_container_name: &ParentRef,
|
||||
_container_namespace_expr: &Expr,
|
||||
_member: &Member,
|
||||
ty: &Type,
|
||||
access: Expr,
|
||||
) -> Result<TokenStream> {
|
||||
let encode = match self.codec {
|
||||
Some(codec_ty) => {
|
||||
Some(ref codec_ty) => {
|
||||
let codec_ty_encode = quote_spanned! {codec_ty.span()=> <#codec_ty as ::xso::TextCodec::<#ty>>::encode};
|
||||
quote! {
|
||||
#codec_ty_encode(#ident)
|
||||
#codec_ty_encode(#access)
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let ty_into_optional_xml_text = quote_spanned! {ty.span()=> <#ty as ::xso::IntoOptionalXmlText>::into_optional_xml_text};
|
||||
quote! {
|
||||
#ty_into_optional_xml_text(#ident)
|
||||
#ty_into_optional_xml_text(#access)
|
||||
}
|
||||
}
|
||||
};
|
||||
let name = self.name;
|
||||
let name = &self.name;
|
||||
Ok(quote! {
|
||||
match #encode {
|
||||
Some(v) => builder.attr(#name, v),
|
||||
|
@ -170,4 +168,13 @@ impl AttributeField {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn build_set_namespace(
|
||||
&self,
|
||||
_input: &Ident,
|
||||
_ty: &Type,
|
||||
_access: Expr,
|
||||
) -> Result<TokenStream> {
|
||||
Ok(TokenStream::default())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,13 +9,13 @@ use crate::compound::Compound;
|
|||
use crate::error_message::{self, ParentRef};
|
||||
use crate::meta::{Flag, FlagOr, NameRef, NamespaceRef, XmlFieldMeta};
|
||||
|
||||
use super::{ChildMode, FieldDef, FieldNamespace, FieldParsePart};
|
||||
use super::{ChildMode, Field, FieldDef, FieldNamespace, FieldParsePart};
|
||||
|
||||
/// Definition of a child data extraction.
|
||||
///
|
||||
/// This is used to implement fields annotated with
|
||||
/// `#[xml(child(.., extract(..))]` or `#[xml(children(.., extract(..)))]`.
|
||||
#[cfg_attr(feature = "debug", derive(Debug))]
|
||||
#[derive(Debug)]
|
||||
pub(super) struct ExtractDef {
|
||||
namespace: FieldNamespace,
|
||||
|
||||
|
@ -100,13 +100,13 @@ impl ExtractDef {
|
|||
/// latched into a local variable (in the generated code) and that is
|
||||
/// passed on to the inner call to [`Compound::build_into_element`].
|
||||
fn build_extract(
|
||||
self,
|
||||
&self,
|
||||
container_name: &ParentRef,
|
||||
container_namespace_expr: &Expr,
|
||||
residual: &Ident,
|
||||
) -> Result<TokenStream> {
|
||||
let namespace_expr = self.expand_namespace(container_namespace_expr);
|
||||
let xml_name = self.name;
|
||||
let xml_name = &self.name;
|
||||
|
||||
let nfields = self.parts.field_count();
|
||||
|
||||
|
@ -121,7 +121,7 @@ impl ExtractDef {
|
|||
.build_try_from_element(container_name, &namespace_expr, residual, &[])?;
|
||||
|
||||
let test_expr = match self.namespace {
|
||||
FieldNamespace::Static(xml_namespace) => quote! {
|
||||
FieldNamespace::Static(ref xml_namespace) => quote! {
|
||||
#residual.is(#xml_name, #xml_namespace)
|
||||
},
|
||||
FieldNamespace::Super(_) => quote! {
|
||||
|
@ -153,13 +153,13 @@ impl ExtractDef {
|
|||
/// latched into a local variable (in the generated code) and that is
|
||||
/// passed on to the inner call to [`Compound::build_into_element`].
|
||||
fn build_assemble(
|
||||
self,
|
||||
&self,
|
||||
container_name: &ParentRef,
|
||||
container_namespace_expr: &Expr,
|
||||
field: &Expr,
|
||||
) -> Result<TokenStream> {
|
||||
let xml_namespace = self.expand_namespace(container_namespace_expr);
|
||||
let xml_name = self.name;
|
||||
let xml_name = &self.name;
|
||||
|
||||
let nfields = self.parts.field_count();
|
||||
|
||||
|
@ -174,7 +174,7 @@ impl ExtractDef {
|
|||
let builder = Ident::new("builder", Span::call_site());
|
||||
|
||||
let builder_init = match self.namespace {
|
||||
FieldNamespace::Static(xml_namespace) => quote! {
|
||||
FieldNamespace::Static(ref xml_namespace) => quote! {
|
||||
::xso::exports::minidom::Element::builder(
|
||||
#xml_name,
|
||||
#xml_namespace
|
||||
|
@ -226,7 +226,7 @@ impl ExtractDef {
|
|||
/// A field parsed from an XML child, destructured into a Rust data structure.
|
||||
///
|
||||
/// Maps to `#[xml(child)]` and `#[xml(children)]`.
|
||||
#[cfg_attr(feature = "debug", derive(Debug))]
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ChildField {
|
||||
/// Determines whether one or more matching child elements are expected.
|
||||
///
|
||||
|
@ -373,41 +373,23 @@ impl ChildField {
|
|||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct the code necessary to parse the child from a
|
||||
/// `minidom::Element` into a field.
|
||||
///
|
||||
/// - `name` should be the identifier or path of the containing compound
|
||||
/// and is used to generate runtime error messages.
|
||||
///
|
||||
/// - `tempname` must be an identifier which the implementation can freely
|
||||
/// use for a temporary variable related to parsing.
|
||||
///
|
||||
/// - `ident` must be the target struct member identifier or index, used
|
||||
/// for error messages.
|
||||
///
|
||||
/// - `ty` must be the field's type. If this field is not an extract, the
|
||||
/// type's `FromXml` implementation is used to destructure matching XML
|
||||
/// element(s).
|
||||
///
|
||||
/// - `parent_namespace_expr` may be an expression under which the
|
||||
/// parent compound's `::xso::DynNamespaceEnum` value is
|
||||
/// obtainable, if any. This is needed for fields and compounds
|
||||
/// using `#[xml(namespace = super)]`.
|
||||
pub(super) fn build_try_from_element(
|
||||
self,
|
||||
name: &ParentRef,
|
||||
tempname: Ident,
|
||||
ident: Member,
|
||||
ty: Type,
|
||||
impl Field for ChildField {
|
||||
fn build_try_from_element(
|
||||
&self,
|
||||
container_name: &ParentRef,
|
||||
container_namespace_expr: &Expr,
|
||||
tempname: Ident,
|
||||
member: &Member,
|
||||
ty: &Type,
|
||||
) -> Result<FieldParsePart> {
|
||||
let ty_span = ty.span();
|
||||
let ty_default = quote_spanned! {ty_span=> <#ty as std::default::Default>::default};
|
||||
match self.mode {
|
||||
ChildMode::Single => {
|
||||
let missingerr = error_message::on_missing_child(name, &ident);
|
||||
let duperr = error_message::on_duplicate_child(name, &ident);
|
||||
let missingerr = error_message::on_missing_child(container_name, &member);
|
||||
let duperr = error_message::on_duplicate_child(container_name, &member);
|
||||
let on_missing = match self.default_ {
|
||||
FlagOr::Absent => {
|
||||
quote! {
|
||||
|
@ -419,16 +401,16 @@ impl ChildField {
|
|||
#ty_default()
|
||||
}
|
||||
}
|
||||
FlagOr::Value { value, .. } => {
|
||||
FlagOr::Value { ref value, .. } => {
|
||||
quote! {
|
||||
#value()
|
||||
}
|
||||
}
|
||||
};
|
||||
match self.extract {
|
||||
Some(extract) => {
|
||||
Some(ref extract) => {
|
||||
let extract = extract.build_extract(
|
||||
&name.child(ident),
|
||||
&container_name.child(member.clone()),
|
||||
container_namespace_expr,
|
||||
&Ident::new("residual", Span::call_site()),
|
||||
)?;
|
||||
|
@ -467,9 +449,9 @@ impl ChildField {
|
|||
},
|
||||
};
|
||||
let codec_ty = match self.codec {
|
||||
Some(ty) => Type::Path(TypePath {
|
||||
Some(ref ty) => Type::Path(TypePath {
|
||||
qself: None,
|
||||
path: ty,
|
||||
path: ty.clone(),
|
||||
}),
|
||||
None => ty.clone(),
|
||||
};
|
||||
|
@ -523,9 +505,9 @@ impl ChildField {
|
|||
let ty_extend =
|
||||
quote_spanned! {ty_span=> <#ty as ::std::iter::Extend<#item_ty>>::extend};
|
||||
match self.extract {
|
||||
Some(extract) => {
|
||||
Some(ref extract) => {
|
||||
let extract = extract.build_extract(
|
||||
&name.child(ident),
|
||||
&container_name.child(member.clone()),
|
||||
container_namespace_expr,
|
||||
&Ident::new("residual", Span::call_site()),
|
||||
)?;
|
||||
|
@ -554,9 +536,9 @@ impl ChildField {
|
|||
));
|
||||
}
|
||||
let codec_ty = match self.codec {
|
||||
Some(ty) => Type::Path(TypePath {
|
||||
Some(ref ty) => Type::Path(TypePath {
|
||||
qself: None,
|
||||
path: ty,
|
||||
path: ty.clone(),
|
||||
}),
|
||||
None => item_ty.clone(),
|
||||
};
|
||||
|
@ -587,24 +569,19 @@ impl ChildField {
|
|||
}
|
||||
}
|
||||
|
||||
/// Construct the code necessary to update the namespace on the field.
|
||||
pub(super) fn build_set_namespace(
|
||||
&self,
|
||||
input: &Ident,
|
||||
member: &Expr,
|
||||
member_ty: &Type,
|
||||
) -> Result<TokenStream> {
|
||||
fn build_set_namespace(&self, input: &Ident, ty: &Type, access: Expr) -> Result<TokenStream> {
|
||||
match self.mode {
|
||||
ChildMode::Single => match self.extract {
|
||||
Some(_) => Ok(quote! {}),
|
||||
None => match self.super_namespace {
|
||||
Flag::Absent => Ok(quote! {}),
|
||||
Flag::Present(_) => {
|
||||
let member_ty_span = member_ty.span();
|
||||
let ty_span = ty.span();
|
||||
// using quote_spanned in this way here causes the "the trait `DynNamespace` is not implemented for `..`" error message appear on member_ty instead of on the derive macro invocation.
|
||||
let method = quote_spanned! {member_ty_span=> <#member_ty as ::xso::DynNamespace>::set_namespace};
|
||||
let method =
|
||||
quote_spanned! {ty_span=> <#ty as ::xso::DynNamespace>::set_namespace};
|
||||
Ok(quote! {
|
||||
#method(&mut #member, #input.clone());
|
||||
#method(&mut #access, #input.clone());
|
||||
})
|
||||
}
|
||||
},
|
||||
|
@ -613,28 +590,17 @@ impl ChildField {
|
|||
}
|
||||
}
|
||||
|
||||
/// Construct an expression which consumes the ident `builder`, which must
|
||||
/// be a minidom `Builder`, and returns it, modified in such a way that it
|
||||
/// contains the field's data.
|
||||
///
|
||||
/// - `ident` must be an expression to consume the field's data. It is
|
||||
/// evaluated exactly once.
|
||||
/// - `ty` must be the field's type.
|
||||
/// - `parent_namespace_expr` may be an expression under which the
|
||||
/// parent compound's `::xso::DynNamespaceEnum` value is
|
||||
/// obtainable, if any. This is needed for fields and compounds
|
||||
/// using `#[xml(namespace = super)]`.
|
||||
pub(super) fn build_into_element(
|
||||
self,
|
||||
name: &ParentRef,
|
||||
member: Member,
|
||||
ident: Expr,
|
||||
ty: Type,
|
||||
fn build_into_element(
|
||||
&self,
|
||||
container_name: &ParentRef,
|
||||
container_namespace_expr: &Expr,
|
||||
member: &Member,
|
||||
ty: &Type,
|
||||
access: Expr,
|
||||
) -> Result<TokenStream> {
|
||||
let temp_ident = Ident::new("__data", Span::call_site());
|
||||
let skip_map = match self.skip_if {
|
||||
Some(callable) => quote! {
|
||||
Some(ref callable) => quote! {
|
||||
match #callable(&#temp_ident) {
|
||||
false => Some(#temp_ident),
|
||||
true => None,
|
||||
|
@ -644,7 +610,7 @@ impl ChildField {
|
|||
};
|
||||
match self.mode {
|
||||
ChildMode::Single => match self.extract {
|
||||
Some(extract) => {
|
||||
Some(ref extract) => {
|
||||
let temp_expr = Expr::Path(ExprPath {
|
||||
attrs: Vec::new(),
|
||||
qself: None,
|
||||
|
@ -655,12 +621,12 @@ impl ChildField {
|
|||
.expect("child extract can only have one field!")
|
||||
.clone();
|
||||
let assemble = extract.build_assemble(
|
||||
&name.child(member),
|
||||
&container_name.child(member.clone()),
|
||||
container_namespace_expr,
|
||||
&temp_expr,
|
||||
)?;
|
||||
Ok(quote! {
|
||||
match Option::<#inner_ty>::from(#ident).and_then(|#temp_ident| #skip_map) {
|
||||
match Option::<#inner_ty>::from(#access).and_then(|#temp_ident| #skip_map) {
|
||||
Some(#temp_ident) => builder.append(::xso::exports::minidom::Node::Element(#assemble)),
|
||||
None => builder,
|
||||
}
|
||||
|
@ -668,9 +634,9 @@ impl ChildField {
|
|||
}
|
||||
None => {
|
||||
let codec_ty = match self.codec {
|
||||
Some(ty) => Type::Path(TypePath {
|
||||
Some(ref ty) => Type::Path(TypePath {
|
||||
qself: None,
|
||||
path: ty,
|
||||
path: ty.clone(),
|
||||
}),
|
||||
None => ty.clone(),
|
||||
};
|
||||
|
@ -681,7 +647,7 @@ impl ChildField {
|
|||
let codec_ty_encode = quote_spanned! {codec_ty_span=> <#codec_ty as ::xso::ElementCodec::<#field_ty>>::encode};
|
||||
Ok(quote! {
|
||||
{
|
||||
let #temp_ident = #ident;
|
||||
let #temp_ident = #access;
|
||||
match #skip_map.map(#codec_ty_encode).and_then(#codec_ty_into_tree) {
|
||||
Some(#temp_ident) => builder.append(::xso::exports::minidom::Node::Element(#temp_ident)),
|
||||
None => builder,
|
||||
|
@ -691,9 +657,9 @@ impl ChildField {
|
|||
}
|
||||
},
|
||||
ChildMode::Collection => match self.extract {
|
||||
Some(extract) => {
|
||||
Some(ref extract) => {
|
||||
let assemble = extract.build_assemble(
|
||||
&name.child(member),
|
||||
&container_name.child(member.clone()),
|
||||
container_namespace_expr,
|
||||
&Expr::Path(ExprPath {
|
||||
attrs: Vec::new(),
|
||||
|
@ -703,7 +669,7 @@ impl ChildField {
|
|||
)?;
|
||||
Ok(quote! {
|
||||
builder.append_all(
|
||||
#ident.into_iter().filter_map(|#temp_ident| {
|
||||
#access.into_iter().filter_map(|#temp_ident| {
|
||||
match #skip_map {
|
||||
Some(#temp_ident) => Some(#assemble),
|
||||
None => None,
|
||||
|
@ -719,9 +685,9 @@ impl ChildField {
|
|||
})
|
||||
.expect("failed to construct item type");
|
||||
let codec_ty = match self.codec {
|
||||
Some(ty) => Type::Path(TypePath {
|
||||
Some(ref ty) => Type::Path(TypePath {
|
||||
qself: None,
|
||||
path: ty,
|
||||
path: ty.clone(),
|
||||
}),
|
||||
None => item_ty.clone(),
|
||||
};
|
||||
|
@ -730,7 +696,7 @@ impl ChildField {
|
|||
quote_spanned! {codec_ty_span=> <#codec_ty as ::xso::IntoXml>::into_tree};
|
||||
let codec_ty_encode = quote_spanned! {codec_ty_span=> <#codec_ty as ::xso::ElementCodec::<#item_ty>>::encode};
|
||||
Ok(quote! {
|
||||
builder.append_all(#ident.into_iter().filter_map(|#temp_ident| {
|
||||
builder.append_all(#access.into_iter().filter_map(|#temp_ident| {
|
||||
#skip_map.map(#codec_ty_encode).and_then(#codec_ty_into_tree).map(|el| ::xso::exports::minidom::Node::Element(el))
|
||||
}))
|
||||
})
|
||||
|
|
|
@ -9,13 +9,13 @@ use crate::error_message::{self, ParentRef};
|
|||
use crate::meta::{FlagOr, Name, NameRef, NamespaceRef, StaticNamespace};
|
||||
use crate::structs::ElementSelector;
|
||||
|
||||
use super::FieldParsePart;
|
||||
use super::{Field, FieldParsePart};
|
||||
|
||||
/// A field parsed from an XML child, without destructuring it into Rust
|
||||
/// data structures.
|
||||
///
|
||||
/// Maps to `#[xml(element)]`.
|
||||
#[cfg_attr(feature = "debug", derive(Debug))]
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ElementField {
|
||||
/// Logic to select matching child elements.
|
||||
selector: ElementSelector,
|
||||
|
@ -71,34 +71,22 @@ impl ElementField {
|
|||
};
|
||||
Ok(Self { selector, default_ })
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct the code necessary to parse the child element from a
|
||||
/// `minidom::Element` into a field.
|
||||
///
|
||||
/// - `name` should be the identifier or path of the containing compound
|
||||
/// and is used to generate runtime error messages.
|
||||
///
|
||||
/// - `tempname` must be an identifier which the implementation can freely
|
||||
/// use for a temporary variable related to parsing.
|
||||
///
|
||||
/// - `ident` must be the target struct member identifier or index, used
|
||||
/// for error messages.
|
||||
///
|
||||
/// - `ty` must be the field's type. This type must implement
|
||||
/// `From<Element>`. If [`Self::default_on_missing`] is set, it also
|
||||
/// needs to implement `Default`.
|
||||
pub(super) fn build_try_from_element(
|
||||
self,
|
||||
name: &ParentRef,
|
||||
impl Field for ElementField {
|
||||
fn build_try_from_element(
|
||||
&self,
|
||||
container_name: &ParentRef,
|
||||
_container_namespace_expr: &Expr,
|
||||
tempname: Ident,
|
||||
ident: Member,
|
||||
ty: Type,
|
||||
member: &Member,
|
||||
ty: &Type,
|
||||
) -> Result<FieldParsePart> {
|
||||
let test = self
|
||||
.selector
|
||||
.build_test(&Ident::new("residual", Span::call_site()));
|
||||
let missingerr = error_message::on_missing_child(name, &ident);
|
||||
let duperr = error_message::on_duplicate_child(name, &ident);
|
||||
let missingerr = error_message::on_missing_child(container_name, member);
|
||||
let duperr = error_message::on_duplicate_child(container_name, member);
|
||||
let ty_span = ty.span();
|
||||
let ty_default = quote_spanned! {ty_span=> <#ty as std::default::Default>::default};
|
||||
let ty_from_element =
|
||||
|
@ -114,7 +102,7 @@ impl ElementField {
|
|||
#ty_default()
|
||||
}
|
||||
}
|
||||
FlagOr::Value { value, .. } => {
|
||||
FlagOr::Value { ref value, .. } => {
|
||||
quote! {
|
||||
#value()
|
||||
}
|
||||
|
@ -146,29 +134,37 @@ impl ElementField {
|
|||
})
|
||||
}
|
||||
|
||||
/// Construct an expression which consumes the ident `builder`, which must
|
||||
/// be a minidom `Builder`, and returns it, modified in such a way that it
|
||||
/// contains the field's data.
|
||||
///
|
||||
/// - `ident` must be an expression to consume the field's data. It is
|
||||
/// evaluated exactly once.
|
||||
/// - `ty` must be the field's type. This type must implement
|
||||
/// `Into<Option<Element>>`.
|
||||
pub(super) fn build_into_element(self, ident: Expr, ty: Type) -> Result<TokenStream> {
|
||||
fn build_into_element(
|
||||
&self,
|
||||
_container_name: &ParentRef,
|
||||
_container_namespace_expr: &Expr,
|
||||
_member: &Member,
|
||||
ty: &Type,
|
||||
access: Expr,
|
||||
) -> Result<TokenStream> {
|
||||
Ok(quote! {
|
||||
match <#ty as Into<Option<::xso::exports::minidom::Element>>>::into(#ident) {
|
||||
match <#ty as Into<Option<::xso::exports::minidom::Element>>>::into(#access) {
|
||||
Some(v) => builder.append(::xso::exports::minidom::Node::Element(v)),
|
||||
None => builder
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn build_set_namespace(
|
||||
&self,
|
||||
_input: &Ident,
|
||||
_ty: &Type,
|
||||
_access: Expr,
|
||||
) -> Result<TokenStream> {
|
||||
Ok(TokenStream::default())
|
||||
}
|
||||
}
|
||||
|
||||
/// A field parsed from a XML children, without destructuring them into Rust
|
||||
/// data structures.
|
||||
///
|
||||
/// Maps to `#[xml(elements)]`.
|
||||
#[cfg_attr(feature = "debug", derive(Debug))]
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ElementsField {
|
||||
/// Selector to choose the child elements to collect.
|
||||
///
|
||||
|
@ -225,30 +221,26 @@ impl ElementsField {
|
|||
selector: namespace.map(|x| (x, name.map(|x| x.into()))),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct the code necessary to parse the child elements from a
|
||||
/// `minidom::Element` into a field.
|
||||
///
|
||||
/// - `name` should be the identifier or path of the containing compound
|
||||
/// and is used to generate runtime error messages.
|
||||
///
|
||||
/// - `tempname` must be an identifier which the implementation can freely
|
||||
/// use for a temporary variable related to parsing.
|
||||
///
|
||||
/// - `ident` must be the target struct member identifier or index, used
|
||||
/// for error messages.
|
||||
///
|
||||
/// - `ty` must be the field's type. This must be `Vec<minidom::Element>`,
|
||||
/// otherwise compile-time errors will follow.
|
||||
pub(super) fn build_try_from_element(
|
||||
self,
|
||||
_name: &ParentRef,
|
||||
impl Field for ElementsField {
|
||||
fn is_child_wildcard(&self) -> bool {
|
||||
match self.selector {
|
||||
None => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn build_try_from_element(
|
||||
&self,
|
||||
_container_name: &ParentRef,
|
||||
_container_namespace_expr: &Expr,
|
||||
tempname: Ident,
|
||||
_ident: Member,
|
||||
_ty: Type,
|
||||
_member: &Member,
|
||||
_ty: &Type,
|
||||
) -> Result<FieldParsePart> {
|
||||
match self.selector {
|
||||
Some((field_namespace, field_name)) => {
|
||||
Some((ref field_namespace, ref field_name)) => {
|
||||
let childiter = match field_name {
|
||||
// namespace match only
|
||||
None => quote! {
|
||||
|
@ -290,16 +282,25 @@ impl ElementsField {
|
|||
}
|
||||
}
|
||||
|
||||
/// Construct an expression which consumes the ident `builder`, which must
|
||||
/// be a minidom `Builder`, and returns it, modified in such a way that it
|
||||
/// contains the field's data.
|
||||
///
|
||||
/// - `ident` must be an expression to consume the field's data. It is
|
||||
/// evaluated exactly once.
|
||||
/// - `ty` must be the field's type.
|
||||
pub(super) fn build_into_element(self, ident: Expr, _ty: Type) -> Result<TokenStream> {
|
||||
fn build_into_element(
|
||||
&self,
|
||||
_container_name: &ParentRef,
|
||||
_container_namespace_expr: &Expr,
|
||||
_member: &Member,
|
||||
_ty: &Type,
|
||||
access: Expr,
|
||||
) -> Result<TokenStream> {
|
||||
Ok(quote! {
|
||||
builder.append_all(#ident.into_iter().map(|elem| ::xso::exports::minidom::Node::Element(elem)))
|
||||
builder.append_all(#access.into_iter().map(|elem| ::xso::exports::minidom::Node::Element(elem)))
|
||||
})
|
||||
}
|
||||
|
||||
fn build_set_namespace(
|
||||
&self,
|
||||
_input: &Ident,
|
||||
_ty: &Type,
|
||||
_access: Expr,
|
||||
) -> Result<TokenStream> {
|
||||
Ok(TokenStream::default())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,12 +7,12 @@ use syn::*;
|
|||
use crate::error_message::{self, ParentRef};
|
||||
use crate::meta::{Name, NameRef, NamespaceRef, StaticNamespace};
|
||||
|
||||
use super::FieldParsePart;
|
||||
use super::{Field, FieldParsePart};
|
||||
|
||||
/// A field parsed from the presence of an empty XML child.
|
||||
///
|
||||
/// Maps to `#[xml(flag)]`.
|
||||
#[cfg_attr(feature = "debug", derive(Debug))]
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct FlagField {
|
||||
/// The XML namespace of the child element to look for.
|
||||
namespace: StaticNamespace,
|
||||
|
@ -70,30 +70,20 @@ impl FlagField {
|
|||
name: name.into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct the code necessary to parse the child element from a
|
||||
/// `minidom::Element` into a field.
|
||||
///
|
||||
/// - `name` should be the identifier or path of the containing compound
|
||||
/// and is used to generate runtime error messages.
|
||||
///
|
||||
/// - `tempname` must be an identifier which the implementation can freely
|
||||
/// use for a temporary variable related to parsing.
|
||||
///
|
||||
/// - `ident` must be the target struct member identifier or index, used
|
||||
/// for error messages.
|
||||
///
|
||||
/// - `ty` must be the field's type. This must be `bool`.
|
||||
pub(super) fn build_try_from_element(
|
||||
self,
|
||||
name: &ParentRef,
|
||||
impl Field for FlagField {
|
||||
fn build_try_from_element(
|
||||
&self,
|
||||
container_name: &ParentRef,
|
||||
_container_namespace_expr: &Expr,
|
||||
tempname: Ident,
|
||||
ident: Member,
|
||||
_ty: Type,
|
||||
member: &Member,
|
||||
_ty: &Type,
|
||||
) -> Result<FieldParsePart> {
|
||||
let field_name = self.name;
|
||||
let field_namespace = self.namespace;
|
||||
let duperr = error_message::on_duplicate_child(name, &ident);
|
||||
let field_name = &self.name;
|
||||
let field_namespace = &self.namespace;
|
||||
let duperr = error_message::on_duplicate_child(container_name, member);
|
||||
Ok(FieldParsePart {
|
||||
tempinit: quote! {
|
||||
let mut #tempname = false;
|
||||
|
@ -115,23 +105,31 @@ impl FlagField {
|
|||
})
|
||||
}
|
||||
|
||||
/// Construct an expression which consumes the ident `builder`, which must
|
||||
/// be a minidom `Builder`, and returns it, modified in such a way that it
|
||||
/// contains the field's data.
|
||||
///
|
||||
/// - `ident` must be an expression to consume the field's data. It is
|
||||
/// evaluated exactly once.
|
||||
/// - `ty` must be the field's type. This type must implement
|
||||
/// `Into<Option<Element>>`.
|
||||
pub(super) fn build_into_element(self, ident: Expr, _ty: Type) -> Result<TokenStream> {
|
||||
let child_name = self.name;
|
||||
let child_namespace = self.namespace;
|
||||
fn build_into_element(
|
||||
&self,
|
||||
_container_name: &ParentRef,
|
||||
_container_namespace_expr: &Expr,
|
||||
_member: &Member,
|
||||
_ty: &Type,
|
||||
access: Expr,
|
||||
) -> Result<TokenStream> {
|
||||
let child_name = &self.name;
|
||||
let child_namespace = &self.namespace;
|
||||
Ok(quote! {
|
||||
if #ident {
|
||||
if #access {
|
||||
builder.append(::xso::exports::minidom::Node::Element(::xso::exports::minidom::Element::builder(#child_name, #child_namespace).build()))
|
||||
} else {
|
||||
builder
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn build_set_namespace(
|
||||
&self,
|
||||
_input: &Ident,
|
||||
_ty: &Type,
|
||||
_access: Expr,
|
||||
) -> Result<TokenStream> {
|
||||
Ok(TokenStream::default())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,11 +8,34 @@ The main file of the module contains the outward, generic types covering all
|
|||
use cases. The actual implementations for all but the trivial field kinds are
|
||||
sorted into submodules.
|
||||
*/
|
||||
#[cfg(doc)]
|
||||
pub(crate) mod attribute;
|
||||
#[cfg(not(doc))]
|
||||
mod attribute;
|
||||
|
||||
#[cfg(doc)]
|
||||
pub(crate) mod child;
|
||||
#[cfg(not(doc))]
|
||||
mod child;
|
||||
|
||||
#[cfg(doc)]
|
||||
pub(crate) mod element;
|
||||
#[cfg(not(doc))]
|
||||
mod element;
|
||||
|
||||
#[cfg(doc)]
|
||||
pub(crate) mod flag;
|
||||
#[cfg(not(doc))]
|
||||
mod flag;
|
||||
|
||||
#[cfg(doc)]
|
||||
pub(crate) mod namespace;
|
||||
#[cfg(not(doc))]
|
||||
mod namespace;
|
||||
|
||||
#[cfg(doc)]
|
||||
pub(crate) mod text;
|
||||
#[cfg(not(doc))]
|
||||
mod text;
|
||||
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
|
@ -30,6 +53,92 @@ use self::flag::FlagField;
|
|||
use self::namespace::NamespaceField;
|
||||
use self::text::TextField;
|
||||
|
||||
pub(crate) trait Field: std::fmt::Debug {
|
||||
/// Return true if and only if this field is a field collecting all XML
|
||||
/// text of the element.
|
||||
fn is_text(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Return true if and only if this field is a field collecting *all* XML
|
||||
/// children of the element.
|
||||
fn is_child_wildcard(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Return true if and only if this field is a field storing the namespace
|
||||
/// of the XML element.
|
||||
fn is_namespace(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Construct the code necessary to parse data from a `minidom::Element`
|
||||
/// into the field.
|
||||
///
|
||||
/// - `container_name` should be the identifier or path of the containing
|
||||
/// compound and is used to generate runtime error messages.
|
||||
///
|
||||
/// - `container_namespace_expr` may be an expression under which the
|
||||
/// parent compound's `::xso::DynNamespaceEnum` value is
|
||||
/// obtainable, if any. This is needed for fields and compounds
|
||||
/// using `#[xml(namespace = super)]`.
|
||||
///
|
||||
/// - `tempname` must be an identifier which the implementation can freely
|
||||
/// use for a temporary variable related to parsing.
|
||||
///
|
||||
/// - `member` must be the target struct member identifier or index, used
|
||||
/// for error messages.
|
||||
///
|
||||
/// - `ty` must be the field's type.
|
||||
fn build_try_from_element(
|
||||
&self,
|
||||
container_name: &ParentRef,
|
||||
container_namespace_expr: &Expr,
|
||||
tempname: Ident,
|
||||
member: &Member,
|
||||
ty: &Type,
|
||||
) -> Result<FieldParsePart>;
|
||||
|
||||
/// Construct an expression which consumes the identifier `builder`, which
|
||||
/// must be a minidom `Builder`, and returns it, modified in such a way
|
||||
/// that it contains the field's data.
|
||||
///
|
||||
/// - `container_name` should be the identifier or path of the containing
|
||||
/// compound and is used to generate runtime error messages.
|
||||
///
|
||||
/// - `container_namespace_expr` may be an expression under which the
|
||||
/// parent compound's `::xso::DynNamespaceEnum` value is
|
||||
/// obtainable, if any. This is needed for fields and compounds
|
||||
/// using `#[xml(namespace = super)]`.
|
||||
///
|
||||
/// - `member` must be the target struct member identifier or index, used
|
||||
/// for error messages.
|
||||
///
|
||||
/// - `ty` must be the field's type.
|
||||
///
|
||||
/// - `access` must be an expression to consume the field's data. It is
|
||||
/// evaluated exactly once.
|
||||
fn build_into_element(
|
||||
&self,
|
||||
container_name: &ParentRef,
|
||||
container_namespace_expr: &Expr,
|
||||
member: &Member,
|
||||
ty: &Type,
|
||||
access: Expr,
|
||||
) -> Result<TokenStream>;
|
||||
|
||||
/// Construct the code necessary to update the namespace on the field.
|
||||
///
|
||||
/// - `input` must be the identifier of the variable holding the new
|
||||
/// namespace to set.
|
||||
///
|
||||
/// - `ty` must be the field's type.
|
||||
///
|
||||
/// - `access` must be an expression which can be written to, which allows
|
||||
/// updating the field's value.
|
||||
fn build_set_namespace(&self, input: &Ident, ty: &Type, access: Expr) -> Result<TokenStream>;
|
||||
}
|
||||
|
||||
/// Code slices necessary for parsing a single field.
|
||||
#[derive(Default)]
|
||||
pub(crate) struct FieldParsePart {
|
||||
|
@ -68,8 +177,7 @@ pub(crate) struct FieldParsePart {
|
|||
}
|
||||
|
||||
/// A XML namespace as declared on a field.
|
||||
#[derive(Clone)]
|
||||
#[cfg_attr(feature = "debug", derive(Debug))]
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) enum FieldNamespace {
|
||||
/// The namespace is a static string.
|
||||
Static(
|
||||
|
@ -97,49 +205,114 @@ impl From<FieldNamespace> for NamespaceRef {
|
|||
}
|
||||
}
|
||||
|
||||
/// Enumeration of possible XML data ↔ Rust field mappings.
|
||||
///
|
||||
/// This matches the processed `#[xml(..)]` metas on the corresponding enum
|
||||
/// variant or struct fields.
|
||||
///
|
||||
/// This enum only covers the parsing logic; the surrounding metadata, such as
|
||||
/// the field identifier and type, are contained in [`FieldDef`].
|
||||
#[cfg_attr(feature = "debug", derive(Debug))]
|
||||
pub(crate) enum FieldKind {
|
||||
/// This field is parsed from an XML attribute (`#[xml(attribute)]`).
|
||||
Attribute(AttributeField),
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Ignore;
|
||||
|
||||
/// This field is parsed from XML text content (`#[xml(text)]`).
|
||||
Text(TextField),
|
||||
impl Field for Ignore {
|
||||
fn build_try_from_element(
|
||||
&self,
|
||||
_container_name: &ParentRef,
|
||||
_container_namespace_expr: &Expr,
|
||||
_tempname: Ident,
|
||||
_member: &Member,
|
||||
ty: &Type,
|
||||
) -> Result<FieldParsePart> {
|
||||
let ty_default = quote_spanned! {ty.span()=> <#ty as std::default::Default>::default};
|
||||
Ok(FieldParsePart {
|
||||
value: quote! { #ty_default() },
|
||||
..FieldParsePart::default()
|
||||
})
|
||||
}
|
||||
|
||||
/// This field represents the parent compound's namespace
|
||||
/// (`#[xml(namespace)]`).
|
||||
///
|
||||
/// See also
|
||||
/// [`StructNamespace::Dyn`][`crate::structs::StructNamespace::Dyn`].
|
||||
Namespace(NamespaceField),
|
||||
fn build_into_element(
|
||||
&self,
|
||||
_container_name: &ParentRef,
|
||||
_container_namespace_expr: &Expr,
|
||||
_member: &Member,
|
||||
_ty: &Type,
|
||||
_access: Expr,
|
||||
) -> Result<TokenStream> {
|
||||
Ok(quote! { builder })
|
||||
}
|
||||
|
||||
/// This field is parsed from an XML child using the `FromXml` /
|
||||
/// `IntoXml` traits (`#[xml(child)]`, `#[xml(children)]`).
|
||||
Child(ChildField),
|
||||
|
||||
/// This field is parsed from an XML child as raw `minidom::Element`
|
||||
/// (`#[xml(element)]`).
|
||||
Element(ElementField),
|
||||
|
||||
/// This field is parsed from multiple XML children as raw
|
||||
/// `Vec<minidom::Element>` (`#[xml(elements)]`).
|
||||
Elements(ElementsField),
|
||||
|
||||
/// This field represents the presence/absence of an empty XML child
|
||||
/// (`#[xml(flag)]`).
|
||||
Flag(FlagField),
|
||||
|
||||
/// This field is not parsed to/from XML.
|
||||
Ignore,
|
||||
fn build_set_namespace(
|
||||
&self,
|
||||
_input: &Ident,
|
||||
_ty: &Type,
|
||||
_access: Expr,
|
||||
) -> Result<TokenStream> {
|
||||
Ok(TokenStream::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl FieldKind {
|
||||
/// Construct a `Field` implementation 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_field(
|
||||
span: &Span,
|
||||
field_ident: Option<&Ident>,
|
||||
field_ty: &Type,
|
||||
meta: XmlFieldMeta,
|
||||
) -> Result<Box<dyn Field>> {
|
||||
match meta {
|
||||
XmlFieldMeta::Attribute {
|
||||
name,
|
||||
default_,
|
||||
codec,
|
||||
ty,
|
||||
} => {
|
||||
if let Some(ty) = ty {
|
||||
return Err(Error::new_spanned(ty, "cannot set attribute type here"));
|
||||
};
|
||||
Ok(Box::new(AttributeField::new(
|
||||
span,
|
||||
field_ident,
|
||||
name,
|
||||
default_,
|
||||
codec,
|
||||
)?))
|
||||
}
|
||||
XmlFieldMeta::Child {
|
||||
mode,
|
||||
namespace,
|
||||
name,
|
||||
extract,
|
||||
default_,
|
||||
skip_if,
|
||||
codec,
|
||||
} => Ok(Box::new(ChildField::new(
|
||||
span, mode, namespace, name, extract, default_, skip_if, codec, field_ty,
|
||||
)?)),
|
||||
XmlFieldMeta::Text { codec, ty } => {
|
||||
if let Some(ty) = ty {
|
||||
return Err(Error::new_spanned(ty, "cannot set text type here"));
|
||||
};
|
||||
Ok(Box::new(TextField::new(span, codec)?))
|
||||
}
|
||||
XmlFieldMeta::Namespace => Ok(Box::new(NamespaceField::new())),
|
||||
XmlFieldMeta::Element {
|
||||
namespace,
|
||||
name,
|
||||
default_,
|
||||
} => Ok(Box::new(ElementField::new(
|
||||
span, namespace, name, default_,
|
||||
)?)),
|
||||
XmlFieldMeta::Elements { namespace, name } => {
|
||||
Ok(Box::new(ElementsField::new(span, namespace, name)?))
|
||||
}
|
||||
XmlFieldMeta::Flag { namespace, name } => {
|
||||
Ok(Box::new(FlagField::new(span, namespace, name)?))
|
||||
}
|
||||
XmlFieldMeta::Ignore => Ok(Box::new(Ignore)),
|
||||
}
|
||||
}
|
||||
|
||||
/* impl FieldKind {
|
||||
/// Return true if the field kind is equal to [`Self::Text`].
|
||||
pub(crate) fn is_text(&self) -> bool {
|
||||
match self {
|
||||
|
@ -156,78 +329,11 @@ impl FieldKind {
|
|||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// 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,
|
||||
default_,
|
||||
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_,
|
||||
codec,
|
||||
)?))
|
||||
}
|
||||
XmlFieldMeta::Child {
|
||||
mode,
|
||||
namespace,
|
||||
name,
|
||||
extract,
|
||||
default_,
|
||||
skip_if,
|
||||
codec,
|
||||
} => Ok(FieldKind::Child(ChildField::new(
|
||||
span, mode, namespace, name, extract, default_, skip_if, codec, 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_,
|
||||
} => Ok(FieldKind::Element(ElementField::new(
|
||||
span, namespace, name, default_,
|
||||
)?)),
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
} */
|
||||
|
||||
/// All data necessary to generate code to convert a Rust field to or from
|
||||
/// XML.
|
||||
#[cfg_attr(feature = "debug", derive(Debug))]
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct FieldDef {
|
||||
/// The span of the `#[xml]` meta defining this field.
|
||||
pub(crate) span: Span,
|
||||
|
@ -240,7 +346,7 @@ pub(crate) struct FieldDef {
|
|||
pub(crate) ty: Type,
|
||||
|
||||
/// The way the field is mapped to XML.
|
||||
pub(crate) kind: FieldKind,
|
||||
pub(crate) kind: Box<dyn Field>,
|
||||
}
|
||||
|
||||
fn try_unwrap_option_type(ty: Type) -> Type {
|
||||
|
@ -307,7 +413,7 @@ impl FieldDef {
|
|||
let ty = ty
|
||||
.or(single_extract_type.map(try_unwrap_option_type))
|
||||
.unwrap_or(string_ty);
|
||||
let kind = FieldKind::new(
|
||||
let kind = new_field(
|
||||
&span, None, // field_ident is always none here, we use unnamed fields.
|
||||
&ty, extract,
|
||||
)?;
|
||||
|
@ -323,21 +429,22 @@ impl FieldDef {
|
|||
}
|
||||
|
||||
/// Return a reference to the field's type, if and only if it is a
|
||||
/// [`FieldKind::Namespace`].
|
||||
/// [`NamespaceField`][`crate::field::namespace::NamespaceField`].
|
||||
pub(crate) fn namespace_field_type(&self) -> Option<&Type> {
|
||||
match self.kind {
|
||||
FieldKind::Namespace(..) => Some(&self.ty),
|
||||
_ => None,
|
||||
if self.kind.is_namespace() {
|
||||
Some(&self.ty)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a [`FieldDef`] from a [`Field`].
|
||||
/// Generate a [`FieldDef`] from a [`syn::Field`].
|
||||
///
|
||||
/// `index` must be the number of the field within the compound, starting
|
||||
/// at zero. It is used only for unnamed fields.
|
||||
///
|
||||
/// This parses the attributes using [`XmlFieldMeta`].
|
||||
pub(crate) fn from_field(field: &Field, index: u32) -> Result<Self> {
|
||||
pub(crate) fn from_field(field: &syn::Field, index: u32) -> Result<Self> {
|
||||
let mut meta: Option<(XmlFieldMeta, Span)> = None;
|
||||
for attr in field.attrs.iter() {
|
||||
if !attr.path().is_ident("xml") {
|
||||
|
@ -375,7 +482,7 @@ impl FieldDef {
|
|||
));
|
||||
}
|
||||
|
||||
let kind = FieldKind::new(&span, field.ident.as_ref(), &field.ty, meta)?;
|
||||
let kind = new_field(&span, field.ident.as_ref(), &field.ty, meta)?;
|
||||
|
||||
Ok(Self {
|
||||
span,
|
||||
|
@ -388,52 +495,20 @@ impl FieldDef {
|
|||
/// Construct a [`FieldParsePart`] which creates the field's value from
|
||||
/// XML.
|
||||
pub(crate) fn build_try_from_element(
|
||||
self,
|
||||
&self,
|
||||
container_name: &ParentRef,
|
||||
container_namespace_expr: &Expr,
|
||||
) -> Result<FieldParsePart> {
|
||||
let ident = self.ident;
|
||||
let ty = self.ty;
|
||||
let ident = &self.ident;
|
||||
let ty = &self.ty;
|
||||
let tempname = quote::format_ident!("__field_init_{}", ident);
|
||||
match self.kind {
|
||||
FieldKind::Attribute(field) => {
|
||||
field.build_try_from_element(container_name, tempname, ident, ty)
|
||||
}
|
||||
FieldKind::Child(field) => field.build_try_from_element(
|
||||
container_name,
|
||||
tempname,
|
||||
ident,
|
||||
ty,
|
||||
container_namespace_expr,
|
||||
),
|
||||
FieldKind::Text(field) => {
|
||||
field.build_try_from_element(container_name, tempname, ident, ty)
|
||||
}
|
||||
FieldKind::Element(field) => {
|
||||
field.build_try_from_element(container_name, tempname, ident, ty)
|
||||
}
|
||||
FieldKind::Elements(field) => {
|
||||
field.build_try_from_element(container_name, tempname, ident, ty)
|
||||
}
|
||||
FieldKind::Flag(field) => {
|
||||
field.build_try_from_element(container_name, tempname, ident, ty)
|
||||
}
|
||||
FieldKind::Namespace(field) => field.build_try_from_element(
|
||||
container_name,
|
||||
container_namespace_expr,
|
||||
tempname,
|
||||
ident,
|
||||
ty,
|
||||
),
|
||||
FieldKind::Ignore => {
|
||||
let ty_default =
|
||||
quote_spanned! {ty.span()=> <#ty as std::default::Default>::default};
|
||||
Ok(FieldParsePart {
|
||||
value: quote! { #ty_default() },
|
||||
..FieldParsePart::default()
|
||||
})
|
||||
}
|
||||
}
|
||||
self.kind.build_try_from_element(
|
||||
container_name,
|
||||
container_namespace_expr,
|
||||
tempname,
|
||||
ident,
|
||||
ty,
|
||||
)
|
||||
}
|
||||
|
||||
/// Construct a [`TokenStream`] which propagates the parent's namespace
|
||||
|
@ -446,11 +521,7 @@ impl FieldDef {
|
|||
) -> Result<TokenStream> {
|
||||
let member = access_field(self.ident.clone());
|
||||
let ty = &self.ty;
|
||||
match self.kind {
|
||||
FieldKind::Child(ref field) => field.build_set_namespace(input, &member, &ty),
|
||||
FieldKind::Namespace(ref field) => field.build_set_namespace(input, &member, &ty),
|
||||
_ => Ok(quote! {}),
|
||||
}
|
||||
self.kind.build_set_namespace(input, ty, member)
|
||||
}
|
||||
|
||||
/// Construct an expression which consumes the ident `builder` and returns
|
||||
|
@ -463,29 +534,15 @@ impl FieldDef {
|
|||
/// `TokenStream` is used so that the expressions returned by
|
||||
/// `access_field` and the `builder` ident are accessible.
|
||||
pub(crate) fn build_into_element(
|
||||
self,
|
||||
&self,
|
||||
container_name: &ParentRef,
|
||||
container_namespace_expr: &Expr,
|
||||
mut access_field: impl FnMut(Member) -> Expr,
|
||||
) -> Result<TokenStream> {
|
||||
let member = self.ident.clone();
|
||||
let ident = access_field(self.ident);
|
||||
let ty = self.ty;
|
||||
match self.kind {
|
||||
FieldKind::Attribute(field) => field.build_into_element(ident, ty),
|
||||
FieldKind::Text(field) => field.build_into_element(ident, ty),
|
||||
FieldKind::Child(field) => field.build_into_element(
|
||||
container_name,
|
||||
member,
|
||||
ident,
|
||||
ty,
|
||||
container_namespace_expr,
|
||||
),
|
||||
FieldKind::Element(field) => field.build_into_element(ident, ty),
|
||||
FieldKind::Elements(field) => field.build_into_element(ident, ty),
|
||||
FieldKind::Flag(field) => field.build_into_element(ident, ty),
|
||||
FieldKind::Namespace(field) => field.build_into_element(ident, ty),
|
||||
FieldKind::Ignore => Ok(quote! { builder }),
|
||||
}
|
||||
let member = &self.ident;
|
||||
let ident = access_field(member.clone());
|
||||
let ty = &self.ty;
|
||||
self.kind
|
||||
.build_into_element(container_name, container_namespace_expr, member, ty, ident)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,13 +6,16 @@ use syn::*;
|
|||
|
||||
use crate::error_message::ParentRef;
|
||||
|
||||
use super::FieldParsePart;
|
||||
use super::{Field, FieldParsePart};
|
||||
|
||||
/// A field holding the dynamic namespace of a compound with
|
||||
/// `#[xml(namespace = dyn)]`.
|
||||
///
|
||||
/// See also
|
||||
/// [`StructNamespace::Dyn`][`crate::structs::StructNamespace::Dyn`].
|
||||
///
|
||||
/// Maps to `#[xml(namespace)]`.
|
||||
#[cfg_attr(feature = "debug", derive(Debug))]
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct NamespaceField;
|
||||
|
||||
impl NamespaceField {
|
||||
|
@ -20,6 +23,12 @@ impl NamespaceField {
|
|||
pub(super) fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
impl Field for NamespaceField {
|
||||
fn is_namespace(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// Construct code which copies the value from the `namespace_tempname`
|
||||
/// into the value of the field.
|
||||
|
@ -27,13 +36,13 @@ impl NamespaceField {
|
|||
/// The actual parsing is special, because it has to happen during type
|
||||
/// matching, and happens in
|
||||
/// [`Compound::build_try_from_element`][`crate::compound::Compound::build_try_from_element`].
|
||||
pub(super) fn build_try_from_element(
|
||||
self,
|
||||
fn build_try_from_element(
|
||||
&self,
|
||||
_container_name: &ParentRef,
|
||||
container_namespace_expr: &Expr,
|
||||
_tempname: Ident,
|
||||
_ident: Member,
|
||||
_ty: Type,
|
||||
_member: &Member,
|
||||
_ty: &Type,
|
||||
) -> Result<FieldParsePart> {
|
||||
Ok(FieldParsePart {
|
||||
value: quote! { #container_namespace_expr },
|
||||
|
@ -41,15 +50,13 @@ impl NamespaceField {
|
|||
})
|
||||
}
|
||||
|
||||
/// Construct the code necessary to update the namespace on the field.
|
||||
///
|
||||
/// Actually, this is a no-op, because the actual implementation is in
|
||||
/// This is a no-op, because the actual implementation is in
|
||||
/// [`crate::compound::DynCompound::build_set_namespace`].
|
||||
pub(super) fn build_set_namespace(
|
||||
fn build_set_namespace(
|
||||
&self,
|
||||
_input: &Ident,
|
||||
_member: &Expr,
|
||||
_member_ty: &Type,
|
||||
_ty: &Type,
|
||||
_access: Expr,
|
||||
) -> Result<TokenStream> {
|
||||
// NOTE: does nothing because this is handled specially by
|
||||
// Compound::build_set_namespace
|
||||
|
@ -61,7 +68,14 @@ impl NamespaceField {
|
|||
/// The actual implementation of the specialities of dynamic namespace
|
||||
/// fields resides in
|
||||
/// [`Compound::build_into_element`][`crate::compound::Compound::build_into_element`].
|
||||
pub(super) fn build_into_element(self, _ident: Expr, _ty: Type) -> Result<TokenStream> {
|
||||
fn build_into_element(
|
||||
&self,
|
||||
_container_name: &ParentRef,
|
||||
_container_namespace_expr: &Expr,
|
||||
_member: &Member,
|
||||
_ty: &Type,
|
||||
_access: Expr,
|
||||
) -> Result<TokenStream> {
|
||||
Ok(quote! {builder})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,12 +6,12 @@ use syn::{spanned::Spanned, *};
|
|||
|
||||
use crate::error_message::ParentRef;
|
||||
|
||||
use super::FieldParsePart;
|
||||
use super::{Field, FieldParsePart};
|
||||
|
||||
/// A field parsed from a XML text.
|
||||
///
|
||||
/// Maps to `#[xml(text)]`.
|
||||
#[cfg_attr(feature = "debug", derive(Debug))]
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct TextField {
|
||||
/// The codec implementation to use.
|
||||
///
|
||||
|
@ -32,18 +32,23 @@ impl TextField {
|
|||
pub(super) fn new(_attr_span: &Span, codec: Option<Type>) -> Result<Self> {
|
||||
Ok(Self { codec })
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct the code necessary to parse the text from a
|
||||
/// `minidom::Element` into a field.
|
||||
pub(super) fn build_try_from_element(
|
||||
self,
|
||||
_name: &ParentRef,
|
||||
impl Field for TextField {
|
||||
fn is_text(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn build_try_from_element(
|
||||
&self,
|
||||
_container_name: &ParentRef,
|
||||
_container_namespace_expr: &Expr,
|
||||
tempname: Ident,
|
||||
_ident: Member,
|
||||
ty: Type,
|
||||
_member: &Member,
|
||||
ty: &Type,
|
||||
) -> Result<FieldParsePart> {
|
||||
let decode = match self.codec {
|
||||
Some(codec_ty) => {
|
||||
Some(ref codec_ty) => {
|
||||
let codec_ty_decode = quote_spanned! {codec_ty.span()=> <#codec_ty as ::xso::TextCodec::<#ty>>::decode};
|
||||
quote! {
|
||||
#codec_ty_decode(&residual.text())?
|
||||
|
@ -67,26 +72,26 @@ impl TextField {
|
|||
})
|
||||
}
|
||||
|
||||
/// Construct an expression which consumes the ident `builder`, which must
|
||||
/// be a minidom `Builder`, and returns it, modified in such a way that it
|
||||
/// contains the field's data.
|
||||
///
|
||||
/// - `ident` must be an expression to consume the field's data. It is
|
||||
/// evaluated exactly once.
|
||||
/// - `ty` must be the field's type.
|
||||
pub(super) fn build_into_element(self, ident: Expr, ty: Type) -> Result<TokenStream> {
|
||||
fn build_into_element(
|
||||
&self,
|
||||
_container_name: &ParentRef,
|
||||
_container_namespace_expr: &Expr,
|
||||
_member: &Member,
|
||||
ty: &Type,
|
||||
access: Expr,
|
||||
) -> Result<TokenStream> {
|
||||
let encode = match self.codec {
|
||||
Some(codec_ty) => {
|
||||
Some(ref codec_ty) => {
|
||||
let codec_ty_encode = quote_spanned! {codec_ty.span()=> <#codec_ty as ::xso::TextCodec::<#ty>>::encode};
|
||||
quote! {
|
||||
#codec_ty_encode(#ident).unwrap_or_else(String::new)
|
||||
#codec_ty_encode(#access).unwrap_or_else(String::new)
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let ty_into_xml_text =
|
||||
quote_spanned! {ty.span()=> <#ty as ::xso::IntoXmlText>::into_xml_text};
|
||||
quote! {
|
||||
#ty_into_xml_text(#ident)
|
||||
#ty_into_xml_text(#access)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -102,4 +107,13 @@ impl TextField {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn build_set_namespace(
|
||||
&self,
|
||||
_input: &Ident,
|
||||
_ty: &Type,
|
||||
_access: Expr,
|
||||
) -> Result<TokenStream> {
|
||||
Ok(TokenStream::default())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -186,8 +186,7 @@ impl<'a> MetaParse for ParseExtracts<'a> {
|
|||
}
|
||||
|
||||
/// Represents a boolean flag from a `#[xml(..)]` attribute meta.
|
||||
#[derive(Clone, Copy)]
|
||||
#[cfg_attr(feature = "debug", derive(Debug))]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub(crate) enum Flag {
|
||||
/// The flag is not set.
|
||||
Absent,
|
||||
|
@ -226,8 +225,7 @@ impl<T: Spanned> From<T> for Flag {
|
|||
}
|
||||
|
||||
/// A flag with an optional value.
|
||||
#[derive(Clone)]
|
||||
#[cfg_attr(feature = "debug", derive(Debug))]
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) enum FlagOr<T> {
|
||||
/// The flag is not set.
|
||||
Absent,
|
||||
|
@ -290,7 +288,7 @@ pub(crate) type StaticNamespace = Path;
|
|||
///
|
||||
/// XML namespaces can be configured in different ways, which are
|
||||
/// distinguished in this enum.
|
||||
#[cfg_attr(feature = "debug", derive(Debug))]
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum NamespaceRef {
|
||||
/// A dynamic namespace, expressed as `#[xml(namespace = dyn)]`.
|
||||
///
|
||||
|
@ -355,7 +353,7 @@ pub(crate) type NameRef = LitStr;
|
|||
///
|
||||
/// This enum, unlike when passing around the XML name as a string, preserves
|
||||
/// the original tokens, which is useful for better error messages.
|
||||
#[cfg_attr(feature = "debug", derive(Debug))]
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum Name {
|
||||
/// Represented as a string literal.
|
||||
Lit(LitStr),
|
||||
|
@ -417,8 +415,7 @@ impl ToTokens for Name {
|
|||
}
|
||||
|
||||
/// Struct containing namespace and name matchers.
|
||||
#[derive(Default)]
|
||||
#[cfg_attr(feature = "debug", derive(Debug))]
|
||||
#[derive(Default, Debug)]
|
||||
pub(crate) struct NodeFilterMeta {
|
||||
/// The value of the `namespace` option.
|
||||
pub(crate) namespace: Option<NamespaceRef>,
|
||||
|
@ -460,7 +457,7 @@ impl NodeFilterMeta {
|
|||
/// Contents of an `#[xml(..)]` attribute on a struct, enum variant, or enum.
|
||||
///
|
||||
/// This is the counterpart to [`XmlFieldMeta`].
|
||||
#[cfg_attr(feature = "debug", derive(Debug))]
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct XmlCompoundMeta {
|
||||
/// The span of the `#[xml(..)]` meta from which this was parsed.
|
||||
///
|
||||
|
@ -569,19 +566,12 @@ impl XmlCompoundMeta {
|
|||
ParseValue("normalize_with", &mut normalize_with),
|
||||
ParseValue("normalise_with", &mut normalize_with),
|
||||
ParseNodeFilter("element", &mut element),
|
||||
ParseNodeFilter("wrapped", &mut wrapped),
|
||||
ParseValue("on_unknown_attribute", &mut on_unknown_attribute),
|
||||
ParseValue("on_unknown_child", &mut on_unknown_child),
|
||||
)
|
||||
})?;
|
||||
|
||||
#[cfg(not(feature = "debug"))]
|
||||
if let Flag::Present(debug) = debug {
|
||||
return Err(Error::new(
|
||||
debug,
|
||||
"`debug` is only allowed if the macros were built with --feature debug",
|
||||
));
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
span: attr.span(),
|
||||
namespace,
|
||||
|
@ -651,7 +641,7 @@ impl XmlCompoundMeta {
|
|||
/// `default` flag, `type` and `codec` option.
|
||||
///
|
||||
/// These three are used in several places.
|
||||
#[cfg_attr(feature = "debug", derive(Debug))]
|
||||
#[derive(Debug)]
|
||||
struct AttributeMeta {
|
||||
/// The value assigned to `namespace` inside a potentially nested
|
||||
/// `#[xml(..)]`, if any.
|
||||
|
@ -762,7 +752,7 @@ impl AttributeMeta {
|
|||
}
|
||||
|
||||
/// Mode for destructured child collection.
|
||||
#[cfg_attr(feature = "debug", derive(Debug))]
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum ChildMode {
|
||||
/// The field represents a single XML child element.
|
||||
Single,
|
||||
|
@ -774,15 +764,15 @@ pub(crate) enum ChildMode {
|
|||
/// Contents of an `#[xml(..)]` attribute on a field.
|
||||
///
|
||||
/// This is the counterpart to [`XmlCompoundMeta`].
|
||||
#[cfg_attr(feature = "debug", derive(Debug))]
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum XmlFieldMeta {
|
||||
/// Maps the field to an XML attribute.
|
||||
///
|
||||
/// Maps to the `#[xml(attribute)]`, `#[xml(attribute = ..)]` and
|
||||
/// `#[xml(attribute(..))]` Rust syntaxes.
|
||||
///
|
||||
/// Gets transformed into [`crate::field::FieldKind::Attribute`] in later
|
||||
/// processing stages.
|
||||
/// Gets transformed into [`crate::field::attribute::AttributeField`] in
|
||||
/// later processing stages.
|
||||
Attribute {
|
||||
/// Contents of the `name = ..` option or value assigned to
|
||||
/// `attribute` in the shorthand syntax.
|
||||
|
@ -803,7 +793,7 @@ pub(crate) enum XmlFieldMeta {
|
|||
/// Maps to the `#[xml(child)]`, `#[xml(child(..))]`, `#[xml(children)]`,
|
||||
/// and `#[xml(children(..)]` Rust syntaxes.
|
||||
///
|
||||
/// Gets transformed into [`crate::field::FieldKind::Child`] in later
|
||||
/// Gets transformed into [`crate::field::child::ChildField`] in later
|
||||
/// processing stages.
|
||||
Child {
|
||||
/// Distinguishes between `#[xml(child)]` and `#[xml(children)]`
|
||||
|
@ -834,11 +824,17 @@ pub(crate) enum XmlFieldMeta {
|
|||
/// See also [`crate::structs::StructNamespace::Dyn`].
|
||||
///
|
||||
/// Maps to the `#[xml(namespace)]` Rust syntax.
|
||||
///
|
||||
/// Gets transformed into [`crate::field::namespace::NamespaceField`] in
|
||||
/// later processing stages.
|
||||
Namespace,
|
||||
|
||||
/// Maps the field to the text contents of the XML element.
|
||||
///
|
||||
/// Maps to the `#[xml(text)]` Rust syntax.
|
||||
///
|
||||
/// Gets transformed into [`crate::field::text::TextField`] in later
|
||||
/// processing stages.
|
||||
Text {
|
||||
/// Contents of the `codec = ..` option.
|
||||
codec: Option<Type>,
|
||||
|
@ -852,6 +848,9 @@ pub(crate) enum XmlFieldMeta {
|
|||
///
|
||||
/// Maps to the `#[xml(elements)]` and `#[xml(elements(..))]` Rust
|
||||
/// syntaxes.
|
||||
///
|
||||
/// Gets transformed into [`crate::field::element::ElementsField`] in
|
||||
/// later processing stages.
|
||||
Elements {
|
||||
/// Contents of the `namespace = ..` option.
|
||||
namespace: Option<NamespaceRef>,
|
||||
|
@ -864,6 +863,9 @@ pub(crate) enum XmlFieldMeta {
|
|||
/// it into a Rust struct.
|
||||
///
|
||||
/// Maps to the `#[xml(element(..))]` Rust syntax.
|
||||
///
|
||||
/// Gets transformed into [`crate::field::element::ElementField`] in
|
||||
/// later processing stages.
|
||||
Element {
|
||||
/// Contents of the `namespace = ..` option.
|
||||
namespace: Option<NamespaceRef>,
|
||||
|
@ -878,6 +880,9 @@ pub(crate) enum XmlFieldMeta {
|
|||
/// Maps the field to the presence of an empty XML child element.
|
||||
///
|
||||
/// Maps to the `#[xml(flag(..))]` Rust syntax.
|
||||
///
|
||||
/// Gets transformed into [`crate::field::flag::FlagField`] in later
|
||||
/// processing stages.
|
||||
Flag {
|
||||
/// Contents of the `namespace = ..` option.
|
||||
namespace: Option<NamespaceRef>,
|
||||
|
@ -889,6 +894,9 @@ pub(crate) enum XmlFieldMeta {
|
|||
/// Ignores the field for the purpose of XML processing.
|
||||
///
|
||||
/// Maps to the `#[xml(ignore)]` Rust syntax.
|
||||
///
|
||||
/// Gets transformed into [`crate::field::Ignore`] in later processing
|
||||
/// stages.
|
||||
Ignore,
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ use crate::meta::{
|
|||
};
|
||||
|
||||
/// A XML namespace as declared on a struct.
|
||||
#[cfg_attr(feature = "debug", derive(Debug))]
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum StructNamespace {
|
||||
/// The namespace is a static string.
|
||||
Static(
|
||||
|
@ -28,8 +28,8 @@ pub(crate) enum StructNamespace {
|
|||
),
|
||||
|
||||
/// Instead of a fixed namespace, the namespace is dynamic. The allowed
|
||||
/// values are determined by a field with
|
||||
/// [`FieldKind::Namespace`][`crate::field::FieldKind::Namespace`] kind
|
||||
/// values are determined by a
|
||||
/// [`NamespaceField`][`crate::field::namespace::NamespaceField`]
|
||||
/// (declared using `#[xml(namespace)]`).
|
||||
Dyn {
|
||||
/// The `dyn` token from the `#[xml(namespace = dyn)]` meta.
|
||||
|
@ -47,7 +47,7 @@ pub(crate) enum StructNamespace {
|
|||
/// Represent a selector for element-transparent structs.
|
||||
///
|
||||
/// See also [`StructInner::Element`].
|
||||
#[cfg_attr(feature = "debug", derive(Debug))]
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum ElementSelector {
|
||||
/// Any element will be accepted.
|
||||
///
|
||||
|
@ -109,7 +109,7 @@ impl ElementSelector {
|
|||
/// If the `minidom::Element` in `residual` matches the selector, the
|
||||
/// token stream will evaluate to true. Otherwise, it will evaluate to
|
||||
/// false.
|
||||
pub(crate) fn build_test(self, residual: &Ident) -> TokenStream {
|
||||
pub(crate) fn build_test(&self, residual: &Ident) -> TokenStream {
|
||||
match self {
|
||||
Self::Any => quote! { true },
|
||||
Self::ByName(name) => quote! {
|
||||
|
@ -130,7 +130,7 @@ impl ElementSelector {
|
|||
/// This contains all data necessary for the matching logic, but does not
|
||||
/// include validation/preparation of the data. The latter is handled by
|
||||
/// [`StructDef`].
|
||||
#[cfg_attr(feature = "debug", derive(Debug))]
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum StructInner {
|
||||
/// Single-field tuple-like struct declared with `#[xml(transparent)]`.
|
||||
///
|
||||
|
@ -660,7 +660,7 @@ fn make_accessor(struct_path: Path) -> impl FnMut(Member) -> Expr {
|
|||
}
|
||||
|
||||
/// Represent a struct.
|
||||
#[cfg_attr(feature = "debug", derive(Debug))]
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct StructDef {
|
||||
/// The `validate` value, if set on the struct.
|
||||
///
|
||||
|
|
Loading…
Reference in New Issue