Skip to content
Draft
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
5 changes: 5 additions & 0 deletions xsd-parser/src/models/data/complex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,11 @@ pub struct ComplexDataContent<'types> {

/// Additional attributes that will be added to the element.
pub extra_attributes: Vec<TokenStream>,

/// Default value for the content field, used when the element is present
/// but has no text content. Derived from the `default` attribute of the
/// enclosing `xs:element` in the XSD schema.
pub default_value: Option<ValueRendererBox>,
}

/// Contains the details of an XML element.
Expand Down
12 changes: 12 additions & 0 deletions xsd-parser/src/models/meta/complex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ pub struct ComplexMeta {

/// List of attributes defined for this complex type.
pub attributes: AttributesMeta,

/// Default value for the simple content of this complex type.
///
/// This is set when an `xs:element` with a `default` attribute has an
/// inline complex type with `xs:simpleContent`. It defines the value to
/// use when the element is present but has no text content.
pub default: Option<String>,
}

/* GroupMeta */
Expand Down Expand Up @@ -194,6 +201,7 @@ impl Default for ComplexMeta {
is_dynamic: false,
is_mixed: false,
attributes: AttributesMeta::default(),
default: None,
}
}
}
Expand All @@ -208,6 +216,7 @@ impl TypeEq for ComplexMeta {
is_dynamic,
is_mixed: mixed_content,
attributes,
default,
} = self;

base.type_hash(hasher, types);
Expand All @@ -217,6 +226,7 @@ impl TypeEq for ComplexMeta {
is_dynamic.hash(hasher);
mixed_content.hash(hasher);
attributes.type_hash(hasher, types);
default.hash(hasher);
}

fn type_eq(&self, other: &Self, types: &MetaTypes) -> bool {
Expand All @@ -228,6 +238,7 @@ impl TypeEq for ComplexMeta {
is_dynamic,
is_mixed: mixed_content,
attributes,
default,
} = self;

base.type_eq(&other.base, types)
Expand All @@ -237,5 +248,6 @@ impl TypeEq for ComplexMeta {
&& is_dynamic.eq(&other.is_dynamic)
&& mixed_content.eq(&other.is_mixed)
&& attributes.type_eq(&other.attributes, types)
&& default.eq(&other.default)
}
}
25 changes: 22 additions & 3 deletions xsd-parser/src/pipeline/generator/data/complex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ impl<'types> ComplexData<'types> {
MaxOccurs::Bounded(1),
&[],
&meta.elements,
None,
)
}

Expand All @@ -76,6 +77,7 @@ impl<'types> ComplexData<'types> {
MaxOccurs::Bounded(1),
&[],
&meta.elements,
None,
)
}

Expand All @@ -93,6 +95,7 @@ impl<'types> ComplexData<'types> {
MaxOccurs::Bounded(1),
&[],
&meta.elements,
None,
)
}

Expand Down Expand Up @@ -137,6 +140,7 @@ impl<'types> ComplexData<'types> {
meta.max_occurs,
&meta.attributes,
elements,
meta.default.as_deref(),
)
}

Expand All @@ -150,11 +154,18 @@ impl<'types> ComplexData<'types> {
max_occurs: MaxOccurs,
attributes: &'types [AttributeMeta],
elements: &'types [ElementMeta],
default: Option<&str>,
) -> Result<Self, Error> {
match type_mode {
TypeMode::Simple { simple_type } => {
Self::new_simple(ctx, form, simple_type, min_occurs, max_occurs, attributes)
}
TypeMode::Simple { simple_type } => Self::new_simple(
ctx,
form,
simple_type,
min_occurs,
max_occurs,
attributes,
default,
),
TypeMode::Choice => Self::new_enum(
ctx, form, mixed_mode, min_occurs, max_occurs, attributes, elements,
),
Expand All @@ -171,6 +182,7 @@ impl<'types> ComplexData<'types> {
min_occurs: MinOccurs,
max_occurs: MaxOccurs,
attributes: &'types [AttributeMeta],
default: Option<&str>,
) -> Result<Self, Error> {
let base = ComplexBase::new(ctx, form, ComplexFlags::empty())?;
let occurs = Occurs::from_occurs(min_occurs, max_occurs);
Expand All @@ -187,6 +199,10 @@ impl<'types> ComplexData<'types> {
})
.collect::<Result<Vec<_>, _>>()?;

let default_value = default
.map(|default| ctx.make_value_renderer(simple_type, default, ValueGeneratorMode::Value))
.transpose()?;

let content = ComplexDataContent {
occurs,
simple_type: Some(simple_type),
Expand All @@ -195,6 +211,7 @@ impl<'types> ComplexData<'types> {
field_ident,
target_type,
extra_attributes: Vec::new(),
default_value,
};

let type_ = ComplexDataStruct {
Expand Down Expand Up @@ -321,6 +338,7 @@ impl<'types> ComplexData<'types> {
field_ident,
target_type,
extra_attributes: Vec::new(),
default_value: None,
};

StructMode::Content { content }
Expand Down Expand Up @@ -492,6 +510,7 @@ impl<'types> ComplexData<'types> {
field_ident,
target_type,
extra_attributes: Vec::new(),
default_value: None,
};

StructMode::Content { content }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,17 @@ impl<'a, 'state, 'schema> VariantProcessor<'a, 'state, 'schema> {

if has_content {
ret?;

// If the element has a default value, propagate it to the
// inline complex type so the deserializer can use it when
// the element is present but has no text content.
if let Some(default) = &ty.default {
let type_meta = self.owner.get_type_mut(&type_);
if let MetaTypeVariant::ComplexType(ref mut meta) = type_meta.variant {
meta.default = Some(default.clone());
}
}

init_any!(self, Reference, ReferenceMeta::new(type_), true);
} else {
// No actual type content found, default to `xs:anyType`
Expand Down
33 changes: 29 additions & 4 deletions xsd-parser/src/pipeline/renderer/steps/defaults.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
use proc_macro2::TokenStream;
use quote::{format_ident, quote};

use crate::models::data::{ComplexData, ComplexDataAttribute, ComplexDataStruct, DataTypeVariant};
use crate::models::data::{
ComplexData, ComplexDataAttribute, ComplexDataContent, ComplexDataStruct, DataTypeVariant,
StructMode,
};

use super::super::{Context, RenderStep, RenderStepType};

Expand Down Expand Up @@ -52,20 +55,28 @@ impl ComplexData<'_> {
impl ComplexDataStruct<'_> {
pub(crate) fn render_defaults(&self, ctx: &mut Context<'_, '_>) {
let type_ident = &self.type_ident;
let mut has_attributes = false;

let attribute_defaults = self
.attributes
.iter()
.filter_map(|attrib| attrib.render_default_fn(ctx))
.inspect(|_| has_attributes = true);
.collect::<Vec<_>>();

let content_default = match &self.mode {
StructMode::Content { content } => content.render_default_content_fn(ctx),
_ => None,
};

let has_defaults = !attribute_defaults.is_empty() || content_default.is_some();

let impl_ = quote! {
impl #type_ident {
#( #attribute_defaults )*
#content_default
}
};

if has_attributes {
if has_defaults {
ctx.current_module().append(impl_);
}
}
Expand All @@ -85,3 +96,17 @@ impl ComplexDataAttribute<'_> {
})
}
}

impl ComplexDataContent<'_> {
fn render_default_content_fn(&self, ctx: &Context<'_, '_>) -> Option<TokenStream> {
let default = self.default_value.as_ref()?.render(ctx);
let target_ident = ctx.resolve_type_for_module(&self.target_type);

Some(quote! {
#[must_use]
pub fn default_content() -> #target_ident {
#default
}
})
}
}
Loading
Loading