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 custom;
25mod data;
26mod error;
27mod meta;
28mod state;
29
30use std::collections::btree_map::{Entry, VacantEntry};
31use std::collections::{BTreeMap, VecDeque};
32use std::str::FromStr;
33
34use quote::format_ident;
35use tracing::instrument;
36
37use crate::config::{BoxFlags, GeneratorFlags, TypedefMode};
38use crate::models::{
39 code::{IdentPath, ModuleIdent, ModulePath},
40 data::{DataType, DataTypes, PathData},
41 meta::{MetaTypeVariant, MetaTypes},
42 IdentType, TypeIdent,
43};
44use crate::traits::Naming;
45
46pub use self::context::Context;
47pub use self::custom::{ValueGenerator, ValueGeneratorBox, ValueGeneratorMode};
48pub use self::error::Error;
49pub use self::meta::MetaData;
50
51use self::state::{LoopDetection, PendingType, State, TraitInfos, TypeRef};
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 String::from("NotNil"), // NillableContent = 8
106 String::from("Dyn"), // DynamicElement = 9
107 ],
108 box_flags: BoxFlags::AUTO,
109 typedef_mode: TypedefMode::Auto,
110 text_type: IdentPath::from_str("::xsd_parser_types::xml::Text").unwrap(),
111 mixed_type: IdentPath::from_str("::xsd_parser_types::xml::Mixed").unwrap(),
112 nillable_type: IdentPath::from_str("::xsd_parser_types::xml::Nillable").unwrap(),
113 any_type: IdentPath::from_str("::xsd_parser_types::xml::AnyElement").unwrap(),
114 any_attributes_type: IdentPath::from_str("::xsd_parser_types::xml::AnyAttributes")
115 .unwrap(),
116 };
117 let state = State {
118 cache: BTreeMap::new(),
119 pending: VecDeque::new(),
120 trait_infos: None,
121 loop_detection: LoopDetection::default(),
122 };
123
124 Self { meta, state }
125 }
126
127 /// Set the [`BoxFlags`] flags the generator should use for generating the code.
128 pub fn box_flags(mut self, value: BoxFlags) -> Self {
129 self.meta.box_flags = value;
130
131 self
132 }
133
134 /// Set the [`TypedefMode`] value the generator should use for generating the code.
135 pub fn typedef_mode(mut self, value: TypedefMode) -> Self {
136 self.meta.typedef_mode = value;
137
138 self
139 }
140
141 /// Set the [`GeneratorFlags`] flags the generator should use for generating the code.
142 pub fn flags(mut self, value: GeneratorFlags) -> Self {
143 self.meta.flags = value;
144
145 self
146 }
147
148 /// Set the type to use to store unformatted text.
149 ///
150 /// # Errors
151 ///
152 /// Forwards the error that is thrown, if `path` could not be converted.
153 pub fn text_type<P>(mut self, path: P) -> Result<Self, P::Error>
154 where
155 P: TryInto<IdentPath>,
156 {
157 self.meta.text_type = path.try_into()?;
158
159 Ok(self)
160 }
161
162 /// Set the type to use to store mixed types.
163 ///
164 /// # Errors
165 ///
166 /// Forwards the error that is thrown, if `path` could not be converted.
167 pub fn mixed_type<P>(mut self, path: P) -> Result<Self, P::Error>
168 where
169 P: TryInto<IdentPath>,
170 {
171 self.meta.mixed_type = path.try_into()?;
172
173 Ok(self)
174 }
175
176 /// Set the type to use to store nillable types.
177 ///
178 /// # Errors
179 ///
180 /// Forwards the error that is thrown, if `path` could not be converted.
181 pub fn nillable_type<P>(mut self, path: P) -> Result<Self, P::Error>
182 where
183 P: TryInto<IdentPath>,
184 {
185 self.meta.nillable_type = path.try_into()?;
186
187 Ok(self)
188 }
189
190 /// Set the type to use to store unstructured `xs:any` elements.
191 ///
192 /// # Errors
193 ///
194 /// Forwards the error that is thrown, if `path` could not be converted.
195 pub fn any_type<P>(mut self, path: P) -> Result<Self, P::Error>
196 where
197 P: TryInto<IdentPath>,
198 {
199 self.meta.any_type = path.try_into()?;
200
201 Ok(self)
202 }
203
204 /// Set the type to use to store unstructured `xs:anyAttribute` attributes.
205 ///
206 /// # Errors
207 ///
208 /// Forwards the error that is thrown, if `path` could not be converted.
209 pub fn any_attributes_type<P>(mut self, path: P) -> Result<Self, P::Error>
210 where
211 P: TryInto<IdentPath>,
212 {
213 self.meta.any_attributes_type = path.try_into()?;
214
215 Ok(self)
216 }
217
218 /// Add the passed [`GeneratorFlags`] flags the generator should use for generating the code.
219 pub fn with_flags(mut self, value: GeneratorFlags) -> Self {
220 self.meta.flags |= value;
221
222 self
223 }
224
225 /// Set the postfixes the generator should use for the different types.
226 ///
227 /// Default is `"Type"` for the [`IdentType::Type`] type and `""` for the other types.
228 pub fn with_type_postfix<S: Into<String>>(mut self, type_: IdentType, postfix: S) -> Self {
229 self.meta.postfixes[type_ as usize] = postfix.into();
230
231 self
232 }
233
234 /// Add a custom implemented type to the generator.
235 ///
236 /// This will add a custom implemented type to the generator. These types are
237 /// usually implemented and provided by the user of the generated code. The
238 /// generator will just reference to the type definition and will not generate
239 /// any code related to this type.
240 ///
241 /// # Errors
242 ///
243 /// Returns an error if the namespace of the passed identifier is unknown.
244 ///
245 /// # Examples
246 ///
247 /// ```ignore
248 /// let generator = Generator::new(types)
249 /// .with_type(TypeIdent::type_("UserDefinedType"));
250 /// ```
251 pub fn with_type(mut self, ident: TypeIdent) -> Result<Self, Error> {
252 let module_ident = self
253 .meta
254 .types
255 .naming
256 .format_module(self.meta.types, Some(ident.ns));
257 let type_ident = format_ident!("{}", ident.name.to_string());
258 let path = PathData::from_path(IdentPath::from_parts(module_ident, type_ident));
259
260 let id = self.state.loop_detection.next_id(ident.clone());
261 let type_ref = TypeRef::new_fixed(id, path);
262
263 self.state.cache.insert(ident, type_ref);
264
265 Ok(self)
266 }
267
268 /// Will fix the generator by call [`into_fixed`](Self::into_fixed) and then
269 /// [`generate_type`](GeneratorFixed::generate_type).
270 ///
271 /// # Errors
272 ///
273 /// Raises an [`Error`] if the type generation failed.
274 #[instrument(err, level = "trace", skip(self))]
275 pub fn generate_type(self, ident: TypeIdent) -> Result<GeneratorFixed<'types>, Error> {
276 self.into_fixed().generate_type(ident)
277 }
278
279 /// Will fix the generator by call [`into_fixed`](Self::into_fixed) and then
280 /// [`generate_named_types`](GeneratorFixed::generate_named_types).
281 ///
282 /// # Errors
283 ///
284 /// Will just forward the errors from [`generate_named_types`](GeneratorFixed::generate_named_types).
285 #[instrument(err, level = "trace", skip(self))]
286 pub fn generate_named_types(self) -> Result<GeneratorFixed<'types>, Error> {
287 self.into_fixed().generate_named_types()
288 }
289
290 /// Will fix the generator by call [`into_fixed`](Self::into_fixed) and then
291 /// [`generate_all_types`](GeneratorFixed::generate_all_types).
292 ///
293 /// # Errors
294 ///
295 /// Will just forward the errors from [`generate_all_types`](GeneratorFixed::generate_all_types).
296 pub fn generate_all_types(self) -> Result<GeneratorFixed<'types>, Error> {
297 self.into_fixed().generate_all_types()
298 }
299
300 /// Will convert the generator into a [`GeneratorFixed`].
301 ///
302 /// You need to call this method if the general configuration of the generator
303 /// is finished. The resulting [`GeneratorFixed`] type will only provide methods
304 /// to generate data types for specific types. The underlying configuration can
305 /// not be changed anymore.
306 pub fn into_fixed(self) -> GeneratorFixed<'types> {
307 let Self { meta, state } = self;
308
309 let data_types = DataTypes::new(meta);
310
311 GeneratorFixed { state, data_types }
312 }
313}
314
315impl<'types> GeneratorFixed<'types> {
316 /// Generate the code for the given type.
317 ///
318 /// This will generate the code for the passed type identifier and all
319 /// dependencies of this type.
320 ///
321 /// # Errors
322 ///
323 /// Raises an [`Error`] if the type generation failed.
324 ///
325 /// # Examples
326 ///
327 /// ```ignore
328 /// let generator = Generator::new(types)
329 /// .generate_type(Ident::type_("Root"));
330 /// ```
331 #[instrument(err, level = "trace", skip(self))]
332 pub fn generate_type(mut self, ident: TypeIdent) -> Result<GeneratorFixed<'types>, Error> {
333 self.state
334 .get_or_create_type_ref_mut(&self.data_types.meta, &ident)?;
335 self.generate_pending()?;
336
337 Ok(self)
338 }
339
340 /// Generate the code for all types.
341 ///
342 /// This will generate the code for all types that are specified in
343 /// the [`MetaTypes`] object passed to the generator.
344 ///
345 /// # Errors
346 ///
347 /// Raises an [`Error`] if the type generation failed.
348 ///
349 /// # Examples
350 ///
351 /// ```ignore
352 /// let generator = Generator::new(types)
353 /// .generate_all_types();
354 /// ```
355 #[instrument(err, level = "trace", skip(self))]
356 pub fn generate_all_types(mut self) -> Result<Self, Error> {
357 for ident in self.data_types.meta.types.items.keys() {
358 self.state
359 .get_or_create_type_ref_mut(&self.data_types.meta, ident)?;
360 }
361 self.generate_pending()?;
362
363 Ok(self)
364 }
365
366 /// Generate the code for all named types.
367 ///
368 /// This will generate the code for all types with an explicit name and all
369 /// dependencies of these types that are specified in the [`MetaTypes`] object
370 /// passed to the generator.
371 ///
372 /// # Errors
373 ///
374 /// Raises an [`Error`] if the type generation failed.
375 ///
376 /// # Examples
377 ///
378 /// ```ignore
379 /// let generator = Generator::new(types)
380 /// .generate_named_types();
381 /// ```
382 #[instrument(err, level = "trace", skip(self))]
383 pub fn generate_named_types(mut self) -> Result<Self, Error> {
384 for ident in self.data_types.meta.types.items.keys() {
385 if ident.name.is_named() {
386 self.state
387 .get_or_create_type_ref_mut(&self.data_types.meta, ident)?;
388 }
389 }
390 self.generate_pending()?;
391
392 Ok(self)
393 }
394
395 /// Finish the code generation.
396 ///
397 /// This will return the generated data types as [`DataTypes`].
398 #[instrument(level = "trace", skip(self))]
399 pub fn finish(self) -> DataTypes<'types> {
400 self.data_types
401 }
402
403 #[instrument(err, level = "trace", skip(self))]
404 fn generate_pending(&mut self) -> Result<(), Error> {
405 while let Some(args) = self.state.pending.pop_front() {
406 self.generate_type_intern(args)?;
407 }
408
409 Ok(())
410 }
411
412 #[instrument(err, level = "trace", skip(self))]
413 fn generate_type_intern(&mut self, data: PendingType<'types>) -> Result<(), Error> {
414 let PendingType { ty, ident } = data;
415 let Self { state, data_types } = self;
416
417 let mut context = Context::new(&data_types.meta, &ident, state);
418 let ty = DataType::new(ty, &mut context)?;
419
420 data_types.items.insert(ident, ty);
421
422 Ok(())
423 }
424}
425
426impl<'types> State<'types> {
427 #[instrument(level = "trace", skip(self, meta))]
428 fn get_or_create_type_ref_mut(
429 &mut self,
430 meta: &MetaData<'types>,
431 ident: &TypeIdent,
432 ) -> Result<&mut TypeRef, Error> {
433 match self.cache.entry(ident.clone()) {
434 Entry::Occupied(e) => Ok(e.into_mut()),
435 Entry::Vacant(e) => {
436 let id = self.loop_detection.next_id(e.key().clone());
437
438 Self::create_type_ref(id, &*meta.naming, &mut self.pending, e, meta, ident)
439 }
440 }
441 }
442
443 fn create_type_ref<'a>(
444 id: usize,
445 naming: &dyn Naming,
446 pending: &mut VecDeque<PendingType<'types>>,
447 entry: VacantEntry<'a, TypeIdent, TypeRef>,
448 meta: &MetaData<'types>,
449 ident: &TypeIdent,
450 ) -> Result<&'a mut TypeRef, Error> {
451 let ty = meta
452 .types
453 .items
454 .get(ident)
455 .ok_or_else(|| Error::UnknownType(ident.clone()))?;
456 let name = naming.make_type_name(&meta.postfixes, ty, ident);
457 let path = match &ty.variant {
458 MetaTypeVariant::BuildIn(x) => {
459 let path = if meta.check_generator_flags(GeneratorFlags::BUILD_IN_ABSOLUTE_PATHS) {
460 x.absolute_ident_path()
461 } else {
462 x.ident_path()
463 };
464
465 PathData::from_path(path)
466 }
467 MetaTypeVariant::Custom(x) => {
468 if let Some(using) = x.include() {
469 if meta.check_generator_flags(GeneratorFlags::ABSOLUTE_PATHS_INSTEAD_USINGS) {
470 let path = IdentPath::from_str(using)?;
471
472 PathData::from_path(path)
473 } else {
474 let path = IdentPath::from_ident(format_ident!("{}", x.name()));
475
476 PathData::from_path(path).with_using(using)
477 }
478 } else {
479 let path = IdentPath::from_ident(format_ident!("{}", x.name())).with_path(None);
480
481 PathData::from_path(path)
482 }
483 }
484 _ => {
485 let module_ident = ModuleIdent::new(
486 meta.types,
487 ident,
488 meta.check_generator_flags(GeneratorFlags::USE_NAMESPACE_MODULES),
489 meta.check_generator_flags(GeneratorFlags::USE_SCHEMA_MODULES),
490 );
491 let module_path = ModulePath::from_ident(meta.types, module_ident);
492 let type_ident = naming.format_type_ident(&name, ty.display_name.as_deref());
493
494 let path = IdentPath::from_parts(module_path.0, type_ident);
495
496 PathData::from_path(path)
497 }
498 };
499
500 tracing::debug!("Queue new type generation: {ident}");
501
502 pending.push_back(PendingType {
503 ty,
504 ident: ident.clone(),
505 });
506
507 Ok(entry.insert(TypeRef::new_pending(id, path)))
508 }
509}