Skip to main content

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}