Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/target
.vscode
.idea
.devcontainer
.idea
6 changes: 3 additions & 3 deletions xsd-parser-types/src/quick_xml/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down
98 changes: 98 additions & 0 deletions xsd-parser-types/src/quick_xml/serialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T: CollectNamespaces> CollectNamespaces for Option<T> {
fn collect_namespaces(&self, helper: &mut SerializeHelper, bytes: &mut BytesStart<'_>) {
if let Some(inner) = self {
inner.collect_namespaces(helper, bytes);
}
}
}

impl<T: CollectNamespaces> CollectNamespaces for Vec<T> {
fn collect_namespaces(&self, helper: &mut SerializeHelper, bytes: &mut BytesStart<'_>) {
for item in self {
item.collect_namespaces(helper, bytes);
}
}
}

impl<T: CollectNamespaces + ?Sized> CollectNamespaces for Box<T> {
fn collect_namespaces(&self, helper: &mut SerializeHelper, bytes: &mut BytesStart<'_>) {
self.as_ref().collect_namespaces(helper, bytes);
}
}

impl<T: CollectNamespaces, const N: usize> CollectNamespaces for [T; N] {
fn collect_namespaces(&self, helper: &mut SerializeHelper, bytes: &mut BytesStart<'_>) {
for item in self {
item.collect_namespaces(helper, bytes);
}
}
}

impl<T: CollectNamespaces> 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`].
Expand Down
22 changes: 19 additions & 3 deletions xsd-parser-types/src/xml/nillable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -108,6 +108,22 @@ where
}
}

#[cfg(feature = "quick-xml")]
impl<T> CollectNamespaces for Nillable<T>
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<T> WithSerializer for Nillable<T>
where
Expand Down
4 changes: 4 additions & 0 deletions xsd-parser/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
27 changes: 22 additions & 5 deletions xsd-parser/src/config/renderer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -241,6 +249,7 @@ impl RenderStepConfig for RenderStep {
Self::WithNamespaceTrait => RenderStepType::ExtraImpls,
Self::QuickXmlSerialize { .. } => RenderStepType::ExtraImpls,
Self::QuickXmlDeserialize { .. } => RenderStepType::ExtraImpls,
Self::QuickXmlCollectNamespaces => RenderStepType::ExtraImpls,
}
}

Expand All @@ -251,7 +260,8 @@ impl RenderStepConfig for RenderStep {
fn into_render_step(self: Box<Self>) -> Box<dyn RenderStepTrait> {
use crate::pipeline::renderer::{
DefaultsRenderStep, EnumConstantsRenderStep, NamespaceConstantsRenderStep,
PrefixConstantsRenderStep, QuickXmlDeserializeRenderStep, QuickXmlSerializeRenderStep,
PrefixConstantsRenderStep, QuickXmlCollectNamespacesRenderStep,
QuickXmlDeserializeRenderStep, QuickXmlSerializeRenderStep,
SerdeQuickXmlTypesRenderStep, SerdeXmlRsV7TypesRenderStep, SerdeXmlRsV8TypesRenderStep,
TypesRenderStep, WithNamespaceTraitRenderStep,
};
Expand Down Expand Up @@ -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
Expand All @@ -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::<TypesRenderStep>(),
(
Self::TypesSerdeXmlRs {
Expand Down Expand Up @@ -338,6 +351,9 @@ impl RenderStepConfig for RenderStep {
(Self::QuickXmlDeserialize { .. }, None) => {
other_id == TypeId::of::<QuickXmlDeserializeRenderStep>()
}
(Self::QuickXmlCollectNamespaces, None) => {
other_id == TypeId::of::<QuickXmlCollectNamespacesRenderStep>()
}
_ => false,
}
}
Expand All @@ -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,
}
}
Expand Down
8 changes: 4 additions & 4 deletions xsd-parser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down
6 changes: 5 additions & 1 deletion xsd-parser/src/models/data/path_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}

Expand Down
17 changes: 5 additions & 12 deletions xsd-parser/src/pipeline/generator/data/complex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;

Expand Down Expand Up @@ -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 {
Expand All @@ -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,
Expand Down
13 changes: 7 additions & 6 deletions xsd-parser/src/pipeline/generator/data/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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)
Expand Down
3 changes: 1 addition & 2 deletions xsd-parser/src/pipeline/generator/data/reference.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()?;

Expand Down
Loading
Loading