xsd_parser/generator/
misc.rs

1use std::borrow::Cow;
2use std::collections::{BTreeMap, BTreeSet, HashSet};
3use std::ops::Deref;
4
5use bitflags::bitflags;
6use inflector::Inflector;
7use proc_macro2::{Ident as Ident2, TokenStream};
8use quote::{format_ident, quote};
9
10use crate::schema::{MaxOccurs, MinOccurs, NamespaceId};
11use crate::types::{DynamicInfo, Ident, Name, Type, Types};
12
13use super::Error;
14
15bitflags! {
16    /// Different flags that control what code the [`Generator`](super::Generator)
17    /// is generating.
18    #[derive(Debug, Clone, Copy)]
19    pub struct GenerateFlags: u32 {
20        /// None of the features are enabled.
21        ///
22        /// # Examples
23        ///
24        /// Consider the following XML schema:
25        /// ```xml
26        #[doc = include_str!("../../tests/generator/generate_flags/schema.xsd")]
27        /// ```
28        ///
29        /// Setting none of the flags will result in the following code:
30        /// ```rust
31        #[doc = include_str!("../../tests/generator/generate_flags/expected/empty.rs")]
32        /// ```
33        const NONE = 0;
34
35        /// The generated code uses modules for the different namespaces.
36        ///
37        /// # Examples
38        ///
39        /// Consider the following XML schema:
40        /// ```xml
41        #[doc = include_str!("../../tests/generator/generate_flags/schema.xsd")]
42        /// ```
43        ///
44        /// Enable the `USE_MODULES` feature only will result in the following code:
45        /// ```rust,ignore
46        #[doc = include_str!("../../tests/generator/generate_flags/expected/use_modules.rs")]
47        /// ```
48        const USE_MODULES = 1 << 0;
49
50        /// The generator flattens the content type of choice types if it does not
51        /// define any element attributes.
52        ///
53        /// # Examples
54        ///
55        /// Consider the following XML schema:
56        /// ```xml
57        #[doc = include_str!("../../tests/generator/generate_flags/schema.xsd")]
58        /// ```
59        ///
60        /// Enable the `FLATTEN_CONTENT` feature only will result in the following code:
61        /// ```rust
62        #[doc = include_str!("../../tests/generator/generate_flags/expected/flatten_content.rs")]
63        /// ```
64        const FLATTEN_CONTENT = 1 << 1;
65
66        /// The generator will generate code to serialize the generated types using
67        /// the `quick_xml` crate.
68        const QUICK_XML_SERIALIZE = 1 << 2;
69
70        /// The generator will generate code to deserialize the generated types using
71        /// the `quick_xml` crate.
72        const QUICK_XML_DESERIALIZE = 1 << 3;
73
74        /// Combination of `QUICK_XML_SERIALIZE` and [`QUICK_XML_DESERIALIZE`].
75        const QUICK_XML = Self::QUICK_XML_SERIALIZE.bits() | Self::QUICK_XML_DESERIALIZE.bits();
76
77        /// Implement the [`WithNamespace`](crate::WithNamespace) trait for the generated types.
78        const WITH_NAMESPACE_TRAIT = 1 << 4;
79    }
80}
81
82bitflags! {
83    /// Flags to tell the [`Generator`](super::Generator) how to deal with boxed
84    /// types.
85    #[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
86    pub struct BoxFlags: u32 {
87        /// Boxed types will only be used if necessary.
88        ///
89        /// # Examples
90        ///
91        /// Consider the following XML schema:
92        /// ```xml
93        #[doc = include_str!("../../tests/generator/box_flags/schema.xsd")]
94        /// ```
95        ///
96        /// Enable the `AUTO` feature only will result in the following code:
97        /// ```rust
98        #[doc = include_str!("../../tests/generator/box_flags/expected/auto.rs")]
99        /// ```
100        const AUTO = 0;
101
102        /// Elements in a `xs:choice` type will always be boxed.
103        ///
104        /// # Examples
105        ///
106        /// Consider the following XML schema:
107        /// ```xml
108        #[doc = include_str!("../../tests/generator/box_flags/schema.xsd")]
109        /// ```
110        ///
111        /// Enable the `ENUM_ELEMENTS` feature only will result in the following code:
112        /// ```rust
113        #[doc = include_str!("../../tests/generator/box_flags/expected/enum_elements.rs")]
114        /// ```
115        const ENUM_ELEMENTS = 1 << 0;
116
117        /// Elements in a `xs:all` or `xs:sequence` type will always be boxed.
118        ///
119        /// # Examples
120        ///
121        /// Consider the following XML schema:
122        /// ```xml
123        #[doc = include_str!("../../tests/generator/box_flags/schema.xsd")]
124        /// ```
125        ///
126        /// Enable the `STRUCT_ELEMENTS` feature only will result in the following code:
127        /// ```rust
128        #[doc = include_str!("../../tests/generator/box_flags/expected/struct_elements.rs")]
129        /// ```
130        const STRUCT_ELEMENTS = 1 << 1;
131    }
132}
133
134/// Tells the [`Generator`](super::Generator) what type should be generated for
135/// the content of an XML element.
136#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
137pub enum ContentMode {
138    /// The mode is selected depending on the type definition of the element.
139    /// `xs:choice` types will be rendered as enum, `xs:all` and `xs:sequence`
140    /// types will be rendered as struct.
141    ///
142    /// # Examples
143    ///
144    /// Consider the following XML schema:
145    /// ```xml
146    #[doc = include_str!("../../tests/generator/content_mode/schema.xsd")]
147    /// ```
148    ///
149    /// If the content mode is set to [`ContentMode::Auto`] the following code is rendered:
150    /// ```rust
151    #[doc = include_str!("../../tests/generator/content_mode/expected/auto.rs")]
152    /// ```
153    #[default]
154    Auto,
155
156    /// The content of a XML element is always rendered as enum.
157    ///
158    /// # Examples
159    ///
160    /// Consider the following XML schema:
161    /// ```xml
162    #[doc = include_str!("../../tests/generator/content_mode/schema.xsd")]
163    /// ```
164    ///
165    /// If the content mode is set to [`ContentMode::Enum`] the following code is rendered:
166    /// ```rust
167    #[doc = include_str!("../../tests/generator/content_mode/expected/enum.rs")]
168    /// ```
169    Enum,
170
171    /// The content of a XML element is always rendered as struct.
172    ///
173    /// # Examples
174    ///
175    /// Consider the following XML schema:
176    /// ```xml
177    #[doc = include_str!("../../tests/generator/content_mode/schema.xsd")]
178    /// ```
179    ///
180    /// If the content mode is set to [`ContentMode::Struct`] the following code is rendered:
181    /// ```rust
182    #[doc = include_str!("../../tests/generator/content_mode/expected/struct.rs")]
183    /// ```
184    Struct,
185}
186
187/// Tells the [`Generator`](super::Generator) how to deal with type definitions.
188#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
189pub enum TypedefMode {
190    /// The [`Generator`](super::Generator) will automatically detect if a
191    /// new type struct or a simple type definition should be used
192    /// for a [`Reference`](Type::Reference) type.
193    ///
194    /// Detecting the correct type automatically depends basically on the
195    /// occurrence of the references type. If the target type is only referenced
196    /// exactly once, a type definition is rendered. If a different
197    /// occurrence is used, it is wrapped in a new type struct because usually
198    /// additional code needs to be implemented for such types.
199    ///
200    /// # Examples
201    ///
202    /// Consider the following XML schema:
203    /// ```xml
204    #[doc = include_str!("../../tests/generator/typedef_mode/schema.xsd")]
205    /// ```
206    ///
207    /// If the typedef mode is set to [`TypedefMode::Auto`] the following code is rendered:
208    /// ```rust
209    #[doc = include_str!("../../tests/generator/typedef_mode/expected/auto.rs")]
210    /// ```
211    #[default]
212    Auto,
213
214    /// The [`Generator`](super::Generator) will always use a simple type definition
215    /// for a [`Reference`](Type::Reference) type.
216    ///
217    /// # Examples
218    ///
219    /// Consider the following XML schema:
220    /// ```xml
221    #[doc = include_str!("../../tests/generator/typedef_mode/schema.xsd")]
222    /// ```
223    ///
224    /// If the typedef mode is set to [`TypedefMode::Typedef`] the following code is rendered:
225    /// ```rust
226    #[doc = include_str!("../../tests/generator/typedef_mode/expected/typedef.rs")]
227    /// ```
228    Typedef,
229
230    /// The [`Generator`](super::Generator) will always use a new type struct
231    /// for a [`Reference`](Type::Reference) type.
232    ///
233    /// # Examples
234    ///
235    /// Consider the following XML schema:
236    /// ```xml
237    #[doc = include_str!("../../tests/generator/typedef_mode/schema.xsd")]
238    /// ```
239    ///
240    /// If the typedef mode is set to [`TypedefMode::NewType`] the following code is rendered:
241    /// ```rust
242    #[doc = include_str!("../../tests/generator/typedef_mode/expected/new_type.rs")]
243    /// ```
244    NewType,
245}
246
247/// Tells the [`Generator`](super::Generator) how to generate code for the
248/// [`serde`] crate.
249#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
250pub enum SerdeSupport {
251    /// No code for the [`serde`] crate is generated.
252    ///
253    /// # Examples
254    ///
255    /// Consider the following XML schema:
256    /// ```xml
257    #[doc = include_str!("../../tests/generator/serde_support/schema.xsd")]
258    /// ```
259    ///
260    /// If the serde support mode is set to [`SerdeSupport::None`] the following code is rendered:
261    /// ```rust
262    #[doc = include_str!("../../tests/generator/serde_support/expected/none.rs")]
263    /// ```
264    #[default]
265    None,
266
267    /// Generates code that can be serialized and deserialized using the
268    /// [`serde`] create in combination with the with [`quick_xml`] crate.
269    ///
270    /// # Examples
271    ///
272    /// Consider the following XML schema:
273    /// ```xml
274    #[doc = include_str!("../../tests/generator/serde_support/schema.xsd")]
275    /// ```
276    ///
277    /// If the serde support mode is set to [`SerdeSupport::QuickXml`] the following code is rendered:
278    /// ```rust
279    #[doc = include_str!("../../tests/generator/serde_support/expected/quick_xml.rs")]
280    /// ```
281    QuickXml,
282
283    /// Generates code that can be serialized and deserialized using the
284    /// [`serde`] create in combination with the with [`serde-xml-rs`](https://docs.rs/serde-xml-rs) crate.
285    ///
286    /// # Examples
287    ///
288    /// Consider the following XML schema:
289    /// ```xml
290    #[doc = include_str!("../../tests/generator/serde_support/schema.xsd")]
291    /// ```
292    ///
293    /// If the serde support mode is set to [`SerdeSupport::SerdeXmlRs`] the following code is rendered:
294    /// ```rust
295    #[doc = include_str!("../../tests/generator/serde_support/expected/serde_xml_rs.rs")]
296    /// ```
297    SerdeXmlRs,
298}
299
300impl SerdeSupport {
301    /// Returns `true` if this is equal to [`SerdeSupport::None`], `false` otherwise.
302    #[must_use]
303    pub fn is_none(&self) -> bool {
304        matches!(self, Self::None)
305    }
306}
307
308/* Modules */
309
310#[derive(Default, Debug)]
311pub(super) struct Modules(pub BTreeMap<Option<NamespaceId>, Module>);
312
313impl Modules {
314    pub(super) fn get_mut(&mut self, ns: Option<NamespaceId>) -> &mut Module {
315        self.0.entry(ns).or_default()
316    }
317
318    pub(super) fn add_code(&mut self, ns: Option<NamespaceId>, code: TokenStream) {
319        self.get_mut(ns).code.extend(code);
320    }
321}
322
323/* Module */
324
325#[derive(Default, Debug)]
326pub(super) struct Module {
327    pub code: TokenStream,
328    pub quick_xml_serialize: Option<TokenStream>,
329    pub quick_xml_deserialize: Option<TokenStream>,
330}
331
332/* PendingType */
333
334#[derive(Debug)]
335pub(super) struct PendingType<'types> {
336    pub ty: &'types Type,
337    pub ident: Ident,
338}
339
340/* TypeRef */
341
342#[derive(Debug)]
343pub(super) struct TypeRef {
344    pub ns: Option<NamespaceId>,
345    pub module_ident: Option<Ident2>,
346    pub type_ident: Ident2,
347    pub flags: StateFlags,
348    pub boxed_elements: HashSet<Ident>,
349}
350
351/* TraitInfos */
352
353#[derive(Debug)]
354pub(super) struct TraitInfos(BTreeMap<Ident, TraitInfo>);
355
356impl TraitInfos {
357    #[must_use]
358    pub(super) fn new(types: &Types) -> Self {
359        let mut ret = Self(BTreeMap::new());
360
361        for (base_ident, ty) in types.iter() {
362            let Type::Dynamic(ai) = ty else {
363                continue;
364            };
365
366            for type_ident in &ai.derived_types {
367                ret.0
368                    .entry(type_ident.clone())
369                    .or_default()
370                    .traits_all
371                    .insert(base_ident.clone());
372
373                match types.get(type_ident) {
374                    Some(Type::Dynamic(DynamicInfo {
375                        type_: Some(type_ident),
376                        ..
377                    })) => {
378                        ret.0
379                            .entry(type_ident.clone())
380                            .or_default()
381                            .traits_all
382                            .insert(base_ident.clone());
383                    }
384                    Some(Type::Reference(ri)) if ri.is_single() => {
385                        ret.0
386                            .entry(ri.type_.clone())
387                            .or_default()
388                            .traits_all
389                            .insert(base_ident.clone());
390                    }
391                    _ => (),
392                }
393            }
394        }
395
396        for ident in ret.0.keys().cloned().collect::<Vec<_>>() {
397            let mut traits_second_level = BTreeSet::new();
398
399            ret.collect_traits(&ident, 0, &mut traits_second_level);
400
401            let info = ret.0.get_mut(&ident).unwrap();
402            info.traits_direct = info
403                .traits_all
404                .difference(&traits_second_level)
405                .cloned()
406                .collect();
407        }
408
409        ret
410    }
411
412    fn collect_traits(
413        &self,
414        ident: &Ident,
415        depth: usize,
416        traits_second_level: &mut BTreeSet<Ident>,
417    ) {
418        if depth > 1 {
419            traits_second_level.insert(ident.clone());
420        }
421
422        if let Some(info) = self.0.get(ident) {
423            for trait_ in &info.traits_all {
424                self.collect_traits(trait_, depth + 1, traits_second_level);
425            }
426        }
427    }
428}
429
430impl Deref for TraitInfos {
431    type Target = BTreeMap<Ident, TraitInfo>;
432
433    fn deref(&self) -> &Self::Target {
434        &self.0
435    }
436}
437
438/* TraitInfo */
439
440#[derive(Default, Debug)]
441pub(super) struct TraitInfo {
442    pub traits_all: BTreeSet<Ident>,
443    pub traits_direct: BTreeSet<Ident>,
444}
445
446/* StateFlags */
447
448bitflags! {
449    #[derive(Debug, Clone, Copy)]
450    pub(super) struct StateFlags: u32 {
451        const HAS_TYPE = 1 << 0;
452        const HAS_IMPL = 1 << 1;
453
454        const HAS_QUICK_XML_SERIALIZE = 1 << 2;
455        const HAS_QUICK_XML_DESERIALIZE = 1 << 3;
456
457        const HAS_QUICK_XML = Self::HAS_QUICK_XML_SERIALIZE.bits() | Self::HAS_QUICK_XML_DESERIALIZE.bits();
458    }
459}
460
461/* Occurs */
462
463#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
464pub(super) enum Occurs {
465    #[default]
466    None,
467    Single,
468    Optional,
469    DynamicList,
470    StaticList(usize),
471}
472
473impl Occurs {
474    pub(super) fn from_occurs(min: MinOccurs, max: MaxOccurs) -> Self {
475        match (min, max) {
476            (0, MaxOccurs::Bounded(0)) => Self::None,
477            (1, MaxOccurs::Bounded(1)) => Self::Single,
478            (0, MaxOccurs::Bounded(1)) => Self::Optional,
479            (a, MaxOccurs::Bounded(b)) if a == b => Self::StaticList(a),
480            (_, _) => Self::DynamicList,
481        }
482    }
483
484    pub(super) fn make_type(
485        self,
486        ident: &TokenStream,
487        need_indirection: bool,
488    ) -> Option<TokenStream> {
489        match self {
490            Self::None => None,
491            Self::Single if need_indirection => Some(quote! { Box<#ident> }),
492            Self::Single => Some(quote! { #ident }),
493            Self::Optional if need_indirection => Some(quote! { Option<Box<#ident>> }),
494            Self::Optional => Some(quote! { Option<#ident> }),
495            Self::DynamicList => Some(quote! { Vec<#ident> }),
496            Self::StaticList(sz) if need_indirection => Some(quote! { [Box<#ident>; #sz] }),
497            Self::StaticList(sz) => Some(quote! { [#ident; #sz] }),
498        }
499    }
500
501    pub(super) fn is_direct(&self) -> bool {
502        matches!(self, Self::Single | Self::Optional | Self::StaticList(_))
503    }
504}
505
506/* TypeMode */
507
508#[derive(Debug, Clone, Copy, Eq, PartialEq)]
509pub(super) enum TypeMode {
510    All,
511    Choice,
512    Sequence,
513    Simple,
514}
515
516/* DynTypeTraits */
517
518#[derive(Default, Debug)]
519pub(super) enum DynTypeTraits {
520    #[default]
521    Auto,
522    Custom(Vec<TokenStream>),
523}
524
525/* Helper */
526
527pub(super) fn format_field_name(name: &Name, display_name: Option<&str>) -> Cow<'static, str> {
528    if let Some(display_name) = display_name {
529        return Cow::Owned(display_name.to_snake_case());
530    }
531
532    let ident = name
533        .to_type_name(false, None)
534        .as_str()
535        .unwrap()
536        .to_snake_case();
537
538    match KEYWORDS.binary_search_by(|(key, _)| key.cmp(&ident.as_str())) {
539        Ok(idx) => Cow::Borrowed(KEYWORDS[idx].1),
540        Err(_) => {
541            if ident.starts_with(char::is_numeric) {
542                Cow::Owned(format!("_{ident}"))
543            } else {
544                Cow::Owned(ident)
545            }
546        }
547    }
548}
549
550pub(super) fn format_field_ident(name: &Name, display_name: Option<&str>) -> Ident2 {
551    let ident = format_field_name(name, display_name);
552
553    format_ident!("{ident}")
554}
555
556pub(super) fn format_module_ident(name: &Name) -> Ident2 {
557    format_field_ident(name, None)
558}
559
560pub(super) fn format_type_name(name: &Name, display_name: Option<&str>) -> String {
561    if let Some(display_name) = display_name {
562        return display_name.to_pascal_case();
563    }
564
565    let name = name
566        .to_type_name(false, None)
567        .as_str()
568        .unwrap()
569        .to_pascal_case();
570
571    if name.starts_with(char::is_numeric) {
572        format!("_{name}")
573    } else {
574        name
575    }
576}
577
578pub(super) fn format_type_ident(name: &Name, display_name: Option<&str>) -> Ident2 {
579    let ident = format_type_name(name, display_name);
580
581    format_ident!("{ident}")
582}
583
584pub(super) fn format_variant_ident(name: &Name, display_name: Option<&str>) -> Ident2 {
585    format_type_ident(name, display_name)
586}
587
588pub(super) fn format_module(
589    types: &Types,
590    ns: Option<NamespaceId>,
591) -> Result<Option<Ident2>, Error> {
592    let Some(ns) = ns else {
593        return Ok(None);
594    };
595
596    let module = types.modules.get(&ns).ok_or(Error::UnknownNamespace(ns))?;
597    let Some(name) = &module.name else {
598        return Ok(None);
599    };
600
601    Ok(Some(format_module_ident(name)))
602}
603
604pub(super) fn format_type_ref(current_ns: Option<NamespaceId>, type_: &TypeRef) -> TokenStream {
605    format_type_ref_ex(current_ns, None, &type_.type_ident, type_)
606}
607
608pub(super) fn format_type_ref_ex(
609    current_ns: Option<NamespaceId>,
610    extra: Option<&TokenStream>,
611    type_ident: &Ident2,
612    type_: &TypeRef,
613) -> TokenStream {
614    let module_ident = match (current_ns, type_.ns) {
615        (Some(a), Some(b)) if a != b => Some(&type_.module_ident),
616        (_, _) => None,
617    };
618
619    if let Some(module_ident) = module_ident {
620        quote! {
621            #module_ident #extra :: #type_ident
622        }
623    } else {
624        quote! {
625            #type_ident
626        }
627    }
628}
629
630pub(super) fn make_type_name(postfixes: &[String], ty: &Type, ident: &Ident) -> Name {
631    match (ty, &ident.name) {
632        (Type::Reference(ti), Name::Unnamed { .. }) if ti.type_.name.is_named() => {
633            match Occurs::from_occurs(ti.min_occurs, ti.max_occurs) {
634                Occurs::DynamicList => return Name::new(format!("{}List", ti.type_.name)),
635                Occurs::Optional => return Name::new(format!("{}Opt", ti.type_.name)),
636                _ => (),
637            }
638        }
639        (_, _) => (),
640    };
641
642    let postfix = postfixes
643        .get(ident.type_ as usize)
644        .map_or("", |s| s.as_str());
645
646    match &ident.name {
647        Name::Named(s) if s.ends_with(postfix) => Name::Named(s.clone()),
648        Name::Named(s) => Name::Named(Cow::Owned(format!("{s}{postfix}"))),
649        name => name.to_type_name(false, None),
650    }
651}
652
653const KEYWORDS: &[(&str, &str)] = &[
654    ("abstract", "abstract_"),
655    ("as", "as_"),
656    ("become", "become_"),
657    ("box", "box_"),
658    ("break", "break_"),
659    ("const", "const_"),
660    ("continue", "continue_"),
661    ("crate", "crate_"),
662    ("do", "do_"),
663    ("else", "else_"),
664    ("enum", "enum_"),
665    ("extern", "extern_"),
666    ("false", "false_"),
667    ("final", "final_"),
668    ("fn", "fn_"),
669    ("for", "for_"),
670    ("if", "if_"),
671    ("impl", "impl_"),
672    ("in", "in_"),
673    ("let", "let_"),
674    ("loop", "loop_"),
675    ("macro", "macro_"),
676    ("match", "match_"),
677    ("mod", "mod_"),
678    ("move", "move_"),
679    ("mut", "mut_"),
680    ("override", "override_"),
681    ("priv", "priv_"),
682    ("pub", "pub_"),
683    ("ref", "ref_"),
684    ("return", "return_"),
685    ("self", "self_"),
686    ("Self", "Self_"),
687    ("static", "static_"),
688    ("struct", "struct_"),
689    ("super", "super_"),
690    ("trait", "trait_"),
691    ("true", "true_"),
692    ("try", "try_"),
693    ("type", "type_"),
694    ("typeof", "typeof_"),
695    ("union", "union_"),
696    ("unsafe", "unsafe_"),
697    ("unsized", "unsized_"),
698    ("use", "use_"),
699    ("virtual", "virtual_"),
700    ("where", "where_"),
701    ("while", "while_"),
702    ("yield", "yield_"),
703];