Skip to main content

xsd_parser/config/
renderer.rs

1use std::any::{Any, TypeId};
2use std::fmt::Debug;
3
4use bitflags::bitflags;
5
6use xsd_parser_types::misc::Namespace;
7
8use crate::models::code::IdentPath;
9use crate::pipeline::renderer::{
10    NamespaceSerialization, RenderStep as RenderStepTrait, RenderStepType,
11};
12
13/// Configuration for the actual code rendering.
14#[derive(Debug)]
15pub struct RendererConfig {
16    /// List of renderers to use for code rendering.
17    pub steps: Vec<Box<dyn RenderStepConfig>>,
18
19    /// Additional flags to control the renderer.
20    pub flags: RendererFlags,
21
22    /// Sets the traits the generated types should derive from.
23    ///
24    /// See [`derive`](crate::Renderer::derive) for more details.
25    pub derive: Option<Vec<String>>,
26
27    /// Name of the `alloc` crate that is used for the generated code.
28    pub alloc: String,
29
30    /// Set the traits that should be implemented by dynamic types.
31    ///
32    /// See [`dyn_type_traits`](crate::Renderer::dyn_type_traits) for more details.
33    pub dyn_type_traits: Option<Vec<String>>,
34
35    /// Name of the `xsd-parser-types` crate that is used for the generated code.
36    pub xsd_parser_types: String,
37}
38
39impl Default for RendererConfig {
40    fn default() -> Self {
41        Self {
42            steps: vec![Box::new(RenderStep::Types)],
43            flags: RendererFlags::empty(),
44            derive: None,
45            alloc: "std".into(),
46            dyn_type_traits: None,
47            xsd_parser_types: "xsd_parser_types".into(),
48        }
49    }
50}
51
52impl Clone for RendererConfig {
53    fn clone(&self) -> Self {
54        Self {
55            steps: self.steps.iter().map(|x| x.boxed_clone()).collect(),
56            flags: self.flags,
57            derive: self.derive.clone(),
58            alloc: self.alloc.clone(),
59            dyn_type_traits: self.dyn_type_traits.clone(),
60            xsd_parser_types: self.xsd_parser_types.clone(),
61        }
62    }
63}
64
65bitflags! {
66    /// Different flags that control what code the [`Renderer`](super::Renderer)
67    /// is generating.
68    #[derive(Debug, Clone, Copy)]
69    pub struct RendererFlags: u32 {
70        /// None of the features are enabled.
71        ///
72        /// # Examples
73        ///
74        /// Consider the following XML schema:
75        /// ```xml
76        #[doc = include_str!("../../tests/renderer/renderer_flags/schema.xsd")]
77        /// ```
78        ///
79        /// Setting none of the flags will result in the following code:
80        /// ```rust
81        #[doc = include_str!("../../tests/renderer/renderer_flags/expected/empty.rs")]
82        /// ```
83        const NONE = 0;
84
85        /// The renderer adds documentation to the rendered code if a
86        /// `xs:documentation` element was placed in the schema.
87        ///
88        /// # Examples
89        ///
90        /// Consider the following XML schema:
91        /// ```xml
92        #[doc = include_str!("../../tests/renderer/renderer_flags/schema_with_docs.xsd")]
93        /// ```
94        ///
95        /// Enable the `RENDER_DOCS` feature only will result in the following code:
96        /// ```rust
97        #[doc = include_str!("../../tests/renderer/renderer_flags/expected/render_docs.rs")]
98        /// ```
99        const RENDER_DOCS = Self::RENDER_TYPE_DOCS.bits()
100            | Self::RENDER_ELEMENT_DOCS.bits()
101            | Self::RENDER_ATTRIBUTE_DOCS.bits()
102            | Self::RENDER_VARIANT_DOCS.bits();
103
104        /// The renderer adds documentation to the rendered types if a
105        /// `xs:documentation` element was placed in the schema.
106        ///
107        /// See [`RENDER_DOCS`](Self::RENDER_DOCS) for details.
108        const RENDER_TYPE_DOCS = 1 << 0;
109
110        /// The renderer adds documentation to the rendered elements if a
111        /// `xs:documentation` element was placed in the schema.
112        ///
113        /// See [`RENDER_DOCS`](Self::RENDER_DOCS) for details.
114        const RENDER_ELEMENT_DOCS = 1 << 1;
115
116        /// The renderer adds documentation to the rendered attributes if a
117        /// `xs:documentation` element was placed in the schema.
118        ///
119        /// See [`RENDER_DOCS`](Self::RENDER_DOCS) for details.
120        const RENDER_ATTRIBUTE_DOCS = 1 << 2;
121
122        /// The renderer adds documentation to the rendered enum variants for
123        /// `xs:enumeration` types if a `xs:documentation` element was placed
124        /// in the schema.
125        ///
126        /// See [`RENDER_DOCS`](Self::RENDER_DOCS) for details.
127        const RENDER_VARIANT_DOCS = 1 << 3;
128    }
129}
130
131/// Configuration for the [`RenderSteps`](crate::pipeline::renderer::RenderStep)s
132/// the [`Renderer`](crate::Renderer) should use for rendering the code.
133///
134/// <div class="warning">
135/// *Caution*
136/// Some render steps are incompatible to each other (e.g. only
137/// one `TypesXXX` step should be used, because they render the general type
138/// structure). While other render steps depend on each other (e.g. `QuickXmlXXX`
139/// depends on `Types` and `NamespaceConstants`).
140/// </div>
141#[derive(Debug, Clone)]
142pub enum RenderStep {
143    /// Step to render the pure types.
144    Types,
145
146    /// Step to render the types with `serde-xml-rs` support.
147    TypesSerdeXmlRs {
148        /// Version of `serde-xml-rs` to render the code for.
149        version: SerdeXmlRsVersion,
150    },
151
152    /// Step to render the types with `quick_xml` serde support.
153    TypesSerdeQuickXml,
154
155    /// Renderer to render associated methods that return the default values
156    /// of the different fields of a struct.
157    Defaults,
158
159    /// Renderer to add constants for the namespaces to the generated code.
160    NamespaceConstants,
161
162    /// Renderer to add constants for each variant of an enumeration using its simple base type.
163    EnumConstants,
164
165    /// Renderer to add constants for the namespace prefixes to the generated code.
166    PrefixConstants,
167
168    /// Renderer that adds the [`WithNamespace`](xsd_parser_types::WithNamespace) trait to
169    /// the generated types.
170    WithNamespaceTrait,
171
172    /// Renderer that renders code for the `quick_xml` serializer of the
173    /// different types.
174    QuickXmlSerialize {
175        /// Whether to add namespaces to the root element during serialization or not.
176        namespaces: NamespaceSerialization,
177
178        /// Default namespace to use for the serialization.
179        default_namespace: Option<Namespace>,
180    },
181
182    /// Renderer that renders code for the `quick_xml` deserializer of the
183    /// different types.
184    QuickXmlDeserialize {
185        /// Whether to box the deserializer or not.
186        ///
187        /// For more details have a look at [`QuickXmlDeserializeRenderer::boxed_deserializer`](crate::pipeline::renderer::QuickXmlDeserializeRenderStep::boxed_deserializer).
188        boxed_deserializer: bool,
189    },
190
191    /// Renderer that renders the [`CollectNamespaces`](xsd_parser_types::quick_xml::CollectNamespaces)
192    /// trait for each generated type.
193    ///
194    /// This step is required when using [`NamespaceSerialization::Dynamic`] so
195    /// that the serializer can discover which XML namespaces are actually needed
196    /// at runtime before writing the root start element.
197    QuickXmlCollectNamespaces,
198}
199
200/// Helper trait to deal with custom render steps.
201pub trait RenderStepConfig: Debug + Any {
202    /// Returns a boxed clone of the current object.
203    fn boxed_clone(&self) -> Box<dyn RenderStepConfig>;
204
205    /// Creates the actual render step and returned it as a box.
206    fn into_render_step(self: Box<Self>) -> Box<dyn RenderStepTrait>;
207
208    /// Returns the type of this render step.
209    fn render_step_type(&self) -> RenderStepType {
210        RenderStepType::Undefined
211    }
212
213    /// Returns `true` if `self` is mutual exclusive to `other`, `false` otherwise.
214    fn is_mutual_exclusive_to(&self, other: &dyn RenderStepConfig) -> bool {
215        self.type_id() == other.type_id()
216            || self
217                .render_step_type()
218                .is_mutual_exclusive_to(other.render_step_type())
219    }
220}
221
222impl<X> RenderStepConfig for X
223where
224    X: RenderStepTrait + Clone + Any + 'static,
225{
226    fn render_step_type(&self) -> RenderStepType {
227        X::render_step_type(self)
228    }
229
230    fn boxed_clone(&self) -> Box<dyn RenderStepConfig> {
231        Box::new(self.clone())
232    }
233
234    fn into_render_step(self: Box<Self>) -> Box<dyn RenderStepTrait> {
235        self
236    }
237}
238
239impl RenderStepConfig for RenderStep {
240    fn render_step_type(&self) -> RenderStepType {
241        match self {
242            Self::Types => RenderStepType::Types,
243            Self::TypesSerdeXmlRs { .. } => RenderStepType::Types,
244            Self::TypesSerdeQuickXml => RenderStepType::Types,
245            Self::Defaults => RenderStepType::ExtraImpls,
246            Self::EnumConstants => RenderStepType::ExtraImpls,
247            Self::PrefixConstants => RenderStepType::ExtraImpls,
248            Self::NamespaceConstants => RenderStepType::ExtraImpls,
249            Self::WithNamespaceTrait => RenderStepType::ExtraImpls,
250            Self::QuickXmlSerialize { .. } => RenderStepType::ExtraImpls,
251            Self::QuickXmlDeserialize { .. } => RenderStepType::ExtraImpls,
252            Self::QuickXmlCollectNamespaces => RenderStepType::ExtraImpls,
253        }
254    }
255
256    fn boxed_clone(&self) -> Box<dyn RenderStepConfig> {
257        Box::new(self.clone())
258    }
259
260    fn into_render_step(self: Box<Self>) -> Box<dyn RenderStepTrait> {
261        use crate::pipeline::renderer::{
262            DefaultsRenderStep, EnumConstantsRenderStep, NamespaceConstantsRenderStep,
263            PrefixConstantsRenderStep, QuickXmlCollectNamespacesRenderStep,
264            QuickXmlDeserializeRenderStep, QuickXmlSerializeRenderStep,
265            SerdeQuickXmlTypesRenderStep, SerdeXmlRsV7TypesRenderStep, SerdeXmlRsV8TypesRenderStep,
266            TypesRenderStep, WithNamespaceTraitRenderStep,
267        };
268
269        match *self {
270            Self::Types => Box::new(TypesRenderStep),
271            Self::TypesSerdeXmlRs {
272                version: SerdeXmlRsVersion::Version07AndBelow,
273            } => Box::new(SerdeXmlRsV7TypesRenderStep),
274            Self::TypesSerdeXmlRs {
275                version: SerdeXmlRsVersion::Version08AndAbove,
276            } => Box::new(SerdeXmlRsV8TypesRenderStep),
277            Self::TypesSerdeQuickXml => Box::new(SerdeQuickXmlTypesRenderStep),
278            Self::Defaults => Box::new(DefaultsRenderStep),
279            Self::EnumConstants => Box::new(EnumConstantsRenderStep),
280            Self::PrefixConstants => Box::new(PrefixConstantsRenderStep::default()),
281            Self::NamespaceConstants => Box::new(NamespaceConstantsRenderStep::default()),
282            Self::WithNamespaceTrait => Box::new(WithNamespaceTraitRenderStep),
283            Self::QuickXmlSerialize {
284                namespaces,
285                default_namespace,
286            } => Box::new(QuickXmlSerializeRenderStep {
287                namespaces,
288                default_namespace,
289            }),
290            Self::QuickXmlDeserialize { boxed_deserializer } => {
291                Box::new(QuickXmlDeserializeRenderStep { boxed_deserializer })
292            }
293            Self::QuickXmlCollectNamespaces => Box::new(QuickXmlCollectNamespacesRenderStep),
294        }
295    }
296
297    fn is_mutual_exclusive_to(&self, other: &dyn RenderStepConfig) -> bool {
298        use crate::pipeline::renderer::{
299            DefaultsRenderStep, NamespaceConstantsRenderStep, QuickXmlCollectNamespacesRenderStep,
300            QuickXmlDeserializeRenderStep, QuickXmlSerializeRenderStep,
301            SerdeQuickXmlTypesRenderStep, SerdeXmlRsV7TypesRenderStep, SerdeXmlRsV8TypesRenderStep,
302            TypesRenderStep, WithNamespaceTraitRenderStep,
303        };
304
305        if self
306            .render_step_type()
307            .is_mutual_exclusive_to(other.render_step_type())
308        {
309            return true;
310        }
311
312        let other_id = other.type_id();
313        let other = (other as &dyn Any).downcast_ref::<Self>();
314
315        match (self, other) {
316            (Self::Types, Some(Self::Types)) => true,
317            (Self::TypesSerdeXmlRs { .. }, Some(Self::TypesSerdeXmlRs { .. })) => true,
318            (Self::TypesSerdeQuickXml, Some(Self::TypesSerdeQuickXml)) => true,
319            (Self::Defaults, Some(Self::Defaults)) => true,
320            (Self::NamespaceConstants, Some(Self::NamespaceConstants)) => true,
321            (Self::WithNamespaceTrait, Some(Self::WithNamespaceTrait)) => true,
322            (Self::QuickXmlSerialize { .. }, Some(Self::QuickXmlSerialize { .. })) => true,
323            (Self::QuickXmlDeserialize { .. }, Some(Self::QuickXmlDeserialize { .. })) => true,
324            (Self::QuickXmlCollectNamespaces, Some(Self::QuickXmlCollectNamespaces)) => true,
325            (Self::Types, None) => other_id == TypeId::of::<TypesRenderStep>(),
326            (
327                Self::TypesSerdeXmlRs {
328                    version: SerdeXmlRsVersion::Version07AndBelow,
329                },
330                None,
331            ) => other_id == TypeId::of::<SerdeXmlRsV7TypesRenderStep>(),
332            (
333                Self::TypesSerdeXmlRs {
334                    version: SerdeXmlRsVersion::Version08AndAbove,
335                },
336                None,
337            ) => other_id == TypeId::of::<SerdeXmlRsV8TypesRenderStep>(),
338            (Self::TypesSerdeQuickXml, None) => {
339                other_id == TypeId::of::<SerdeQuickXmlTypesRenderStep>()
340            }
341            (Self::Defaults, None) => other_id == TypeId::of::<DefaultsRenderStep>(),
342            (Self::NamespaceConstants, None) => {
343                other_id == TypeId::of::<NamespaceConstantsRenderStep>()
344            }
345            (Self::WithNamespaceTrait, None) => {
346                other_id == TypeId::of::<WithNamespaceTraitRenderStep>()
347            }
348            (Self::QuickXmlSerialize { .. }, None) => {
349                other_id == TypeId::of::<QuickXmlSerializeRenderStep>()
350            }
351            (Self::QuickXmlDeserialize { .. }, None) => {
352                other_id == TypeId::of::<QuickXmlDeserializeRenderStep>()
353            }
354            (Self::QuickXmlCollectNamespaces, None) => {
355                other_id == TypeId::of::<QuickXmlCollectNamespacesRenderStep>()
356            }
357            _ => false,
358        }
359    }
360}
361
362impl RenderStep {
363    /// Return `true` if the passed value identifies the same step, `false` otherwise.
364    #[must_use]
365    pub fn is_same(&self, other: &Self) -> bool {
366        match (self, other) {
367            (
368                Self::Types | Self::TypesSerdeXmlRs { .. } | Self::TypesSerdeQuickXml,
369                Self::Types | Self::TypesSerdeXmlRs { .. } | Self::TypesSerdeQuickXml,
370            )
371            | (Self::Defaults, Self::Defaults)
372            | (Self::NamespaceConstants, Self::NamespaceConstants)
373            | (Self::WithNamespaceTrait, Self::WithNamespaceTrait)
374            | (Self::QuickXmlSerialize { .. }, Self::QuickXmlSerialize { .. })
375            | (Self::QuickXmlDeserialize { .. }, Self::QuickXmlDeserialize { .. })
376            | (Self::QuickXmlCollectNamespaces, Self::QuickXmlCollectNamespaces) => true,
377            (_, _) => false,
378        }
379    }
380}
381
382/// Version of `serde-xml-rs` to render the code for.
383#[derive(Debug, Clone, Copy, Eq, PartialEq)]
384pub enum SerdeXmlRsVersion {
385    /// Render code for `serde-xml-rs <= 0.7`.
386    Version07AndBelow,
387
388    /// Render code for `serde-xml-rs >= 0.8`.
389    Version08AndAbove,
390}
391
392/// Defines which additional traits should be implemented by the generated traits
393/// of dynamic types.
394#[derive(Default, Debug)]
395pub enum DynTypeTraits {
396    /// The traits will be detected automatically.
397    #[default]
398    Auto,
399
400    /// List of trait identifiers that will be implemented.
401    Custom(Vec<IdentPath>),
402}