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 proc_macro2::TokenStream;
31use quote::{format_ident, quote};
32
33use crate::config::{DynTypeTraits, RendererFlags};
34use crate::models::{
35    code::{IdentPath, Module},
36    data::{DataTypeVariant, DataTypes, Occurs, PathData},
37    meta::ModuleMeta,
38};
39
40pub use self::context::{Context, ValueKey, Values};
41pub use self::error::Error;
42pub use self::meta::MetaData;
43pub use self::steps::{
44    DefaultsRenderStep, NamespaceConstantsRenderStep, NamespaceSerialization,
45    PrefixConstantsRenderStep, QuickXmlDeserializeRenderStep, QuickXmlSerializeRenderStep,
46    SerdeQuickXmlTypesRenderStep, SerdeXmlRsV7TypesRenderStep, SerdeXmlRsV8TypesRenderStep,
47    TypesRenderStep, WithNamespaceTraitRenderStep,
48};
49
50/// The [`Renderer`] is the central orchestrator for Rust code generation from
51/// resolved schema types.
52///
53/// It allows the user to define a rendering pipeline using modular [`RenderStep`]s.
54/// Each step contributes part of the code output - such as type definitions,
55/// trait impls, constants, or serialization logic.
56///
57/// The [`Renderer`] holds configuration, shared metadata, and controls the execution
58/// of rendering steps over the input [`DataTypes`].
59///
60/// You can chain configuration methods to adjust derive traits, dynamic trait
61/// injection, and serialization support, and then call [`finish`](Renderer::finish)
62/// to produce a [`Module`] ready for rendering as Rust source.
63#[must_use]
64#[derive(Debug)]
65pub struct Renderer<'types> {
66    meta: MetaData<'types>,
67    steps: Vec<Box<dyn RenderStep + 'types>>,
68    dyn_type_traits: DynTypeTraits,
69}
70
71impl<'types> Renderer<'types> {
72    /// Create a new [`Renderer`] instance from the passed `types`.
73    pub fn new(types: &'types DataTypes<'types>) -> Self {
74        let meta = MetaData {
75            types,
76            flags: RendererFlags::empty(),
77            derive: vec![IdentPath::from_ident(format_ident!("Debug"))],
78            dyn_type_traits: Vec::new(),
79            alloc_crate: format_ident!("std"),
80            xsd_parser_types: format_ident!("xsd_parser_types"),
81        };
82
83        Self {
84            meta,
85            steps: Vec::new(),
86            dyn_type_traits: DynTypeTraits::Auto,
87        }
88    }
89
90    /// Add a [`RenderStep`] to the renderer.
91    pub fn with_step<X>(self, step: X) -> Self
92    where
93        X: RenderStep + 'types,
94    {
95        self.with_step_boxed(Box::new(step))
96    }
97
98    /// Add an already boxed [`RenderStep`] to the renderer.
99    pub fn with_step_boxed(mut self, step: Box<dyn RenderStep + 'types>) -> Self {
100        self.steps.push(step);
101
102        self
103    }
104
105    /// Add the default renderers to the generator.
106    pub fn with_default_steps(self) -> Self {
107        self.with_step(TypesRenderStep)
108    }
109
110    /// Remove all [`Renderer`]s from the generator.
111    pub fn clear_steps(mut self) -> Self {
112        self.steps.clear();
113
114        self
115    }
116
117    /// Set the [`RendererFlags`] flags the renderer should use for rendering the code.
118    pub fn flags(mut self, value: RendererFlags) -> Self {
119        self.meta.flags = value;
120
121        self
122    }
123
124    /// Set the traits the generated types should derive from.
125    ///
126    /// Default is `Debug`.
127    ///
128    /// If you want to set the derive for a single value, please have a look to
129    /// [`DataType::derive`](crate::models::data::DataType::derive).
130    ///
131    /// # Examples
132    ///
133    /// ```ignore
134    /// let renderer = Renderer::new(types)
135    ///     .derive(["Debug", "Clone", "Eq", "PartialEq", "Ord", "PartialOrd"]);
136    /// ```
137    pub fn derive<I>(mut self, value: I) -> Self
138    where
139        I: IntoIterator,
140        I::Item: Display,
141    {
142        self.meta.derive = value
143            .into_iter()
144            .map(|x| IdentPath::from_str(&x.to_string()).expect("Invalid identifier path"))
145            .collect();
146
147        self
148    }
149
150    /// Set the traits that should be implemented by dynamic types.
151    ///
152    /// The passed values must be valid type paths.
153    ///
154    /// # Errors
155    ///
156    /// Will raise a [`InvalidIdentifier`](Error::InvalidIdentifier) error
157    /// if the passed strings does not represent a valid identifier.
158    ///
159    /// # Examples
160    ///
161    /// ```ignore
162    /// let generator = Generator::new(types)
163    ///     .dyn_type_traits(["::core::fmt::Debug", "::core::any::Any"]);
164    /// ```
165    pub fn dyn_type_traits<I>(mut self, value: I) -> Result<Self, Error>
166    where
167        I: IntoIterator,
168        I::Item: AsRef<str>,
169    {
170        let traits = value
171            .into_iter()
172            .map(|x| {
173                let s = x.as_ref();
174                IdentPath::from_str(s)
175            })
176            .collect::<Result<Vec<_>, _>>()?;
177
178        self.dyn_type_traits = DynTypeTraits::Custom(traits);
179
180        Ok(self)
181    }
182
183    /// Set the name of the `alloc` create that the generator should use for
184    /// generating the code.
185    ///
186    /// This is useful if the `alloc` create can not be resolved by the default
187    /// name in your environment. You can just set a name that suites your needs.
188    ///
189    /// By default `std` is used as `alloc` crate.
190    pub fn alloc_crate<S: Display>(mut self, value: S) -> Self {
191        self.meta.alloc_crate = format_ident!("{value}");
192
193        self
194    }
195
196    /// Set the name of the `xsd-parser-types` create that the generator should use for
197    /// generating the code.
198    ///
199    /// This is useful if the `xsd-parser-types` create can not be resolved by the default
200    /// name in your environment. You can just set a name that suites your needs.
201    pub fn xsd_parser_types<S: Display>(mut self, value: S) -> Self {
202        self.meta.xsd_parser_types = format_ident!("{value}");
203
204        self
205    }
206
207    /// Finish the rendering process and return the resulting [`Module`].
208    #[must_use]
209    pub fn finish(self) -> Module {
210        let mut module = Module::default();
211        let Self {
212            mut meta,
213            mut steps,
214            dyn_type_traits,
215        } = self;
216
217        meta.dyn_type_traits = match dyn_type_traits {
218            DynTypeTraits::Auto => {
219                let traits = meta.derive.iter().map(|x| match x.to_string().as_ref() {
220                    "Debug" => IdentPath::from_str("::core::fmt::Debug").unwrap(),
221                    "Hash" => IdentPath::from_str("::core::hash::Hash").unwrap(),
222                    _ => x.clone(),
223                });
224
225                let as_any =
226                    IdentPath::from_parts([meta.xsd_parser_types.clone()], format_ident!("AsAny"));
227
228                traits.chain(Some(as_any)).collect()
229            }
230            DynTypeTraits::Custom(x) => x,
231        };
232
233        for step in &mut steps {
234            step.initialize(&mut meta);
235        }
236
237        for (ident, data) in &meta.types.items {
238            let mut ctx = Context::new(&meta, data, ident, &mut module);
239
240            for step in &mut steps {
241                step.render_type(&mut ctx);
242            }
243        }
244
245        for step in &mut steps {
246            step.finish(&meta, &mut module);
247        }
248
249        module
250    }
251}
252
253/// Trait that is used to define a renderer.
254///
255/// A render step is used to generate the actual code of a specific
256/// [`DataType`](crate::models::data::DataType).
257/// The idea is that different render steps generate different code. This can be
258/// used by the user to compose different render steps depending on his needs, or
259/// he could even implement customized steps on his own.
260pub trait RenderStep: Debug {
261    /// Returns the type of the render step.
262    fn render_step_type(&self) -> RenderStepType;
263
264    /// Initialized the renderer.
265    ///
266    /// This is called once for each renderer when the generator is initialized.
267    fn initialize(&mut self, meta: &mut MetaData<'_>) {
268        let _meta = meta;
269    }
270
271    /// Renders the code for the given type.
272    ///
273    /// This is called once for each type that needs to be rendered.
274    fn render_type(&mut self, ctx: &mut Context<'_, '_>) {
275        let _cxt = ctx;
276    }
277
278    /// Finishes the rendering process.
279    ///
280    /// This is called once for each renderer after the actual rendering of the
281    /// types is finished and the code generation process is being finished.
282    fn finish(&mut self, meta: &MetaData<'_>, module: &mut Module) {
283        let _meta = meta;
284        let _module = module;
285    }
286}
287
288/// Defines the type of the render step.
289///
290/// This defines the type of a render steps and is used to manage mutually
291/// exclusive render steps. For example the renderer pipeline should only
292/// contain one single render step of type [`Types`](RenderStepType::Types).
293#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
294pub enum RenderStepType {
295    /// Render step that renders the actual types defined in the schema.
296    /// This type of render step should only exists once in the whole renderer pipeline.
297    Types,
298
299    /// Render step that renders additional type definitions they are not conflicting
300    /// with the actual types defined in the schema.
301    ExtraTypes,
302
303    /// Render step that renders additional implementation blocks or trait implementations
304    /// for types defined in a different render step.
305    ExtraImpls,
306
307    /// The type of this render step is undefined.
308    /// If you are implementing a custom render step and you are not sure what type
309    /// to use, then use this one.
310    #[default]
311    Undefined,
312}
313
314impl RenderStepType {
315    /// Returns `true` if the two types are mutual exclusive to each other, `false` otherwise.
316    #[must_use]
317    pub fn is_mutual_exclusive_to(&self, other: Self) -> bool {
318        matches!((self, other), (Self::Types, Self::Types))
319    }
320}
321
322impl ModuleMeta {
323    pub(super) fn make_ns_const(&self) -> PathData {
324        let name = self.name().map_or_else(
325            || format!("UNNAMED_{}", self.namespace_id.0),
326            |name| name.as_str().to_screaming_snake_case(),
327        );
328        let ident = format_ident!("NS_{name}");
329        let path = IdentPath::from_parts([], ident);
330
331        PathData::from_path(path)
332    }
333
334    pub(super) fn make_prefix_const(&self) -> Option<PathData> {
335        let name = self.name()?;
336        let name = name.as_str().to_screaming_snake_case();
337        let ident = format_ident!("PREFIX_{name}");
338        let path = IdentPath::from_parts([], ident);
339
340        Some(PathData::from_path(path))
341    }
342}
343
344impl Occurs {
345    /// Wrapped the passed type `ident` into a suitable rust type depending on
346    /// the occurrence and the need of indirection (boxing).
347    ///
348    /// # Examples
349    /// - `Occurs::Single` will return the type as is, or as `Box<T>`
350    /// - `Occurs::Optional` will return the type as `Option<T>`
351    /// - `Occurs::DynamicList` will return the type as `Vec<T>`
352    /// - `Occurs::StaticList` will return the type as array `[T; SIZE]`
353    #[must_use]
354    pub fn make_type(
355        self,
356        ctx: &Context<'_, '_>,
357        ident: &TokenStream,
358        need_indirection: bool,
359    ) -> Option<TokenStream> {
360        match self {
361            Self::None => None,
362            Self::Single if need_indirection => {
363                let box_ = ctx.resolve_build_in("::alloc::boxed::Box");
364
365                Some(quote! { #box_<#ident> })
366            }
367            Self::Single => Some(quote! { #ident }),
368            Self::Optional if need_indirection => {
369                let box_ = ctx.resolve_build_in("::alloc::boxed::Box");
370                let option = ctx.resolve_build_in("::core::option::Option");
371
372                Some(quote! { #option<#box_<#ident>> })
373            }
374            Self::Optional => {
375                let option = ctx.resolve_build_in("::core::option::Option");
376
377                Some(quote! { #option<#ident> })
378            }
379            Self::DynamicList => {
380                let vec = ctx.resolve_build_in("::alloc::vec::Vec");
381
382                Some(quote! { #vec<#ident> })
383            }
384            Self::StaticList(sz) if need_indirection => {
385                let box_ = ctx.resolve_build_in("::alloc::boxed::Box");
386
387                Some(quote! { [#box_<#ident>; #sz] })
388            }
389            Self::StaticList(sz) => Some(quote! { [#ident; #sz] }),
390        }
391    }
392}