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}