xsd_parser/pipeline/renderer/
mod.rs

1//! Code rendering infrastructure for Rust type generation.
2//!
3//! This module defines the [`Renderer`] and supporting components responsible
4//! for converting fully resolved [`DataTypes`] into structured Rust code modules.
5//! It provides a flexible, composable rendering pipeline through the
6//! [`RenderStep`] trait, allowing each rendering step to be added, removed, or
7//! customized as needed.
8//!
9//! The [`Renderer`] can be extended with custom [`RenderStep`] implementations
10//! or modified using configuration methods such as [`flags()`](Renderer::flags),
11//! [`derive()`](Renderer::derive), and [`dyn_type_traits()`](Renderer::dyn_type_traits).
12//!
13//! Example usage:
14//! ```rust,ignore
15//! let module = Renderer::new(&types)
16//!     .with_default_steps()
17//!     .derive(["Debug", "Clone"])
18//!     .finish();
19//! ```
20
21mod context;
22mod error;
23mod meta;
24mod steps;
25
26use std::fmt::{Debug, Display};
27use std::str::FromStr;
28
29use inflector::Inflector;
30use quote::format_ident;
31
32use crate::config::{DynTypeTraits, RendererFlags};
33use crate::models::{
34    code::{IdentPath, Module},
35    data::{DataTypeVariant, DataTypes, PathData},
36    meta::ModuleMeta,
37};
38
39pub use self::context::Context;
40pub use self::error::Error;
41pub use self::meta::MetaData;
42pub use self::steps::{
43    DefaultsRenderStep, NamespaceConstantsRenderStep, QuickXmlDeserializeRenderStep,
44    QuickXmlSerializeRenderStep, SerdeQuickXmlTypesRenderStep, SerdeXmlRsV7TypesRenderStep,
45    SerdeXmlRsV8TypesRenderStep, TypesRenderStep, WithNamespaceTraitRenderStep,
46};
47
48/// The [`Renderer`] is the central orchestrator for Rust code generation from
49/// resolved schema types.
50///
51/// It allows the user to define a rendering pipeline using modular [`RenderStep`]s.
52/// Each step contributes part of the code output - such as type definitions,
53/// trait impls, constants, or serialization logic.
54///
55/// The [`Renderer`] holds configuration, shared metadata, and controls the execution
56/// of rendering steps over the input [`DataTypes`].
57///
58/// You can chain configuration methods to adjust derive traits, dynamic trait
59/// injection, and serialization support, and then call [`finish`](Renderer::finish)
60/// to produce a [`Module`] ready for rendering as Rust source.
61#[must_use]
62#[derive(Debug)]
63pub struct Renderer<'types> {
64    meta: MetaData<'types>,
65    steps: Vec<Box<dyn RenderStep + 'types>>,
66    dyn_type_traits: DynTypeTraits,
67}
68
69impl<'types> Renderer<'types> {
70    /// Create a new [`Renderer`] instance from the passed `types`.
71    pub fn new(types: &'types DataTypes<'types>) -> Self {
72        let meta = MetaData {
73            types,
74            flags: RendererFlags::empty(),
75            derive: vec![format_ident!("Debug")],
76            dyn_type_traits: Vec::new(),
77            xsd_parser_crate: format_ident!("xsd_parser"),
78        };
79
80        Self {
81            meta,
82            steps: Vec::new(),
83            dyn_type_traits: DynTypeTraits::Auto,
84        }
85    }
86
87    /// Add a [`RenderStep`] to the renderer.
88    pub fn with_step<X>(self, step: X) -> Self
89    where
90        X: RenderStep + 'types,
91    {
92        self.with_step_boxed(Box::new(step))
93    }
94
95    /// Add an already boxed [`RenderStep`] to the renderer.
96    pub fn with_step_boxed(mut self, step: Box<dyn RenderStep + 'types>) -> Self {
97        self.steps.push(step);
98
99        self
100    }
101
102    /// Add the default renderers to the generator.
103    pub fn with_default_steps(self) -> Self {
104        self.with_step(TypesRenderStep)
105    }
106
107    /// Remove all [`Renderer`]s from the generator.
108    pub fn clear_steps(mut self) -> Self {
109        self.steps.clear();
110
111        self
112    }
113
114    /// Set the [`RendererFlags`] flags the renderer should use for rendering the code.
115    pub fn flags(mut self, value: RendererFlags) -> Self {
116        self.meta.flags = value;
117
118        self
119    }
120
121    /// Set the traits the generated types should derive from.
122    ///
123    /// Default is `Debug`.
124    ///
125    /// If you want to set the derive for a single value, please have a look to
126    /// [`DataType::derive`](crate::models::data::DataType::derive).
127    ///
128    /// # Examples
129    ///
130    /// ```ignore
131    /// let renderer = Renderer::new(types)
132    ///     .derive(["Debug", "Clone", "Eq", "PartialEq", "Ord", "PartialOrd"]);
133    /// ```
134    pub fn derive<I>(mut self, value: I) -> Self
135    where
136        I: IntoIterator,
137        I::Item: Display,
138    {
139        self.meta.derive = value.into_iter().map(|x| format_ident!("{x}")).collect();
140
141        self
142    }
143
144    /// Set the traits that should be implemented by dynamic types.
145    ///
146    /// The passed values must be valid type paths.
147    ///
148    /// # Errors
149    ///
150    /// Will raise a [`InvalidIdentifier`](Error::InvalidIdentifier) error
151    /// if the passed strings does not represent a valid identifier.
152    ///
153    /// # Examples
154    ///
155    /// ```ignore
156    /// let generator = Generator::new(types)
157    ///     .dyn_type_traits(["core::fmt::Debug", "core::any::Any"]);
158    /// ```
159    pub fn dyn_type_traits<I>(mut self, value: I) -> Result<Self, Error>
160    where
161        I: IntoIterator,
162        I::Item: AsRef<str>,
163    {
164        let traits = value
165            .into_iter()
166            .map(|x| {
167                let s = x.as_ref();
168                IdentPath::from_str(s)
169            })
170            .collect::<Result<Vec<_>, _>>()?;
171
172        self.dyn_type_traits = DynTypeTraits::Custom(traits);
173
174        Ok(self)
175    }
176
177    /// Set the name of the `xsd-parser` create that the generator should use for
178    /// generating the code.
179    ///
180    /// This is useful if the `xsd-parser` create can not be resolved by the default
181    /// name in your environment. You can just set a name that suites your needs.
182    pub fn xsd_parser_crate<S: Display>(mut self, value: S) -> Self {
183        self.meta.xsd_parser_crate = format_ident!("{value}");
184
185        self
186    }
187
188    /// Finish the rendering process and return the resulting [`Module`].
189    #[must_use]
190    pub fn finish(self) -> Module {
191        let mut module = Module::default();
192        let Self {
193            mut meta,
194            mut steps,
195            dyn_type_traits,
196        } = self;
197
198        meta.dyn_type_traits = match dyn_type_traits {
199            DynTypeTraits::Auto => {
200                let traits = meta.derive.iter().map(|x| match x.to_string().as_ref() {
201                    "Debug" => IdentPath::from_str("core::fmt::Debug").unwrap(),
202                    "Hash" => IdentPath::from_str("core::hash::Hash").unwrap(),
203                    _ => IdentPath::from_ident(x.clone()),
204                });
205
206                let as_any = IdentPath::from_parts(
207                    Some(meta.xsd_parser_crate.clone()),
208                    format_ident!("AsAny"),
209                );
210
211                traits.chain(Some(as_any)).collect()
212            }
213            DynTypeTraits::Custom(x) => x,
214        };
215
216        for step in &mut steps {
217            step.initialize(&mut meta);
218        }
219
220        for (ident, data) in &meta.types.items {
221            let mut ctx = Context::new(&meta, data, ident, &mut module);
222
223            for step in &mut steps {
224                step.render_type(&mut ctx);
225            }
226        }
227
228        for step in &mut steps {
229            step.finish(&meta, &mut module);
230        }
231
232        module
233    }
234}
235
236/// Trait that is used to define a renderer.
237///
238/// A render step is used to generate the actual code of a specific
239/// [`DataType`](crate::models::data::DataType).
240/// The idea is that different render steps generate different code. This can be
241/// used by the user to compose different render steps depending on his needs, or
242/// he could even implement customized steps on his own.
243pub trait RenderStep: Debug {
244    /// Returns the type of the render step.
245    fn render_step_type(&self) -> RenderStepType;
246
247    /// Initialized the renderer.
248    ///
249    /// This is called once for each renderer when the generator is initialized.
250    fn initialize(&mut self, meta: &mut MetaData<'_>) {
251        let _meta = meta;
252    }
253
254    /// Renders the code for the given type.
255    ///
256    /// This is called once for each type that needs to be rendered.
257    fn render_type(&mut self, ctx: &mut Context<'_, '_>) {
258        let _cxt = ctx;
259    }
260
261    /// Finishes the rendering process.
262    ///
263    /// This is called once for each renderer after the actual rendering of the
264    /// types is finished and the code generation process is being finished.
265    fn finish(&mut self, meta: &MetaData<'_>, module: &mut Module) {
266        let _meta = meta;
267        let _module = module;
268    }
269}
270
271/// Defines the type of the render step.
272///
273/// This defines the type of a render steps and is used to manage mutually
274/// exclusive render steps. For example the renderer pipeline should only
275/// contain one single render step of type [`Types`](RenderStepType::Types).
276#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
277pub enum RenderStepType {
278    /// Render step that renders the actual types defined in the schema.
279    /// This type of render step should only exists once in the whole renderer pipeline.
280    Types,
281
282    /// Render step that renders additional type definitions they are not conflicting
283    /// with the actual types defined in the schema.
284    ExtraTypes,
285
286    /// Render step that renders additional implementation blocks or trait implementations
287    /// for types defined in a different render step.
288    ExtraImpls,
289
290    /// The type of this render step is undefined.
291    /// If you are implementing a custom render step and you are not sure what type
292    /// to use, then use this one.
293    #[default]
294    Undefined,
295}
296
297impl RenderStepType {
298    /// Returns `true` if the two types are mutual exclusive to each other, `false` otherwise.
299    #[must_use]
300    pub fn is_mutual_exclusive_to(&self, other: Self) -> bool {
301        matches!((self, other), (Self::Types, Self::Types))
302    }
303}
304
305impl ModuleMeta {
306    pub(super) fn make_ns_const(&self) -> PathData {
307        let ident = format_ident!(
308            "NS_{}",
309            self.name.as_ref().or(self.prefix.as_ref()).map_or_else(
310                || String::from("DEFAULT"),
311                |name| name.as_str().to_screaming_snake_case()
312            )
313        );
314
315        let path = IdentPath::from_parts([], ident);
316
317        PathData::from_path(path)
318    }
319}