xsd_parser/pipeline/generator/mod.rs
1//! Code generation pipeline for transforming resolved schema models into Rust data structures.
2//!
3//! This module defines the [`Generator`] and [`GeneratorFixed`] types, along with supporting
4//! logic and configuration mechanisms for converting fully interpreted [`MetaTypes`] into
5//! concrete Rust representations [`DataTypes`].
6//!
7//! The [`Generator`] allows fine-grained configuration such as boxing strategy, type naming,
8//! serde support, and handling of `xs:any`/`xs:anyAttribute`. Once configured, the generator
9//! can be "fixed" into a [`GeneratorFixed`] state to emit type definitions in a controlled,
10//! deterministic fashion.
11//!
12//! Code generation is performed by walking the dependency graph of types, resolving references,
13//! and emitting type-safe Rust structures including enums, structs, and aliases.
14//!
15//! # Example
16//! ```rust,ignore
17//! let data_types = Generator::new(meta_types)
18//! .with_flags(GeneratorFlags::USE_MODULES)
19//! .generate_named_types()?
20//! .finish();
21//! ```
22
23mod context;
24mod data;
25mod error;
26mod meta;
27mod state;
28mod walk;
29
30use std::collections::{BTreeMap, HashSet, VecDeque};
31
32use proc_macro2::Ident as Ident2;
33use quote::format_ident;
34use tracing::instrument;
35
36use crate::config::{BoxFlags, GeneratorFlags, TypedefMode};
37use crate::models::code::{ModuleIdent, ModulePath};
38use crate::models::{
39 code::{format_module_ident, format_type_ident, IdentPath},
40 data::{DataType, DataTypes, PathData},
41 meta::{ElementMetaVariant, MetaType, MetaTypeVariant, MetaTypes},
42 schema::{MaxOccurs, NamespaceId},
43 Ident, IdentType, Name,
44};
45
46pub use self::context::Context;
47pub use self::error::Error;
48pub use self::meta::MetaData;
49
50use self::state::{PendingType, State, TraitInfos, TypeRef};
51use self::walk::Walk;
52
53/// Configurable Rust code generator for schema-derived type information.
54///
55/// The [`Generator`] type provides a flexible interface to customize how Rust code
56/// structures are generated from XSD-like schema models represented as [`MetaTypes`].
57/// It supports configuration of type postfixes, boxing rules, serde integration, and more.
58///
59/// Once all configuration is applied, the generator can be "sealed" into a
60/// [`GeneratorFixed`] instance using [`into_fixed`](Self::into_fixed),
61/// after which only code generation (not configuration) is permitted.
62#[must_use]
63#[derive(Debug)]
64pub struct Generator<'types> {
65 meta: MetaData<'types>,
66 state: State<'types>,
67}
68
69/// Finalized code generator that emits Rust types from resolved schema definitions.
70///
71/// [`GeneratorFixed`] is produced by sealing a [`Generator`] with [`Generator::into_fixed()`],
72/// locking its configuration and enabling deterministic generation of all required types.
73///
74/// It offers methods to:
75/// - generate a specific type
76/// - generate all named types
77/// - generate all available types
78///
79/// Once generation is complete, use [`finish`](Self::finish) to retrieve the generated
80/// [`DataTypes`] output for rendering.
81#[must_use]
82#[derive(Debug)]
83pub struct GeneratorFixed<'types> {
84 state: State<'types>,
85 data_types: DataTypes<'types>,
86}
87
88/* Generator */
89
90impl<'types> Generator<'types> {
91 /// Create a new code generator from the passed `types`.
92 pub fn new(types: &'types MetaTypes) -> Self {
93 let meta = MetaData {
94 types,
95 flags: GeneratorFlags::empty(),
96 postfixes: [
97 String::from("Type"), // Type = 0
98 String::new(), // Group = 1
99 String::from("ElementType"), // Element = 2
100 String::new(), // ElementType = 3
101 String::new(), // Attribute = 4
102 String::new(), // AttributeGroup = 5
103 String::new(), // BuildIn = 6
104 String::new(), // Enumeration = 7
105 ],
106 box_flags: BoxFlags::AUTO,
107 typedef_mode: TypedefMode::Auto,
108 any_type: None,
109 any_attribute_type: None,
110 };
111 let state = State {
112 cache: BTreeMap::new(),
113 pending: VecDeque::new(),
114 trait_infos: None,
115 };
116
117 Self { meta, state }
118 }
119
120 /// Set the [`BoxFlags`] flags the generator should use for generating the code.
121 pub fn box_flags(mut self, value: BoxFlags) -> Self {
122 self.meta.box_flags = value;
123
124 self
125 }
126
127 /// Set the [`TypedefMode`] value the generator should use for generating the code.
128 pub fn typedef_mode(mut self, value: TypedefMode) -> Self {
129 self.meta.typedef_mode = value;
130
131 self
132 }
133
134 /// Set the [`GeneratorFlags`] flags the generator should use for generating the code.
135 pub fn flags(mut self, value: GeneratorFlags) -> Self {
136 self.meta.flags = value;
137
138 self
139 }
140
141 /// Set the type to use to store unstructured `xs:any` elements.
142 ///
143 /// If this is set, the generator will create additional fields to store
144 /// unstructured XML data for elements that has `xs:any` set.
145 ///
146 /// # Errors
147 ///
148 /// Forwards the error that is thrown, if `path` could not be converted.
149 pub fn any_type<P>(mut self, path: P) -> Result<Self, P::Error>
150 where
151 P: TryInto<IdentPath>,
152 {
153 self.meta.any_type = Some(path.try_into()?);
154
155 Ok(self)
156 }
157
158 /// Set the type to use to store unstructured `xs:anyAttribute` attributes.
159 ///
160 /// If this is set, the generator will create additional fields to store
161 /// unstructured XML attributes for elements that has `xs:anyAttribute` set.
162 ///
163 /// # Errors
164 ///
165 /// Forwards the error that is thrown, if `path` could not be converted.
166 pub fn any_attribute_type<P>(mut self, path: P) -> Result<Self, P::Error>
167 where
168 P: TryInto<IdentPath>,
169 {
170 self.meta.any_attribute_type = Some(path.try_into()?);
171
172 Ok(self)
173 }
174
175 /// Add the passed [`GeneratorFlags`] flags the generator should use for generating the code.
176 pub fn with_flags(mut self, value: GeneratorFlags) -> Self {
177 self.meta.flags |= value;
178
179 self
180 }
181
182 /// Set the postfixes the generator should use for the different types.
183 ///
184 /// Default is `"Type"` for the [`IdentType::Type`] type and `""` for the other types.
185 pub fn with_type_postfix<S: Into<String>>(mut self, type_: IdentType, postfix: S) -> Self {
186 self.meta.postfixes[type_ as usize] = postfix.into();
187
188 self
189 }
190
191 /// Add a custom implemented type to the generator.
192 ///
193 /// This will add a custom implemented type to the generator. These types are
194 /// usually implemented and provided by the user of the generated code. The
195 /// generator will just reference to the type definition and will not generate
196 /// any code related to this type.
197 ///
198 /// # Errors
199 ///
200 /// Returns an error if the namespace of the passed identifier is unknown.
201 ///
202 /// # Examples
203 ///
204 /// ```ignore
205 /// let generator = Generator::new(types)
206 /// .with_type(Ident::type_("UserDefinedType"));
207 /// ```
208 pub fn with_type(mut self, ident: Ident) -> Result<Self, Error> {
209 let module_ident = format_module(self.meta.types, ident.ns)?;
210 let type_ident = format_ident!("{}", ident.name.to_string());
211 let path = PathData::from_path(IdentPath::from_parts(module_ident, type_ident));
212
213 let type_ref = TypeRef {
214 path,
215 boxed_elements: HashSet::new(),
216 };
217 self.state.cache.insert(ident, type_ref);
218
219 Ok(self)
220 }
221
222 /// Will fix the generator by call [`into_fixed`](Self::into_fixed) and then
223 /// [`generate_type`](GeneratorFixed::generate_type).
224 ///
225 /// # Errors
226 ///
227 /// Raises an [`Error`] if the type generation failed.
228 #[instrument(err, level = "trace", skip(self))]
229 pub fn generate_type(self, ident: Ident) -> Result<GeneratorFixed<'types>, Error> {
230 self.into_fixed().generate_type(ident)
231 }
232
233 /// Will fix the generator by call [`into_fixed`](Self::into_fixed) and then
234 /// [`generate_named_types`](GeneratorFixed::generate_named_types).
235 ///
236 /// # Errors
237 ///
238 /// Will just forward the errors from [`generate_named_types`](GeneratorFixed::generate_named_types).
239 #[instrument(err, level = "trace", skip(self))]
240 pub fn generate_named_types(self) -> Result<GeneratorFixed<'types>, Error> {
241 self.into_fixed().generate_named_types()
242 }
243
244 /// Will fix the generator by call [`into_fixed`](Self::into_fixed) and then
245 /// [`generate_all_types`](GeneratorFixed::generate_all_types).
246 ///
247 /// # Errors
248 ///
249 /// Will just forward the errors from [`generate_all_types`](GeneratorFixed::generate_all_types).
250 pub fn generate_all_types(self) -> Result<GeneratorFixed<'types>, Error> {
251 self.into_fixed().generate_all_types()
252 }
253
254 /// Will convert the generator into a [`GeneratorFixed`].
255 ///
256 /// You need to call this method if the general configuration of the generator
257 /// is finished. The resulting [`GeneratorFixed`] type will only provide methods
258 /// to generate data types for specific types. The underlying configuration can
259 /// not be changed anymore.
260 pub fn into_fixed(self) -> GeneratorFixed<'types> {
261 let Self { meta, state } = self;
262
263 let data_types = DataTypes::new(meta);
264
265 GeneratorFixed { state, data_types }
266 }
267}
268
269impl<'types> GeneratorFixed<'types> {
270 /// Generate the code for the given type.
271 ///
272 /// This will generate the code for the passed type identifier and all
273 /// dependencies of this type.
274 ///
275 /// # Errors
276 ///
277 /// Raises an [`Error`] if the type generation failed.
278 ///
279 /// # Examples
280 ///
281 /// ```ignore
282 /// let generator = Generator::new(types)
283 /// .generate_type(Ident::type_("Root"));
284 /// ```
285 #[instrument(err, level = "trace", skip(self))]
286 pub fn generate_type(mut self, ident: Ident) -> Result<GeneratorFixed<'types>, Error> {
287 self.state
288 .get_or_create_type_ref(&self.data_types.meta, &ident)?;
289 self.generate_pending()?;
290
291 Ok(self)
292 }
293
294 /// Generate the code for all types.
295 ///
296 /// This will generate the code for all types that are specified in
297 /// the [`MetaTypes`] object passed to the generator.
298 ///
299 /// # Errors
300 ///
301 /// Raises an [`Error`] if the type generation failed.
302 ///
303 /// # Examples
304 ///
305 /// ```ignore
306 /// let generator = Generator::new(types)
307 /// .generate_all_types();
308 /// ```
309 #[instrument(err, level = "trace", skip(self))]
310 pub fn generate_all_types(mut self) -> Result<Self, Error> {
311 for ident in self.data_types.meta.types.items.keys() {
312 self.state
313 .get_or_create_type_ref(&self.data_types.meta, ident)?;
314 }
315 self.generate_pending()?;
316
317 Ok(self)
318 }
319
320 /// Generate the code for all named types.
321 ///
322 /// This will generate the code for all types with an explicit name and all
323 /// dependencies of these types that are specified in the [`MetaTypes`] object
324 /// passed to the generator.
325 ///
326 /// # Errors
327 ///
328 /// Raises an [`Error`] if the type generation failed.
329 ///
330 /// # Examples
331 ///
332 /// ```ignore
333 /// let generator = Generator::new(types)
334 /// .generate_named_types();
335 /// ```
336 #[instrument(err, level = "trace", skip(self))]
337 pub fn generate_named_types(mut self) -> Result<Self, Error> {
338 for ident in self.data_types.meta.types.items.keys() {
339 if ident.name.is_named() {
340 self.state
341 .get_or_create_type_ref(&self.data_types.meta, ident)?;
342 }
343 }
344 self.generate_pending()?;
345
346 Ok(self)
347 }
348
349 /// Finish the code generation.
350 ///
351 /// This will return the generated data types as [`DataTypes`].
352 #[instrument(level = "trace", skip(self))]
353 pub fn finish(self) -> DataTypes<'types> {
354 self.data_types
355 }
356
357 #[instrument(err, level = "trace", skip(self))]
358 fn generate_pending(&mut self) -> Result<(), Error> {
359 while let Some(args) = self.state.pending.pop_front() {
360 self.generate_type_intern(args)?;
361 }
362
363 Ok(())
364 }
365
366 #[instrument(err, level = "trace", skip(self))]
367 fn generate_type_intern(&mut self, data: PendingType<'types>) -> Result<(), Error> {
368 let PendingType { ty, ident } = data;
369 let Self { state, data_types } = self;
370
371 let mut context = Context::new(&data_types.meta, &ident, state);
372 let ty = DataType::new(ty, &mut context)?;
373
374 data_types.items.insert(ident, ty);
375
376 Ok(())
377 }
378}
379
380impl<'types> State<'types> {
381 #[instrument(level = "trace", skip(self, meta))]
382 fn get_or_create_type_ref(
383 &mut self,
384 meta: &MetaData<'types>,
385 ident: &Ident,
386 ) -> Result<&TypeRef, Error> {
387 if !self.cache.contains_key(ident) {
388 let ty = meta
389 .types
390 .items
391 .get(ident)
392 .ok_or_else(|| Error::UnknownType(ident.clone()))?;
393 let name = make_type_name(&meta.postfixes, ty, ident);
394 let path = match &ty.variant {
395 MetaTypeVariant::BuildIn(x) => {
396 let path =
397 if meta.check_generator_flags(GeneratorFlags::BUILD_IN_ABSOLUTE_PATHS) {
398 x.absolute_ident_path()
399 } else {
400 x.ident_path()
401 };
402
403 PathData::from_path(path)
404 }
405 MetaTypeVariant::Custom(x) => {
406 let path = IdentPath::from_ident(format_ident!("{}", x.name()));
407
408 if let Some(using) = x.include() {
409 PathData::from_path(path).with_using(using)
410 } else {
411 PathData::from_path(path.with_path(None))
412 }
413 }
414 _ => {
415 let module_ident = ModuleIdent::new(
416 meta.types,
417 ident,
418 meta.check_generator_flags(GeneratorFlags::USE_NAMESPACE_MODULES),
419 meta.check_generator_flags(GeneratorFlags::USE_SCHEMA_MODULES),
420 );
421 let module_path = ModulePath::from_ident(meta.types, module_ident);
422 let type_ident = format_type_ident(&name, ty.display_name.as_deref());
423
424 let path = IdentPath::from_parts(module_path.0, type_ident);
425
426 PathData::from_path(path)
427 }
428 };
429
430 tracing::debug!("Queue new type generation: {ident}");
431
432 let boxed_elements = get_boxed_elements(ident, ty, meta.types, &self.cache);
433 self.pending.push_back(PendingType {
434 ty,
435 ident: ident.clone(),
436 });
437
438 let type_ref = TypeRef {
439 path,
440 boxed_elements,
441 };
442
443 assert!(self.cache.insert(ident.clone(), type_ref).is_none());
444 }
445
446 Ok(self.cache.get_mut(ident).unwrap())
447 }
448}
449
450/* Helper */
451
452fn get_boxed_elements<'a>(
453 ident: &Ident,
454 mut ty: &'a MetaType,
455 types: &'a MetaTypes,
456 cache: &BTreeMap<Ident, TypeRef>,
457) -> HashSet<Ident> {
458 if let MetaTypeVariant::ComplexType(ci) = &ty.variant {
459 if let Some(type_) = ci.content.as_ref().and_then(|ident| types.items.get(ident)) {
460 ty = type_;
461 }
462 }
463
464 match &ty.variant {
465 MetaTypeVariant::All(si) | MetaTypeVariant::Choice(si) | MetaTypeVariant::Sequence(si) => {
466 si.elements
467 .iter()
468 .filter_map(|f| {
469 if let ElementMetaVariant::Type { type_, .. } = &f.variant {
470 if Walk::new(types, cache).is_loop(ident, type_) {
471 return Some(f.ident.clone());
472 }
473 }
474
475 None
476 })
477 .collect()
478 }
479 _ => HashSet::new(),
480 }
481}
482
483fn make_type_name(postfixes: &[String], ty: &MetaType, ident: &Ident) -> Name {
484 if let MetaTypeVariant::Reference(ti) = &ty.variant {
485 if ident.name.is_generated() && ti.type_.name.is_named() {
486 let s = ti.type_.name.to_type_name();
487
488 if ti.max_occurs > MaxOccurs::Bounded(1) {
489 return Name::new_generated(format!("{s}List"));
490 } else if ti.min_occurs == 0 {
491 return Name::new_generated(format!("{s}Opt"));
492 }
493 }
494 }
495
496 let postfix = postfixes
497 .get(ident.type_ as usize)
498 .map_or("", |s| s.as_str());
499
500 let s = ident.name.to_type_name();
501
502 if s.ends_with(postfix) {
503 ident.name.clone()
504 } else {
505 Name::new_generated(format!("{s}{postfix}"))
506 }
507}
508
509fn format_module(types: &MetaTypes, ns: Option<NamespaceId>) -> Result<Option<Ident2>, Error> {
510 let Some(ns) = ns else {
511 return Ok(None);
512 };
513
514 let module = types.modules.get(&ns).ok_or(Error::UnknownNamespace(ns))?;
515 let Some(name) = &module.name else {
516 return Ok(None);
517 };
518
519 Ok(Some(format_module_ident(name)))
520}