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}