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