diff --git a/parsers-macros/Cargo.toml b/parsers-macros/Cargo.toml index 1931249..36125b2 100644 --- a/parsers-macros/Cargo.toml +++ b/parsers-macros/Cargo.toml @@ -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"] diff --git a/parsers-macros/src/compound.rs b/parsers-macros/src/compound.rs index 46d0a17..8a5edf6 100644 --- a/parsers-macros/src/compound.rs +++ b/parsers-macros/src/compound.rs @@ -70,7 +70,7 @@ fn default_on_unknown_attribute(p: Option) -> 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, @@ -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 { 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 { 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, diff --git a/parsers-macros/src/enums.rs b/parsers-macros/src/enums.rs index f7e3bd4..792d422 100644 --- a/parsers-macros/src/enums.rs +++ b/parsers-macros/src/enums.rs @@ -57,7 +57,7 @@ fn build_ident_mapping>( /// /// 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, @@ -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. /// diff --git a/parsers-macros/src/error_message.rs b/parsers-macros/src/error_message.rs index 5b98838..b051fd1 100644 --- a/parsers-macros/src/error_message.rs +++ b/parsers-macros/src/error_message.rs @@ -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. diff --git a/parsers-macros/src/field/attribute.rs b/parsers-macros/src/field/attribute.rs index 095ccb7..2c48d51 100644 --- a/parsers-macros/src/field/attribute.rs +++ b/parsers-macros/src/field/attribute.rs @@ -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 { - 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 ::xmpp_parsers_core::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 { + fn build_into_element( + &self, + _container_name: &ParentRef, + _container_namespace_expr: &Expr, + _member: &Member, + ty: &Type, + access: Expr, + ) -> Result { let encode = match self.codec { - Some(codec_ty) => { + Some(ref codec_ty) => { let codec_ty_encode = quote_spanned! {codec_ty.span()=> <#codec_ty as ::xmpp_parsers_core::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 ::xmpp_parsers_core::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 { + Ok(TokenStream::default()) + } } diff --git a/parsers-macros/src/field/child.rs b/parsers-macros/src/field/child.rs index 84531ba..7c929e6 100644 --- a/parsers-macros/src/field/child.rs +++ b/parsers-macros/src/field/child.rs @@ -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 { 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 { 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! { ::xmpp_parsers_core::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 `::xmpp_parsers_core::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 { 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(), }; @@ -522,9 +504,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()), )?; @@ -553,9 +535,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(), }; @@ -586,24 +568,18 @@ 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 { + fn build_set_namespace(&self, input: &Ident, ty: &Type, access: Expr) -> Result { 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 ::xmpp_parsers_core::DynNamespace>::set_namespace}; + let method = quote_spanned! {ty_span=> <#ty as ::xmpp_parsers_core::DynNamespace>::set_namespace}; Ok(quote! { - #method(&mut #member, #input.clone()); + #method(&mut #access, #input.clone()); }) } }, @@ -612,28 +588,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 `::xmpp_parsers_core::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 { 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, @@ -643,7 +608,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, @@ -654,12 +619,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(::xmpp_parsers_core::exports::minidom::Node::Element(#assemble)), None => builder, } @@ -667,9 +632,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(), }; @@ -679,7 +644,7 @@ impl ChildField { let codec_ty_encode = quote_spanned! {codec_ty_span=> <#codec_ty as ::xmpp_parsers_core::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(::xmpp_parsers_core::exports::minidom::Node::Element(#temp_ident)), None => builder, @@ -689,9 +654,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(), @@ -701,7 +666,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, @@ -717,9 +682,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(), }; @@ -727,7 +692,7 @@ impl ChildField { let codec_ty_into_tree = quote_spanned! {codec_ty_span=> <#codec_ty as ::xmpp_parsers_core::IntoXml>::into_tree}; let codec_ty_encode = quote_spanned! {codec_ty_span=> <#codec_ty as ::xmpp_parsers_core::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| ::xmpp_parsers_core::exports::minidom::Node::Element(el)) })) }) diff --git a/parsers-macros/src/field/element.rs b/parsers-macros/src/field/element.rs index 94b6d51..488384e 100644 --- a/parsers-macros/src/field/element.rs +++ b/parsers-macros/src/field/element.rs @@ -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`. 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 { 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 = quote_spanned! {ty_span=> <#ty as From<::xmpp_parsers_core::exports::minidom::Element>>::from}; @@ -113,7 +101,7 @@ impl ElementField { #ty_default() } } - FlagOr::Value { value, .. } => { + FlagOr::Value { ref value, .. } => { quote! { #value() } @@ -145,29 +133,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>`. - pub(super) fn build_into_element(self, ident: Expr, ty: Type) -> Result { + fn build_into_element( + &self, + _container_name: &ParentRef, + _container_namespace_expr: &Expr, + _member: &Member, + ty: &Type, + access: Expr, + ) -> Result { Ok(quote! { - match <#ty as Into>>::into(#ident) { + match <#ty as Into>>::into(#access) { Some(v) => builder.append(::xmpp_parsers_core::exports::minidom::Node::Element(v)), None => builder } }) } + + fn build_set_namespace( + &self, + _input: &Ident, + _ty: &Type, + _access: Expr, + ) -> Result { + 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. /// @@ -224,30 +220,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`, - /// 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 { 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! { @@ -289,16 +281,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 { + fn build_into_element( + &self, + _container_name: &ParentRef, + _container_namespace_expr: &Expr, + _member: &Member, + _ty: &Type, + access: Expr, + ) -> Result { Ok(quote! { - builder.append_all(#ident.into_iter().map(|elem| ::xmpp_parsers_core::exports::minidom::Node::Element(elem))) + builder.append_all(#access.into_iter().map(|elem| ::xmpp_parsers_core::exports::minidom::Node::Element(elem))) }) } + + fn build_set_namespace( + &self, + _input: &Ident, + _ty: &Type, + _access: Expr, + ) -> Result { + Ok(TokenStream::default()) + } } diff --git a/parsers-macros/src/field/flag.rs b/parsers-macros/src/field/flag.rs index 43546b4..6498e8c 100644 --- a/parsers-macros/src/field/flag.rs +++ b/parsers-macros/src/field/flag.rs @@ -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 { - 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>`. - pub(super) fn build_into_element(self, ident: Expr, _ty: Type) -> Result { - 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 { + let child_name = &self.name; + let child_namespace = &self.namespace; Ok(quote! { - if #ident { + if #access { builder.append(::xmpp_parsers_core::exports::minidom::Node::Element(::xmpp_parsers_core::exports::minidom::Element::builder(#child_name, #child_namespace).build())) } else { builder } }) } + + fn build_set_namespace( + &self, + _input: &Ident, + _ty: &Type, + _access: Expr, + ) -> Result { + Ok(TokenStream::default()) + } } diff --git a/parsers-macros/src/field/mod.rs b/parsers-macros/src/field/mod.rs index 919e99a..b115768 100644 --- a/parsers-macros/src/field/mod.rs +++ b/parsers-macros/src/field/mod.rs @@ -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 `::xmpp_parsers_core::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; + + /// 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 `::xmpp_parsers_core::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; + + /// 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; +} + /// 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 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 { + 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 { + 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` (`#[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 { + 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> { + 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 { - 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, } 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 { + pub(crate) fn from_field(field: &syn::Field, index: u32) -> Result { 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 { - 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 { 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 { - 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) } } diff --git a/parsers-macros/src/field/namespace.rs b/parsers-macros/src/field/namespace.rs index 1ef5a3e..6069b6d 100644 --- a/parsers-macros/src/field/namespace.rs +++ b/parsers-macros/src/field/namespace.rs @@ -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 { 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 { // 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 { + fn build_into_element( + &self, + _container_name: &ParentRef, + _container_namespace_expr: &Expr, + _member: &Member, + _ty: &Type, + _access: Expr, + ) -> Result { Ok(quote! {builder}) } } diff --git a/parsers-macros/src/field/text.rs b/parsers-macros/src/field/text.rs index 23bff02..b66e075 100644 --- a/parsers-macros/src/field/text.rs +++ b/parsers-macros/src/field/text.rs @@ -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) -> Result { 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 { let decode = match self.codec { - Some(codec_ty) => { + Some(ref codec_ty) => { let codec_ty_decode = quote_spanned! {codec_ty.span()=> <#codec_ty as ::xmpp_parsers_core::TextCodec::<#ty>>::decode}; quote! { #codec_ty_decode(&residual.text())? @@ -66,25 +71,25 @@ 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 { + fn build_into_element( + &self, + _container_name: &ParentRef, + _container_namespace_expr: &Expr, + _member: &Member, + ty: &Type, + access: Expr, + ) -> Result { let encode = match self.codec { - Some(codec_ty) => { + Some(ref codec_ty) => { let codec_ty_encode = quote_spanned! {codec_ty.span()=> <#codec_ty as ::xmpp_parsers_core::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 ::xmpp_parsers_core::IntoXmlText>::into_xml_text}; quote! { - #ty_into_xml_text(#ident) + #ty_into_xml_text(#access) } } }; @@ -100,4 +105,13 @@ impl TextField { } }) } + + fn build_set_namespace( + &self, + _input: &Ident, + _ty: &Type, + _access: Expr, + ) -> Result { + Ok(TokenStream::default()) + } } diff --git a/parsers-macros/src/meta.rs b/parsers-macros/src/meta.rs index 813c2fc..9043723 100644 --- a/parsers-macros/src/meta.rs +++ b/parsers-macros/src/meta.rs @@ -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 From for Flag { } /// A flag with an optional value. -#[derive(Clone)] -#[cfg_attr(feature = "debug", derive(Debug))] +#[derive(Clone, Debug)] pub(crate) enum FlagOr { /// 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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, } diff --git a/parsers-macros/src/structs.rs b/parsers-macros/src/structs.rs index ee81e10..6881544 100644 --- a/parsers-macros/src/structs.rs +++ b/parsers-macros/src/structs.rs @@ -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)]`. /// @@ -662,7 +662,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. /// diff --git a/parsers/src/macro_tests/mod.rs b/parsers/src/macro_tests/mod.rs index 3066831..0cd454c 100644 --- a/parsers/src/macro_tests/mod.rs +++ b/parsers/src/macro_tests/mod.rs @@ -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::( + "some data", + ); +} + +#[test] +fn child_switched_enum_roundtrip_2() { + crate::util::test::roundtrip_full::( + "", + ); +}