From 567ee6c39e4020b64f223667e96337ab35303550 Mon Sep 17 00:00:00 2001 From: Bergmann89 Date: Fri, 20 Mar 2026 17:58:33 +0000 Subject: [PATCH] Implement dynamic namespace evaluation for quick-xml renderer THis commits adds a new `Dynamic` variant to the `NamespaceEvaluation` enum, which allows for dynamic evaluation of namespaces during serialization. In contrast to the other variants, which evaluates the namespaces during code generation, the `Dynamic` variant evaluates the namespaces at runtime, allowing for more flexibility and reducing the number of namespaces emitted for a XML document. --- .gitignore | 3 +- xsd-parser-types/src/quick_xml/mod.rs | 6 +- xsd-parser-types/src/quick_xml/serialize.rs | 98 ++ xsd-parser-types/src/xml/nillable.rs | 22 +- xsd-parser/src/config/mod.rs | 4 + xsd-parser/src/config/renderer.rs | 27 +- xsd-parser/src/lib.rs | 8 +- xsd-parser/src/models/data/path_data.rs | 6 +- .../src/pipeline/generator/data/complex.rs | 17 +- xsd-parser/src/pipeline/generator/data/mod.rs | 13 +- .../src/pipeline/generator/data/reference.rs | 3 +- xsd-parser/src/pipeline/renderer/mod.rs | 7 +- xsd-parser/src/pipeline/renderer/steps/mod.rs | 3 +- .../steps/quick_xml/collect_namespaces.rs | 349 +++++++ .../pipeline/renderer/steps/quick_xml/mod.rs | 2 + .../renderer/steps/quick_xml/serialize.rs | 63 +- .../example/dynamic_alt.xml | 8 + .../example/dynamic_no_alt.xml | 8 + .../expected/quick_xml_dynamic_alt.rs | 924 ++++++++++++++++++ .../expected/quick_xml_dynamic_no_alt.rs | 922 +++++++++++++++++ .../tests/feature/namespaces_qualified/mod.rs | 106 ++ .../nillable/example/dynamic_no_nil.xml | 5 + .../nillable/example/dynamic_with_nil.xml | 4 + .../nillable/expected/dynamic_quick_xml.rs | 924 ++++++++++++++++++ xsd-parser/tests/feature/nillable/mod.rs | 66 +- 25 files changed, 3539 insertions(+), 59 deletions(-) create mode 100644 xsd-parser/src/pipeline/renderer/steps/quick_xml/collect_namespaces.rs create mode 100644 xsd-parser/tests/feature/namespaces_qualified/example/dynamic_alt.xml create mode 100644 xsd-parser/tests/feature/namespaces_qualified/example/dynamic_no_alt.xml create mode 100644 xsd-parser/tests/feature/namespaces_qualified/expected/quick_xml_dynamic_alt.rs create mode 100644 xsd-parser/tests/feature/namespaces_qualified/expected/quick_xml_dynamic_no_alt.rs create mode 100644 xsd-parser/tests/feature/nillable/example/dynamic_no_nil.xml create mode 100644 xsd-parser/tests/feature/nillable/example/dynamic_with_nil.xml create mode 100644 xsd-parser/tests/feature/nillable/expected/dynamic_quick_xml.rs diff --git a/.gitignore b/.gitignore index 18e2dca6..52faae91 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target .vscode -.idea \ No newline at end of file +.devcontainer +.idea diff --git a/xsd-parser-types/src/quick_xml/mod.rs b/xsd-parser-types/src/quick_xml/mod.rs index 5dcc60ab..c1a56245 100644 --- a/xsd-parser-types/src/quick_xml/mod.rs +++ b/xsd-parser-types/src/quick_xml/mod.rs @@ -29,9 +29,9 @@ pub use self::deserialize::{ pub use self::error::{Error, Kind as ErrorKind, UnionError, ValidateError}; pub use self::reader::{ErrorReader, IoReader, SliceReader, XmlReader, XmlReaderSync}; pub use self::serialize::{ - BoxedSerializer, ContentSerializer, DerefIter, IterSerializer, SerializeBytes, - SerializeBytesToString, SerializeHelper, SerializeSync, Serializer, WithBoxedSerializer, - WithSerializeToBytes, WithSerializer, + BoxedSerializer, CollectNamespaces, ContentSerializer, DerefIter, IterSerializer, + SerializeBytes, SerializeBytesToString, SerializeHelper, SerializeSync, Serializer, + WithBoxedSerializer, WithSerializeToBytes, WithSerializer, }; #[cfg(feature = "async")] diff --git a/xsd-parser-types/src/quick_xml/serialize.rs b/xsd-parser-types/src/quick_xml/serialize.rs index 470990cb..29abe9cc 100644 --- a/xsd-parser-types/src/quick_xml/serialize.rs +++ b/xsd-parser-types/src/quick_xml/serialize.rs @@ -512,6 +512,104 @@ where } } +/// Trait for collecting namespaces of a type and all its sub-types. +/// +/// Implement this trait to allow the dynamic serialization mode to discover which +/// XML namespaces a value actually requires at runtime. +pub trait CollectNamespaces { + /// Collect all XML namespaces that `self` (and its children) need into `helper`. + fn collect_namespaces(&self, helper: &mut SerializeHelper, bytes: &mut BytesStart<'_>); +} + +macro_rules! impl_collect_namespaces { + ($ident:path) => { + impl CollectNamespaces for $ident { + fn collect_namespaces(&self, _: &mut SerializeHelper, _: &mut BytesStart<'_>) {} + } + }; +} + +// Implementations for primitive types (all no-ops). +impl_collect_namespaces!(bool); +impl_collect_namespaces!(char); +impl_collect_namespaces!(str); +impl_collect_namespaces!(String); + +impl_collect_namespaces!(u8); +impl_collect_namespaces!(u16); +impl_collect_namespaces!(u32); +impl_collect_namespaces!(u64); +impl_collect_namespaces!(u128); +impl_collect_namespaces!(usize); + +impl_collect_namespaces!(i8); +impl_collect_namespaces!(i16); +impl_collect_namespaces!(i32); +impl_collect_namespaces!(i64); +impl_collect_namespaces!(i128); +impl_collect_namespaces!(isize); + +impl_collect_namespaces!(f32); +impl_collect_namespaces!(f64); + +impl_collect_namespaces!(std::num::NonZeroU8); +impl_collect_namespaces!(std::num::NonZeroU16); +impl_collect_namespaces!(std::num::NonZeroU32); +impl_collect_namespaces!(std::num::NonZeroU64); +impl_collect_namespaces!(std::num::NonZeroU128); +impl_collect_namespaces!(std::num::NonZeroUsize); + +impl_collect_namespaces!(std::num::NonZeroI8); +impl_collect_namespaces!(std::num::NonZeroI16); +impl_collect_namespaces!(std::num::NonZeroI32); +impl_collect_namespaces!(std::num::NonZeroI64); +impl_collect_namespaces!(std::num::NonZeroI128); +impl_collect_namespaces!(std::num::NonZeroIsize); + +#[cfg(feature = "num")] +impl_collect_namespaces!(num::BigInt); + +#[cfg(feature = "num")] +impl_collect_namespaces!(num::BigUint); + +impl CollectNamespaces for Option { + fn collect_namespaces(&self, helper: &mut SerializeHelper, bytes: &mut BytesStart<'_>) { + if let Some(inner) = self { + inner.collect_namespaces(helper, bytes); + } + } +} + +impl CollectNamespaces for Vec { + fn collect_namespaces(&self, helper: &mut SerializeHelper, bytes: &mut BytesStart<'_>) { + for item in self { + item.collect_namespaces(helper, bytes); + } + } +} + +impl CollectNamespaces for Box { + fn collect_namespaces(&self, helper: &mut SerializeHelper, bytes: &mut BytesStart<'_>) { + self.as_ref().collect_namespaces(helper, bytes); + } +} + +impl CollectNamespaces for [T; N] { + fn collect_namespaces(&self, helper: &mut SerializeHelper, bytes: &mut BytesStart<'_>) { + for item in self { + item.collect_namespaces(helper, bytes); + } + } +} + +impl CollectNamespaces for [T] { + fn collect_namespaces(&self, helper: &mut SerializeHelper, bytes: &mut BytesStart<'_>) { + for item in self { + item.collect_namespaces(helper, bytes); + } + } +} + /// Helper that defines some useful methods needed for `quick_xml` serialization. /// /// Mainly used in [`Serializer`] and [`SerializeBytes`]. diff --git a/xsd-parser-types/src/xml/nillable.rs b/xsd-parser-types/src/xml/nillable.rs index 18f7eb0f..2aebff4b 100644 --- a/xsd-parser-types/src/xml/nillable.rs +++ b/xsd-parser-types/src/xml/nillable.rs @@ -8,9 +8,9 @@ use crate::misc::Namespace; #[cfg(feature = "quick-xml")] use crate::quick_xml::{ - DeserializeHelper, Deserializer, DeserializerArtifact, DeserializerEvent, DeserializerOutput, - DeserializerResult, Error, ErrorKind, SerializeHelper, Serializer, WithDeserializer, - WithSerializer, + CollectNamespaces, DeserializeHelper, Deserializer, DeserializerArtifact, DeserializerEvent, + DeserializerOutput, DeserializerResult, Error, ErrorKind, SerializeHelper, Serializer, + WithDeserializer, WithSerializer, }; use crate::traits::WithNamespace; @@ -108,6 +108,22 @@ where } } +#[cfg(feature = "quick-xml")] +impl CollectNamespaces for Nillable +where + T: CollectNamespaces, +{ + fn collect_namespaces(&self, helper: &mut SerializeHelper, bytes: &mut BytesStart<'_>) { + use crate::misc::NamespacePrefix; + + if self.0.is_none() { + helper.write_xmlns(bytes, Some(&NamespacePrefix::XSI), &Namespace::XSI); + } else if let Some(inner) = &self.0 { + inner.collect_namespaces(helper, bytes); + } + } +} + #[cfg(feature = "quick-xml")] impl WithSerializer for Nillable where diff --git a/xsd-parser/src/config/mod.rs b/xsd-parser/src/config/mod.rs index b39e499a..6c5d0be9 100644 --- a/xsd-parser/src/config/mod.rs +++ b/xsd-parser/src/config/mod.rs @@ -277,6 +277,10 @@ impl Config { self = self.with_render_step(RenderStep::PrefixConstants); } + if namespaces == NamespaceSerialization::Dynamic { + self = self.with_render_step(RenderStep::QuickXmlCollectNamespaces); + } + self.with_render_steps([ RenderStep::NamespaceConstants, RenderStep::QuickXmlSerialize { diff --git a/xsd-parser/src/config/renderer.rs b/xsd-parser/src/config/renderer.rs index 07e959af..be0826ec 100644 --- a/xsd-parser/src/config/renderer.rs +++ b/xsd-parser/src/config/renderer.rs @@ -187,6 +187,14 @@ pub enum RenderStep { /// For more details have a look at [`QuickXmlDeserializeRenderer::boxed_deserializer`](crate::pipeline::renderer::QuickXmlDeserializeRenderStep::boxed_deserializer). boxed_deserializer: bool, }, + + /// Renderer that renders the [`CollectNamespaces`](xsd_parser_types::quick_xml::CollectNamespaces) + /// trait for each generated type. + /// + /// This step is required when using [`NamespaceSerialization::Dynamic`] so + /// that the serializer can discover which XML namespaces are actually needed + /// at runtime before writing the root start element. + QuickXmlCollectNamespaces, } /// Helper trait to deal with custom render steps. @@ -241,6 +249,7 @@ impl RenderStepConfig for RenderStep { Self::WithNamespaceTrait => RenderStepType::ExtraImpls, Self::QuickXmlSerialize { .. } => RenderStepType::ExtraImpls, Self::QuickXmlDeserialize { .. } => RenderStepType::ExtraImpls, + Self::QuickXmlCollectNamespaces => RenderStepType::ExtraImpls, } } @@ -251,7 +260,8 @@ impl RenderStepConfig for RenderStep { fn into_render_step(self: Box) -> Box { use crate::pipeline::renderer::{ DefaultsRenderStep, EnumConstantsRenderStep, NamespaceConstantsRenderStep, - PrefixConstantsRenderStep, QuickXmlDeserializeRenderStep, QuickXmlSerializeRenderStep, + PrefixConstantsRenderStep, QuickXmlCollectNamespacesRenderStep, + QuickXmlDeserializeRenderStep, QuickXmlSerializeRenderStep, SerdeQuickXmlTypesRenderStep, SerdeXmlRsV7TypesRenderStep, SerdeXmlRsV8TypesRenderStep, TypesRenderStep, WithNamespaceTraitRenderStep, }; @@ -280,14 +290,16 @@ impl RenderStepConfig for RenderStep { Self::QuickXmlDeserialize { boxed_deserializer } => { Box::new(QuickXmlDeserializeRenderStep { boxed_deserializer }) } + Self::QuickXmlCollectNamespaces => Box::new(QuickXmlCollectNamespacesRenderStep), } } fn is_mutual_exclusive_to(&self, other: &dyn RenderStepConfig) -> bool { use crate::pipeline::renderer::{ - DefaultsRenderStep, NamespaceConstantsRenderStep, QuickXmlDeserializeRenderStep, - QuickXmlSerializeRenderStep, SerdeQuickXmlTypesRenderStep, SerdeXmlRsV7TypesRenderStep, - SerdeXmlRsV8TypesRenderStep, TypesRenderStep, WithNamespaceTraitRenderStep, + DefaultsRenderStep, NamespaceConstantsRenderStep, QuickXmlCollectNamespacesRenderStep, + QuickXmlDeserializeRenderStep, QuickXmlSerializeRenderStep, + SerdeQuickXmlTypesRenderStep, SerdeXmlRsV7TypesRenderStep, SerdeXmlRsV8TypesRenderStep, + TypesRenderStep, WithNamespaceTraitRenderStep, }; if self @@ -309,6 +321,7 @@ impl RenderStepConfig for RenderStep { (Self::WithNamespaceTrait, Some(Self::WithNamespaceTrait)) => true, (Self::QuickXmlSerialize { .. }, Some(Self::QuickXmlSerialize { .. })) => true, (Self::QuickXmlDeserialize { .. }, Some(Self::QuickXmlDeserialize { .. })) => true, + (Self::QuickXmlCollectNamespaces, Some(Self::QuickXmlCollectNamespaces)) => true, (Self::Types, None) => other_id == TypeId::of::(), ( Self::TypesSerdeXmlRs { @@ -338,6 +351,9 @@ impl RenderStepConfig for RenderStep { (Self::QuickXmlDeserialize { .. }, None) => { other_id == TypeId::of::() } + (Self::QuickXmlCollectNamespaces, None) => { + other_id == TypeId::of::() + } _ => false, } } @@ -356,7 +372,8 @@ impl RenderStep { | (Self::NamespaceConstants, Self::NamespaceConstants) | (Self::WithNamespaceTrait, Self::WithNamespaceTrait) | (Self::QuickXmlSerialize { .. }, Self::QuickXmlSerialize { .. }) - | (Self::QuickXmlDeserialize { .. }, Self::QuickXmlDeserialize { .. }) => true, + | (Self::QuickXmlDeserialize { .. }, Self::QuickXmlDeserialize { .. }) + | (Self::QuickXmlCollectNamespaces, Self::QuickXmlCollectNamespaces) => true, (_, _) => false, } } diff --git a/xsd-parser/src/lib.rs b/xsd-parser/src/lib.rs index f061c9d9..7c1c0333 100644 --- a/xsd-parser/src/lib.rs +++ b/xsd-parser/src/lib.rs @@ -44,10 +44,10 @@ pub use self::pipeline::{ optimizer::Optimizer, parser::Parser, renderer::{ - DefaultsRenderStep, NamespaceConstantsRenderStep, QuickXmlDeserializeRenderStep, - QuickXmlSerializeRenderStep, RenderStep, Renderer, SerdeQuickXmlTypesRenderStep, - SerdeXmlRsV7TypesRenderStep, SerdeXmlRsV8TypesRenderStep, TypesRenderStep, - WithNamespaceTraitRenderStep, + DefaultsRenderStep, NamespaceConstantsRenderStep, QuickXmlCollectNamespacesRenderStep, + QuickXmlDeserializeRenderStep, QuickXmlSerializeRenderStep, RenderStep, Renderer, + SerdeQuickXmlTypesRenderStep, SerdeXmlRsV7TypesRenderStep, SerdeXmlRsV8TypesRenderStep, + TypesRenderStep, WithNamespaceTraitRenderStep, }, }; pub use self::traits::{VecHelper, WithIdent}; diff --git a/xsd-parser/src/models/data/path_data.rs b/xsd-parser/src/models/data/path_data.rs index 957e4eac..3ceb1b8b 100644 --- a/xsd-parser/src/models/data/path_data.rs +++ b/xsd-parser/src/models/data/path_data.rs @@ -71,7 +71,11 @@ impl PathData { if !self.generics.is_empty() { ret.extend(quote!(<)); - for x in &self.generics { + for (idx, x) in self.generics.iter().enumerate() { + if idx > 0 { + ret.extend(quote!(,)); + } + ret.extend(x.relative_to(path)); } diff --git a/xsd-parser/src/pipeline/generator/data/complex.rs b/xsd-parser/src/pipeline/generator/data/complex.rs index 48690c93..37cd8889 100644 --- a/xsd-parser/src/pipeline/generator/data/complex.rs +++ b/xsd-parser/src/pipeline/generator/data/complex.rs @@ -611,11 +611,9 @@ impl<'types> ComplexDataElement<'types> { } let type_ = &ctx.any_type; - let absolute = - ctx.check_generator_flags(GeneratorFlags::ABSOLUTE_PATHS_INSTEAD_USINGS); let target_type = PathData::from_path(type_.clone()).into_included(); let target_type = ctx - .path_data_mixed(mixed, absolute, target_type) + .path_data_mixed(mixed, target_type) .with_using(format!("{type_}")); let target_is_dynamic = false; @@ -634,25 +632,21 @@ impl<'types> ComplexDataElement<'types> { occurs = Occurs::Optional; } - let absolute = - ctx.check_generator_flags(GeneratorFlags::ABSOLUTE_PATHS_INSTEAD_USINGS); let (target_ref, need_box) = ctx.get_or_create_type_ref_for_element( type_, !force_box && direct_usage && occurs.is_direct(), )?; let target_type = target_ref.path.clone(); - let target_type = ctx.path_data_mixed(mixed, absolute, target_type); - let target_type = ctx.path_data_nillable(nillable, absolute, target_type); + let target_type = ctx.path_data_mixed(mixed, target_type); + let target_type = ctx.path_data_nillable(nillable, target_type); let target_is_dynamic = is_dynamic(type_, ctx.types); (target_type, target_is_dynamic, need_box) } ElementMetaVariant::Text => { - let absolute = - ctx.check_generator_flags(GeneratorFlags::ABSOLUTE_PATHS_INSTEAD_USINGS); - let target_type = ctx.path_data_text(absolute); + let target_type = ctx.path_data_text(); let target_is_dynamic = false; let need_box = false; @@ -699,7 +693,6 @@ impl<'types> ComplexDataElement<'types> { .naming .format_variant_ident(&meta.ident.name, None); let origin = ComplexDataElementOrigin::Generated(Box::new(meta)); - let absolute = ctx.check_generator_flags(GeneratorFlags::ABSOLUTE_PATHS_INSTEAD_USINGS); let extra_attributes = Vec::new(); Self { @@ -710,7 +703,7 @@ impl<'types> ComplexDataElement<'types> { tag_name: TagName::default(), field_ident, variant_ident, - target_type: ctx.path_data_text(absolute), + target_type: ctx.path_data_text(), need_indirection: false, target_is_dynamic: false, extra_attributes, diff --git a/xsd-parser/src/pipeline/generator/data/mod.rs b/xsd-parser/src/pipeline/generator/data/mod.rs index 3063fa87..9aed97a7 100644 --- a/xsd-parser/src/pipeline/generator/data/mod.rs +++ b/xsd-parser/src/pipeline/generator/data/mod.rs @@ -10,6 +10,7 @@ mod union; use std::borrow::Cow; use std::mem::swap; +use crate::config::GeneratorFlags; use crate::models::{ code::IdentPath, data::{BuildInData, CustomData, PathData}, @@ -35,10 +36,10 @@ impl<'types> CustomData<'types> { } impl Context<'_, '_> { - fn path_data_nillable(&self, is_mixed: bool, absolute: bool, mut path: PathData) -> PathData { + fn path_data_nillable(&self, is_mixed: bool, mut path: PathData) -> PathData { if !is_mixed { path - } else if absolute { + } else if self.check_generator_flags(GeneratorFlags::ABSOLUTE_PATHS_INSTEAD_USINGS) { let mut tmp = self.nillable_type.clone(); swap(&mut path.path, &mut tmp); @@ -54,10 +55,10 @@ impl Context<'_, '_> { } } - fn path_data_mixed(&self, is_mixed: bool, absolute: bool, mut path: PathData) -> PathData { + fn path_data_mixed(&self, is_mixed: bool, mut path: PathData) -> PathData { if !is_mixed { path - } else if absolute { + } else if self.check_generator_flags(GeneratorFlags::ABSOLUTE_PATHS_INSTEAD_USINGS) { let mut tmp = self.mixed_type.clone(); swap(&mut path.path, &mut tmp); @@ -73,8 +74,8 @@ impl Context<'_, '_> { } } - fn path_data_text(&self, absolute: bool) -> PathData { - if absolute { + fn path_data_text(&self) -> PathData { + if self.check_generator_flags(GeneratorFlags::ABSOLUTE_PATHS_INSTEAD_USINGS) { let target_type = self.text_type.clone(); PathData::from_path(target_type) diff --git a/xsd-parser/src/pipeline/generator/data/reference.rs b/xsd-parser/src/pipeline/generator/data/reference.rs index 2afa3b3e..e15d7f4a 100644 --- a/xsd-parser/src/pipeline/generator/data/reference.rs +++ b/xsd-parser/src/pipeline/generator/data/reference.rs @@ -16,12 +16,11 @@ impl<'types> ReferenceData<'types> { let occurs = Occurs::from_occurs(meta.min_occurs, meta.max_occurs); let nillable = meta.nillable && ctx.check_generator_flags(GeneratorFlags::NILLABLE_TYPE_SUPPORT); - let absolute = ctx.check_generator_flags(GeneratorFlags::ABSOLUTE_PATHS_INSTEAD_USINGS); let type_ident = ctx.current_type_ref().path.ident().clone(); let target_ref = ctx.get_or_create_type_ref_for_value(&meta.type_, occurs.is_direct())?; let target_type = target_ref.path.clone(); - let target_type = ctx.path_data_nillable(nillable, absolute, target_type); + let target_type = ctx.path_data_nillable(nillable, target_type); let trait_impls = ctx.make_trait_impls()?; diff --git a/xsd-parser/src/pipeline/renderer/mod.rs b/xsd-parser/src/pipeline/renderer/mod.rs index c593ecf4..6cc74449 100644 --- a/xsd-parser/src/pipeline/renderer/mod.rs +++ b/xsd-parser/src/pipeline/renderer/mod.rs @@ -44,9 +44,10 @@ pub use self::error::Error; pub use self::meta::MetaData; pub use self::steps::{ DefaultsRenderStep, EnumConstantsRenderStep, NamespaceConstantsRenderStep, - NamespaceSerialization, PrefixConstantsRenderStep, QuickXmlDeserializeRenderStep, - QuickXmlSerializeRenderStep, SerdeQuickXmlTypesRenderStep, SerdeXmlRsV7TypesRenderStep, - SerdeXmlRsV8TypesRenderStep, TypesRenderStep, WithNamespaceTraitRenderStep, + NamespaceSerialization, PrefixConstantsRenderStep, QuickXmlCollectNamespacesRenderStep, + QuickXmlDeserializeRenderStep, QuickXmlSerializeRenderStep, SerdeQuickXmlTypesRenderStep, + SerdeXmlRsV7TypesRenderStep, SerdeXmlRsV8TypesRenderStep, TypesRenderStep, + WithNamespaceTraitRenderStep, }; /// The [`Renderer`] is the central orchestrator for Rust code generation from diff --git a/xsd-parser/src/pipeline/renderer/steps/mod.rs b/xsd-parser/src/pipeline/renderer/steps/mod.rs index 04e7262b..cca635c8 100644 --- a/xsd-parser/src/pipeline/renderer/steps/mod.rs +++ b/xsd-parser/src/pipeline/renderer/steps/mod.rs @@ -27,7 +27,8 @@ pub use self::enum_const::EnumConstantsRenderStep; pub use self::namespace_const::NamespaceConstantsRenderStep; pub use self::prefix_const::PrefixConstantsRenderStep; pub use self::quick_xml::{ - NamespaceSerialization, QuickXmlDeserializeRenderStep, QuickXmlSerializeRenderStep, + NamespaceSerialization, QuickXmlCollectNamespacesRenderStep, QuickXmlDeserializeRenderStep, + QuickXmlSerializeRenderStep, }; pub use self::serde::{ SerdeQuickXmlTypesRenderStep, SerdeXmlRsV7TypesRenderStep, SerdeXmlRsV8TypesRenderStep, diff --git a/xsd-parser/src/pipeline/renderer/steps/quick_xml/collect_namespaces.rs b/xsd-parser/src/pipeline/renderer/steps/quick_xml/collect_namespaces.rs new file mode 100644 index 00000000..0a995aff --- /dev/null +++ b/xsd-parser/src/pipeline/renderer/steps/quick_xml/collect_namespaces.rs @@ -0,0 +1,349 @@ +use proc_macro2::{Ident as Ident2, TokenStream}; +use quote::{format_ident, quote}; + +use crate::config::TypedefMode; +use crate::models::data::TagName; +use crate::models::{ + code::IdentPath, + data::{ + ComplexBase, ComplexData, ComplexDataAttribute, ComplexDataElement, ComplexDataEnum, + ComplexDataStruct, DynamicData, EnumerationData, Occurs, ReferenceData, SimpleData, + StructMode, UnionData, + }, + schema::xs::FormChoiceType, +}; + +use super::super::super::{ + context::Context, DataTypeVariant, MetaData, RenderStep, RenderStepType, +}; + +macro_rules! resolve_ident { + ($ctx:ident, $path:expr) => { + $ctx.resolve_ident_path($path) + }; +} + +/// Implements a [`RenderStep`] that renders the [`CollectNamespaces`](xsd_parser_types::quick_xml::CollectNamespaces) +/// trait for every generated type. +/// +/// This is required when using [`NamespaceSerialization::Dynamic`](super::NamespaceSerialization::Dynamic) +/// so that the serializer can traverse the value tree at runtime to discover which +/// XML namespaces are actually needed before emitting the root start element. +#[derive(Debug, Clone, Copy)] +pub struct QuickXmlCollectNamespacesRenderStep; + +impl RenderStep for QuickXmlCollectNamespacesRenderStep { + fn render_step_type(&self) -> RenderStepType { + RenderStepType::ExtraImpls + } + + fn initialize(&mut self, meta: &mut MetaData<'_>) { + let ident = IdentPath::from_parts( + [meta.xsd_parser_types.clone(), format_ident!("quick_xml")], + format_ident!("CollectNamespaces"), + ); + + if !meta.dyn_type_traits.contains(&ident) { + meta.dyn_type_traits.push(ident); + } + } + + fn render_type(&mut self, ctx: &mut Context<'_, '_>) { + match &ctx.data.variant { + DataTypeVariant::BuildIn(_) | DataTypeVariant::Custom(_) => (), + DataTypeVariant::Union(x) => x.render_collect_namespaces(ctx), + DataTypeVariant::Dynamic(x) => x.render_collect_namespaces(ctx), + DataTypeVariant::Reference(x) => x.render_collect_namespaces(ctx), + DataTypeVariant::Enumeration(x) => x.render_collect_namespaces(ctx), + DataTypeVariant::Simple(x) => x.render_collect_namespaces(ctx), + DataTypeVariant::Complex(x) => x.render_collect_namespaces(ctx), + } + } +} + +/* UnionData */ + +impl UnionData<'_> { + pub(crate) fn render_collect_namespaces(&self, ctx: &mut Context<'_, '_>) { + ctx.render_collect_namespaces_noop(&self.type_ident); + } +} + +/* DynamicData */ + +impl DynamicData<'_> { + pub(crate) fn render_collect_namespaces(&self, ctx: &mut Context<'_, '_>) { + let Self { type_ident, .. } = self; + + let bytes_start = resolve_ident!(ctx, "::xsd_parser_types::quick_xml::BytesStart"); + let collect_namespaces = + resolve_ident!(ctx, "::xsd_parser_types::quick_xml::CollectNamespaces"); + let serialize_helper = + resolve_ident!(ctx, "::xsd_parser_types::quick_xml::SerializeHelper"); + + let code = quote! { + impl #collect_namespaces for #type_ident { + fn collect_namespaces(&self, helper: &mut #serialize_helper, bytes: &mut #bytes_start<'_>) { + self.0.collect_namespaces(helper, bytes); + } + } + }; + + ctx.current_module().append(code); + } +} + +/* ReferenceData */ + +impl ReferenceData<'_> { + pub(crate) fn render_collect_namespaces(&self, ctx: &mut Context<'_, '_>) { + let Self { + mode, type_ident, .. + } = self; + + if matches!(mode, TypedefMode::Auto | TypedefMode::Typedef) { + return; + } + + let bytes_start = resolve_ident!(ctx, "::xsd_parser_types::quick_xml::BytesStart"); + let collect_namespaces = + resolve_ident!(ctx, "::xsd_parser_types::quick_xml::CollectNamespaces"); + let serialize_helper = + resolve_ident!(ctx, "::xsd_parser_types::quick_xml::SerializeHelper"); + + let code = quote! { + impl #collect_namespaces for #type_ident { + fn collect_namespaces(&self, helper: &mut #serialize_helper, bytes: &mut #bytes_start<'_>) { + self.0.collect_namespaces(helper, bytes); + } + } + }; + + ctx.current_module().append(code); + } +} + +/* EnumerationData */ + +impl EnumerationData<'_> { + pub(crate) fn render_collect_namespaces(&self, ctx: &mut Context<'_, '_>) { + ctx.render_collect_namespaces_noop(&self.type_ident); + } +} + +/* SimpleData */ + +impl SimpleData<'_> { + pub(crate) fn render_collect_namespaces(&self, ctx: &mut Context<'_, '_>) { + ctx.render_collect_namespaces_noop(&self.type_ident); + } +} + +/* ComplexData */ + +impl ComplexData<'_> { + pub(crate) fn render_collect_namespaces(&self, ctx: &mut Context<'_, '_>) { + match self { + Self::Enum { + type_, + content_type, + } => { + type_.render_collect_namespaces(ctx); + + if let Some(content_type) = content_type { + content_type.render_collect_namespaces(ctx); + } + } + Self::Struct { + type_, + content_type, + } => { + type_.render_collect_namespaces(ctx); + + if let Some(content_type) = content_type { + content_type.render_collect_namespaces(ctx); + } + } + } + } +} + +impl ComplexDataEnum<'_> { + fn render_collect_namespaces(&self, ctx: &mut Context<'_, '_>) { + let type_ident = &self.type_ident; + + let own_ns = self.render_collect_own_namespace(ctx); + + let variants = self + .elements + .iter() + .map(ComplexDataElement::render_collect_namespaces_match_arm); + + let bytes_start = resolve_ident!(ctx, "::xsd_parser_types::quick_xml::BytesStart"); + let collect_namespaces = + resolve_ident!(ctx, "::xsd_parser_types::quick_xml::CollectNamespaces"); + let serialize_helper = + resolve_ident!(ctx, "::xsd_parser_types::quick_xml::SerializeHelper"); + + let code = quote! { + impl #collect_namespaces for #type_ident { + fn collect_namespaces(&self, helper: &mut #serialize_helper, bytes: &mut #bytes_start<'_>) { + #own_ns + match self { + #( #variants )* + } + } + } + }; + + ctx.current_module().append(code); + } +} + +impl ComplexDataStruct<'_> { + fn render_collect_namespaces(&self, ctx: &mut Context<'_, '_>) { + let type_ident = &self.type_ident; + + let own_ns = self.render_collect_own_namespace(ctx); + let attrib_ns = self.render_collect_attribute_namespaces(ctx); + + let field_calls: Vec<_> = match &self.mode { + StructMode::Empty { .. } => Vec::new(), + StructMode::Content { .. } => { + vec![quote! { + self.content.collect_namespaces(helper, bytes); + }] + } + StructMode::All { elements, .. } | StructMode::Sequence { elements, .. } => elements + .iter() + .map(ComplexDataElement::render_collect_namespaces_field_call) + .collect(), + }; + + let bytes_start = resolve_ident!(ctx, "::xsd_parser_types::quick_xml::BytesStart"); + let collect_namespaces = + resolve_ident!(ctx, "::xsd_parser_types::quick_xml::CollectNamespaces"); + let serialize_helper = + resolve_ident!(ctx, "::xsd_parser_types::quick_xml::SerializeHelper"); + + let code = quote! { + impl #collect_namespaces for #type_ident { + fn collect_namespaces(&self, helper: &mut #serialize_helper, bytes: &mut #bytes_start<'_>) { + #own_ns + #( #attrib_ns )* + #( #field_calls )* + } + } + }; + + ctx.current_module().append(code); + } +} + +impl ComplexBase<'_> { + fn render_collect_own_namespace(&self, ctx: &Context<'_, '_>) -> Option { + self.tag_name.as_ref()?.render_collect_namespace(ctx) + } +} + +impl ComplexDataStruct<'_> { + fn render_collect_attribute_namespaces(&self, ctx: &Context<'_, '_>) -> Vec { + self.attributes + .iter() + .filter_map(|attrib| attrib.render_collect_attribute_namespace(ctx)) + .collect() + } +} + +impl ComplexDataAttribute<'_> { + fn render_collect_attribute_namespace(&self, ctx: &Context<'_, '_>) -> Option { + self.tag_name.render_collect_namespace(ctx) + } +} + +impl ComplexDataElement<'_> { + fn render_collect_namespaces_field_call(&self) -> TokenStream { + let field_ident = &self.field_ident; + + match self.occurs { + Occurs::None => unreachable!(), + Occurs::Single => quote! { + self.#field_ident.collect_namespaces(helper, bytes); + }, + Occurs::Optional => quote! { + if let Some(inner) = &self.#field_ident { + inner.collect_namespaces(helper, bytes); + } + }, + Occurs::DynamicList | Occurs::StaticList(_) => quote! { + for item in &self.#field_ident { + item.collect_namespaces(helper, bytes); + } + }, + } + } + + fn render_collect_namespaces_match_arm(&self) -> TokenStream { + let variant_ident = &self.variant_ident; + + match self.occurs { + Occurs::None => unreachable!(), + Occurs::Single => quote! { + Self::#variant_ident(x) => x.collect_namespaces(helper, bytes), + }, + Occurs::Optional => quote! { + Self::#variant_ident(x) => { + if let Some(inner) = x { + inner.collect_namespaces(helper, bytes); + } + } + }, + Occurs::DynamicList | Occurs::StaticList(_) => quote! { + Self::#variant_ident(x) => { + for item in x.iter() { + item.collect_namespaces(helper, bytes); + } + } + }, + } + } +} + +/* Misc */ + +impl Context<'_, '_> { + fn render_collect_namespaces_noop(&mut self, type_ident: &Ident2) { + let bytes_start = resolve_ident!(self, "::xsd_parser_types::quick_xml::BytesStart"); + let collect_namespaces = + resolve_ident!(self, "::xsd_parser_types::quick_xml::CollectNamespaces"); + let serialize_helper = + resolve_ident!(self, "::xsd_parser_types::quick_xml::SerializeHelper"); + + let code = quote! { + impl #collect_namespaces for #type_ident { + fn collect_namespaces(&self, _helper: &mut #serialize_helper, _bytes: &mut #bytes_start<'_>) { } + } + }; + + self.current_module().append(code); + } +} + +impl TagName<'_> { + fn render_collect_namespace(&self, ctx: &Context<'_, '_>) -> Option { + if self.form != FormChoiceType::Qualified { + return None; + } + + let module = self.module?; + let ns = module.make_ns_const()?; + let ns_const = ctx.resolve_type_for_module(&ns); + + let prefix = module.make_prefix_const()?; + let prefix_const = ctx.resolve_type_for_module(&prefix); + + Some(quote! { + helper.write_xmlns(bytes, Some(&#prefix_const), &#ns_const); + }) + } +} diff --git a/xsd-parser/src/pipeline/renderer/steps/quick_xml/mod.rs b/xsd-parser/src/pipeline/renderer/steps/quick_xml/mod.rs index 49842407..0d49496a 100644 --- a/xsd-parser/src/pipeline/renderer/steps/quick_xml/mod.rs +++ b/xsd-parser/src/pipeline/renderer/steps/quick_xml/mod.rs @@ -1,3 +1,4 @@ +mod collect_namespaces; mod deserialize; mod serialize; @@ -6,6 +7,7 @@ use crate::models::{ meta::{ElementMetaVariant, ElementMode}, }; +pub use self::collect_namespaces::QuickXmlCollectNamespacesRenderStep; pub use self::deserialize::QuickXmlDeserializeRenderStep; pub use self::serialize::{NamespaceSerialization, QuickXmlSerializeRenderStep}; diff --git a/xsd-parser/src/pipeline/renderer/steps/quick_xml/serialize.rs b/xsd-parser/src/pipeline/renderer/steps/quick_xml/serialize.rs index 9f6b8ec7..bd0303bb 100644 --- a/xsd-parser/src/pipeline/renderer/steps/quick_xml/serialize.rs +++ b/xsd-parser/src/pipeline/renderer/steps/quick_xml/serialize.rs @@ -12,14 +12,13 @@ use quote::{format_ident, quote}; use xsd_parser_types::misc::Namespace; use crate::config::{GeneratorFlags, TypedefMode}; -use crate::models::data::EnumerationVariantValue; use crate::models::{ code::IdentPath, data::{ ComplexBase, ComplexData, ComplexDataAttribute, ComplexDataContent, ComplexDataElement, ComplexDataEnum, ComplexDataStruct, DataTypeVariant, DynamicData, EnumerationData, - EnumerationDataVariant, Occurs, PathData, ReferenceData, SimpleData, UnionData, - UnionTypeVariant, + EnumerationDataVariant, EnumerationVariantValue, Occurs, PathData, ReferenceData, + SimpleData, TagName, UnionData, UnionTypeVariant, }, meta::{CustomMetaNamespace, ElementMetaVariant, MetaTypeVariant, MetaTypes}, schema::{xs::FormChoiceType, NamespaceId}, @@ -54,6 +53,16 @@ pub enum NamespaceSerialization { /// All namespace definitions are serialized to the root element. Global, + + /// Namespace definitions are collected at runtime by traversing the value + /// tree via the [`CollectNamespaces`](xsd_parser_types::quick_xml::CollectNamespaces) + /// trait, and only the namespaces that are actually needed by the current + /// value are written to the root element. + /// + /// This requires the [`QuickXmlCollectNamespacesRenderStep`](super::QuickXmlCollectNamespacesRenderStep) + /// to be added to the renderer pipeline as well, so that `CollectNamespaces` + /// is implemented for all generated types. + Dynamic, } impl NamespaceSerialization { @@ -64,12 +73,13 @@ impl NamespaceSerialization { matches!(self, Self::None) } - /// Returns `true` if this is [`NamespaceSerialization::Local`] or - /// [`NamespaceSerialization::Global`], `false` otherwise. + /// Returns `true` if this is [`NamespaceSerialization::Local`], + /// [`NamespaceSerialization::Global`], or [`NamespaceSerialization::Dynamic`], + /// `false` otherwise. #[inline] #[must_use] pub fn is_some(&self) -> bool { - matches!(self, Self::Local | Self::Global) + matches!(self, Self::Local | Self::Global | Self::Dynamic) } } @@ -739,17 +749,7 @@ impl ComplexBase<'_> { .clone(); let mut collector = collector.borrow_mut(); - let xmlns = self.tag_name.as_ref().and_then(|tag| { - let module = tag.module?; - if tag.form != FormChoiceType::Qualified || module.prefix().is_some() { - return None; - } - - let ns = module.make_ns_const()?; - let ns_const = ctx.resolve_type_for_serialize_module(&ns); - - Some(quote!(helper.write_xmlns(&mut bytes, None, &#ns_const);)) - }); + let xmlns = self.tag_name.as_ref().and_then(|x| x.render_xmlns(ctx)); let global_xmlns = collector .get_namespaces(ctx.types, ctx.ident, default_namespace.as_ref()) @@ -785,6 +785,21 @@ impl ComplexBase<'_> { } }) } + NamespaceSerialization::Dynamic => { + let xmlns = self.tag_name.as_ref().and_then(|x| x.render_xmlns(ctx)); + + let collect_namespaces = resolve_quick_xml_ident!( + ctx, + "::xsd_parser_types::quick_xml::CollectNamespaces" + ); + + Some(quote! { + #xmlns + if self.is_root { + #collect_namespaces::collect_namespaces(self.value, helper, &mut bytes); + } + }) + } }; let mut_ = xmlns.is_some() || !attributes.is_empty(); @@ -1374,6 +1389,20 @@ impl Occurs { } } +impl TagName<'_> { + fn render_xmlns(&self, ctx: &mut Context<'_, '_>) -> Option { + let module = self.module?; + if self.form != FormChoiceType::Qualified || module.prefix().is_some() { + return None; + } + + let ns = module.make_ns_const()?; + let ns_const = ctx.resolve_type_for_serialize_module(&ns); + + Some(quote!(helper.write_xmlns(&mut bytes, None, &#ns_const);)) + } +} + impl ValueKey for NamespaceCollector { type Type = Rc>; } diff --git a/xsd-parser/tests/feature/namespaces_qualified/example/dynamic_alt.xml b/xsd-parser/tests/feature/namespaces_qualified/example/dynamic_alt.xml new file mode 100644 index 00000000..51d1c16f --- /dev/null +++ b/xsd-parser/tests/feature/namespaces_qualified/example/dynamic_alt.xml @@ -0,0 +1,8 @@ + + + Bar String + + + Baz String + + diff --git a/xsd-parser/tests/feature/namespaces_qualified/example/dynamic_no_alt.xml b/xsd-parser/tests/feature/namespaces_qualified/example/dynamic_no_alt.xml new file mode 100644 index 00000000..e4e2baad --- /dev/null +++ b/xsd-parser/tests/feature/namespaces_qualified/example/dynamic_no_alt.xml @@ -0,0 +1,8 @@ + + + Bar String + + + Baz String + + diff --git a/xsd-parser/tests/feature/namespaces_qualified/expected/quick_xml_dynamic_alt.rs b/xsd-parser/tests/feature/namespaces_qualified/expected/quick_xml_dynamic_alt.rs new file mode 100644 index 00000000..2ba3bdbf --- /dev/null +++ b/xsd-parser/tests/feature/namespaces_qualified/expected/quick_xml_dynamic_alt.rs @@ -0,0 +1,924 @@ +use xsd_parser_types::{ + misc::{Namespace, NamespacePrefix}, + quick_xml::{ + BytesStart, CollectNamespaces, Error, SerializeHelper, WithDeserializer, WithSerializer, + }, +}; +pub const PREFIX_XS: NamespacePrefix = NamespacePrefix::new_const(b"xs"); +pub const PREFIX_XML: NamespacePrefix = NamespacePrefix::new_const(b"xml"); +pub const PREFIX_XSI: NamespacePrefix = NamespacePrefix::new_const(b"xsi"); +pub const PREFIX_BAR: NamespacePrefix = NamespacePrefix::new_const(b"bar"); +pub const PREFIX_BAZ: NamespacePrefix = NamespacePrefix::new_const(b"baz"); +pub const NS_XS: Namespace = Namespace::new_const(b"http://www.w3.org/2001/XMLSchema"); +pub const NS_XML: Namespace = Namespace::new_const(b"http://www.w3.org/XML/1998/namespace"); +pub const NS_XSI: Namespace = Namespace::new_const(b"http://www.w3.org/2001/XMLSchema-instance"); +pub const NS_UNNAMED_5: Namespace = Namespace::new_const(b"Foo"); +pub const NS_BAR: Namespace = Namespace::new_const(b"Bar"); +pub const NS_BAZ: Namespace = Namespace::new_const(b"Baz"); +pub type Foo = FooType; +#[derive(Debug)] +pub struct FooType { + pub inner_1: Inner1Type, + pub inner_2: Inner2Type, +} +impl WithDeserializer for FooType { + type Deserializer = quick_xml_deserialize::FooTypeDeserializer; +} +impl CollectNamespaces for FooType { + fn collect_namespaces(&self, helper: &mut SerializeHelper, bytes: &mut BytesStart<'_>) { + self.inner_1.collect_namespaces(helper, bytes); + self.inner_2.collect_namespaces(helper, bytes); + } +} +impl WithSerializer for FooType { + type Serializer<'x> = quick_xml_serialize::FooTypeSerializer<'x>; + fn serializer<'ser>( + &'ser self, + name: Option<&'ser str>, + is_root: bool, + ) -> Result, Error> { + Ok(quick_xml_serialize::FooTypeSerializer { + value: self, + state: Box::new(quick_xml_serialize::FooTypeSerializerState::Init__), + name: name.unwrap_or("Foo"), + is_root, + }) + } +} +#[derive(Debug)] +pub struct Inner1Type { + pub a: String, +} +impl WithDeserializer for Inner1Type { + type Deserializer = quick_xml_deserialize::Inner1TypeDeserializer; +} +impl CollectNamespaces for Inner1Type { + fn collect_namespaces(&self, helper: &mut SerializeHelper, bytes: &mut BytesStart<'_>) { + helper.write_xmlns(bytes, Some(&PREFIX_BAR), &NS_BAR); + self.a.collect_namespaces(helper, bytes); + } +} +impl WithSerializer for Inner1Type { + type Serializer<'x> = quick_xml_serialize::Inner1TypeSerializer<'x>; + fn serializer<'ser>( + &'ser self, + name: Option<&'ser str>, + is_root: bool, + ) -> Result, Error> { + Ok(quick_xml_serialize::Inner1TypeSerializer { + value: self, + state: Box::new(quick_xml_serialize::Inner1TypeSerializerState::Init__), + name: name.unwrap_or("bar:Inner1"), + is_root, + }) + } +} +#[derive(Debug)] +pub struct Inner2Type { + pub b: String, +} +impl WithDeserializer for Inner2Type { + type Deserializer = quick_xml_deserialize::Inner2TypeDeserializer; +} +impl CollectNamespaces for Inner2Type { + fn collect_namespaces(&self, helper: &mut SerializeHelper, bytes: &mut BytesStart<'_>) { + helper.write_xmlns(bytes, Some(&PREFIX_BAZ), &NS_BAZ); + self.b.collect_namespaces(helper, bytes); + } +} +impl WithSerializer for Inner2Type { + type Serializer<'x> = quick_xml_serialize::Inner2TypeSerializer<'x>; + fn serializer<'ser>( + &'ser self, + name: Option<&'ser str>, + is_root: bool, + ) -> Result, Error> { + Ok(quick_xml_serialize::Inner2TypeSerializer { + value: self, + state: Box::new(quick_xml_serialize::Inner2TypeSerializerState::Init__), + name: name.unwrap_or("baz:Inner2"), + is_root, + }) + } +} +pub mod quick_xml_deserialize { + use core::mem::replace; + use xsd_parser_types::quick_xml::{ + BytesStart, DeserializeHelper, Deserializer, DeserializerArtifact, DeserializerEvent, + DeserializerOutput, DeserializerResult, ElementHandlerOutput, Error, ErrorKind, Event, + RawByteStr, WithDeserializer, + }; + #[derive(Debug)] + pub struct FooTypeDeserializer { + inner_1: Option, + inner_2: Option, + state__: Box, + } + #[derive(Debug)] + enum FooTypeDeserializerState { + Init__, + Inner1(Option<::Deserializer>), + Inner2(Option<::Deserializer>), + Done__, + Unknown__, + } + impl FooTypeDeserializer { + fn from_bytes_start( + helper: &mut DeserializeHelper, + bytes_start: &BytesStart<'_>, + ) -> Result { + for attrib in helper.filter_xmlns_attributes(bytes_start) { + let attrib = attrib?; + helper.raise_unexpected_attrib_checked(&attrib)?; + } + Ok(Self { + inner_1: None, + inner_2: None, + state__: Box::new(FooTypeDeserializerState::Init__), + }) + } + fn finish_state( + &mut self, + helper: &mut DeserializeHelper, + state: FooTypeDeserializerState, + ) -> Result<(), Error> { + use FooTypeDeserializerState as S; + match state { + S::Inner1(Some(deserializer)) => { + self.store_inner_1(deserializer.finish(helper)?)? + } + S::Inner2(Some(deserializer)) => { + self.store_inner_2(deserializer.finish(helper)?)? + } + _ => (), + } + Ok(()) + } + fn store_inner_1(&mut self, value: super::Inner1Type) -> Result<(), Error> { + if self.inner_1.is_some() { + Err(ErrorKind::DuplicateElement(RawByteStr::from_slice( + b"Inner1", + )))?; + } + self.inner_1 = Some(value); + Ok(()) + } + fn store_inner_2(&mut self, value: super::Inner2Type) -> Result<(), Error> { + if self.inner_2.is_some() { + Err(ErrorKind::DuplicateElement(RawByteStr::from_slice( + b"Inner2", + )))?; + } + self.inner_2 = Some(value); + Ok(()) + } + fn handle_inner_1<'de>( + &mut self, + helper: &mut DeserializeHelper, + output: DeserializerOutput<'de, super::Inner1Type>, + fallback: &mut Option, + ) -> Result, Error> { + use FooTypeDeserializerState as S; + let DeserializerOutput { + artifact, + event, + allow_any, + } = output; + if artifact.is_none() { + fallback.get_or_insert(S::Inner1(None)); + if matches!(&fallback, Some(S::Init__)) { + return Ok(ElementHandlerOutput::break_(event, allow_any)); + } else { + return Ok(ElementHandlerOutput::return_to_root(event, allow_any)); + } + } + if let Some(fallback) = fallback.take() { + self.finish_state(helper, fallback)?; + } + match artifact { + DeserializerArtifact::None => unreachable!(), + DeserializerArtifact::Data(data) => { + self.store_inner_1(data)?; + *self.state__ = S::Inner2(None); + Ok(ElementHandlerOutput::from_event(event, allow_any)) + } + DeserializerArtifact::Deserializer(deserializer) => { + fallback.get_or_insert(S::Inner1(Some(deserializer))); + *self.state__ = S::Inner2(None); + Ok(ElementHandlerOutput::from_event(event, allow_any)) + } + } + } + fn handle_inner_2<'de>( + &mut self, + helper: &mut DeserializeHelper, + output: DeserializerOutput<'de, super::Inner2Type>, + fallback: &mut Option, + ) -> Result, Error> { + use FooTypeDeserializerState as S; + let DeserializerOutput { + artifact, + event, + allow_any, + } = output; + if artifact.is_none() { + fallback.get_or_insert(S::Inner2(None)); + if matches!(&fallback, Some(S::Init__)) { + return Ok(ElementHandlerOutput::break_(event, allow_any)); + } else { + return Ok(ElementHandlerOutput::return_to_root(event, allow_any)); + } + } + if let Some(fallback) = fallback.take() { + self.finish_state(helper, fallback)?; + } + match artifact { + DeserializerArtifact::None => unreachable!(), + DeserializerArtifact::Data(data) => { + self.store_inner_2(data)?; + *self.state__ = S::Done__; + Ok(ElementHandlerOutput::from_event(event, allow_any)) + } + DeserializerArtifact::Deserializer(deserializer) => { + fallback.get_or_insert(S::Inner2(Some(deserializer))); + *self.state__ = S::Done__; + Ok(ElementHandlerOutput::from_event(event, allow_any)) + } + } + } + } + impl<'de> Deserializer<'de, super::FooType> for FooTypeDeserializer { + fn init( + helper: &mut DeserializeHelper, + event: Event<'de>, + ) -> DeserializerResult<'de, super::FooType> { + helper.init_deserializer_from_start_event(event, Self::from_bytes_start) + } + fn next( + mut self, + helper: &mut DeserializeHelper, + event: Event<'de>, + ) -> DeserializerResult<'de, super::FooType> { + use FooTypeDeserializerState as S; + let mut event = event; + let mut fallback = None; + let mut allow_any_element = false; + let (event, allow_any) = loop { + let state = replace(&mut *self.state__, S::Unknown__); + event = match (state, event) { + (S::Unknown__, _) => unreachable!(), + (S::Inner1(Some(deserializer)), event) => { + let output = deserializer.next(helper, event)?; + match self.handle_inner_1(helper, output, &mut fallback)? { + ElementHandlerOutput::Continue { event, allow_any } => { + allow_any_element = allow_any_element || allow_any; + event + } + ElementHandlerOutput::Break { event, allow_any } => { + break (event, allow_any) + } + } + } + (S::Inner2(Some(deserializer)), event) => { + let output = deserializer.next(helper, event)?; + match self.handle_inner_2(helper, output, &mut fallback)? { + ElementHandlerOutput::Continue { event, allow_any } => { + allow_any_element = allow_any_element || allow_any; + event + } + ElementHandlerOutput::Break { event, allow_any } => { + break (event, allow_any) + } + } + } + (_, Event::End(_)) => { + if let Some(fallback) = fallback.take() { + self.finish_state(helper, fallback)?; + } + return Ok(DeserializerOutput { + artifact: DeserializerArtifact::Data(self.finish(helper)?), + event: DeserializerEvent::None, + allow_any: false, + }); + } + (S::Init__, event) => { + fallback.get_or_insert(S::Init__); + *self.state__ = S::Inner1(None); + event + } + (S::Inner1(None), event @ (Event::Start(_) | Event::Empty(_))) => { + let output = helper.init_start_tag_deserializer( + event, + Some(&super::NS_BAR), + b"Inner1", + false, + )?; + match self.handle_inner_1(helper, output, &mut fallback)? { + ElementHandlerOutput::Continue { event, allow_any } => { + allow_any_element = allow_any_element || allow_any; + event + } + ElementHandlerOutput::Break { event, allow_any } => { + break (event, allow_any) + } + } + } + (S::Inner2(None), event @ (Event::Start(_) | Event::Empty(_))) => { + let output = helper.init_start_tag_deserializer( + event, + Some(&super::NS_BAZ), + b"Inner2", + false, + )?; + match self.handle_inner_2(helper, output, &mut fallback)? { + ElementHandlerOutput::Continue { event, allow_any } => { + allow_any_element = allow_any_element || allow_any; + event + } + ElementHandlerOutput::Break { event, allow_any } => { + break (event, allow_any) + } + } + } + (S::Done__, event) => { + *self.state__ = S::Done__; + break (DeserializerEvent::Continue(event), allow_any_element); + } + (state, event) => { + *self.state__ = state; + break (DeserializerEvent::Break(event), false); + } + } + }; + if let Some(fallback) = fallback { + *self.state__ = fallback; + } + Ok(DeserializerOutput { + artifact: DeserializerArtifact::Deserializer(self), + event, + allow_any, + }) + } + fn finish(mut self, helper: &mut DeserializeHelper) -> Result { + let state = replace(&mut *self.state__, FooTypeDeserializerState::Unknown__); + self.finish_state(helper, state)?; + Ok(super::FooType { + inner_1: helper.finish_element("Inner1", self.inner_1)?, + inner_2: helper.finish_element("Inner2", self.inner_2)?, + }) + } + } + #[derive(Debug)] + pub struct Inner1TypeDeserializer { + a: Option, + state__: Box, + } + #[derive(Debug)] + enum Inner1TypeDeserializerState { + Init__, + A(Option<::Deserializer>), + Done__, + Unknown__, + } + impl Inner1TypeDeserializer { + fn from_bytes_start( + helper: &mut DeserializeHelper, + bytes_start: &BytesStart<'_>, + ) -> Result { + for attrib in helper.filter_xmlns_attributes(bytes_start) { + let attrib = attrib?; + helper.raise_unexpected_attrib_checked(&attrib)?; + } + Ok(Self { + a: None, + state__: Box::new(Inner1TypeDeserializerState::Init__), + }) + } + fn finish_state( + &mut self, + helper: &mut DeserializeHelper, + state: Inner1TypeDeserializerState, + ) -> Result<(), Error> { + use Inner1TypeDeserializerState as S; + match state { + S::A(Some(deserializer)) => self.store_a(deserializer.finish(helper)?)?, + _ => (), + } + Ok(()) + } + fn store_a(&mut self, value: String) -> Result<(), Error> { + if self.a.is_some() { + Err(ErrorKind::DuplicateElement(RawByteStr::from_slice(b"A")))?; + } + self.a = Some(value); + Ok(()) + } + fn handle_a<'de>( + &mut self, + helper: &mut DeserializeHelper, + output: DeserializerOutput<'de, String>, + fallback: &mut Option, + ) -> Result, Error> { + use Inner1TypeDeserializerState as S; + let DeserializerOutput { + artifact, + event, + allow_any, + } = output; + if artifact.is_none() { + fallback.get_or_insert(S::A(None)); + if matches!(&fallback, Some(S::Init__)) { + return Ok(ElementHandlerOutput::break_(event, allow_any)); + } else { + return Ok(ElementHandlerOutput::return_to_root(event, allow_any)); + } + } + if let Some(fallback) = fallback.take() { + self.finish_state(helper, fallback)?; + } + match artifact { + DeserializerArtifact::None => unreachable!(), + DeserializerArtifact::Data(data) => { + self.store_a(data)?; + *self.state__ = S::Done__; + Ok(ElementHandlerOutput::from_event(event, allow_any)) + } + DeserializerArtifact::Deserializer(deserializer) => { + fallback.get_or_insert(S::A(Some(deserializer))); + *self.state__ = S::Done__; + Ok(ElementHandlerOutput::from_event(event, allow_any)) + } + } + } + } + impl<'de> Deserializer<'de, super::Inner1Type> for Inner1TypeDeserializer { + fn init( + helper: &mut DeserializeHelper, + event: Event<'de>, + ) -> DeserializerResult<'de, super::Inner1Type> { + helper.init_deserializer_from_start_event(event, Self::from_bytes_start) + } + fn next( + mut self, + helper: &mut DeserializeHelper, + event: Event<'de>, + ) -> DeserializerResult<'de, super::Inner1Type> { + use Inner1TypeDeserializerState as S; + let mut event = event; + let mut fallback = None; + let mut allow_any_element = false; + let (event, allow_any) = loop { + let state = replace(&mut *self.state__, S::Unknown__); + event = match (state, event) { + (S::Unknown__, _) => unreachable!(), + (S::A(Some(deserializer)), event) => { + let output = deserializer.next(helper, event)?; + match self.handle_a(helper, output, &mut fallback)? { + ElementHandlerOutput::Continue { event, allow_any } => { + allow_any_element = allow_any_element || allow_any; + event + } + ElementHandlerOutput::Break { event, allow_any } => { + break (event, allow_any) + } + } + } + (_, Event::End(_)) => { + if let Some(fallback) = fallback.take() { + self.finish_state(helper, fallback)?; + } + return Ok(DeserializerOutput { + artifact: DeserializerArtifact::Data(self.finish(helper)?), + event: DeserializerEvent::None, + allow_any: false, + }); + } + (S::Init__, event) => { + fallback.get_or_insert(S::Init__); + *self.state__ = S::A(None); + event + } + (S::A(None), event @ (Event::Start(_) | Event::Empty(_))) => { + let output = helper.init_start_tag_deserializer( + event, + Some(&super::NS_BAR), + b"A", + false, + )?; + match self.handle_a(helper, output, &mut fallback)? { + ElementHandlerOutput::Continue { event, allow_any } => { + allow_any_element = allow_any_element || allow_any; + event + } + ElementHandlerOutput::Break { event, allow_any } => { + break (event, allow_any) + } + } + } + (S::Done__, event) => { + *self.state__ = S::Done__; + break (DeserializerEvent::Continue(event), allow_any_element); + } + (state, event) => { + *self.state__ = state; + break (DeserializerEvent::Break(event), false); + } + } + }; + if let Some(fallback) = fallback { + *self.state__ = fallback; + } + Ok(DeserializerOutput { + artifact: DeserializerArtifact::Deserializer(self), + event, + allow_any, + }) + } + fn finish(mut self, helper: &mut DeserializeHelper) -> Result { + let state = replace(&mut *self.state__, Inner1TypeDeserializerState::Unknown__); + self.finish_state(helper, state)?; + Ok(super::Inner1Type { + a: helper.finish_element("A", self.a)?, + }) + } + } + #[derive(Debug)] + pub struct Inner2TypeDeserializer { + b: Option, + state__: Box, + } + #[derive(Debug)] + enum Inner2TypeDeserializerState { + Init__, + B(Option<::Deserializer>), + Done__, + Unknown__, + } + impl Inner2TypeDeserializer { + fn from_bytes_start( + helper: &mut DeserializeHelper, + bytes_start: &BytesStart<'_>, + ) -> Result { + for attrib in helper.filter_xmlns_attributes(bytes_start) { + let attrib = attrib?; + helper.raise_unexpected_attrib_checked(&attrib)?; + } + Ok(Self { + b: None, + state__: Box::new(Inner2TypeDeserializerState::Init__), + }) + } + fn finish_state( + &mut self, + helper: &mut DeserializeHelper, + state: Inner2TypeDeserializerState, + ) -> Result<(), Error> { + use Inner2TypeDeserializerState as S; + match state { + S::B(Some(deserializer)) => self.store_b(deserializer.finish(helper)?)?, + _ => (), + } + Ok(()) + } + fn store_b(&mut self, value: String) -> Result<(), Error> { + if self.b.is_some() { + Err(ErrorKind::DuplicateElement(RawByteStr::from_slice(b"B")))?; + } + self.b = Some(value); + Ok(()) + } + fn handle_b<'de>( + &mut self, + helper: &mut DeserializeHelper, + output: DeserializerOutput<'de, String>, + fallback: &mut Option, + ) -> Result, Error> { + use Inner2TypeDeserializerState as S; + let DeserializerOutput { + artifact, + event, + allow_any, + } = output; + if artifact.is_none() { + fallback.get_or_insert(S::B(None)); + if matches!(&fallback, Some(S::Init__)) { + return Ok(ElementHandlerOutput::break_(event, allow_any)); + } else { + return Ok(ElementHandlerOutput::return_to_root(event, allow_any)); + } + } + if let Some(fallback) = fallback.take() { + self.finish_state(helper, fallback)?; + } + match artifact { + DeserializerArtifact::None => unreachable!(), + DeserializerArtifact::Data(data) => { + self.store_b(data)?; + *self.state__ = S::Done__; + Ok(ElementHandlerOutput::from_event(event, allow_any)) + } + DeserializerArtifact::Deserializer(deserializer) => { + fallback.get_or_insert(S::B(Some(deserializer))); + *self.state__ = S::Done__; + Ok(ElementHandlerOutput::from_event(event, allow_any)) + } + } + } + } + impl<'de> Deserializer<'de, super::Inner2Type> for Inner2TypeDeserializer { + fn init( + helper: &mut DeserializeHelper, + event: Event<'de>, + ) -> DeserializerResult<'de, super::Inner2Type> { + helper.init_deserializer_from_start_event(event, Self::from_bytes_start) + } + fn next( + mut self, + helper: &mut DeserializeHelper, + event: Event<'de>, + ) -> DeserializerResult<'de, super::Inner2Type> { + use Inner2TypeDeserializerState as S; + let mut event = event; + let mut fallback = None; + let mut allow_any_element = false; + let (event, allow_any) = loop { + let state = replace(&mut *self.state__, S::Unknown__); + event = match (state, event) { + (S::Unknown__, _) => unreachable!(), + (S::B(Some(deserializer)), event) => { + let output = deserializer.next(helper, event)?; + match self.handle_b(helper, output, &mut fallback)? { + ElementHandlerOutput::Continue { event, allow_any } => { + allow_any_element = allow_any_element || allow_any; + event + } + ElementHandlerOutput::Break { event, allow_any } => { + break (event, allow_any) + } + } + } + (_, Event::End(_)) => { + if let Some(fallback) = fallback.take() { + self.finish_state(helper, fallback)?; + } + return Ok(DeserializerOutput { + artifact: DeserializerArtifact::Data(self.finish(helper)?), + event: DeserializerEvent::None, + allow_any: false, + }); + } + (S::Init__, event) => { + fallback.get_or_insert(S::Init__); + *self.state__ = S::B(None); + event + } + (S::B(None), event @ (Event::Start(_) | Event::Empty(_))) => { + let output = helper.init_start_tag_deserializer( + event, + Some(&super::NS_BAZ), + b"B", + false, + )?; + match self.handle_b(helper, output, &mut fallback)? { + ElementHandlerOutput::Continue { event, allow_any } => { + allow_any_element = allow_any_element || allow_any; + event + } + ElementHandlerOutput::Break { event, allow_any } => { + break (event, allow_any) + } + } + } + (S::Done__, event) => { + *self.state__ = S::Done__; + break (DeserializerEvent::Continue(event), allow_any_element); + } + (state, event) => { + *self.state__ = state; + break (DeserializerEvent::Break(event), false); + } + } + }; + if let Some(fallback) = fallback { + *self.state__ = fallback; + } + Ok(DeserializerOutput { + artifact: DeserializerArtifact::Deserializer(self), + event, + allow_any, + }) + } + fn finish(mut self, helper: &mut DeserializeHelper) -> Result { + let state = replace(&mut *self.state__, Inner2TypeDeserializerState::Unknown__); + self.finish_state(helper, state)?; + Ok(super::Inner2Type { + b: helper.finish_element("B", self.b)?, + }) + } + } +} +pub mod quick_xml_serialize { + use xsd_parser_types::quick_xml::{ + BytesEnd, BytesStart, CollectNamespaces, Error, Event, SerializeHelper, Serializer, + WithSerializer, + }; + #[derive(Debug)] + pub struct FooTypeSerializer<'ser> { + pub(super) value: &'ser super::FooType, + pub(super) state: Box>, + pub(super) name: &'ser str, + pub(super) is_root: bool, + } + #[derive(Debug)] + pub(super) enum FooTypeSerializerState<'ser> { + Init__, + Inner1(::Serializer<'ser>), + Inner2(::Serializer<'ser>), + End__, + Done__, + Phantom__(&'ser ()), + } + impl<'ser> FooTypeSerializer<'ser> { + fn next_event( + &mut self, + helper: &mut SerializeHelper, + ) -> Result>, Error> { + loop { + match &mut *self.state { + FooTypeSerializerState::Init__ => { + *self.state = FooTypeSerializerState::Inner1(WithSerializer::serializer( + &self.value.inner_1, + Some("bar:Inner1"), + false, + )?); + let mut bytes = BytesStart::new(self.name); + helper.begin_ns_scope(); + helper.write_xmlns(&mut bytes, None, &super::NS_UNNAMED_5); + if self.is_root { + CollectNamespaces::collect_namespaces(self.value, helper, &mut bytes); + } + return Ok(Some(Event::Start(bytes))); + } + FooTypeSerializerState::Inner1(x) => match x.next(helper).transpose()? { + Some(event) => return Ok(Some(event)), + None => { + *self.state = + FooTypeSerializerState::Inner2(WithSerializer::serializer( + &self.value.inner_2, + Some("baz:Inner2"), + false, + )?) + } + }, + FooTypeSerializerState::Inner2(x) => match x.next(helper).transpose()? { + Some(event) => return Ok(Some(event)), + None => *self.state = FooTypeSerializerState::End__, + }, + FooTypeSerializerState::End__ => { + *self.state = FooTypeSerializerState::Done__; + helper.end_ns_scope(); + return Ok(Some(Event::End(BytesEnd::new(self.name)))); + } + FooTypeSerializerState::Done__ => return Ok(None), + FooTypeSerializerState::Phantom__(_) => unreachable!(), + } + } + } + } + impl<'ser> Serializer<'ser> for FooTypeSerializer<'ser> { + fn next(&mut self, helper: &mut SerializeHelper) -> Option, Error>> { + match self.next_event(helper) { + Ok(Some(event)) => Some(Ok(event)), + Ok(None) => None, + Err(error) => { + *self.state = FooTypeSerializerState::Done__; + Some(Err(error)) + } + } + } + } + #[derive(Debug)] + pub struct Inner1TypeSerializer<'ser> { + pub(super) value: &'ser super::Inner1Type, + pub(super) state: Box>, + pub(super) name: &'ser str, + pub(super) is_root: bool, + } + #[derive(Debug)] + pub(super) enum Inner1TypeSerializerState<'ser> { + Init__, + A(::Serializer<'ser>), + End__, + Done__, + Phantom__(&'ser ()), + } + impl<'ser> Inner1TypeSerializer<'ser> { + fn next_event( + &mut self, + helper: &mut SerializeHelper, + ) -> Result>, Error> { + loop { + match &mut *self.state { + Inner1TypeSerializerState::Init__ => { + *self.state = Inner1TypeSerializerState::A(WithSerializer::serializer( + &self.value.a, + Some("bar:A"), + false, + )?); + let mut bytes = BytesStart::new(self.name); + helper.begin_ns_scope(); + if self.is_root { + CollectNamespaces::collect_namespaces(self.value, helper, &mut bytes); + } + return Ok(Some(Event::Start(bytes))); + } + Inner1TypeSerializerState::A(x) => match x.next(helper).transpose()? { + Some(event) => return Ok(Some(event)), + None => *self.state = Inner1TypeSerializerState::End__, + }, + Inner1TypeSerializerState::End__ => { + *self.state = Inner1TypeSerializerState::Done__; + helper.end_ns_scope(); + return Ok(Some(Event::End(BytesEnd::new(self.name)))); + } + Inner1TypeSerializerState::Done__ => return Ok(None), + Inner1TypeSerializerState::Phantom__(_) => unreachable!(), + } + } + } + } + impl<'ser> Serializer<'ser> for Inner1TypeSerializer<'ser> { + fn next(&mut self, helper: &mut SerializeHelper) -> Option, Error>> { + match self.next_event(helper) { + Ok(Some(event)) => Some(Ok(event)), + Ok(None) => None, + Err(error) => { + *self.state = Inner1TypeSerializerState::Done__; + Some(Err(error)) + } + } + } + } + #[derive(Debug)] + pub struct Inner2TypeSerializer<'ser> { + pub(super) value: &'ser super::Inner2Type, + pub(super) state: Box>, + pub(super) name: &'ser str, + pub(super) is_root: bool, + } + #[derive(Debug)] + pub(super) enum Inner2TypeSerializerState<'ser> { + Init__, + B(::Serializer<'ser>), + End__, + Done__, + Phantom__(&'ser ()), + } + impl<'ser> Inner2TypeSerializer<'ser> { + fn next_event( + &mut self, + helper: &mut SerializeHelper, + ) -> Result>, Error> { + loop { + match &mut *self.state { + Inner2TypeSerializerState::Init__ => { + *self.state = Inner2TypeSerializerState::B(WithSerializer::serializer( + &self.value.b, + Some("baz:B"), + false, + )?); + let mut bytes = BytesStart::new(self.name); + helper.begin_ns_scope(); + if self.is_root { + CollectNamespaces::collect_namespaces(self.value, helper, &mut bytes); + } + return Ok(Some(Event::Start(bytes))); + } + Inner2TypeSerializerState::B(x) => match x.next(helper).transpose()? { + Some(event) => return Ok(Some(event)), + None => *self.state = Inner2TypeSerializerState::End__, + }, + Inner2TypeSerializerState::End__ => { + *self.state = Inner2TypeSerializerState::Done__; + helper.end_ns_scope(); + return Ok(Some(Event::End(BytesEnd::new(self.name)))); + } + Inner2TypeSerializerState::Done__ => return Ok(None), + Inner2TypeSerializerState::Phantom__(_) => unreachable!(), + } + } + } + } + impl<'ser> Serializer<'ser> for Inner2TypeSerializer<'ser> { + fn next(&mut self, helper: &mut SerializeHelper) -> Option, Error>> { + match self.next_event(helper) { + Ok(Some(event)) => Some(Ok(event)), + Ok(None) => None, + Err(error) => { + *self.state = Inner2TypeSerializerState::Done__; + Some(Err(error)) + } + } + } + } +} diff --git a/xsd-parser/tests/feature/namespaces_qualified/expected/quick_xml_dynamic_no_alt.rs b/xsd-parser/tests/feature/namespaces_qualified/expected/quick_xml_dynamic_no_alt.rs new file mode 100644 index 00000000..597c4e1e --- /dev/null +++ b/xsd-parser/tests/feature/namespaces_qualified/expected/quick_xml_dynamic_no_alt.rs @@ -0,0 +1,922 @@ +use xsd_parser_types::{ + misc::{Namespace, NamespacePrefix}, + quick_xml::{ + BytesStart, CollectNamespaces, Error, SerializeHelper, WithDeserializer, WithSerializer, + }, +}; +pub const PREFIX_XS: NamespacePrefix = NamespacePrefix::new_const(b"xs"); +pub const PREFIX_XML: NamespacePrefix = NamespacePrefix::new_const(b"xml"); +pub const PREFIX_XSI: NamespacePrefix = NamespacePrefix::new_const(b"xsi"); +pub const NS_XS: Namespace = Namespace::new_const(b"http://www.w3.org/2001/XMLSchema"); +pub const NS_XML: Namespace = Namespace::new_const(b"http://www.w3.org/XML/1998/namespace"); +pub const NS_XSI: Namespace = Namespace::new_const(b"http://www.w3.org/2001/XMLSchema-instance"); +pub const NS_UNNAMED_5: Namespace = Namespace::new_const(b"Foo"); +pub const NS_UNNAMED_6: Namespace = Namespace::new_const(b"Bar"); +pub const NS_UNNAMED_7: Namespace = Namespace::new_const(b"Baz"); +pub type Foo = FooType; +#[derive(Debug)] +pub struct FooType { + pub inner_1: Inner1Type, + pub inner_2: Inner2Type, +} +impl WithDeserializer for FooType { + type Deserializer = quick_xml_deserialize::FooTypeDeserializer; +} +impl CollectNamespaces for FooType { + fn collect_namespaces(&self, helper: &mut SerializeHelper, bytes: &mut BytesStart<'_>) { + self.inner_1.collect_namespaces(helper, bytes); + self.inner_2.collect_namespaces(helper, bytes); + } +} +impl WithSerializer for FooType { + type Serializer<'x> = quick_xml_serialize::FooTypeSerializer<'x>; + fn serializer<'ser>( + &'ser self, + name: Option<&'ser str>, + is_root: bool, + ) -> Result, Error> { + Ok(quick_xml_serialize::FooTypeSerializer { + value: self, + state: Box::new(quick_xml_serialize::FooTypeSerializerState::Init__), + name: name.unwrap_or("Foo"), + is_root, + }) + } +} +#[derive(Debug)] +pub struct Inner1Type { + pub a: String, +} +impl WithDeserializer for Inner1Type { + type Deserializer = quick_xml_deserialize::Inner1TypeDeserializer; +} +impl CollectNamespaces for Inner1Type { + fn collect_namespaces(&self, helper: &mut SerializeHelper, bytes: &mut BytesStart<'_>) { + self.a.collect_namespaces(helper, bytes); + } +} +impl WithSerializer for Inner1Type { + type Serializer<'x> = quick_xml_serialize::Inner1TypeSerializer<'x>; + fn serializer<'ser>( + &'ser self, + name: Option<&'ser str>, + is_root: bool, + ) -> Result, Error> { + Ok(quick_xml_serialize::Inner1TypeSerializer { + value: self, + state: Box::new(quick_xml_serialize::Inner1TypeSerializerState::Init__), + name: name.unwrap_or("Inner1"), + is_root, + }) + } +} +#[derive(Debug)] +pub struct Inner2Type { + pub b: String, +} +impl WithDeserializer for Inner2Type { + type Deserializer = quick_xml_deserialize::Inner2TypeDeserializer; +} +impl CollectNamespaces for Inner2Type { + fn collect_namespaces(&self, helper: &mut SerializeHelper, bytes: &mut BytesStart<'_>) { + self.b.collect_namespaces(helper, bytes); + } +} +impl WithSerializer for Inner2Type { + type Serializer<'x> = quick_xml_serialize::Inner2TypeSerializer<'x>; + fn serializer<'ser>( + &'ser self, + name: Option<&'ser str>, + is_root: bool, + ) -> Result, Error> { + Ok(quick_xml_serialize::Inner2TypeSerializer { + value: self, + state: Box::new(quick_xml_serialize::Inner2TypeSerializerState::Init__), + name: name.unwrap_or("Inner2"), + is_root, + }) + } +} +pub mod quick_xml_deserialize { + use core::mem::replace; + use xsd_parser_types::quick_xml::{ + BytesStart, DeserializeHelper, Deserializer, DeserializerArtifact, DeserializerEvent, + DeserializerOutput, DeserializerResult, ElementHandlerOutput, Error, ErrorKind, Event, + RawByteStr, WithDeserializer, + }; + #[derive(Debug)] + pub struct FooTypeDeserializer { + inner_1: Option, + inner_2: Option, + state__: Box, + } + #[derive(Debug)] + enum FooTypeDeserializerState { + Init__, + Inner1(Option<::Deserializer>), + Inner2(Option<::Deserializer>), + Done__, + Unknown__, + } + impl FooTypeDeserializer { + fn from_bytes_start( + helper: &mut DeserializeHelper, + bytes_start: &BytesStart<'_>, + ) -> Result { + for attrib in helper.filter_xmlns_attributes(bytes_start) { + let attrib = attrib?; + helper.raise_unexpected_attrib_checked(&attrib)?; + } + Ok(Self { + inner_1: None, + inner_2: None, + state__: Box::new(FooTypeDeserializerState::Init__), + }) + } + fn finish_state( + &mut self, + helper: &mut DeserializeHelper, + state: FooTypeDeserializerState, + ) -> Result<(), Error> { + use FooTypeDeserializerState as S; + match state { + S::Inner1(Some(deserializer)) => { + self.store_inner_1(deserializer.finish(helper)?)? + } + S::Inner2(Some(deserializer)) => { + self.store_inner_2(deserializer.finish(helper)?)? + } + _ => (), + } + Ok(()) + } + fn store_inner_1(&mut self, value: super::Inner1Type) -> Result<(), Error> { + if self.inner_1.is_some() { + Err(ErrorKind::DuplicateElement(RawByteStr::from_slice( + b"Inner1", + )))?; + } + self.inner_1 = Some(value); + Ok(()) + } + fn store_inner_2(&mut self, value: super::Inner2Type) -> Result<(), Error> { + if self.inner_2.is_some() { + Err(ErrorKind::DuplicateElement(RawByteStr::from_slice( + b"Inner2", + )))?; + } + self.inner_2 = Some(value); + Ok(()) + } + fn handle_inner_1<'de>( + &mut self, + helper: &mut DeserializeHelper, + output: DeserializerOutput<'de, super::Inner1Type>, + fallback: &mut Option, + ) -> Result, Error> { + use FooTypeDeserializerState as S; + let DeserializerOutput { + artifact, + event, + allow_any, + } = output; + if artifact.is_none() { + fallback.get_or_insert(S::Inner1(None)); + if matches!(&fallback, Some(S::Init__)) { + return Ok(ElementHandlerOutput::break_(event, allow_any)); + } else { + return Ok(ElementHandlerOutput::return_to_root(event, allow_any)); + } + } + if let Some(fallback) = fallback.take() { + self.finish_state(helper, fallback)?; + } + match artifact { + DeserializerArtifact::None => unreachable!(), + DeserializerArtifact::Data(data) => { + self.store_inner_1(data)?; + *self.state__ = S::Inner2(None); + Ok(ElementHandlerOutput::from_event(event, allow_any)) + } + DeserializerArtifact::Deserializer(deserializer) => { + fallback.get_or_insert(S::Inner1(Some(deserializer))); + *self.state__ = S::Inner2(None); + Ok(ElementHandlerOutput::from_event(event, allow_any)) + } + } + } + fn handle_inner_2<'de>( + &mut self, + helper: &mut DeserializeHelper, + output: DeserializerOutput<'de, super::Inner2Type>, + fallback: &mut Option, + ) -> Result, Error> { + use FooTypeDeserializerState as S; + let DeserializerOutput { + artifact, + event, + allow_any, + } = output; + if artifact.is_none() { + fallback.get_or_insert(S::Inner2(None)); + if matches!(&fallback, Some(S::Init__)) { + return Ok(ElementHandlerOutput::break_(event, allow_any)); + } else { + return Ok(ElementHandlerOutput::return_to_root(event, allow_any)); + } + } + if let Some(fallback) = fallback.take() { + self.finish_state(helper, fallback)?; + } + match artifact { + DeserializerArtifact::None => unreachable!(), + DeserializerArtifact::Data(data) => { + self.store_inner_2(data)?; + *self.state__ = S::Done__; + Ok(ElementHandlerOutput::from_event(event, allow_any)) + } + DeserializerArtifact::Deserializer(deserializer) => { + fallback.get_or_insert(S::Inner2(Some(deserializer))); + *self.state__ = S::Done__; + Ok(ElementHandlerOutput::from_event(event, allow_any)) + } + } + } + } + impl<'de> Deserializer<'de, super::FooType> for FooTypeDeserializer { + fn init( + helper: &mut DeserializeHelper, + event: Event<'de>, + ) -> DeserializerResult<'de, super::FooType> { + helper.init_deserializer_from_start_event(event, Self::from_bytes_start) + } + fn next( + mut self, + helper: &mut DeserializeHelper, + event: Event<'de>, + ) -> DeserializerResult<'de, super::FooType> { + use FooTypeDeserializerState as S; + let mut event = event; + let mut fallback = None; + let mut allow_any_element = false; + let (event, allow_any) = loop { + let state = replace(&mut *self.state__, S::Unknown__); + event = match (state, event) { + (S::Unknown__, _) => unreachable!(), + (S::Inner1(Some(deserializer)), event) => { + let output = deserializer.next(helper, event)?; + match self.handle_inner_1(helper, output, &mut fallback)? { + ElementHandlerOutput::Continue { event, allow_any } => { + allow_any_element = allow_any_element || allow_any; + event + } + ElementHandlerOutput::Break { event, allow_any } => { + break (event, allow_any) + } + } + } + (S::Inner2(Some(deserializer)), event) => { + let output = deserializer.next(helper, event)?; + match self.handle_inner_2(helper, output, &mut fallback)? { + ElementHandlerOutput::Continue { event, allow_any } => { + allow_any_element = allow_any_element || allow_any; + event + } + ElementHandlerOutput::Break { event, allow_any } => { + break (event, allow_any) + } + } + } + (_, Event::End(_)) => { + if let Some(fallback) = fallback.take() { + self.finish_state(helper, fallback)?; + } + return Ok(DeserializerOutput { + artifact: DeserializerArtifact::Data(self.finish(helper)?), + event: DeserializerEvent::None, + allow_any: false, + }); + } + (S::Init__, event) => { + fallback.get_or_insert(S::Init__); + *self.state__ = S::Inner1(None); + event + } + (S::Inner1(None), event @ (Event::Start(_) | Event::Empty(_))) => { + let output = helper.init_start_tag_deserializer( + event, + Some(&super::NS_UNNAMED_6), + b"Inner1", + false, + )?; + match self.handle_inner_1(helper, output, &mut fallback)? { + ElementHandlerOutput::Continue { event, allow_any } => { + allow_any_element = allow_any_element || allow_any; + event + } + ElementHandlerOutput::Break { event, allow_any } => { + break (event, allow_any) + } + } + } + (S::Inner2(None), event @ (Event::Start(_) | Event::Empty(_))) => { + let output = helper.init_start_tag_deserializer( + event, + Some(&super::NS_UNNAMED_7), + b"Inner2", + false, + )?; + match self.handle_inner_2(helper, output, &mut fallback)? { + ElementHandlerOutput::Continue { event, allow_any } => { + allow_any_element = allow_any_element || allow_any; + event + } + ElementHandlerOutput::Break { event, allow_any } => { + break (event, allow_any) + } + } + } + (S::Done__, event) => { + *self.state__ = S::Done__; + break (DeserializerEvent::Continue(event), allow_any_element); + } + (state, event) => { + *self.state__ = state; + break (DeserializerEvent::Break(event), false); + } + } + }; + if let Some(fallback) = fallback { + *self.state__ = fallback; + } + Ok(DeserializerOutput { + artifact: DeserializerArtifact::Deserializer(self), + event, + allow_any, + }) + } + fn finish(mut self, helper: &mut DeserializeHelper) -> Result { + let state = replace(&mut *self.state__, FooTypeDeserializerState::Unknown__); + self.finish_state(helper, state)?; + Ok(super::FooType { + inner_1: helper.finish_element("Inner1", self.inner_1)?, + inner_2: helper.finish_element("Inner2", self.inner_2)?, + }) + } + } + #[derive(Debug)] + pub struct Inner1TypeDeserializer { + a: Option, + state__: Box, + } + #[derive(Debug)] + enum Inner1TypeDeserializerState { + Init__, + A(Option<::Deserializer>), + Done__, + Unknown__, + } + impl Inner1TypeDeserializer { + fn from_bytes_start( + helper: &mut DeserializeHelper, + bytes_start: &BytesStart<'_>, + ) -> Result { + for attrib in helper.filter_xmlns_attributes(bytes_start) { + let attrib = attrib?; + helper.raise_unexpected_attrib_checked(&attrib)?; + } + Ok(Self { + a: None, + state__: Box::new(Inner1TypeDeserializerState::Init__), + }) + } + fn finish_state( + &mut self, + helper: &mut DeserializeHelper, + state: Inner1TypeDeserializerState, + ) -> Result<(), Error> { + use Inner1TypeDeserializerState as S; + match state { + S::A(Some(deserializer)) => self.store_a(deserializer.finish(helper)?)?, + _ => (), + } + Ok(()) + } + fn store_a(&mut self, value: String) -> Result<(), Error> { + if self.a.is_some() { + Err(ErrorKind::DuplicateElement(RawByteStr::from_slice(b"A")))?; + } + self.a = Some(value); + Ok(()) + } + fn handle_a<'de>( + &mut self, + helper: &mut DeserializeHelper, + output: DeserializerOutput<'de, String>, + fallback: &mut Option, + ) -> Result, Error> { + use Inner1TypeDeserializerState as S; + let DeserializerOutput { + artifact, + event, + allow_any, + } = output; + if artifact.is_none() { + fallback.get_or_insert(S::A(None)); + if matches!(&fallback, Some(S::Init__)) { + return Ok(ElementHandlerOutput::break_(event, allow_any)); + } else { + return Ok(ElementHandlerOutput::return_to_root(event, allow_any)); + } + } + if let Some(fallback) = fallback.take() { + self.finish_state(helper, fallback)?; + } + match artifact { + DeserializerArtifact::None => unreachable!(), + DeserializerArtifact::Data(data) => { + self.store_a(data)?; + *self.state__ = S::Done__; + Ok(ElementHandlerOutput::from_event(event, allow_any)) + } + DeserializerArtifact::Deserializer(deserializer) => { + fallback.get_or_insert(S::A(Some(deserializer))); + *self.state__ = S::Done__; + Ok(ElementHandlerOutput::from_event(event, allow_any)) + } + } + } + } + impl<'de> Deserializer<'de, super::Inner1Type> for Inner1TypeDeserializer { + fn init( + helper: &mut DeserializeHelper, + event: Event<'de>, + ) -> DeserializerResult<'de, super::Inner1Type> { + helper.init_deserializer_from_start_event(event, Self::from_bytes_start) + } + fn next( + mut self, + helper: &mut DeserializeHelper, + event: Event<'de>, + ) -> DeserializerResult<'de, super::Inner1Type> { + use Inner1TypeDeserializerState as S; + let mut event = event; + let mut fallback = None; + let mut allow_any_element = false; + let (event, allow_any) = loop { + let state = replace(&mut *self.state__, S::Unknown__); + event = match (state, event) { + (S::Unknown__, _) => unreachable!(), + (S::A(Some(deserializer)), event) => { + let output = deserializer.next(helper, event)?; + match self.handle_a(helper, output, &mut fallback)? { + ElementHandlerOutput::Continue { event, allow_any } => { + allow_any_element = allow_any_element || allow_any; + event + } + ElementHandlerOutput::Break { event, allow_any } => { + break (event, allow_any) + } + } + } + (_, Event::End(_)) => { + if let Some(fallback) = fallback.take() { + self.finish_state(helper, fallback)?; + } + return Ok(DeserializerOutput { + artifact: DeserializerArtifact::Data(self.finish(helper)?), + event: DeserializerEvent::None, + allow_any: false, + }); + } + (S::Init__, event) => { + fallback.get_or_insert(S::Init__); + *self.state__ = S::A(None); + event + } + (S::A(None), event @ (Event::Start(_) | Event::Empty(_))) => { + let output = helper.init_start_tag_deserializer( + event, + Some(&super::NS_UNNAMED_6), + b"A", + false, + )?; + match self.handle_a(helper, output, &mut fallback)? { + ElementHandlerOutput::Continue { event, allow_any } => { + allow_any_element = allow_any_element || allow_any; + event + } + ElementHandlerOutput::Break { event, allow_any } => { + break (event, allow_any) + } + } + } + (S::Done__, event) => { + *self.state__ = S::Done__; + break (DeserializerEvent::Continue(event), allow_any_element); + } + (state, event) => { + *self.state__ = state; + break (DeserializerEvent::Break(event), false); + } + } + }; + if let Some(fallback) = fallback { + *self.state__ = fallback; + } + Ok(DeserializerOutput { + artifact: DeserializerArtifact::Deserializer(self), + event, + allow_any, + }) + } + fn finish(mut self, helper: &mut DeserializeHelper) -> Result { + let state = replace(&mut *self.state__, Inner1TypeDeserializerState::Unknown__); + self.finish_state(helper, state)?; + Ok(super::Inner1Type { + a: helper.finish_element("A", self.a)?, + }) + } + } + #[derive(Debug)] + pub struct Inner2TypeDeserializer { + b: Option, + state__: Box, + } + #[derive(Debug)] + enum Inner2TypeDeserializerState { + Init__, + B(Option<::Deserializer>), + Done__, + Unknown__, + } + impl Inner2TypeDeserializer { + fn from_bytes_start( + helper: &mut DeserializeHelper, + bytes_start: &BytesStart<'_>, + ) -> Result { + for attrib in helper.filter_xmlns_attributes(bytes_start) { + let attrib = attrib?; + helper.raise_unexpected_attrib_checked(&attrib)?; + } + Ok(Self { + b: None, + state__: Box::new(Inner2TypeDeserializerState::Init__), + }) + } + fn finish_state( + &mut self, + helper: &mut DeserializeHelper, + state: Inner2TypeDeserializerState, + ) -> Result<(), Error> { + use Inner2TypeDeserializerState as S; + match state { + S::B(Some(deserializer)) => self.store_b(deserializer.finish(helper)?)?, + _ => (), + } + Ok(()) + } + fn store_b(&mut self, value: String) -> Result<(), Error> { + if self.b.is_some() { + Err(ErrorKind::DuplicateElement(RawByteStr::from_slice(b"B")))?; + } + self.b = Some(value); + Ok(()) + } + fn handle_b<'de>( + &mut self, + helper: &mut DeserializeHelper, + output: DeserializerOutput<'de, String>, + fallback: &mut Option, + ) -> Result, Error> { + use Inner2TypeDeserializerState as S; + let DeserializerOutput { + artifact, + event, + allow_any, + } = output; + if artifact.is_none() { + fallback.get_or_insert(S::B(None)); + if matches!(&fallback, Some(S::Init__)) { + return Ok(ElementHandlerOutput::break_(event, allow_any)); + } else { + return Ok(ElementHandlerOutput::return_to_root(event, allow_any)); + } + } + if let Some(fallback) = fallback.take() { + self.finish_state(helper, fallback)?; + } + match artifact { + DeserializerArtifact::None => unreachable!(), + DeserializerArtifact::Data(data) => { + self.store_b(data)?; + *self.state__ = S::Done__; + Ok(ElementHandlerOutput::from_event(event, allow_any)) + } + DeserializerArtifact::Deserializer(deserializer) => { + fallback.get_or_insert(S::B(Some(deserializer))); + *self.state__ = S::Done__; + Ok(ElementHandlerOutput::from_event(event, allow_any)) + } + } + } + } + impl<'de> Deserializer<'de, super::Inner2Type> for Inner2TypeDeserializer { + fn init( + helper: &mut DeserializeHelper, + event: Event<'de>, + ) -> DeserializerResult<'de, super::Inner2Type> { + helper.init_deserializer_from_start_event(event, Self::from_bytes_start) + } + fn next( + mut self, + helper: &mut DeserializeHelper, + event: Event<'de>, + ) -> DeserializerResult<'de, super::Inner2Type> { + use Inner2TypeDeserializerState as S; + let mut event = event; + let mut fallback = None; + let mut allow_any_element = false; + let (event, allow_any) = loop { + let state = replace(&mut *self.state__, S::Unknown__); + event = match (state, event) { + (S::Unknown__, _) => unreachable!(), + (S::B(Some(deserializer)), event) => { + let output = deserializer.next(helper, event)?; + match self.handle_b(helper, output, &mut fallback)? { + ElementHandlerOutput::Continue { event, allow_any } => { + allow_any_element = allow_any_element || allow_any; + event + } + ElementHandlerOutput::Break { event, allow_any } => { + break (event, allow_any) + } + } + } + (_, Event::End(_)) => { + if let Some(fallback) = fallback.take() { + self.finish_state(helper, fallback)?; + } + return Ok(DeserializerOutput { + artifact: DeserializerArtifact::Data(self.finish(helper)?), + event: DeserializerEvent::None, + allow_any: false, + }); + } + (S::Init__, event) => { + fallback.get_or_insert(S::Init__); + *self.state__ = S::B(None); + event + } + (S::B(None), event @ (Event::Start(_) | Event::Empty(_))) => { + let output = helper.init_start_tag_deserializer( + event, + Some(&super::NS_UNNAMED_7), + b"B", + false, + )?; + match self.handle_b(helper, output, &mut fallback)? { + ElementHandlerOutput::Continue { event, allow_any } => { + allow_any_element = allow_any_element || allow_any; + event + } + ElementHandlerOutput::Break { event, allow_any } => { + break (event, allow_any) + } + } + } + (S::Done__, event) => { + *self.state__ = S::Done__; + break (DeserializerEvent::Continue(event), allow_any_element); + } + (state, event) => { + *self.state__ = state; + break (DeserializerEvent::Break(event), false); + } + } + }; + if let Some(fallback) = fallback { + *self.state__ = fallback; + } + Ok(DeserializerOutput { + artifact: DeserializerArtifact::Deserializer(self), + event, + allow_any, + }) + } + fn finish(mut self, helper: &mut DeserializeHelper) -> Result { + let state = replace(&mut *self.state__, Inner2TypeDeserializerState::Unknown__); + self.finish_state(helper, state)?; + Ok(super::Inner2Type { + b: helper.finish_element("B", self.b)?, + }) + } + } +} +pub mod quick_xml_serialize { + use xsd_parser_types::quick_xml::{ + BytesEnd, BytesStart, CollectNamespaces, Error, Event, SerializeHelper, Serializer, + WithSerializer, + }; + #[derive(Debug)] + pub struct FooTypeSerializer<'ser> { + pub(super) value: &'ser super::FooType, + pub(super) state: Box>, + pub(super) name: &'ser str, + pub(super) is_root: bool, + } + #[derive(Debug)] + pub(super) enum FooTypeSerializerState<'ser> { + Init__, + Inner1(::Serializer<'ser>), + Inner2(::Serializer<'ser>), + End__, + Done__, + Phantom__(&'ser ()), + } + impl<'ser> FooTypeSerializer<'ser> { + fn next_event( + &mut self, + helper: &mut SerializeHelper, + ) -> Result>, Error> { + loop { + match &mut *self.state { + FooTypeSerializerState::Init__ => { + *self.state = FooTypeSerializerState::Inner1(WithSerializer::serializer( + &self.value.inner_1, + Some("Inner1"), + false, + )?); + let mut bytes = BytesStart::new(self.name); + helper.begin_ns_scope(); + helper.write_xmlns(&mut bytes, None, &super::NS_UNNAMED_5); + if self.is_root { + CollectNamespaces::collect_namespaces(self.value, helper, &mut bytes); + } + return Ok(Some(Event::Start(bytes))); + } + FooTypeSerializerState::Inner1(x) => match x.next(helper).transpose()? { + Some(event) => return Ok(Some(event)), + None => { + *self.state = + FooTypeSerializerState::Inner2(WithSerializer::serializer( + &self.value.inner_2, + Some("Inner2"), + false, + )?) + } + }, + FooTypeSerializerState::Inner2(x) => match x.next(helper).transpose()? { + Some(event) => return Ok(Some(event)), + None => *self.state = FooTypeSerializerState::End__, + }, + FooTypeSerializerState::End__ => { + *self.state = FooTypeSerializerState::Done__; + helper.end_ns_scope(); + return Ok(Some(Event::End(BytesEnd::new(self.name)))); + } + FooTypeSerializerState::Done__ => return Ok(None), + FooTypeSerializerState::Phantom__(_) => unreachable!(), + } + } + } + } + impl<'ser> Serializer<'ser> for FooTypeSerializer<'ser> { + fn next(&mut self, helper: &mut SerializeHelper) -> Option, Error>> { + match self.next_event(helper) { + Ok(Some(event)) => Some(Ok(event)), + Ok(None) => None, + Err(error) => { + *self.state = FooTypeSerializerState::Done__; + Some(Err(error)) + } + } + } + } + #[derive(Debug)] + pub struct Inner1TypeSerializer<'ser> { + pub(super) value: &'ser super::Inner1Type, + pub(super) state: Box>, + pub(super) name: &'ser str, + pub(super) is_root: bool, + } + #[derive(Debug)] + pub(super) enum Inner1TypeSerializerState<'ser> { + Init__, + A(::Serializer<'ser>), + End__, + Done__, + Phantom__(&'ser ()), + } + impl<'ser> Inner1TypeSerializer<'ser> { + fn next_event( + &mut self, + helper: &mut SerializeHelper, + ) -> Result>, Error> { + loop { + match &mut *self.state { + Inner1TypeSerializerState::Init__ => { + *self.state = Inner1TypeSerializerState::A(WithSerializer::serializer( + &self.value.a, + Some("A"), + false, + )?); + let mut bytes = BytesStart::new(self.name); + helper.begin_ns_scope(); + helper.write_xmlns(&mut bytes, None, &super::NS_UNNAMED_6); + if self.is_root { + CollectNamespaces::collect_namespaces(self.value, helper, &mut bytes); + } + return Ok(Some(Event::Start(bytes))); + } + Inner1TypeSerializerState::A(x) => match x.next(helper).transpose()? { + Some(event) => return Ok(Some(event)), + None => *self.state = Inner1TypeSerializerState::End__, + }, + Inner1TypeSerializerState::End__ => { + *self.state = Inner1TypeSerializerState::Done__; + helper.end_ns_scope(); + return Ok(Some(Event::End(BytesEnd::new(self.name)))); + } + Inner1TypeSerializerState::Done__ => return Ok(None), + Inner1TypeSerializerState::Phantom__(_) => unreachable!(), + } + } + } + } + impl<'ser> Serializer<'ser> for Inner1TypeSerializer<'ser> { + fn next(&mut self, helper: &mut SerializeHelper) -> Option, Error>> { + match self.next_event(helper) { + Ok(Some(event)) => Some(Ok(event)), + Ok(None) => None, + Err(error) => { + *self.state = Inner1TypeSerializerState::Done__; + Some(Err(error)) + } + } + } + } + #[derive(Debug)] + pub struct Inner2TypeSerializer<'ser> { + pub(super) value: &'ser super::Inner2Type, + pub(super) state: Box>, + pub(super) name: &'ser str, + pub(super) is_root: bool, + } + #[derive(Debug)] + pub(super) enum Inner2TypeSerializerState<'ser> { + Init__, + B(::Serializer<'ser>), + End__, + Done__, + Phantom__(&'ser ()), + } + impl<'ser> Inner2TypeSerializer<'ser> { + fn next_event( + &mut self, + helper: &mut SerializeHelper, + ) -> Result>, Error> { + loop { + match &mut *self.state { + Inner2TypeSerializerState::Init__ => { + *self.state = Inner2TypeSerializerState::B(WithSerializer::serializer( + &self.value.b, + Some("B"), + false, + )?); + let mut bytes = BytesStart::new(self.name); + helper.begin_ns_scope(); + helper.write_xmlns(&mut bytes, None, &super::NS_UNNAMED_7); + if self.is_root { + CollectNamespaces::collect_namespaces(self.value, helper, &mut bytes); + } + return Ok(Some(Event::Start(bytes))); + } + Inner2TypeSerializerState::B(x) => match x.next(helper).transpose()? { + Some(event) => return Ok(Some(event)), + None => *self.state = Inner2TypeSerializerState::End__, + }, + Inner2TypeSerializerState::End__ => { + *self.state = Inner2TypeSerializerState::Done__; + helper.end_ns_scope(); + return Ok(Some(Event::End(BytesEnd::new(self.name)))); + } + Inner2TypeSerializerState::Done__ => return Ok(None), + Inner2TypeSerializerState::Phantom__(_) => unreachable!(), + } + } + } + } + impl<'ser> Serializer<'ser> for Inner2TypeSerializer<'ser> { + fn next(&mut self, helper: &mut SerializeHelper) -> Option, Error>> { + match self.next_event(helper) { + Ok(Some(event)) => Some(Ok(event)), + Ok(None) => None, + Err(error) => { + *self.state = Inner2TypeSerializerState::Done__; + Some(Err(error)) + } + } + } + } +} diff --git a/xsd-parser/tests/feature/namespaces_qualified/mod.rs b/xsd-parser/tests/feature/namespaces_qualified/mod.rs index 90664e85..2a9de17b 100644 --- a/xsd-parser/tests/feature/namespaces_qualified/mod.rs +++ b/xsd-parser/tests/feature/namespaces_qualified/mod.rs @@ -228,3 +228,109 @@ mod quick_xml_global_no_alt { include!("expected/quick_xml_global_no_alt.rs"); } + +/* quick_xml_dynamic_alt */ + +#[test] +fn generate_quick_xml_dynamic_alt() { + generate_test( + "tests/feature/namespaces_qualified/foo.xsd", + "tests/feature/namespaces_qualified/expected/quick_xml_dynamic_alt.rs", + config().with_quick_xml_serialize_config(NamespaceSerialization::Dynamic, None), + ); +} + +#[test] +#[cfg(not(feature = "update-expectations"))] +fn read_quick_xml_dynamic_alt() { + use quick_xml_dynamic_alt::Foo; + + let obj = crate::utils::quick_xml_read_test::( + "tests/feature/namespaces_qualified/example/dynamic_alt.xml", + ); + + assert_eq!(obj.inner_1.a, "Bar String"); + assert_eq!(obj.inner_2.b, "Baz String"); +} + +#[test] +#[cfg(not(feature = "update-expectations"))] +fn write_quick_xml_dynamic_alt() { + use quick_xml_dynamic_alt::{Foo, Inner1Type, Inner2Type}; + + let obj = Foo { + inner_1: Inner1Type { + a: "Bar String".into(), + }, + inner_2: Inner2Type { + b: "Baz String".into(), + }, + }; + + crate::utils::quick_xml_write_test( + &obj, + "Foo", + "tests/feature/namespaces_qualified/example/dynamic_alt.xml", + ); +} + +#[cfg(not(feature = "update-expectations"))] +mod quick_xml_dynamic_alt { + #![allow(unused_imports)] + + include!("expected/quick_xml_dynamic_alt.rs"); +} + +/* quick_xml_dynamic_no_alt */ + +#[test] +fn generate_quick_xml_dynamic_no_alt() { + generate_test( + "tests/feature/namespaces_qualified/foo.xsd", + "tests/feature/namespaces_qualified/expected/quick_xml_dynamic_no_alt.rs", + config() + .without_parser_flags(ParserFlags::ALTERNATIVE_PREFIXES) + .with_quick_xml_serialize_config(NamespaceSerialization::Dynamic, None), + ); +} + +#[test] +#[cfg(not(feature = "update-expectations"))] +fn read_quick_xml_dynamic_no_alt() { + use quick_xml_dynamic_no_alt::Foo; + + let obj = crate::utils::quick_xml_read_test::( + "tests/feature/namespaces_qualified/example/dynamic_no_alt.xml", + ); + + assert_eq!(obj.inner_1.a, "Bar String"); + assert_eq!(obj.inner_2.b, "Baz String"); +} + +#[test] +#[cfg(not(feature = "update-expectations"))] +fn write_quick_xml_dynamic_no_alt() { + use quick_xml_dynamic_no_alt::{Foo, Inner1Type, Inner2Type}; + + let obj = Foo { + inner_1: Inner1Type { + a: "Bar String".into(), + }, + inner_2: Inner2Type { + b: "Baz String".into(), + }, + }; + + crate::utils::quick_xml_write_test( + &obj, + "Foo", + "tests/feature/namespaces_qualified/example/dynamic_no_alt.xml", + ); +} + +#[cfg(not(feature = "update-expectations"))] +mod quick_xml_dynamic_no_alt { + #![allow(unused_imports)] + + include!("expected/quick_xml_dynamic_no_alt.rs"); +} diff --git a/xsd-parser/tests/feature/nillable/example/dynamic_no_nil.xml b/xsd-parser/tests/feature/nillable/example/dynamic_no_nil.xml new file mode 100644 index 00000000..5d3b7b76 --- /dev/null +++ b/xsd-parser/tests/feature/nillable/example/dynamic_no_nil.xml @@ -0,0 +1,5 @@ + + 42 + 100 + 200 + diff --git a/xsd-parser/tests/feature/nillable/example/dynamic_with_nil.xml b/xsd-parser/tests/feature/nillable/example/dynamic_with_nil.xml new file mode 100644 index 00000000..b3b4c7f0 --- /dev/null +++ b/xsd-parser/tests/feature/nillable/example/dynamic_with_nil.xml @@ -0,0 +1,4 @@ + + 42 + + diff --git a/xsd-parser/tests/feature/nillable/expected/dynamic_quick_xml.rs b/xsd-parser/tests/feature/nillable/expected/dynamic_quick_xml.rs new file mode 100644 index 00000000..4da74a09 --- /dev/null +++ b/xsd-parser/tests/feature/nillable/expected/dynamic_quick_xml.rs @@ -0,0 +1,924 @@ +use xsd_parser_types::{ + misc::{Namespace, NamespacePrefix}, + quick_xml::{ + BytesStart, CollectNamespaces, Error, SerializeHelper, WithDeserializer, WithSerializer, + }, + xml::Nillable, +}; +pub const NS_XS: Namespace = Namespace::new_const(b"http://www.w3.org/2001/XMLSchema"); +pub const NS_XML: Namespace = Namespace::new_const(b"http://www.w3.org/XML/1998/namespace"); +pub const NS_XSI: Namespace = Namespace::new_const(b"http://www.w3.org/2001/XMLSchema-instance"); +pub const NS_TNS: Namespace = Namespace::new_const(b"http://example.com"); +pub const PREFIX_XS: NamespacePrefix = NamespacePrefix::new_const(b"xs"); +pub const PREFIX_XML: NamespacePrefix = NamespacePrefix::new_const(b"xml"); +pub const PREFIX_XSI: NamespacePrefix = NamespacePrefix::new_const(b"xsi"); +pub const PREFIX_TNS: NamespacePrefix = NamespacePrefix::new_const(b"tns"); +pub type Foo = FooType; +#[derive(Debug)] +pub struct FooType { + pub a: i32, + pub b: Nillable, + pub c: Option, + pub d: Option>, +} +impl CollectNamespaces for FooType { + fn collect_namespaces(&self, helper: &mut SerializeHelper, bytes: &mut BytesStart<'_>) { + self.a.collect_namespaces(helper, bytes); + self.b.collect_namespaces(helper, bytes); + if let Some(inner) = &self.c { + inner.collect_namespaces(helper, bytes); + } + if let Some(inner) = &self.d { + inner.collect_namespaces(helper, bytes); + } + } +} +impl WithSerializer for FooType { + type Serializer<'x> = quick_xml_serialize::FooTypeSerializer<'x>; + fn serializer<'ser>( + &'ser self, + name: Option<&'ser str>, + is_root: bool, + ) -> Result, Error> { + Ok(quick_xml_serialize::FooTypeSerializer { + value: self, + state: Box::new(quick_xml_serialize::FooTypeSerializerState::Init__), + name: name.unwrap_or("FooType"), + is_root, + }) + } +} +impl WithDeserializer for FooType { + type Deserializer = quick_xml_deserialize::FooTypeDeserializer; +} +pub type NillableFoo = Nillable; +pub type Bar = BarType; +#[derive(Debug)] +pub struct BarType { + pub x: i32, + pub y: i32, +} +impl CollectNamespaces for BarType { + fn collect_namespaces(&self, helper: &mut SerializeHelper, bytes: &mut BytesStart<'_>) { + self.x.collect_namespaces(helper, bytes); + self.y.collect_namespaces(helper, bytes); + } +} +impl WithSerializer for BarType { + type Serializer<'x> = quick_xml_serialize::BarTypeSerializer<'x>; + fn serializer<'ser>( + &'ser self, + name: Option<&'ser str>, + is_root: bool, + ) -> Result, Error> { + Ok(quick_xml_serialize::BarTypeSerializer { + value: self, + state: Box::new(quick_xml_serialize::BarTypeSerializerState::Init__), + name: name.unwrap_or("BarType"), + is_root, + }) + } +} +impl WithDeserializer for BarType { + type Deserializer = quick_xml_deserialize::BarTypeDeserializer; +} +pub mod quick_xml_deserialize { + use core::mem::replace; + use xsd_parser_types::{ + quick_xml::{ + BytesStart, DeserializeHelper, Deserializer, DeserializerArtifact, DeserializerEvent, + DeserializerOutput, DeserializerResult, ElementHandlerOutput, Error, ErrorKind, Event, + RawByteStr, WithDeserializer, + }, + xml::Nillable, + }; + #[derive(Debug)] + pub struct FooTypeDeserializer { + a: Option, + b: Option>, + c: Option, + d: Option>, + state__: Box, + } + #[derive(Debug)] + enum FooTypeDeserializerState { + Init__, + A(Option<::Deserializer>), + B(Option< as WithDeserializer>::Deserializer>), + C(Option<::Deserializer>), + D(Option< as WithDeserializer>::Deserializer>), + Done__, + Unknown__, + } + impl FooTypeDeserializer { + fn from_bytes_start( + helper: &mut DeserializeHelper, + bytes_start: &BytesStart<'_>, + ) -> Result { + for attrib in helper.filter_xmlns_attributes(bytes_start) { + let attrib = attrib?; + helper.raise_unexpected_attrib_checked(&attrib)?; + } + Ok(Self { + a: None, + b: None, + c: None, + d: None, + state__: Box::new(FooTypeDeserializerState::Init__), + }) + } + fn finish_state( + &mut self, + helper: &mut DeserializeHelper, + state: FooTypeDeserializerState, + ) -> Result<(), Error> { + use FooTypeDeserializerState as S; + match state { + S::A(Some(deserializer)) => self.store_a(deserializer.finish(helper)?)?, + S::B(Some(deserializer)) => self.store_b(deserializer.finish(helper)?)?, + S::C(Some(deserializer)) => self.store_c(deserializer.finish(helper)?)?, + S::D(Some(deserializer)) => self.store_d(deserializer.finish(helper)?)?, + _ => (), + } + Ok(()) + } + fn store_a(&mut self, value: i32) -> Result<(), Error> { + if self.a.is_some() { + Err(ErrorKind::DuplicateElement(RawByteStr::from_slice(b"A")))?; + } + self.a = Some(value); + Ok(()) + } + fn store_b(&mut self, value: Nillable) -> Result<(), Error> { + if self.b.is_some() { + Err(ErrorKind::DuplicateElement(RawByteStr::from_slice(b"B")))?; + } + self.b = Some(value); + Ok(()) + } + fn store_c(&mut self, value: i32) -> Result<(), Error> { + if self.c.is_some() { + Err(ErrorKind::DuplicateElement(RawByteStr::from_slice(b"C")))?; + } + self.c = Some(value); + Ok(()) + } + fn store_d(&mut self, value: Nillable) -> Result<(), Error> { + if self.d.is_some() { + Err(ErrorKind::DuplicateElement(RawByteStr::from_slice(b"D")))?; + } + self.d = Some(value); + Ok(()) + } + fn handle_a<'de>( + &mut self, + helper: &mut DeserializeHelper, + output: DeserializerOutput<'de, i32>, + fallback: &mut Option, + ) -> Result, Error> { + use FooTypeDeserializerState as S; + let DeserializerOutput { + artifact, + event, + allow_any, + } = output; + if artifact.is_none() { + fallback.get_or_insert(S::A(None)); + if matches!(&fallback, Some(S::Init__)) { + return Ok(ElementHandlerOutput::break_(event, allow_any)); + } else { + return Ok(ElementHandlerOutput::return_to_root(event, allow_any)); + } + } + if let Some(fallback) = fallback.take() { + self.finish_state(helper, fallback)?; + } + match artifact { + DeserializerArtifact::None => unreachable!(), + DeserializerArtifact::Data(data) => { + self.store_a(data)?; + *self.state__ = S::B(None); + Ok(ElementHandlerOutput::from_event(event, allow_any)) + } + DeserializerArtifact::Deserializer(deserializer) => { + fallback.get_or_insert(S::A(Some(deserializer))); + *self.state__ = S::B(None); + Ok(ElementHandlerOutput::from_event(event, allow_any)) + } + } + } + fn handle_b<'de>( + &mut self, + helper: &mut DeserializeHelper, + output: DeserializerOutput<'de, Nillable>, + fallback: &mut Option, + ) -> Result, Error> { + use FooTypeDeserializerState as S; + let DeserializerOutput { + artifact, + event, + allow_any, + } = output; + if artifact.is_none() { + fallback.get_or_insert(S::B(None)); + if matches!(&fallback, Some(S::Init__)) { + return Ok(ElementHandlerOutput::break_(event, allow_any)); + } else { + return Ok(ElementHandlerOutput::return_to_root(event, allow_any)); + } + } + if let Some(fallback) = fallback.take() { + self.finish_state(helper, fallback)?; + } + match artifact { + DeserializerArtifact::None => unreachable!(), + DeserializerArtifact::Data(data) => { + self.store_b(data)?; + *self.state__ = S::C(None); + Ok(ElementHandlerOutput::from_event(event, allow_any)) + } + DeserializerArtifact::Deserializer(deserializer) => { + fallback.get_or_insert(S::B(Some(deserializer))); + *self.state__ = S::C(None); + Ok(ElementHandlerOutput::from_event(event, allow_any)) + } + } + } + fn handle_c<'de>( + &mut self, + helper: &mut DeserializeHelper, + output: DeserializerOutput<'de, i32>, + fallback: &mut Option, + ) -> Result, Error> { + use FooTypeDeserializerState as S; + let DeserializerOutput { + artifact, + event, + allow_any, + } = output; + if artifact.is_none() { + *self.state__ = S::D(None); + return Ok(ElementHandlerOutput::from_event(event, allow_any)); + } + if let Some(fallback) = fallback.take() { + self.finish_state(helper, fallback)?; + } + match artifact { + DeserializerArtifact::None => unreachable!(), + DeserializerArtifact::Data(data) => { + self.store_c(data)?; + *self.state__ = S::D(None); + Ok(ElementHandlerOutput::from_event(event, allow_any)) + } + DeserializerArtifact::Deserializer(deserializer) => { + fallback.get_or_insert(S::C(Some(deserializer))); + *self.state__ = S::D(None); + Ok(ElementHandlerOutput::from_event(event, allow_any)) + } + } + } + fn handle_d<'de>( + &mut self, + helper: &mut DeserializeHelper, + output: DeserializerOutput<'de, Nillable>, + fallback: &mut Option, + ) -> Result, Error> { + use FooTypeDeserializerState as S; + let DeserializerOutput { + artifact, + event, + allow_any, + } = output; + if artifact.is_none() { + *self.state__ = S::Done__; + return Ok(ElementHandlerOutput::from_event(event, allow_any)); + } + if let Some(fallback) = fallback.take() { + self.finish_state(helper, fallback)?; + } + match artifact { + DeserializerArtifact::None => unreachable!(), + DeserializerArtifact::Data(data) => { + self.store_d(data)?; + *self.state__ = S::Done__; + Ok(ElementHandlerOutput::from_event(event, allow_any)) + } + DeserializerArtifact::Deserializer(deserializer) => { + fallback.get_or_insert(S::D(Some(deserializer))); + *self.state__ = S::Done__; + Ok(ElementHandlerOutput::from_event(event, allow_any)) + } + } + } + } + impl<'de> Deserializer<'de, super::FooType> for FooTypeDeserializer { + fn init( + helper: &mut DeserializeHelper, + event: Event<'de>, + ) -> DeserializerResult<'de, super::FooType> { + helper.init_deserializer_from_start_event(event, Self::from_bytes_start) + } + fn next( + mut self, + helper: &mut DeserializeHelper, + event: Event<'de>, + ) -> DeserializerResult<'de, super::FooType> { + use FooTypeDeserializerState as S; + let mut event = event; + let mut fallback = None; + let mut allow_any_element = false; + let (event, allow_any) = loop { + let state = replace(&mut *self.state__, S::Unknown__); + event = match (state, event) { + (S::Unknown__, _) => unreachable!(), + (S::A(Some(deserializer)), event) => { + let output = deserializer.next(helper, event)?; + match self.handle_a(helper, output, &mut fallback)? { + ElementHandlerOutput::Continue { event, allow_any } => { + allow_any_element = allow_any_element || allow_any; + event + } + ElementHandlerOutput::Break { event, allow_any } => { + break (event, allow_any) + } + } + } + (S::B(Some(deserializer)), event) => { + let output = deserializer.next(helper, event)?; + match self.handle_b(helper, output, &mut fallback)? { + ElementHandlerOutput::Continue { event, allow_any } => { + allow_any_element = allow_any_element || allow_any; + event + } + ElementHandlerOutput::Break { event, allow_any } => { + break (event, allow_any) + } + } + } + (S::C(Some(deserializer)), event) => { + let output = deserializer.next(helper, event)?; + match self.handle_c(helper, output, &mut fallback)? { + ElementHandlerOutput::Continue { event, allow_any } => { + allow_any_element = allow_any_element || allow_any; + event + } + ElementHandlerOutput::Break { event, allow_any } => { + break (event, allow_any) + } + } + } + (S::D(Some(deserializer)), event) => { + let output = deserializer.next(helper, event)?; + match self.handle_d(helper, output, &mut fallback)? { + ElementHandlerOutput::Continue { event, allow_any } => { + allow_any_element = allow_any_element || allow_any; + event + } + ElementHandlerOutput::Break { event, allow_any } => { + break (event, allow_any) + } + } + } + (_, Event::End(_)) => { + if let Some(fallback) = fallback.take() { + self.finish_state(helper, fallback)?; + } + return Ok(DeserializerOutput { + artifact: DeserializerArtifact::Data(self.finish(helper)?), + event: DeserializerEvent::None, + allow_any: false, + }); + } + (S::Init__, event) => { + fallback.get_or_insert(S::Init__); + *self.state__ = S::A(None); + event + } + (S::A(None), event @ (Event::Start(_) | Event::Empty(_))) => { + let output = helper.init_start_tag_deserializer( + event, + Some(&super::NS_TNS), + b"A", + false, + )?; + match self.handle_a(helper, output, &mut fallback)? { + ElementHandlerOutput::Continue { event, allow_any } => { + allow_any_element = allow_any_element || allow_any; + event + } + ElementHandlerOutput::Break { event, allow_any } => { + break (event, allow_any) + } + } + } + (S::B(None), event @ (Event::Start(_) | Event::Empty(_))) => { + let output = helper.init_start_tag_deserializer( + event, + Some(&super::NS_TNS), + b"B", + false, + )?; + match self.handle_b(helper, output, &mut fallback)? { + ElementHandlerOutput::Continue { event, allow_any } => { + allow_any_element = allow_any_element || allow_any; + event + } + ElementHandlerOutput::Break { event, allow_any } => { + break (event, allow_any) + } + } + } + (S::C(None), event @ (Event::Start(_) | Event::Empty(_))) => { + let output = helper.init_start_tag_deserializer( + event, + Some(&super::NS_TNS), + b"C", + false, + )?; + match self.handle_c(helper, output, &mut fallback)? { + ElementHandlerOutput::Continue { event, allow_any } => { + allow_any_element = allow_any_element || allow_any; + event + } + ElementHandlerOutput::Break { event, allow_any } => { + break (event, allow_any) + } + } + } + (S::D(None), event @ (Event::Start(_) | Event::Empty(_))) => { + let output = helper.init_start_tag_deserializer( + event, + Some(&super::NS_TNS), + b"D", + false, + )?; + match self.handle_d(helper, output, &mut fallback)? { + ElementHandlerOutput::Continue { event, allow_any } => { + allow_any_element = allow_any_element || allow_any; + event + } + ElementHandlerOutput::Break { event, allow_any } => { + break (event, allow_any) + } + } + } + (S::Done__, event) => { + *self.state__ = S::Done__; + break (DeserializerEvent::Continue(event), allow_any_element); + } + (state, event) => { + *self.state__ = state; + break (DeserializerEvent::Break(event), false); + } + } + }; + if let Some(fallback) = fallback { + *self.state__ = fallback; + } + Ok(DeserializerOutput { + artifact: DeserializerArtifact::Deserializer(self), + event, + allow_any, + }) + } + fn finish(mut self, helper: &mut DeserializeHelper) -> Result { + let state = replace(&mut *self.state__, FooTypeDeserializerState::Unknown__); + self.finish_state(helper, state)?; + Ok(super::FooType { + a: helper.finish_element("A", self.a)?, + b: helper.finish_element("B", self.b)?, + c: self.c, + d: self.d, + }) + } + } + #[derive(Debug)] + pub struct BarTypeDeserializer { + x: Option, + y: Option, + state__: Box, + } + #[derive(Debug)] + enum BarTypeDeserializerState { + Init__, + X(Option<::Deserializer>), + Y(Option<::Deserializer>), + Done__, + Unknown__, + } + impl BarTypeDeserializer { + fn from_bytes_start( + helper: &mut DeserializeHelper, + bytes_start: &BytesStart<'_>, + ) -> Result { + for attrib in helper.filter_xmlns_attributes(bytes_start) { + let attrib = attrib?; + helper.raise_unexpected_attrib_checked(&attrib)?; + } + Ok(Self { + x: None, + y: None, + state__: Box::new(BarTypeDeserializerState::Init__), + }) + } + fn finish_state( + &mut self, + helper: &mut DeserializeHelper, + state: BarTypeDeserializerState, + ) -> Result<(), Error> { + use BarTypeDeserializerState as S; + match state { + S::X(Some(deserializer)) => self.store_x(deserializer.finish(helper)?)?, + S::Y(Some(deserializer)) => self.store_y(deserializer.finish(helper)?)?, + _ => (), + } + Ok(()) + } + fn store_x(&mut self, value: i32) -> Result<(), Error> { + if self.x.is_some() { + Err(ErrorKind::DuplicateElement(RawByteStr::from_slice(b"X")))?; + } + self.x = Some(value); + Ok(()) + } + fn store_y(&mut self, value: i32) -> Result<(), Error> { + if self.y.is_some() { + Err(ErrorKind::DuplicateElement(RawByteStr::from_slice(b"Y")))?; + } + self.y = Some(value); + Ok(()) + } + fn handle_x<'de>( + &mut self, + helper: &mut DeserializeHelper, + output: DeserializerOutput<'de, i32>, + fallback: &mut Option, + ) -> Result, Error> { + use BarTypeDeserializerState as S; + let DeserializerOutput { + artifact, + event, + allow_any, + } = output; + if artifact.is_none() { + fallback.get_or_insert(S::X(None)); + if matches!(&fallback, Some(S::Init__)) { + return Ok(ElementHandlerOutput::break_(event, allow_any)); + } else { + return Ok(ElementHandlerOutput::return_to_root(event, allow_any)); + } + } + if let Some(fallback) = fallback.take() { + self.finish_state(helper, fallback)?; + } + match artifact { + DeserializerArtifact::None => unreachable!(), + DeserializerArtifact::Data(data) => { + self.store_x(data)?; + *self.state__ = S::Y(None); + Ok(ElementHandlerOutput::from_event(event, allow_any)) + } + DeserializerArtifact::Deserializer(deserializer) => { + fallback.get_or_insert(S::X(Some(deserializer))); + *self.state__ = S::Y(None); + Ok(ElementHandlerOutput::from_event(event, allow_any)) + } + } + } + fn handle_y<'de>( + &mut self, + helper: &mut DeserializeHelper, + output: DeserializerOutput<'de, i32>, + fallback: &mut Option, + ) -> Result, Error> { + use BarTypeDeserializerState as S; + let DeserializerOutput { + artifact, + event, + allow_any, + } = output; + if artifact.is_none() { + fallback.get_or_insert(S::Y(None)); + if matches!(&fallback, Some(S::Init__)) { + return Ok(ElementHandlerOutput::break_(event, allow_any)); + } else { + return Ok(ElementHandlerOutput::return_to_root(event, allow_any)); + } + } + if let Some(fallback) = fallback.take() { + self.finish_state(helper, fallback)?; + } + match artifact { + DeserializerArtifact::None => unreachable!(), + DeserializerArtifact::Data(data) => { + self.store_y(data)?; + *self.state__ = S::Done__; + Ok(ElementHandlerOutput::from_event(event, allow_any)) + } + DeserializerArtifact::Deserializer(deserializer) => { + fallback.get_or_insert(S::Y(Some(deserializer))); + *self.state__ = S::Done__; + Ok(ElementHandlerOutput::from_event(event, allow_any)) + } + } + } + } + impl<'de> Deserializer<'de, super::BarType> for BarTypeDeserializer { + fn init( + helper: &mut DeserializeHelper, + event: Event<'de>, + ) -> DeserializerResult<'de, super::BarType> { + helper.init_deserializer_from_start_event(event, Self::from_bytes_start) + } + fn next( + mut self, + helper: &mut DeserializeHelper, + event: Event<'de>, + ) -> DeserializerResult<'de, super::BarType> { + use BarTypeDeserializerState as S; + let mut event = event; + let mut fallback = None; + let mut allow_any_element = false; + let (event, allow_any) = loop { + let state = replace(&mut *self.state__, S::Unknown__); + event = match (state, event) { + (S::Unknown__, _) => unreachable!(), + (S::X(Some(deserializer)), event) => { + let output = deserializer.next(helper, event)?; + match self.handle_x(helper, output, &mut fallback)? { + ElementHandlerOutput::Continue { event, allow_any } => { + allow_any_element = allow_any_element || allow_any; + event + } + ElementHandlerOutput::Break { event, allow_any } => { + break (event, allow_any) + } + } + } + (S::Y(Some(deserializer)), event) => { + let output = deserializer.next(helper, event)?; + match self.handle_y(helper, output, &mut fallback)? { + ElementHandlerOutput::Continue { event, allow_any } => { + allow_any_element = allow_any_element || allow_any; + event + } + ElementHandlerOutput::Break { event, allow_any } => { + break (event, allow_any) + } + } + } + (_, Event::End(_)) => { + if let Some(fallback) = fallback.take() { + self.finish_state(helper, fallback)?; + } + return Ok(DeserializerOutput { + artifact: DeserializerArtifact::Data(self.finish(helper)?), + event: DeserializerEvent::None, + allow_any: false, + }); + } + (S::Init__, event) => { + fallback.get_or_insert(S::Init__); + *self.state__ = S::X(None); + event + } + (S::X(None), event @ (Event::Start(_) | Event::Empty(_))) => { + let output = helper.init_start_tag_deserializer( + event, + Some(&super::NS_TNS), + b"X", + false, + )?; + match self.handle_x(helper, output, &mut fallback)? { + ElementHandlerOutput::Continue { event, allow_any } => { + allow_any_element = allow_any_element || allow_any; + event + } + ElementHandlerOutput::Break { event, allow_any } => { + break (event, allow_any) + } + } + } + (S::Y(None), event @ (Event::Start(_) | Event::Empty(_))) => { + let output = helper.init_start_tag_deserializer( + event, + Some(&super::NS_TNS), + b"Y", + false, + )?; + match self.handle_y(helper, output, &mut fallback)? { + ElementHandlerOutput::Continue { event, allow_any } => { + allow_any_element = allow_any_element || allow_any; + event + } + ElementHandlerOutput::Break { event, allow_any } => { + break (event, allow_any) + } + } + } + (S::Done__, event) => { + *self.state__ = S::Done__; + break (DeserializerEvent::Continue(event), allow_any_element); + } + (state, event) => { + *self.state__ = state; + break (DeserializerEvent::Break(event), false); + } + } + }; + if let Some(fallback) = fallback { + *self.state__ = fallback; + } + Ok(DeserializerOutput { + artifact: DeserializerArtifact::Deserializer(self), + event, + allow_any, + }) + } + fn finish(mut self, helper: &mut DeserializeHelper) -> Result { + let state = replace(&mut *self.state__, BarTypeDeserializerState::Unknown__); + self.finish_state(helper, state)?; + Ok(super::BarType { + x: helper.finish_element("X", self.x)?, + y: helper.finish_element("Y", self.y)?, + }) + } + } +} +pub mod quick_xml_serialize { + use xsd_parser_types::{ + quick_xml::{ + BytesEnd, BytesStart, CollectNamespaces, Error, Event, IterSerializer, SerializeHelper, + Serializer, WithSerializer, + }, + xml::Nillable, + }; + #[derive(Debug)] + pub struct FooTypeSerializer<'ser> { + pub(super) value: &'ser super::FooType, + pub(super) state: Box>, + pub(super) name: &'ser str, + pub(super) is_root: bool, + } + #[derive(Debug)] + pub(super) enum FooTypeSerializerState<'ser> { + Init__, + A(::Serializer<'ser>), + B( as WithSerializer>::Serializer<'ser>), + C(IterSerializer<'ser, Option<&'ser i32>, i32>), + D(IterSerializer<'ser, Option<&'ser Nillable>, Nillable>), + End__, + Done__, + Phantom__(&'ser ()), + } + impl<'ser> FooTypeSerializer<'ser> { + fn next_event( + &mut self, + helper: &mut SerializeHelper, + ) -> Result>, Error> { + loop { + match &mut *self.state { + FooTypeSerializerState::Init__ => { + *self.state = FooTypeSerializerState::A(WithSerializer::serializer( + &self.value.a, + Some("A"), + false, + )?); + let mut bytes = BytesStart::new(self.name); + helper.begin_ns_scope(); + if self.is_root { + CollectNamespaces::collect_namespaces(self.value, helper, &mut bytes); + } + return Ok(Some(Event::Start(bytes))); + } + FooTypeSerializerState::A(x) => match x.next(helper).transpose()? { + Some(event) => return Ok(Some(event)), + None => { + *self.state = FooTypeSerializerState::B(WithSerializer::serializer( + &self.value.b, + Some("B"), + false, + )?) + } + }, + FooTypeSerializerState::B(x) => match x.next(helper).transpose()? { + Some(event) => return Ok(Some(event)), + None => { + *self.state = FooTypeSerializerState::C(IterSerializer::new( + self.value.c.as_ref(), + Some("C"), + false, + )) + } + }, + FooTypeSerializerState::C(x) => match x.next(helper).transpose()? { + Some(event) => return Ok(Some(event)), + None => { + *self.state = FooTypeSerializerState::D(IterSerializer::new( + self.value.d.as_ref(), + Some("D"), + false, + )) + } + }, + FooTypeSerializerState::D(x) => match x.next(helper).transpose()? { + Some(event) => return Ok(Some(event)), + None => *self.state = FooTypeSerializerState::End__, + }, + FooTypeSerializerState::End__ => { + *self.state = FooTypeSerializerState::Done__; + helper.end_ns_scope(); + return Ok(Some(Event::End(BytesEnd::new(self.name)))); + } + FooTypeSerializerState::Done__ => return Ok(None), + FooTypeSerializerState::Phantom__(_) => unreachable!(), + } + } + } + } + impl<'ser> Serializer<'ser> for FooTypeSerializer<'ser> { + fn next(&mut self, helper: &mut SerializeHelper) -> Option, Error>> { + match self.next_event(helper) { + Ok(Some(event)) => Some(Ok(event)), + Ok(None) => None, + Err(error) => { + *self.state = FooTypeSerializerState::Done__; + Some(Err(error)) + } + } + } + } + #[derive(Debug)] + pub struct BarTypeSerializer<'ser> { + pub(super) value: &'ser super::BarType, + pub(super) state: Box>, + pub(super) name: &'ser str, + pub(super) is_root: bool, + } + #[derive(Debug)] + pub(super) enum BarTypeSerializerState<'ser> { + Init__, + X(::Serializer<'ser>), + Y(::Serializer<'ser>), + End__, + Done__, + Phantom__(&'ser ()), + } + impl<'ser> BarTypeSerializer<'ser> { + fn next_event( + &mut self, + helper: &mut SerializeHelper, + ) -> Result>, Error> { + loop { + match &mut *self.state { + BarTypeSerializerState::Init__ => { + *self.state = BarTypeSerializerState::X(WithSerializer::serializer( + &self.value.x, + Some("X"), + false, + )?); + let mut bytes = BytesStart::new(self.name); + helper.begin_ns_scope(); + if self.is_root { + CollectNamespaces::collect_namespaces(self.value, helper, &mut bytes); + } + return Ok(Some(Event::Start(bytes))); + } + BarTypeSerializerState::X(x) => match x.next(helper).transpose()? { + Some(event) => return Ok(Some(event)), + None => { + *self.state = BarTypeSerializerState::Y(WithSerializer::serializer( + &self.value.y, + Some("Y"), + false, + )?) + } + }, + BarTypeSerializerState::Y(x) => match x.next(helper).transpose()? { + Some(event) => return Ok(Some(event)), + None => *self.state = BarTypeSerializerState::End__, + }, + BarTypeSerializerState::End__ => { + *self.state = BarTypeSerializerState::Done__; + helper.end_ns_scope(); + return Ok(Some(Event::End(BytesEnd::new(self.name)))); + } + BarTypeSerializerState::Done__ => return Ok(None), + BarTypeSerializerState::Phantom__(_) => unreachable!(), + } + } + } + } + impl<'ser> Serializer<'ser> for BarTypeSerializer<'ser> { + fn next(&mut self, helper: &mut SerializeHelper) -> Option, Error>> { + match self.next_event(helper) { + Ok(Some(event)) => Some(Ok(event)), + Ok(None) => None, + Err(error) => { + *self.state = BarTypeSerializerState::Done__; + Some(Err(error)) + } + } + } + } +} diff --git a/xsd-parser/tests/feature/nillable/mod.rs b/xsd-parser/tests/feature/nillable/mod.rs index c9fcb9f3..123a4bae 100644 --- a/xsd-parser/tests/feature/nillable/mod.rs +++ b/xsd-parser/tests/feature/nillable/mod.rs @@ -1,4 +1,6 @@ -use xsd_parser::{config::OptimizerFlags, Config, IdentType}; +use xsd_parser::{ + config::OptimizerFlags, pipeline::renderer::NamespaceSerialization, Config, IdentType, +}; use crate::utils::{generate_test, ConfigEx}; @@ -93,3 +95,65 @@ mod quick_xml { include!("expected/quick_xml.rs"); } + +/* dynamic_quick_xml */ + +#[test] +fn generate_dynamic_quick_xml() { + generate_test( + "tests/feature/nillable/schema.xsd", + "tests/feature/nillable/expected/dynamic_quick_xml.rs", + config().with_quick_xml_config(NamespaceSerialization::Dynamic, None, false), + ); +} + +#[test] +#[cfg(not(feature = "update-expectations"))] +fn write_dynamic_quick_xml_with_nil() { + use crate::utils::quick_xml_write_test; + use dynamic_quick_xml::Foo; + use xsd_parser_types::xml::Nillable; + + // Verify that serializing Foo with nil 'b' DOES emit xmlns:xsi at root + let obj = Foo { + a: 42, + b: Nillable::nil(), + c: None, + d: None, + }; + + quick_xml_write_test( + &obj, + "Foo", + "tests/feature/nillable/example/dynamic_with_nil.xml", + ); +} + +#[test] +#[cfg(not(feature = "update-expectations"))] +fn write_dynamic_quick_xml_no_nil() { + use crate::utils::quick_xml_write_test; + use dynamic_quick_xml::Foo; + use xsd_parser_types::xml::Nillable; + + // Verify that serializing Foo WITHOUT any nil values does NOT emit xmlns:xsi at root + let obj = Foo { + a: 42, + b: Nillable::new(100), + c: Some(200), + d: None, + }; + + quick_xml_write_test( + &obj, + "Foo", + "tests/feature/nillable/example/dynamic_no_nil.xml", + ); +} + +#[cfg(not(feature = "update-expectations"))] +mod dynamic_quick_xml { + #![allow(unused_imports)] + + include!("expected/dynamic_quick_xml.rs"); +}