xsd_parser/generator/
misc.rs

1use std::collections::{BTreeMap, BTreeSet, HashSet};
2use std::ops::Deref;
3
4use bitflags::bitflags;
5use proc_macro2::{Ident as Ident2, TokenStream};
6use quote::quote;
7
8use crate::code::IdentPath;
9use crate::schema::{MaxOccurs, MinOccurs};
10use crate::types::{DynamicInfo, Ident, Type, TypeVariant, Types};
11
12bitflags! {
13    /// Different flags that control what code the [`Generator`](super::Generator)
14    /// is generating.
15    #[derive(Debug, Clone, Copy)]
16    pub struct GeneratorFlags: u32 {
17        /// None of the features are enabled.
18        ///
19        /// # Examples
20        ///
21        /// Consider the following XML schema:
22        /// ```xml
23        #[doc = include_str!("../../tests/generator/generator_flags/schema.xsd")]
24        /// ```
25        ///
26        /// Setting none of the flags will result in the following code:
27        /// ```rust
28        #[doc = include_str!("../../tests/generator/generator_flags/expected/empty.rs")]
29        /// ```
30        const NONE = 0;
31
32        /// The generated code uses modules for the different namespaces.
33        ///
34        /// # Examples
35        ///
36        /// Consider the following XML schema:
37        /// ```xml
38        #[doc = include_str!("../../tests/generator/generator_flags/schema.xsd")]
39        /// ```
40        ///
41        /// Enable the `USE_MODULES` feature only will result in the following code:
42        /// ```rust,ignore
43        #[doc = include_str!("../../tests/generator/generator_flags/expected/use_modules.rs")]
44        /// ```
45        const USE_MODULES = 1 << 0;
46
47        /// The generator flattens the content type of choice types if it does not
48        /// define any element attributes.
49        ///
50        /// # Examples
51        ///
52        /// Consider the following XML schema:
53        /// ```xml
54        #[doc = include_str!("../../tests/generator/generator_flags/schema.xsd")]
55        /// ```
56        ///
57        /// Enable the `FLATTEN_CONTENT` feature only will result in the following code:
58        /// ```rust
59        #[doc = include_str!("../../tests/generator/generator_flags/expected/flatten_content.rs")]
60        /// ```
61        const FLATTEN_CONTENT = Self::FLATTEN_ENUM_CONTENT.bits()
62            | Self::FLATTEN_STRUCT_CONTENT.bits();
63
64        /// The generator flattens the content of enum types if possible.
65        ///
66        /// See [`FLATTEN_CONTENT`](Self::FLATTEN_CONTENT) for details.
67        const FLATTEN_ENUM_CONTENT = 1 << 1;
68
69        /// The generator flattens the content of struct types if possible.
70        ///
71        /// See [`FLATTEN_CONTENT`](Self::FLATTEN_CONTENT) for details.
72        const FLATTEN_STRUCT_CONTENT = 1 << 2;
73    }
74}
75
76bitflags! {
77    /// Flags to tell the [`Generator`](super::Generator) how to deal with boxed
78    /// types.
79    #[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
80    pub struct BoxFlags: u32 {
81        /// Boxed types will only be used if necessary.
82        ///
83        /// # Examples
84        ///
85        /// Consider the following XML schema:
86        /// ```xml
87        #[doc = include_str!("../../tests/generator/box_flags/schema.xsd")]
88        /// ```
89        ///
90        /// Enable the `AUTO` feature only will result in the following code:
91        /// ```rust
92        #[doc = include_str!("../../tests/generator/box_flags/expected/auto.rs")]
93        /// ```
94        const AUTO = 0;
95
96        /// Elements in a `xs:choice` type will always be boxed.
97        ///
98        /// # Examples
99        ///
100        /// Consider the following XML schema:
101        /// ```xml
102        #[doc = include_str!("../../tests/generator/box_flags/schema.xsd")]
103        /// ```
104        ///
105        /// Enable the `ENUM_ELEMENTS` feature only will result in the following code:
106        /// ```rust
107        #[doc = include_str!("../../tests/generator/box_flags/expected/enum_elements.rs")]
108        /// ```
109        const ENUM_ELEMENTS = 1 << 0;
110
111        /// Elements in a `xs:all` or `xs:sequence` type will always be boxed.
112        ///
113        /// # Examples
114        ///
115        /// Consider the following XML schema:
116        /// ```xml
117        #[doc = include_str!("../../tests/generator/box_flags/schema.xsd")]
118        /// ```
119        ///
120        /// Enable the `STRUCT_ELEMENTS` feature only will result in the following code:
121        /// ```rust
122        #[doc = include_str!("../../tests/generator/box_flags/expected/struct_elements.rs")]
123        /// ```
124        const STRUCT_ELEMENTS = 1 << 1;
125    }
126}
127
128/// Tells the [`Generator`](super::Generator) how to deal with type definitions.
129#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
130pub enum TypedefMode {
131    /// The [`Generator`](super::Generator) will automatically detect if a
132    /// new type struct or a simple type definition should be used
133    /// for a [`Reference`](TypeVariant::Reference) type.
134    ///
135    /// Detecting the correct type automatically depends basically on the
136    /// occurrence of the references type. If the target type is only referenced
137    /// exactly once, a type definition is rendered. If a different
138    /// occurrence is used, it is wrapped in a new type struct because usually
139    /// additional code needs to be implemented for such types.
140    ///
141    /// # Examples
142    ///
143    /// Consider the following XML schema:
144    /// ```xml
145    #[doc = include_str!("../../tests/generator/typedef_mode/schema.xsd")]
146    /// ```
147    ///
148    /// If the typedef mode is set to [`TypedefMode::Auto`] the following code is rendered:
149    /// ```rust
150    #[doc = include_str!("../../tests/generator/typedef_mode/expected/auto.rs")]
151    /// ```
152    #[default]
153    Auto,
154
155    /// The [`Generator`](super::Generator) will always use a simple type definition
156    /// for a [`Reference`](TypeVariant::Reference) type.
157    ///
158    /// # Examples
159    ///
160    /// Consider the following XML schema:
161    /// ```xml
162    #[doc = include_str!("../../tests/generator/typedef_mode/schema.xsd")]
163    /// ```
164    ///
165    /// If the typedef mode is set to [`TypedefMode::Typedef`] the following code is rendered:
166    /// ```rust
167    #[doc = include_str!("../../tests/generator/typedef_mode/expected/typedef.rs")]
168    /// ```
169    Typedef,
170
171    /// The [`Generator`](super::Generator) will always use a new type struct
172    /// for a [`Reference`](TypeVariant::Reference) type.
173    ///
174    /// # Examples
175    ///
176    /// Consider the following XML schema:
177    /// ```xml
178    #[doc = include_str!("../../tests/generator/typedef_mode/schema.xsd")]
179    /// ```
180    ///
181    /// If the typedef mode is set to [`TypedefMode::NewType`] the following code is rendered:
182    /// ```rust
183    #[doc = include_str!("../../tests/generator/typedef_mode/expected/new_type.rs")]
184    /// ```
185    NewType,
186}
187
188/// Tells the [`Generator`](super::Generator) how to generate code for the
189/// [`serde`] crate.
190#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
191pub enum SerdeSupport {
192    /// No code for the [`serde`] crate is generated.
193    ///
194    /// # Examples
195    ///
196    /// Consider the following XML schema:
197    /// ```xml
198    #[doc = include_str!("../../tests/generator/serde_support/schema.xsd")]
199    /// ```
200    ///
201    /// If the serde support mode is set to [`SerdeSupport::None`] the following code is rendered:
202    /// ```rust
203    #[doc = include_str!("../../tests/generator/serde_support/expected/none.rs")]
204    /// ```
205    #[default]
206    None,
207
208    /// Generates code that can be serialized and deserialized using the
209    /// [`serde`] create in combination with the with [`quick_xml`] crate.
210    ///
211    /// # Examples
212    ///
213    /// Consider the following XML schema:
214    /// ```xml
215    #[doc = include_str!("../../tests/generator/serde_support/schema.xsd")]
216    /// ```
217    ///
218    /// If the serde support mode is set to [`SerdeSupport::QuickXml`] the following code is rendered:
219    /// ```rust
220    #[doc = include_str!("../../tests/generator/serde_support/expected/quick_xml.rs")]
221    /// ```
222    QuickXml,
223
224    /// Generates code that can be serialized and deserialized using the
225    /// [`serde`] create in combination with the with [`serde-xml-rs`](https://docs.rs/serde-xml-rs) crate.
226    ///
227    /// # Examples
228    ///
229    /// Consider the following XML schema:
230    /// ```xml
231    #[doc = include_str!("../../tests/generator/serde_support/schema.xsd")]
232    /// ```
233    ///
234    /// If the serde support mode is set to [`SerdeSupport::SerdeXmlRs`] the following code is rendered:
235    /// ```rust
236    #[doc = include_str!("../../tests/generator/serde_support/expected/serde_xml_rs.rs")]
237    /// ```
238    SerdeXmlRs,
239}
240
241impl SerdeSupport {
242    /// Returns `true` if this is equal to [`SerdeSupport::None`], `false` otherwise.
243    #[must_use]
244    pub fn is_none(&self) -> bool {
245        matches!(self, Self::None)
246    }
247
248    /// Returns `false` if this is equal to [`SerdeSupport::None`], `true` otherwise.
249    #[must_use]
250    pub fn is_some(&self) -> bool {
251        !matches!(self, Self::None)
252    }
253}
254
255#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
256pub enum Occurs {
257    #[default]
258    None,
259    Single,
260    Optional,
261    DynamicList,
262    StaticList(usize),
263}
264
265impl Occurs {
266    pub fn from_occurs(min: MinOccurs, max: MaxOccurs) -> Self {
267        match (min, max) {
268            (0, MaxOccurs::Bounded(0)) => Self::None,
269            (1, MaxOccurs::Bounded(1)) => Self::Single,
270            (0, MaxOccurs::Bounded(1)) => Self::Optional,
271            (a, MaxOccurs::Bounded(b)) if a == b => Self::StaticList(a),
272            (_, _) => Self::DynamicList,
273        }
274    }
275
276    pub fn make_type(self, ident: &TokenStream, need_indirection: bool) -> Option<TokenStream> {
277        match self {
278            Self::None => None,
279            Self::Single if need_indirection => Some(quote! { Box<#ident> }),
280            Self::Single => Some(quote! { #ident }),
281            Self::Optional if need_indirection => Some(quote! { Option<Box<#ident>> }),
282            Self::Optional => Some(quote! { Option<#ident> }),
283            Self::DynamicList => Some(quote! { Vec<#ident> }),
284            Self::StaticList(sz) if need_indirection => Some(quote! { [Box<#ident>; #sz] }),
285            Self::StaticList(sz) => Some(quote! { [#ident; #sz] }),
286        }
287    }
288
289    pub fn is_some(&self) -> bool {
290        *self != Self::None
291    }
292
293    pub fn is_direct(&self) -> bool {
294        matches!(self, Self::Single | Self::Optional | Self::StaticList(_))
295    }
296}
297
298#[derive(Default, Debug)]
299pub enum DynTypeTraits {
300    #[default]
301    Auto,
302    Custom(Vec<IdentPath>),
303}
304
305/* PendingType */
306
307#[derive(Debug)]
308pub(super) struct PendingType<'types> {
309    pub ty: &'types Type,
310    pub ident: Ident,
311}
312
313/* TypeRef */
314
315#[derive(Debug)]
316pub(super) struct TypeRef {
317    pub ident: Ident,
318    pub type_ident: Ident2,
319    pub module_ident: Option<Ident2>,
320    pub boxed_elements: HashSet<Ident>,
321}
322
323impl TypeRef {
324    pub(super) fn to_ident_path(&self) -> IdentPath {
325        if self.ident.is_build_in() {
326            IdentPath::from_ident(self.type_ident.clone())
327        } else {
328            IdentPath::from_ident(self.type_ident.clone()).with_path(self.module_ident.clone())
329        }
330    }
331}
332
333/* TraitInfos */
334
335#[derive(Debug)]
336pub(super) struct TraitInfos(BTreeMap<Ident, TraitInfo>);
337
338impl TraitInfos {
339    #[must_use]
340    pub(super) fn new(types: &Types) -> Self {
341        let mut ret = Self(BTreeMap::new());
342
343        for (base_ident, ty) in types.iter() {
344            let TypeVariant::Dynamic(ai) = &ty.variant else {
345                continue;
346            };
347
348            for type_ident in &ai.derived_types {
349                ret.0
350                    .entry(type_ident.clone())
351                    .or_default()
352                    .traits_all
353                    .insert(base_ident.clone());
354
355                match types.get_variant(type_ident) {
356                    Some(TypeVariant::Dynamic(DynamicInfo {
357                        type_: Some(type_ident),
358                        ..
359                    })) => {
360                        ret.0
361                            .entry(type_ident.clone())
362                            .or_default()
363                            .traits_all
364                            .insert(base_ident.clone());
365                    }
366                    Some(TypeVariant::Reference(ri)) if ri.is_single() => {
367                        ret.0
368                            .entry(ri.type_.clone())
369                            .or_default()
370                            .traits_all
371                            .insert(base_ident.clone());
372                    }
373                    _ => (),
374                }
375            }
376        }
377
378        for ident in ret.0.keys().cloned().collect::<Vec<_>>() {
379            let mut traits_second_level = BTreeSet::new();
380
381            ret.collect_traits(&ident, 0, &mut traits_second_level);
382
383            let info = ret.0.get_mut(&ident).unwrap();
384            info.traits_direct = info
385                .traits_all
386                .difference(&traits_second_level)
387                .cloned()
388                .collect();
389        }
390
391        ret
392    }
393
394    fn collect_traits(
395        &self,
396        ident: &Ident,
397        depth: usize,
398        traits_second_level: &mut BTreeSet<Ident>,
399    ) {
400        if depth > 1 {
401            traits_second_level.insert(ident.clone());
402        }
403
404        if let Some(info) = self.0.get(ident) {
405            for trait_ in &info.traits_all {
406                self.collect_traits(trait_, depth + 1, traits_second_level);
407            }
408        }
409    }
410}
411
412impl Deref for TraitInfos {
413    type Target = BTreeMap<Ident, TraitInfo>;
414
415    fn deref(&self) -> &Self::Target {
416        &self.0
417    }
418}
419
420/* TraitInfo */
421
422#[derive(Default, Debug)]
423pub(super) struct TraitInfo {
424    pub traits_all: BTreeSet<Ident>,
425    pub traits_direct: BTreeSet<Ident>,
426}