Skip to content

Commit 5166cfb

Browse files
committed
Implement display name for content fields
When generating code for a XSD schema that defines a `content` attribute, the corresponding field in the generated code will conflict with the field generated for the actual content of that element. To fix this we introduced a user defined display name for the generated content field.
1 parent f011aa7 commit 5166cfb

23 files changed

Lines changed: 559 additions & 34 deletions

File tree

xsd-parser/src/config/generator.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@ pub struct GeneratorConfig {
5353
/// See [`Generator::any_attributes_type`](crate::Generator::any_attributes_type)
5454
/// for details.
5555
pub any_attributes_type: String,
56+
57+
/// Name to use for the generated content field of the generated types.
58+
///
59+
/// See [`Generator::content_display_name`](crate::Generator::content_display_name)
60+
/// for details.
61+
pub content_display_name: String,
5662
}
5763

5864
impl Default for GeneratorConfig {
@@ -69,6 +75,7 @@ impl Default for GeneratorConfig {
6975
nillable_type: "::xsd_parser_types::xml::Nillable".into(),
7076
any_type: "::xsd_parser_types::xml::AnyElement".into(),
7177
any_attributes_type: "::xsd_parser_types::xml::AnyAttributes".into(),
78+
content_display_name: "content".into(),
7279
}
7380
}
7481
}

xsd-parser/src/config/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,13 @@ pub struct Config {
5151
}
5252

5353
impl Config {
54+
/// Set the name to use for the generated content field of the generated types.
55+
pub fn with_content_display_name<S: Into<String>>(mut self, name: S) -> Self {
56+
self.generator.content_display_name = name.into();
57+
58+
self
59+
}
60+
5461
/// Adds the passed `schema` to the list of schemas to parse.
5562
pub fn with_schema<T>(mut self, schema: T) -> Self
5663
where

xsd-parser/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,8 @@ pub fn exec_generator_with_ident_cache<'types>(
356356
let mut generator = Generator::new(types)
357357
.flags(config.flags)
358358
.box_flags(config.box_flags)
359-
.typedef_mode(config.typedef_mode);
359+
.typedef_mode(config.typedef_mode)
360+
.content_display_name(config.content_display_name);
360361

361362
generator = generator
362363
.text_type(config.text_type)

xsd-parser/src/models/data/complex.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,9 @@ pub struct ComplexDataContent<'types> {
196196
/// Maximum occurrence.
197197
pub max_occurs: MaxOccurs,
198198

199+
/// Field identifier of the element.
200+
pub field_ident: Ident2,
201+
199202
/// Actual target type of the content.
200203
pub target_type: PathData,
201204

xsd-parser/src/pipeline/generator/data/complex.rs

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::borrow::Cow;
22
use std::mem::take;
33

4+
use inflector::Inflector;
45
use proc_macro2::{Ident as Ident2, Literal};
56
use quote::format_ident;
67

@@ -23,6 +24,7 @@ use crate::models::{
2324
ElementIdent, TypeIdent,
2425
};
2526
use crate::pipeline::generator::ValueGeneratorMode;
27+
use crate::Name;
2628

2729
use super::super::{Context, Error};
2830

@@ -172,6 +174,7 @@ impl<'types> ComplexData<'types> {
172174
) -> Result<Self, Error> {
173175
let base = ComplexBase::new(ctx, form, ComplexFlags::empty())?;
174176
let occurs = Occurs::from_occurs(min_occurs, max_occurs);
177+
let field_ident = format_ident!("{}", ctx.content_display_name);
175178

176179
let content_ref = ctx.get_or_create_type_ref_for_value(simple_type, occurs.is_direct())?;
177180
let target_type = content_ref.path.clone();
@@ -189,6 +192,7 @@ impl<'types> ComplexData<'types> {
189192
simple_type: Some(simple_type),
190193
min_occurs,
191194
max_occurs,
195+
field_ident,
192196
target_type,
193197
extra_attributes: Vec::new(),
194198
};
@@ -206,6 +210,7 @@ impl<'types> ComplexData<'types> {
206210
})
207211
}
208212

213+
#[allow(clippy::too_many_lines)]
209214
fn new_enum(
210215
ctx: &mut Context<'_, 'types>,
211216
form: FormChoiceType,
@@ -303,6 +308,7 @@ impl<'types> ComplexData<'types> {
303308
}
304309
} else {
305310
let type_ref = ctx.current_type_ref();
311+
let field_ident = format_ident!("{}", ctx.content_display_name);
306312

307313
let target_type = (*type_ref.path).clone().with_ident(content_ident.clone());
308314
let target_type = PathData::from_path(target_type);
@@ -312,6 +318,7 @@ impl<'types> ComplexData<'types> {
312318
simple_type: None,
313319
min_occurs,
314320
max_occurs,
321+
field_ident,
315322
target_type,
316323
extra_attributes: Vec::new(),
317324
};
@@ -472,6 +479,7 @@ impl<'types> ComplexData<'types> {
472479
}
473480
} else {
474481
let type_ref = ctx.current_type_ref();
482+
let field_ident = format_ident!("{}", ctx.content_display_name);
475483

476484
let target_type = (*type_ref.path).clone().with_ident(content_ident.clone());
477485
let target_type = PathData::from_path(target_type);
@@ -481,6 +489,7 @@ impl<'types> ComplexData<'types> {
481489
simple_type: None,
482490
min_occurs,
483491
max_occurs,
492+
field_ident,
484493
target_type,
485494
extra_attributes: Vec::new(),
486495
};
@@ -716,8 +725,13 @@ impl<'types> ComplexDataElement<'types> {
716725
max_occurs: MaxOccurs,
717726
content_ident: Ident2,
718727
) -> Self {
728+
let s_name = ctx.content_display_name.clone();
729+
let b_name = Literal::byte_string(s_name.as_bytes());
730+
let field_ident = format_ident!("{s_name}");
731+
let variant_ident = format_ident!("{}", s_name.to_pascal_case());
732+
719733
let meta = ElementMeta {
720-
ident: ElementIdent::named("content"),
734+
ident: ElementIdent::new(Name::new_named(s_name.clone())),
721735
variant: ElementMetaVariant::Type {
722736
type_: TypeIdent::UNKNOWN,
723737
mode: ElementMode::Group,
@@ -741,11 +755,11 @@ impl<'types> ComplexDataElement<'types> {
741755
Self {
742756
origin,
743757
occurs,
744-
s_name: "content".into(),
745-
b_name: Literal::byte_string(b"content"),
758+
s_name,
759+
b_name,
746760
tag_name: TagName::default(),
747-
field_ident: format_ident!("content"),
748-
variant_ident: format_ident!("Content"),
761+
field_ident,
762+
variant_ident,
749763
target_type,
750764
need_indirection: false,
751765
target_is_dynamic: false,

xsd-parser/src/pipeline/generator/meta.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ pub struct MetaData<'types> {
4040

4141
/// Type to use to store unstructured `xs:anyAttribute` attributes.
4242
pub any_attributes_type: IdentPath,
43+
44+
/// Name to use for the generated content field of the generated types.
45+
pub content_display_name: String,
4346
}
4447

4548
impl MetaData<'_> {

xsd-parser/src/pipeline/generator/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ impl<'types> Generator<'types> {
113113
any_type: IdentPath::from_str("::xsd_parser_types::xml::AnyElement").unwrap(),
114114
any_attributes_type: IdentPath::from_str("::xsd_parser_types::xml::AnyAttributes")
115115
.unwrap(),
116+
content_display_name: String::from("content"),
116117
};
117118
let state = State {
118119
cache: BTreeMap::new(),
@@ -215,6 +216,13 @@ impl<'types> Generator<'types> {
215216
Ok(self)
216217
}
217218

219+
/// Set the name to use for the generated content field of the generated types.
220+
pub fn content_display_name<S: Into<String>>(mut self, name: S) -> Self {
221+
self.meta.content_display_name = name.into();
222+
223+
self
224+
}
225+
218226
/// Add the passed [`GeneratorFlags`] flags the generator should use for generating the code.
219227
pub fn with_flags(mut self, value: GeneratorFlags) -> Self {
220228
self.meta.flags |= value;

xsd-parser/src/pipeline/renderer/steps/quick_xml/collect_namespaces.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,9 +209,11 @@ impl ComplexDataStruct<'_> {
209209

210210
let field_calls: Vec<_> = match &self.mode {
211211
StructMode::Empty { .. } => Vec::new(),
212-
StructMode::Content { .. } => {
212+
StructMode::Content { content } => {
213+
let field_ident = &content.field_ident;
214+
213215
vec![quote! {
214-
self.content.collect_namespaces(helper, bytes);
216+
self.#field_ident.collect_namespaces(helper, bytes);
215217
}]
216218
}
217219
StructMode::All { elements, .. } | StructMode::Sequence { elements, .. } => elements

xsd-parser/src/pipeline/renderer/steps/quick_xml/deserialize.rs

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2379,6 +2379,7 @@ impl ComplexDataContent<'_> {
23792379
}
23802380

23812381
fn deserializer_field_decl(&self, ctx: &Context<'_, '_>) -> TokenStream {
2382+
let field_ident = &self.field_ident;
23822383
let target_type = ctx.resolve_type_for_deserialize_module(&self.target_type);
23832384

23842385
let target_type = match self.occurs {
@@ -2396,60 +2397,64 @@ impl ComplexDataContent<'_> {
23962397
};
23972398

23982399
quote! {
2399-
content: #target_type,
2400+
#field_ident: #target_type,
24002401
}
24012402
}
24022403

24032404
fn deserializer_struct_field_init(&self, ctx: &Context<'_, '_>) -> TokenStream {
2405+
let field_ident = &self.field_ident;
2406+
24042407
match self.occurs {
24052408
Occurs::None => quote!(),
2406-
Occurs::Single | Occurs::Optional => quote!(content: None,),
2409+
Occurs::Single | Occurs::Optional => quote!(#field_ident: None,),
24072410
Occurs::DynamicList | Occurs::StaticList(_) => {
24082411
let vec = resolve_build_in!(ctx, "::alloc::vec::Vec");
24092412

2410-
quote!(content: #vec::new(),)
2413+
quote!(#field_ident: #vec::new(),)
24112414
}
24122415
}
24132416
}
24142417

24152418
fn deserializer_struct_field_finish(&self, ctx: &mut Context<'_, '_>) -> TokenStream {
2419+
let field_ident = &self.field_ident;
24162420
let convert = match self.occurs {
24172421
Occurs::None => crate::unreachable!(),
24182422
Occurs::Single => {
24192423
if ctx.has_defaultable_content() {
2420-
quote!(helper.finish_default(self.content)?)
2424+
quote!(helper.finish_default(self.#field_ident)?)
24212425
} else {
2422-
quote!(helper.finish_content(self.content)?)
2426+
quote!(helper.finish_content(self.#field_ident)?)
24232427
}
24242428
}
24252429
Occurs::Optional => {
2426-
quote! { self.content }
2430+
quote! { self.#field_ident }
24272431
}
24282432
Occurs::DynamicList => {
24292433
let min = self.min_occurs;
24302434
let max = self.max_occurs.render_opt();
24312435

24322436
if ctx.has_defaultable_content() {
2433-
quote!(helper.finish_vec_default(#min, self.content)?)
2437+
quote!(helper.finish_vec_default(#min, self.#field_ident)?)
24342438
} else {
2435-
quote!(helper.finish_vec(#min, #max, self.content)?)
2439+
quote!(helper.finish_vec(#min, #max, self.#field_ident)?)
24362440
}
24372441
}
24382442
Occurs::StaticList(sz) => {
24392443
if ctx.has_defaultable_content() {
2440-
quote!(helper.finish_arr_default::<_, #sz>(self.content)?)
2444+
quote!(helper.finish_arr_default::<_, #sz>(self.#field_ident)?)
24412445
} else {
2442-
quote!(helper.finish_arr::<_, #sz>(self.content)?)
2446+
quote!(helper.finish_arr::<_, #sz>(self.#field_ident)?)
24432447
}
24442448
}
24452449
};
24462450

24472451
quote! {
2448-
content: #convert,
2452+
#field_ident: #convert,
24492453
}
24502454
}
24512455

24522456
fn deserializer_struct_field_fn_store(&self, ctx: &Context<'_, '_>) -> TokenStream {
2457+
let field_ident = &self.field_ident;
24532458
let target_type = ctx.resolve_type_for_deserialize_module(&self.target_type);
24542459

24552460
let body = match self.occurs {
@@ -2459,15 +2464,15 @@ impl ComplexDataContent<'_> {
24592464
resolve_quick_xml_ident!(ctx, "::xsd_parser_types::quick_xml::ErrorKind");
24602465

24612466
quote! {
2462-
if self.content.is_some() {
2467+
if self.#field_ident.is_some() {
24632468
Err(#error_kind::DuplicateContent)?;
24642469
}
24652470

2466-
self.content = Some(value);
2471+
self.#field_ident = Some(value);
24672472
}
24682473
}
24692474
Occurs::DynamicList | Occurs::StaticList(_) => quote! {
2470-
self.content.push(value);
2475+
self.#field_ident.push(value);
24712476
},
24722477
};
24732478

xsd-parser/src/pipeline/renderer/steps/quick_xml/serialize.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1193,6 +1193,8 @@ impl ComplexDataContent<'_> {
11931193
ctx: &Context<'_, '_>,
11941194
state_ident: &Ident2,
11951195
) -> TokenStream {
1196+
let field_ident = &self.field_ident;
1197+
11961198
match self.occurs {
11971199
Occurs::None => crate::unreachable!(),
11981200
Occurs::Single => {
@@ -1201,7 +1203,7 @@ impl ComplexDataContent<'_> {
12011203

12021204
quote! {
12031205
*self.state = #state_ident::Content__(
1204-
#with_serializer::serializer(&self.value.content, None, false)?
1206+
#with_serializer::serializer(&self.value.#field_ident, None, false)?
12051207
)
12061208
}
12071209
}
@@ -1212,7 +1214,7 @@ impl ComplexDataContent<'_> {
12121214
quote! {
12131215
*self.state = #state_ident::Content__(
12141216
#iter_serializer::new(
1215-
self.value.content.as_ref(),
1217+
self.value.#field_ident.as_ref(),
12161218
None,
12171219
false
12181220
)
@@ -1226,7 +1228,7 @@ impl ComplexDataContent<'_> {
12261228
quote! {
12271229
*self.state = #state_ident::Content__(
12281230
#iter_serializer::new(
1229-
&self.value.content[..],
1231+
&self.value.#field_ident[..],
12301232
None,
12311233
false
12321234
)

0 commit comments

Comments
 (0)