From c18220c1f0a05f088b11720a918f20660ac0c59e Mon Sep 17 00:00:00 2001 From: "kidkool850@gmail.com" Date: Tue, 26 May 2026 18:54:50 -0500 Subject: [PATCH 01/11] add flattened content helpers rendering step, add flags --- xsd-parser/src/config/renderer.rs | 43 ++++- xsd-parser/src/pipeline/renderer/mod.rs | 2 +- .../src/pipeline/renderer/steps/flattened.rs | 179 ++++++++++++++++++ xsd-parser/src/pipeline/renderer/steps/mod.rs | 2 + 4 files changed, 222 insertions(+), 4 deletions(-) create mode 100644 xsd-parser/src/pipeline/renderer/steps/flattened.rs diff --git a/xsd-parser/src/config/renderer.rs b/xsd-parser/src/config/renderer.rs index 3ea4a3c2..b6926244 100644 --- a/xsd-parser/src/config/renderer.rs +++ b/xsd-parser/src/config/renderer.rs @@ -125,6 +125,26 @@ bitflags! { /// /// See [`RENDER_DOCS`](Self::RENDER_DOCS) for details. const RENDER_VARIANT_DOCS = 1 << 3; + + /// The renderer generates ergonomic helper accessor methods for + /// flattened struct content fields (i.e. `content: Vec`). + /// + /// For each variant of the inner enum `FooContent`, a method is + /// generated on the outer struct that returns `Option<&T>` by + /// finding the first matching variant in the `content` vector. + /// + /// Example: given + /// ```ignore + /// pub struct Foo { pub content: Vec, ... } + /// pub enum FooContent { PrivateNote(String), ... } + /// ``` + /// this flag enables generating: + /// ```ignore + /// impl Foo { + /// pub fn private_note(&self) -> Option<&String> { ... } + /// } + /// ``` + const FLATTENED_CONTENT_HELPERS = 1 << 4; } } @@ -195,6 +215,14 @@ pub enum RenderStep { /// that the serializer can discover which XML namespaces are actually needed /// at runtime before writing the root start element. QuickXmlCollectNamespaces, + + /// Renderer that generates ergonomic helper accessors for flattened + /// struct content fields. + /// + /// This step is enabled via the [`RendererFlags::FLATTENED_CONTENT_HELPERS`] + /// renderer flag. It generates one convenience method per variant on the + /// outer struct that returns `Option<&T>` for the first matching variant. + FlattenedContentHelpers, } /// Helper trait to deal with custom render steps. @@ -250,6 +278,7 @@ impl RenderStepConfig for RenderStep { Self::QuickXmlSerialize { .. } => RenderStepType::ExtraImpls, Self::QuickXmlDeserialize { .. } => RenderStepType::ExtraImpls, Self::QuickXmlCollectNamespaces => RenderStepType::ExtraImpls, + Self::FlattenedContentHelpers => RenderStepType::ExtraImpls, } } @@ -259,7 +288,8 @@ impl RenderStepConfig for RenderStep { fn into_render_step(self: Box) -> Box { use crate::pipeline::renderer::{ - DefaultsRenderStep, EnumConstantsRenderStep, NamespaceConstantsRenderStep, + DefaultsRenderStep, EnumConstantsRenderStep, FlattenedContentHelpersRenderStep, + NamespaceConstantsRenderStep, PrefixConstantsRenderStep, QuickXmlCollectNamespacesRenderStep, QuickXmlDeserializeRenderStep, QuickXmlSerializeRenderStep, SerdeQuickXmlTypesRenderStep, SerdeXmlRsV7TypesRenderStep, SerdeXmlRsV8TypesRenderStep, @@ -291,12 +321,14 @@ impl RenderStepConfig for RenderStep { Box::new(QuickXmlDeserializeRenderStep { boxed_deserializer }) } Self::QuickXmlCollectNamespaces => Box::new(QuickXmlCollectNamespacesRenderStep), + Self::FlattenedContentHelpers => Box::new(FlattenedContentHelpersRenderStep), } } fn is_mutual_exclusive_to(&self, other: &dyn RenderStepConfig) -> bool { use crate::pipeline::renderer::{ - DefaultsRenderStep, EnumConstantsRenderStep, NamespaceConstantsRenderStep, + DefaultsRenderStep, EnumConstantsRenderStep, FlattenedContentHelpersRenderStep, + NamespaceConstantsRenderStep, PrefixConstantsRenderStep, QuickXmlCollectNamespacesRenderStep, QuickXmlDeserializeRenderStep, QuickXmlSerializeRenderStep, SerdeQuickXmlTypesRenderStep, SerdeXmlRsV7TypesRenderStep, SerdeXmlRsV8TypesRenderStep, @@ -325,6 +357,7 @@ impl RenderStepConfig for RenderStep { (Self::QuickXmlSerialize { .. }, Some(Self::QuickXmlSerialize { .. })) => true, (Self::QuickXmlDeserialize { .. }, Some(Self::QuickXmlDeserialize { .. })) => true, (Self::QuickXmlCollectNamespaces, Some(Self::QuickXmlCollectNamespaces)) => true, + (Self::FlattenedContentHelpers, Some(Self::FlattenedContentHelpers)) => true, (Self::Types, None) => other_id == TypeId::of::(), ( Self::TypesSerdeXmlRs { @@ -359,6 +392,9 @@ impl RenderStepConfig for RenderStep { (Self::QuickXmlCollectNamespaces, None) => { other_id == TypeId::of::() } + (Self::FlattenedContentHelpers, None) => { + other_id == TypeId::of::() + } _ => false, } } @@ -380,7 +416,8 @@ impl RenderStep { | (Self::WithNamespaceTrait, Self::WithNamespaceTrait) | (Self::QuickXmlSerialize { .. }, Self::QuickXmlSerialize { .. }) | (Self::QuickXmlDeserialize { .. }, Self::QuickXmlDeserialize { .. }) - | (Self::QuickXmlCollectNamespaces, Self::QuickXmlCollectNamespaces) => true, + | (Self::QuickXmlCollectNamespaces, Self::QuickXmlCollectNamespaces) + | (Self::FlattenedContentHelpers, Self::FlattenedContentHelpers) => true, (_, _) => false, } } diff --git a/xsd-parser/src/pipeline/renderer/mod.rs b/xsd-parser/src/pipeline/renderer/mod.rs index 6cc74449..ae458a67 100644 --- a/xsd-parser/src/pipeline/renderer/mod.rs +++ b/xsd-parser/src/pipeline/renderer/mod.rs @@ -43,7 +43,7 @@ pub use self::custom::{ValueRenderer, ValueRendererBox}; pub use self::error::Error; pub use self::meta::MetaData; pub use self::steps::{ - DefaultsRenderStep, EnumConstantsRenderStep, NamespaceConstantsRenderStep, + DefaultsRenderStep, EnumConstantsRenderStep, NamespaceConstantsRenderStep, FlattenedContentHelpersRenderStep, NamespaceSerialization, PrefixConstantsRenderStep, QuickXmlCollectNamespacesRenderStep, QuickXmlDeserializeRenderStep, QuickXmlSerializeRenderStep, SerdeQuickXmlTypesRenderStep, SerdeXmlRsV7TypesRenderStep, SerdeXmlRsV8TypesRenderStep, TypesRenderStep, diff --git a/xsd-parser/src/pipeline/renderer/steps/flattened.rs b/xsd-parser/src/pipeline/renderer/steps/flattened.rs new file mode 100644 index 00000000..9ad909a9 --- /dev/null +++ b/xsd-parser/src/pipeline/renderer/steps/flattened.rs @@ -0,0 +1,179 @@ +use inflector::Inflector; +use proc_macro2::TokenStream; +use quote::{format_ident, quote}; + +use crate::config::RendererFlags; +use crate::models::data::{ + ComplexData, ComplexDataEnum, DataType, DataTypeVariant, Occurs, UnionData, +}; +use crate::pipeline::renderer::{Context, RenderStep, RenderStepType}; + +/// RenderStep that generates ergonomic helper accessors for flattened struct content. +/// +/// This targets the pattern: +/// pub struct Foo { pub content: Vec, ... } +/// pub enum FooContent { PrivateNote(String), ... } +/// +/// and generates: +/// impl Foo { +/// pub fn private_note(&self) -> Option<&String> { ... } +/// } +/// Enabled via [`RendererFlags::FLATTENED_CONTENT_HELPERS`]. +#[derive(Debug, Clone, Copy)] +pub struct FlattenedContentHelpersRenderStep; + +impl RenderStep for FlattenedContentHelpersRenderStep { + fn render_step_type(&self) -> RenderStepType { + RenderStepType::ExtraImpls + } + + fn render_type(&mut self, ctx: &mut Context<'_, '_>) { + if !ctx.meta.check_renderer_flags(RendererFlags::FLATTENED_CONTENT_HELPERS) { + return; + } + + let DataTypeVariant::Complex(complex) = &ctx.data.variant else { + return; + }; + let ComplexData::Struct { type_, .. } = complex else { + return; + }; + let Some(content) = type_.content() else { + return; + }; + if content.occurs != Occurs::DynamicList { + return; + } + + let content_rust_ident = content.target_type.path.ident(); + + let Some(content_dt) = + find_data_type_by_rust_ident(ctx.meta.types.items.values(), content_rust_ident) + else { + return; + }; + + let impl_block = match &content_dt.variant { + DataTypeVariant::Union(union_data) => { + render_helpers_for_union(ctx, &type_.base.type_ident, &content.field_ident, union_data) + } + DataTypeVariant::Complex(ComplexData::Enum { type_: enum_type, .. }) => { + render_helpers_for_complex_enum(ctx, &type_.base.type_ident, &content.field_ident, enum_type) + } + _ => return, + }; + + ctx.current_module().append(impl_block); + } +} + +fn render_helpers_for_union( + ctx: &Context<'_, '_>, + struct_ident: &proc_macro2::Ident, + content_field_ident: &proc_macro2::Ident, + union: &UnionData<'_>, +) -> TokenStream { + let enum_ident = &union.type_ident; + + // Generate one helper per variant: + // pub fn private_note(&self) -> Option<&T> { + // self.content.iter().find_map(|x| match x { Enum::PrivateNote(v) => Some(v), _ => None }) + // } + let methods = union.variants.iter().map(|v| { + let variant_ident = &v.variant_ident; + + // method name: snake_case of the variant ident. + // We keep it simple/minimal here: lower-case the ident string. + // If you want proper casing (PrivateNote -> private_note), we can use `inflector` + // like other renderer pieces do, but I’m keeping dependencies minimal in this step. + let method_name_str = variant_ident.to_string().to_snake_case(); + let method_ident = format_ident!("{}", method_name_str); + + // Return type is Option<&TargetType>. + // We render the same target type the enum variant already uses. + let target_ty = &v.target_type; + let target_ty = ctx.resolve_type_for_module(target_ty); + + let option = ctx.resolve_build_in("::core::option::Option"); + + quote! { + #[inline] + pub fn #method_ident(&self) -> #option<&#target_ty> { + self.#content_field_ident.iter().find_map(|x| { + match x { + #enum_ident::#variant_ident(v) => #option::Some(v), + _ => #option::None, + } + }) + } + } + }); + + quote! { + impl #struct_ident { + #( #methods )* + } + } +} + +/// Find a [`DataType`] in the types collection by its rendered Rust type identifier. +fn find_data_type_by_rust_ident<'a, I>( + items: I, + rust_ident: &proc_macro2::Ident, +) -> Option<&'a DataType<'a>> +where + I: IntoIterator>, +{ + items.into_iter().find(|dt| { + let candidate = match &dt.variant { + DataTypeVariant::Complex(c) => match c { + ComplexData::Enum { type_, .. } => &type_.base.type_ident, + ComplexData::Struct { type_, .. } => &type_.base.type_ident, + }, + DataTypeVariant::Union(u) => &u.type_ident, + DataTypeVariant::Enumeration(e) => &e.type_ident, + _ => return false, + }; + candidate == rust_ident + }) +} + +/// Generate helper accessor methods for a [`ComplexDataEnum`] content type. +fn render_helpers_for_complex_enum( + ctx: &Context<'_, '_>, + struct_ident: &proc_macro2::Ident, + content_field_ident: &proc_macro2::Ident, + enum_type: &ComplexDataEnum<'_>, +) -> TokenStream { + let enum_ident = &enum_type.base.type_ident; + + let methods = enum_type.elements.iter().map(|e| { + let variant_ident = &e.variant_ident; + let method_ident = snake_case_ident(variant_ident); + let target_ty = ctx.resolve_type_for_module(&e.target_type); + let option = ctx.resolve_build_in("::core::option::Option"); + + quote! { + #[inline] + pub fn #method_ident(&self) -> #option<&#target_ty> { + self.#content_field_ident.iter().find_map(|x| { + match x { + #enum_ident::#variant_ident(v) => #option::Some(v), + _ => #option::None, + } + }) + } + } + }); + + quote! { + impl #struct_ident { + #( #methods )* + } + } +} + +/// Convert an identifier to a snake_case method name identifier. +fn snake_case_ident(ident: &proc_macro2::Ident) -> proc_macro2::Ident { + format_ident!("{}", ident.to_string().to_snake_case()) +} diff --git a/xsd-parser/src/pipeline/renderer/steps/mod.rs b/xsd-parser/src/pipeline/renderer/steps/mod.rs index cca635c8..c428b4ac 100644 --- a/xsd-parser/src/pipeline/renderer/steps/mod.rs +++ b/xsd-parser/src/pipeline/renderer/steps/mod.rs @@ -1,5 +1,6 @@ mod defaults; mod enum_const; +mod flattened; mod namespace_const; mod prefix_const; mod quick_xml; @@ -24,6 +25,7 @@ use super::Context; pub use self::defaults::DefaultsRenderStep; pub use self::enum_const::EnumConstantsRenderStep; +pub use self::flattened::FlattenedContentHelpersRenderStep; pub use self::namespace_const::NamespaceConstantsRenderStep; pub use self::prefix_const::PrefixConstantsRenderStep; pub use self::quick_xml::{ From 67daf48da7752e6ecaeafadb19706f09c44460eb Mon Sep 17 00:00:00 2001 From: "kidkool850@gmail.com" Date: Tue, 26 May 2026 19:05:48 -0500 Subject: [PATCH 02/11] remove rendererflag, only publicly add as a renderstep --- xsd-parser/src/config/renderer.rs | 43 +----- xsd-parser/src/pipeline/renderer/mod.rs | 10 +- .../src/pipeline/renderer/steps/flattened.rs | 124 +++++------------- 3 files changed, 39 insertions(+), 138 deletions(-) diff --git a/xsd-parser/src/config/renderer.rs b/xsd-parser/src/config/renderer.rs index b6926244..3ea4a3c2 100644 --- a/xsd-parser/src/config/renderer.rs +++ b/xsd-parser/src/config/renderer.rs @@ -125,26 +125,6 @@ bitflags! { /// /// See [`RENDER_DOCS`](Self::RENDER_DOCS) for details. const RENDER_VARIANT_DOCS = 1 << 3; - - /// The renderer generates ergonomic helper accessor methods for - /// flattened struct content fields (i.e. `content: Vec`). - /// - /// For each variant of the inner enum `FooContent`, a method is - /// generated on the outer struct that returns `Option<&T>` by - /// finding the first matching variant in the `content` vector. - /// - /// Example: given - /// ```ignore - /// pub struct Foo { pub content: Vec, ... } - /// pub enum FooContent { PrivateNote(String), ... } - /// ``` - /// this flag enables generating: - /// ```ignore - /// impl Foo { - /// pub fn private_note(&self) -> Option<&String> { ... } - /// } - /// ``` - const FLATTENED_CONTENT_HELPERS = 1 << 4; } } @@ -215,14 +195,6 @@ pub enum RenderStep { /// that the serializer can discover which XML namespaces are actually needed /// at runtime before writing the root start element. QuickXmlCollectNamespaces, - - /// Renderer that generates ergonomic helper accessors for flattened - /// struct content fields. - /// - /// This step is enabled via the [`RendererFlags::FLATTENED_CONTENT_HELPERS`] - /// renderer flag. It generates one convenience method per variant on the - /// outer struct that returns `Option<&T>` for the first matching variant. - FlattenedContentHelpers, } /// Helper trait to deal with custom render steps. @@ -278,7 +250,6 @@ impl RenderStepConfig for RenderStep { Self::QuickXmlSerialize { .. } => RenderStepType::ExtraImpls, Self::QuickXmlDeserialize { .. } => RenderStepType::ExtraImpls, Self::QuickXmlCollectNamespaces => RenderStepType::ExtraImpls, - Self::FlattenedContentHelpers => RenderStepType::ExtraImpls, } } @@ -288,8 +259,7 @@ impl RenderStepConfig for RenderStep { fn into_render_step(self: Box) -> Box { use crate::pipeline::renderer::{ - DefaultsRenderStep, EnumConstantsRenderStep, FlattenedContentHelpersRenderStep, - NamespaceConstantsRenderStep, + DefaultsRenderStep, EnumConstantsRenderStep, NamespaceConstantsRenderStep, PrefixConstantsRenderStep, QuickXmlCollectNamespacesRenderStep, QuickXmlDeserializeRenderStep, QuickXmlSerializeRenderStep, SerdeQuickXmlTypesRenderStep, SerdeXmlRsV7TypesRenderStep, SerdeXmlRsV8TypesRenderStep, @@ -321,14 +291,12 @@ impl RenderStepConfig for RenderStep { Box::new(QuickXmlDeserializeRenderStep { boxed_deserializer }) } Self::QuickXmlCollectNamespaces => Box::new(QuickXmlCollectNamespacesRenderStep), - Self::FlattenedContentHelpers => Box::new(FlattenedContentHelpersRenderStep), } } fn is_mutual_exclusive_to(&self, other: &dyn RenderStepConfig) -> bool { use crate::pipeline::renderer::{ - DefaultsRenderStep, EnumConstantsRenderStep, FlattenedContentHelpersRenderStep, - NamespaceConstantsRenderStep, + DefaultsRenderStep, EnumConstantsRenderStep, NamespaceConstantsRenderStep, PrefixConstantsRenderStep, QuickXmlCollectNamespacesRenderStep, QuickXmlDeserializeRenderStep, QuickXmlSerializeRenderStep, SerdeQuickXmlTypesRenderStep, SerdeXmlRsV7TypesRenderStep, SerdeXmlRsV8TypesRenderStep, @@ -357,7 +325,6 @@ impl RenderStepConfig for RenderStep { (Self::QuickXmlSerialize { .. }, Some(Self::QuickXmlSerialize { .. })) => true, (Self::QuickXmlDeserialize { .. }, Some(Self::QuickXmlDeserialize { .. })) => true, (Self::QuickXmlCollectNamespaces, Some(Self::QuickXmlCollectNamespaces)) => true, - (Self::FlattenedContentHelpers, Some(Self::FlattenedContentHelpers)) => true, (Self::Types, None) => other_id == TypeId::of::(), ( Self::TypesSerdeXmlRs { @@ -392,9 +359,6 @@ impl RenderStepConfig for RenderStep { (Self::QuickXmlCollectNamespaces, None) => { other_id == TypeId::of::() } - (Self::FlattenedContentHelpers, None) => { - other_id == TypeId::of::() - } _ => false, } } @@ -416,8 +380,7 @@ impl RenderStep { | (Self::WithNamespaceTrait, Self::WithNamespaceTrait) | (Self::QuickXmlSerialize { .. }, Self::QuickXmlSerialize { .. }) | (Self::QuickXmlDeserialize { .. }, Self::QuickXmlDeserialize { .. }) - | (Self::QuickXmlCollectNamespaces, Self::QuickXmlCollectNamespaces) - | (Self::FlattenedContentHelpers, Self::FlattenedContentHelpers) => true, + | (Self::QuickXmlCollectNamespaces, Self::QuickXmlCollectNamespaces) => true, (_, _) => false, } } diff --git a/xsd-parser/src/pipeline/renderer/mod.rs b/xsd-parser/src/pipeline/renderer/mod.rs index ae458a67..c2ce07c3 100644 --- a/xsd-parser/src/pipeline/renderer/mod.rs +++ b/xsd-parser/src/pipeline/renderer/mod.rs @@ -43,11 +43,11 @@ pub use self::custom::{ValueRenderer, ValueRendererBox}; pub use self::error::Error; pub use self::meta::MetaData; pub use self::steps::{ - DefaultsRenderStep, EnumConstantsRenderStep, NamespaceConstantsRenderStep, FlattenedContentHelpersRenderStep, - NamespaceSerialization, PrefixConstantsRenderStep, QuickXmlCollectNamespacesRenderStep, - QuickXmlDeserializeRenderStep, QuickXmlSerializeRenderStep, SerdeQuickXmlTypesRenderStep, - SerdeXmlRsV7TypesRenderStep, SerdeXmlRsV8TypesRenderStep, TypesRenderStep, - WithNamespaceTraitRenderStep, + DefaultsRenderStep, EnumConstantsRenderStep, FlattenedContentHelpersRenderStep, + NamespaceConstantsRenderStep, 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/flattened.rs b/xsd-parser/src/pipeline/renderer/steps/flattened.rs index 9ad909a9..95e854ef 100644 --- a/xsd-parser/src/pipeline/renderer/steps/flattened.rs +++ b/xsd-parser/src/pipeline/renderer/steps/flattened.rs @@ -2,10 +2,7 @@ use inflector::Inflector; use proc_macro2::TokenStream; use quote::{format_ident, quote}; -use crate::config::RendererFlags; -use crate::models::data::{ - ComplexData, ComplexDataEnum, DataType, DataTypeVariant, Occurs, UnionData, -}; +use crate::models::data::{ComplexData, ComplexDataEnum, DataTypeVariant, Occurs}; use crate::pipeline::renderer::{Context, RenderStep, RenderStepType}; /// RenderStep that generates ergonomic helper accessors for flattened struct content. @@ -28,14 +25,14 @@ impl RenderStep for FlattenedContentHelpersRenderStep { } fn render_type(&mut self, ctx: &mut Context<'_, '_>) { - if !ctx.meta.check_renderer_flags(RendererFlags::FLATTENED_CONTENT_HELPERS) { - return; - } - let DataTypeVariant::Complex(complex) = &ctx.data.variant else { return; }; - let ComplexData::Struct { type_, .. } = complex else { + let ComplexData::Struct { + type_, + content_type, + } = complex + else { return; }; let Some(content) = type_.content() else { @@ -43,101 +40,32 @@ impl RenderStep for FlattenedContentHelpersRenderStep { }; if content.occurs != Occurs::DynamicList { return; - } - - let content_rust_ident = content.target_type.path.ident(); + }; - let Some(content_dt) = - find_data_type_by_rust_ident(ctx.meta.types.items.values(), content_rust_ident) - else { + // The content enum is stored inline as content_type of ComplexData::Struct + let Some(content_type) = content_type else { return; }; - let impl_block = match &content_dt.variant { - DataTypeVariant::Union(union_data) => { - render_helpers_for_union(ctx, &type_.base.type_ident, &content.field_ident, union_data) - } - DataTypeVariant::Complex(ComplexData::Enum { type_: enum_type, .. }) => { - render_helpers_for_complex_enum(ctx, &type_.base.type_ident, &content.field_ident, enum_type) + let impl_block = match content_type.as_ref() { + ComplexData::Enum { + type_: enum_type, .. + } => render_helpers_for_complex_enum( + ctx, + &type_.base.type_ident, + &content.field_ident, + enum_type, + ), + ComplexData::Struct { .. } => { + // Struct content (e.g. a sequence) — no enum variants to flatten + return; } - _ => return, }; ctx.current_module().append(impl_block); } } -fn render_helpers_for_union( - ctx: &Context<'_, '_>, - struct_ident: &proc_macro2::Ident, - content_field_ident: &proc_macro2::Ident, - union: &UnionData<'_>, -) -> TokenStream { - let enum_ident = &union.type_ident; - - // Generate one helper per variant: - // pub fn private_note(&self) -> Option<&T> { - // self.content.iter().find_map(|x| match x { Enum::PrivateNote(v) => Some(v), _ => None }) - // } - let methods = union.variants.iter().map(|v| { - let variant_ident = &v.variant_ident; - - // method name: snake_case of the variant ident. - // We keep it simple/minimal here: lower-case the ident string. - // If you want proper casing (PrivateNote -> private_note), we can use `inflector` - // like other renderer pieces do, but I’m keeping dependencies minimal in this step. - let method_name_str = variant_ident.to_string().to_snake_case(); - let method_ident = format_ident!("{}", method_name_str); - - // Return type is Option<&TargetType>. - // We render the same target type the enum variant already uses. - let target_ty = &v.target_type; - let target_ty = ctx.resolve_type_for_module(target_ty); - - let option = ctx.resolve_build_in("::core::option::Option"); - - quote! { - #[inline] - pub fn #method_ident(&self) -> #option<&#target_ty> { - self.#content_field_ident.iter().find_map(|x| { - match x { - #enum_ident::#variant_ident(v) => #option::Some(v), - _ => #option::None, - } - }) - } - } - }); - - quote! { - impl #struct_ident { - #( #methods )* - } - } -} - -/// Find a [`DataType`] in the types collection by its rendered Rust type identifier. -fn find_data_type_by_rust_ident<'a, I>( - items: I, - rust_ident: &proc_macro2::Ident, -) -> Option<&'a DataType<'a>> -where - I: IntoIterator>, -{ - items.into_iter().find(|dt| { - let candidate = match &dt.variant { - DataTypeVariant::Complex(c) => match c { - ComplexData::Enum { type_, .. } => &type_.base.type_ident, - ComplexData::Struct { type_, .. } => &type_.base.type_ident, - }, - DataTypeVariant::Union(u) => &u.type_ident, - DataTypeVariant::Enumeration(e) => &e.type_ident, - _ => return false, - }; - candidate == rust_ident - }) -} - /// Generate helper accessor methods for a [`ComplexDataEnum`] content type. fn render_helpers_for_complex_enum( ctx: &Context<'_, '_>, @@ -150,6 +78,7 @@ fn render_helpers_for_complex_enum( let methods = enum_type.elements.iter().map(|e| { let variant_ident = &e.variant_ident; let method_ident = snake_case_ident(variant_ident); + let mut_method_ident = format_ident!("{}_mut", method_ident); let target_ty = ctx.resolve_type_for_module(&e.target_type); let option = ctx.resolve_build_in("::core::option::Option"); @@ -163,6 +92,15 @@ fn render_helpers_for_complex_enum( } }) } + #[inline] + pub fn #mut_method_ident(&mut self) -> #option<&mut #target_ty> { + self.#content_field_ident.iter_mut().find_map(|x| { + match x { + #enum_ident::#variant_ident(v) => #option::Some(v), + _ => #option::None, + } + }) + } } }); From f1e7ba674a3d2ebf5076f4c02ef400e9544e6033 Mon Sep 17 00:00:00 2001 From: "kidkool850@gmail.com" Date: Tue, 26 May 2026 19:05:52 -0500 Subject: [PATCH 03/11] add test --- .../example/default.xml | 6 +++ .../expected/default.rs | 40 +++++++++++++++++++ .../feature/flattened_content_helpers/mod.rs | 18 +++++++++ .../flattened_content_helpers/schema.xsd | 15 +++++++ xsd-parser/tests/feature/mod.rs | 1 + 5 files changed, 80 insertions(+) create mode 100644 xsd-parser/tests/feature/flattened_content_helpers/example/default.xml create mode 100644 xsd-parser/tests/feature/flattened_content_helpers/expected/default.rs create mode 100644 xsd-parser/tests/feature/flattened_content_helpers/mod.rs create mode 100644 xsd-parser/tests/feature/flattened_content_helpers/schema.xsd diff --git a/xsd-parser/tests/feature/flattened_content_helpers/example/default.xml b/xsd-parser/tests/feature/flattened_content_helpers/example/default.xml new file mode 100644 index 00000000..1ffd5e0f --- /dev/null +++ b/xsd-parser/tests/feature/flattened_content_helpers/example/default.xml @@ -0,0 +1,6 @@ + + + hello + 42 + world + \ No newline at end of file diff --git a/xsd-parser/tests/feature/flattened_content_helpers/expected/default.rs b/xsd-parser/tests/feature/flattened_content_helpers/expected/default.rs new file mode 100644 index 00000000..ada04e50 --- /dev/null +++ b/xsd-parser/tests/feature/flattened_content_helpers/expected/default.rs @@ -0,0 +1,40 @@ +pub type Foo = FooType; +#[derive(Debug)] +pub struct FooType { + pub content: Vec, +} +#[derive(Debug)] +pub enum FooTypeContent { + Bar(String), + Baz(i32), +} +impl FooType { + #[inline] + pub fn bar(&self) -> Option<&String> { + self.content.iter().find_map(|x| match x { + FooTypeContent::Bar(v) => Option::Some(v), + _ => Option::None, + }) + } + #[inline] + pub fn bar_mut(&mut self) -> Option<&mut String> { + self.content.iter_mut().find_map(|x| match x { + FooTypeContent::Bar(v) => Option::Some(v), + _ => Option::None, + }) + } + #[inline] + pub fn baz(&self) -> Option<&i32> { + self.content.iter().find_map(|x| match x { + FooTypeContent::Baz(v) => Option::Some(v), + _ => Option::None, + }) + } + #[inline] + pub fn baz_mut(&mut self) -> Option<&mut i32> { + self.content.iter_mut().find_map(|x| match x { + FooTypeContent::Baz(v) => Option::Some(v), + _ => Option::None, + }) + } +} diff --git a/xsd-parser/tests/feature/flattened_content_helpers/mod.rs b/xsd-parser/tests/feature/flattened_content_helpers/mod.rs new file mode 100644 index 00000000..f2d657cd --- /dev/null +++ b/xsd-parser/tests/feature/flattened_content_helpers/mod.rs @@ -0,0 +1,18 @@ +use xsd_parser::{pipeline::renderer::FlattenedContentHelpersRenderStep, Config, IdentType}; + +use crate::utils::{generate_test, ConfigEx}; + +fn config() -> Config { + Config::test_default() + .with_render_step(FlattenedContentHelpersRenderStep) + .with_generate([(IdentType::Element, "tns:Foo")]) +} + +#[test] +fn generate_default() { + generate_test( + "tests/feature/flattened_content_helpers/schema.xsd", + "tests/feature/flattened_content_helpers/expected/default.rs", + config(), + ); +} diff --git a/xsd-parser/tests/feature/flattened_content_helpers/schema.xsd b/xsd-parser/tests/feature/flattened_content_helpers/schema.xsd new file mode 100644 index 00000000..8f3d40ac --- /dev/null +++ b/xsd-parser/tests/feature/flattened_content_helpers/schema.xsd @@ -0,0 +1,15 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/xsd-parser/tests/feature/mod.rs b/xsd-parser/tests/feature/mod.rs index a62fe4f8..44589db0 100644 --- a/xsd-parser/tests/feature/mod.rs +++ b/xsd-parser/tests/feature/mod.rs @@ -19,6 +19,7 @@ mod complex_type_with_repeated_content; mod content_display_name; mod custom_type; mod defaultable_content; +mod flattened_content_helpers; mod derive; mod documentation; mod duplicate_idents; From 44106f3ae77db7ef9c734faf0bfe3bab174be88f Mon Sep 17 00:00:00 2001 From: Exotik850 Date: Wed, 27 May 2026 12:42:48 -0500 Subject: [PATCH 04/11] make sure emitted method names are not keywords --- xsd-parser/src/models/mod.rs | 1 + xsd-parser/src/models/naming/mod.rs | 2 +- xsd-parser/src/pipeline/renderer/steps/flattened.rs | 13 +++++++++++-- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/xsd-parser/src/models/mod.rs b/xsd-parser/src/models/mod.rs index a3720871..cc59799e 100644 --- a/xsd-parser/src/models/mod.rs +++ b/xsd-parser/src/models/mod.rs @@ -30,3 +30,4 @@ pub use self::naming::{ format_ident, format_unknown_variant, make_type_name, unify_string, ExplicitNameBuilder, ExplicitNaming, NameBuilder, Naming, }; +pub(crate) use naming::KEYWORDS; \ No newline at end of file diff --git a/xsd-parser/src/models/naming/mod.rs b/xsd-parser/src/models/naming/mod.rs index 9589cf22..ab5f2beb 100644 --- a/xsd-parser/src/models/naming/mod.rs +++ b/xsd-parser/src/models/naming/mod.rs @@ -97,7 +97,7 @@ where /// List of keywords that needs to be replaced by something else. /// This list needs to be sorted, because we use it in [`core::slice::binary_search_by`] -const KEYWORDS: &[(&str, &str)] = &[ +pub(crate) const KEYWORDS: &[(&str, &str)] = &[ ("Self", "Self_"), ("abstract", "abstract_"), ("as", "as_"), diff --git a/xsd-parser/src/pipeline/renderer/steps/flattened.rs b/xsd-parser/src/pipeline/renderer/steps/flattened.rs index 95e854ef..93b9ede6 100644 --- a/xsd-parser/src/pipeline/renderer/steps/flattened.rs +++ b/xsd-parser/src/pipeline/renderer/steps/flattened.rs @@ -77,8 +77,17 @@ fn render_helpers_for_complex_enum( let methods = enum_type.elements.iter().map(|e| { let variant_ident = &e.variant_ident; - let method_ident = snake_case_ident(variant_ident); - let mut_method_ident = format_ident!("{}_mut", method_ident); + let method_ident = crate::models::KEYWORDS + .iter() + .find_map(|(key, value)| { + if variant_ident == key { + Some(format_ident!("{value}")) + } else { + None + } + }) + .unwrap_or(snake_case_ident(variant_ident)); + let mut_method_ident = format_ident!("{method_ident}_mut"); let target_ty = ctx.resolve_type_for_module(&e.target_type); let option = ctx.resolve_build_in("::core::option::Option"); From b39ed780ad31a1013769a42a5c316a50cb17f0fd Mon Sep 17 00:00:00 2001 From: "kidkool850@gmail.com" Date: Wed, 27 May 2026 17:51:34 -0500 Subject: [PATCH 05/11] remove em dash --- xsd-parser/src/pipeline/renderer/steps/flattened.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xsd-parser/src/pipeline/renderer/steps/flattened.rs b/xsd-parser/src/pipeline/renderer/steps/flattened.rs index 93b9ede6..4acdcff0 100644 --- a/xsd-parser/src/pipeline/renderer/steps/flattened.rs +++ b/xsd-parser/src/pipeline/renderer/steps/flattened.rs @@ -57,7 +57,7 @@ impl RenderStep for FlattenedContentHelpersRenderStep { enum_type, ), ComplexData::Struct { .. } => { - // Struct content (e.g. a sequence) — no enum variants to flatten + // Struct content (e.g. a sequence) - no enum variants to flatten return; } }; From b34078fc5f09fcfb0039430a7a6ad4eede0c61f0 Mon Sep 17 00:00:00 2001 From: "kidkool850@gmail.com" Date: Wed, 27 May 2026 17:53:32 -0500 Subject: [PATCH 06/11] rename render step and module --- xsd-parser/src/pipeline/renderer/mod.rs | 2 +- .../renderer/steps/{flattened.rs => content_helper.rs} | 4 ++-- xsd-parser/src/pipeline/renderer/steps/mod.rs | 4 ++-- xsd-parser/tests/feature/flattened_content_helpers/mod.rs | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) rename xsd-parser/src/pipeline/renderer/steps/{flattened.rs => content_helper.rs} (97%) diff --git a/xsd-parser/src/pipeline/renderer/mod.rs b/xsd-parser/src/pipeline/renderer/mod.rs index c2ce07c3..e9ef279f 100644 --- a/xsd-parser/src/pipeline/renderer/mod.rs +++ b/xsd-parser/src/pipeline/renderer/mod.rs @@ -43,7 +43,7 @@ pub use self::custom::{ValueRenderer, ValueRendererBox}; pub use self::error::Error; pub use self::meta::MetaData; pub use self::steps::{ - DefaultsRenderStep, EnumConstantsRenderStep, FlattenedContentHelpersRenderStep, + DefaultsRenderStep, EnumConstantsRenderStep, ContentHelpersRenderStep, NamespaceConstantsRenderStep, NamespaceSerialization, PrefixConstantsRenderStep, QuickXmlCollectNamespacesRenderStep, QuickXmlDeserializeRenderStep, QuickXmlSerializeRenderStep, SerdeQuickXmlTypesRenderStep, SerdeXmlRsV7TypesRenderStep, diff --git a/xsd-parser/src/pipeline/renderer/steps/flattened.rs b/xsd-parser/src/pipeline/renderer/steps/content_helper.rs similarity index 97% rename from xsd-parser/src/pipeline/renderer/steps/flattened.rs rename to xsd-parser/src/pipeline/renderer/steps/content_helper.rs index 4acdcff0..9556a679 100644 --- a/xsd-parser/src/pipeline/renderer/steps/flattened.rs +++ b/xsd-parser/src/pipeline/renderer/steps/content_helper.rs @@ -17,9 +17,9 @@ use crate::pipeline::renderer::{Context, RenderStep, RenderStepType}; /// } /// Enabled via [`RendererFlags::FLATTENED_CONTENT_HELPERS`]. #[derive(Debug, Clone, Copy)] -pub struct FlattenedContentHelpersRenderStep; +pub struct ContentHelpersRenderStep; -impl RenderStep for FlattenedContentHelpersRenderStep { +impl RenderStep for ContentHelpersRenderStep { fn render_step_type(&self) -> RenderStepType { RenderStepType::ExtraImpls } diff --git a/xsd-parser/src/pipeline/renderer/steps/mod.rs b/xsd-parser/src/pipeline/renderer/steps/mod.rs index c428b4ac..076c6228 100644 --- a/xsd-parser/src/pipeline/renderer/steps/mod.rs +++ b/xsd-parser/src/pipeline/renderer/steps/mod.rs @@ -1,6 +1,6 @@ mod defaults; mod enum_const; -mod flattened; +mod content_helper; mod namespace_const; mod prefix_const; mod quick_xml; @@ -25,7 +25,7 @@ use super::Context; pub use self::defaults::DefaultsRenderStep; pub use self::enum_const::EnumConstantsRenderStep; -pub use self::flattened::FlattenedContentHelpersRenderStep; +pub use self::content_helper::ContentHelpersRenderStep; pub use self::namespace_const::NamespaceConstantsRenderStep; pub use self::prefix_const::PrefixConstantsRenderStep; pub use self::quick_xml::{ diff --git a/xsd-parser/tests/feature/flattened_content_helpers/mod.rs b/xsd-parser/tests/feature/flattened_content_helpers/mod.rs index f2d657cd..773ffe4b 100644 --- a/xsd-parser/tests/feature/flattened_content_helpers/mod.rs +++ b/xsd-parser/tests/feature/flattened_content_helpers/mod.rs @@ -1,10 +1,10 @@ -use xsd_parser::{pipeline::renderer::FlattenedContentHelpersRenderStep, Config, IdentType}; +use xsd_parser::{pipeline::renderer::ContentHelpersRenderStep, Config, IdentType}; use crate::utils::{generate_test, ConfigEx}; fn config() -> Config { Config::test_default() - .with_render_step(FlattenedContentHelpersRenderStep) + .with_render_step(ContentHelpersRenderStep) .with_generate([(IdentType::Element, "tns:Foo")]) } From fd626c26af00011320a6bfd3da67078fdcf06c2f Mon Sep 17 00:00:00 2001 From: "kidkool850@gmail.com" Date: Wed, 27 May 2026 17:56:04 -0500 Subject: [PATCH 07/11] use complex data field ident instead of generating manually --- .../pipeline/renderer/steps/content_helper.rs | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/xsd-parser/src/pipeline/renderer/steps/content_helper.rs b/xsd-parser/src/pipeline/renderer/steps/content_helper.rs index 9556a679..7d944eb2 100644 --- a/xsd-parser/src/pipeline/renderer/steps/content_helper.rs +++ b/xsd-parser/src/pipeline/renderer/steps/content_helper.rs @@ -77,16 +77,7 @@ fn render_helpers_for_complex_enum( let methods = enum_type.elements.iter().map(|e| { let variant_ident = &e.variant_ident; - let method_ident = crate::models::KEYWORDS - .iter() - .find_map(|(key, value)| { - if variant_ident == key { - Some(format_ident!("{value}")) - } else { - None - } - }) - .unwrap_or(snake_case_ident(variant_ident)); + let method_ident = &e.field_ident; let mut_method_ident = format_ident!("{method_ident}_mut"); let target_ty = ctx.resolve_type_for_module(&e.target_type); let option = ctx.resolve_build_in("::core::option::Option"); @@ -118,9 +109,4 @@ fn render_helpers_for_complex_enum( #( #methods )* } } -} - -/// Convert an identifier to a snake_case method name identifier. -fn snake_case_ident(ident: &proc_macro2::Ident) -> proc_macro2::Ident { - format_ident!("{}", ident.to_string().to_snake_case()) -} +} \ No newline at end of file From c75b20cc0a4c9df09dd2e06a3ee35586946d17fc Mon Sep 17 00:00:00 2001 From: "kidkool850@gmail.com" Date: Wed, 27 May 2026 17:59:15 -0500 Subject: [PATCH 08/11] only emit helpers for types that appear once --- .../src/pipeline/renderer/steps/content_helper.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/xsd-parser/src/pipeline/renderer/steps/content_helper.rs b/xsd-parser/src/pipeline/renderer/steps/content_helper.rs index 7d944eb2..3c0ebc8b 100644 --- a/xsd-parser/src/pipeline/renderer/steps/content_helper.rs +++ b/xsd-parser/src/pipeline/renderer/steps/content_helper.rs @@ -75,14 +75,17 @@ fn render_helpers_for_complex_enum( ) -> TokenStream { let enum_ident = &enum_type.base.type_ident; - let methods = enum_type.elements.iter().map(|e| { + let methods = enum_type.elements.iter().filter_map(|e| { + if e.occurs != Occurs::Single { + return None; + } let variant_ident = &e.variant_ident; let method_ident = &e.field_ident; let mut_method_ident = format_ident!("{method_ident}_mut"); let target_ty = ctx.resolve_type_for_module(&e.target_type); let option = ctx.resolve_build_in("::core::option::Option"); - quote! { + let out = quote! { #[inline] pub fn #method_ident(&self) -> #option<&#target_ty> { self.#content_field_ident.iter().find_map(|x| { @@ -101,7 +104,8 @@ fn render_helpers_for_complex_enum( } }) } - } + }; + Some(out) }); quote! { From 9402240596767e078a1d0403d1dd512c2220f29c Mon Sep 17 00:00:00 2001 From: "kidkool850@gmail.com" Date: Wed, 27 May 2026 18:02:40 -0500 Subject: [PATCH 09/11] add content helpers render step to RenderStep enum --- xsd-parser/src/config/renderer.rs | 37 +++++++++++++------ .../pipeline/renderer/steps/content_helper.rs | 2 - 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/xsd-parser/src/config/renderer.rs b/xsd-parser/src/config/renderer.rs index 3ea4a3c2..cfe3fe7f 100644 --- a/xsd-parser/src/config/renderer.rs +++ b/xsd-parser/src/config/renderer.rs @@ -195,6 +195,17 @@ pub enum RenderStep { /// that the serializer can discover which XML namespaces are actually needed /// at runtime before writing the root start element. QuickXmlCollectNamespaces, + + /// Renderer that generates ergonomic helper accessors for flattened struct content. + /// + /// This targets the pattern: + /// pub struct Foo { pub content: Vec, ... } + /// pub enum FooContent { PrivateNote(String), ... } + /// and generates: + /// impl Foo { + /// pub fn private_note(&self) -> Option<&String> { ... } + /// } + ContentHelpers, } /// Helper trait to deal with custom render steps. @@ -250,6 +261,7 @@ impl RenderStepConfig for RenderStep { Self::QuickXmlSerialize { .. } => RenderStepType::ExtraImpls, Self::QuickXmlDeserialize { .. } => RenderStepType::ExtraImpls, Self::QuickXmlCollectNamespaces => RenderStepType::ExtraImpls, + Self::ContentHelpers => RenderStepType::ExtraImpls, } } @@ -259,11 +271,11 @@ impl RenderStepConfig for RenderStep { fn into_render_step(self: Box) -> Box { use crate::pipeline::renderer::{ - DefaultsRenderStep, EnumConstantsRenderStep, NamespaceConstantsRenderStep, - PrefixConstantsRenderStep, QuickXmlCollectNamespacesRenderStep, - QuickXmlDeserializeRenderStep, QuickXmlSerializeRenderStep, - SerdeQuickXmlTypesRenderStep, SerdeXmlRsV7TypesRenderStep, SerdeXmlRsV8TypesRenderStep, - TypesRenderStep, WithNamespaceTraitRenderStep, + ContentHelpersRenderStep, DefaultsRenderStep, EnumConstantsRenderStep, + NamespaceConstantsRenderStep, PrefixConstantsRenderStep, + QuickXmlCollectNamespacesRenderStep, QuickXmlDeserializeRenderStep, + QuickXmlSerializeRenderStep, SerdeQuickXmlTypesRenderStep, SerdeXmlRsV7TypesRenderStep, + SerdeXmlRsV8TypesRenderStep, TypesRenderStep, WithNamespaceTraitRenderStep, }; match *self { @@ -291,16 +303,17 @@ impl RenderStepConfig for RenderStep { Box::new(QuickXmlDeserializeRenderStep { boxed_deserializer }) } Self::QuickXmlCollectNamespaces => Box::new(QuickXmlCollectNamespacesRenderStep), + Self::ContentHelpers => Box::new(ContentHelpersRenderStep), } } fn is_mutual_exclusive_to(&self, other: &dyn RenderStepConfig) -> bool { use crate::pipeline::renderer::{ - DefaultsRenderStep, EnumConstantsRenderStep, NamespaceConstantsRenderStep, - PrefixConstantsRenderStep, QuickXmlCollectNamespacesRenderStep, - QuickXmlDeserializeRenderStep, QuickXmlSerializeRenderStep, - SerdeQuickXmlTypesRenderStep, SerdeXmlRsV7TypesRenderStep, SerdeXmlRsV8TypesRenderStep, - TypesRenderStep, WithNamespaceTraitRenderStep, + ContentHelpersRenderStep, DefaultsRenderStep, EnumConstantsRenderStep, + NamespaceConstantsRenderStep, PrefixConstantsRenderStep, + QuickXmlCollectNamespacesRenderStep, QuickXmlDeserializeRenderStep, + QuickXmlSerializeRenderStep, SerdeQuickXmlTypesRenderStep, SerdeXmlRsV7TypesRenderStep, + SerdeXmlRsV8TypesRenderStep, TypesRenderStep, WithNamespaceTraitRenderStep, }; if self @@ -359,6 +372,7 @@ impl RenderStepConfig for RenderStep { (Self::QuickXmlCollectNamespaces, None) => { other_id == TypeId::of::() } + (Self::ContentHelpers, None) => other_id == TypeId::of::(), _ => false, } } @@ -380,7 +394,8 @@ impl RenderStep { | (Self::WithNamespaceTrait, Self::WithNamespaceTrait) | (Self::QuickXmlSerialize { .. }, Self::QuickXmlSerialize { .. }) | (Self::QuickXmlDeserialize { .. }, Self::QuickXmlDeserialize { .. }) - | (Self::QuickXmlCollectNamespaces, Self::QuickXmlCollectNamespaces) => true, + | (Self::QuickXmlCollectNamespaces, Self::QuickXmlCollectNamespaces) + | (Self::ContentHelpers, Self::ContentHelpers) => true, (_, _) => false, } } diff --git a/xsd-parser/src/pipeline/renderer/steps/content_helper.rs b/xsd-parser/src/pipeline/renderer/steps/content_helper.rs index 3c0ebc8b..6326c80d 100644 --- a/xsd-parser/src/pipeline/renderer/steps/content_helper.rs +++ b/xsd-parser/src/pipeline/renderer/steps/content_helper.rs @@ -1,4 +1,3 @@ -use inflector::Inflector; use proc_macro2::TokenStream; use quote::{format_ident, quote}; @@ -15,7 +14,6 @@ use crate::pipeline::renderer::{Context, RenderStep, RenderStepType}; /// impl Foo { /// pub fn private_note(&self) -> Option<&String> { ... } /// } -/// Enabled via [`RendererFlags::FLATTENED_CONTENT_HELPERS`]. #[derive(Debug, Clone, Copy)] pub struct ContentHelpersRenderStep; From a38502e1f237cea15dee2b0bf0badfe175f34fee Mon Sep 17 00:00:00 2001 From: Exotik850 Date: Fri, 29 May 2026 10:35:39 -0500 Subject: [PATCH 10/11] remove keywords export --- xsd-parser/src/models/mod.rs | 3 +-- xsd-parser/src/models/naming/mod.rs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/xsd-parser/src/models/mod.rs b/xsd-parser/src/models/mod.rs index cc59799e..27769e61 100644 --- a/xsd-parser/src/models/mod.rs +++ b/xsd-parser/src/models/mod.rs @@ -29,5 +29,4 @@ pub use self::name::Name; pub use self::naming::{ format_ident, format_unknown_variant, make_type_name, unify_string, ExplicitNameBuilder, ExplicitNaming, NameBuilder, Naming, -}; -pub(crate) use naming::KEYWORDS; \ No newline at end of file +}; \ No newline at end of file diff --git a/xsd-parser/src/models/naming/mod.rs b/xsd-parser/src/models/naming/mod.rs index ab5f2beb..9589cf22 100644 --- a/xsd-parser/src/models/naming/mod.rs +++ b/xsd-parser/src/models/naming/mod.rs @@ -97,7 +97,7 @@ where /// List of keywords that needs to be replaced by something else. /// This list needs to be sorted, because we use it in [`core::slice::binary_search_by`] -pub(crate) const KEYWORDS: &[(&str, &str)] = &[ +const KEYWORDS: &[(&str, &str)] = &[ ("Self", "Self_"), ("abstract", "abstract_"), ("as", "as_"), From 4aacf94fff46e0491801ad9989ac7d6a16bf14bc Mon Sep 17 00:00:00 2001 From: Exotik850 Date: Sat, 30 May 2026 10:38:54 -0500 Subject: [PATCH 11/11] dedupe underscore for mut method --- xsd-parser/src/pipeline/renderer/steps/content_helper.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/xsd-parser/src/pipeline/renderer/steps/content_helper.rs b/xsd-parser/src/pipeline/renderer/steps/content_helper.rs index 6326c80d..6b8c6ca6 100644 --- a/xsd-parser/src/pipeline/renderer/steps/content_helper.rs +++ b/xsd-parser/src/pipeline/renderer/steps/content_helper.rs @@ -79,7 +79,8 @@ fn render_helpers_for_complex_enum( } let variant_ident = &e.variant_ident; let method_ident = &e.field_ident; - let mut_method_ident = format_ident!("{method_ident}_mut"); + let mut_method = format!("{}_mut", method_ident).replace("__", "_"); + let mut_method_ident = format_ident!("{mut_method}"); let target_ty = ctx.resolve_type_for_module(&e.target_type); let option = ctx.resolve_build_in("::core::option::Option");