xsd_parser/generator/
misc.rs

1use std::borrow::Cow;
2use std::collections::{BTreeMap, BTreeSet, HashSet};
3use std::ops::{Deref, DerefMut};
4use std::str::FromStr;
5
6use bitflags::bitflags;
7use inflector::Inflector;
8use proc_macro2::{Ident as Ident2, TokenStream};
9use quote::{format_ident, quote, ToTokens};
10use smallvec::SmallVec;
11
12use crate::schema::{MaxOccurs, MinOccurs, NamespaceId};
13use crate::types::{DynamicInfo, Ident, Name, Type, TypeVariant, Types};
14
15use super::helper::render_usings;
16use super::Error;
17
18bitflags! {
19    /// Different flags that control what code the [`Generator`](super::Generator)
20    /// is generating.
21    #[derive(Debug, Clone, Copy)]
22    pub struct GeneratorFlags: u32 {
23        /// None of the features are enabled.
24        ///
25        /// # Examples
26        ///
27        /// Consider the following XML schema:
28        /// ```xml
29        #[doc = include_str!("../../tests/generator/generator_flags/schema.xsd")]
30        /// ```
31        ///
32        /// Setting none of the flags will result in the following code:
33        /// ```rust
34        #[doc = include_str!("../../tests/generator/generator_flags/expected/empty.rs")]
35        /// ```
36        const NONE = 0;
37
38        /// The generated code uses modules for the different namespaces.
39        ///
40        /// # Examples
41        ///
42        /// Consider the following XML schema:
43        /// ```xml
44        #[doc = include_str!("../../tests/generator/generator_flags/schema.xsd")]
45        /// ```
46        ///
47        /// Enable the `USE_MODULES` feature only will result in the following code:
48        /// ```rust,ignore
49        #[doc = include_str!("../../tests/generator/generator_flags/expected/use_modules.rs")]
50        /// ```
51        const USE_MODULES = 1 << 0;
52
53        /// The generator flattens the content type of choice types if it does not
54        /// define any element attributes.
55        ///
56        /// # Examples
57        ///
58        /// Consider the following XML schema:
59        /// ```xml
60        #[doc = include_str!("../../tests/generator/generator_flags/schema.xsd")]
61        /// ```
62        ///
63        /// Enable the `FLATTEN_CONTENT` feature only will result in the following code:
64        /// ```rust
65        #[doc = include_str!("../../tests/generator/generator_flags/expected/flatten_content.rs")]
66        /// ```
67        const FLATTEN_CONTENT = Self::FLATTEN_ENUM_CONTENT.bits()
68            | Self::FLATTEN_STRUCT_CONTENT.bits();
69
70        /// The generator flattens the content of enum types if possible.
71        ///
72        /// See [`FLATTEN_CONTENT`](Self::FLATTEN_CONTENT) for details.
73        const FLATTEN_ENUM_CONTENT = 1 << 1;
74
75        /// The generator flattens the content of struct types if possible.
76        ///
77        /// See [`FLATTEN_CONTENT`](Self::FLATTEN_CONTENT) for details.
78        const FLATTEN_STRUCT_CONTENT = 1 << 2;
79
80        /// The generator will generate code to serialize the generated types using
81        /// the `quick_xml` crate.
82        const QUICK_XML_SERIALIZE = 1 << 3;
83
84        /// The generator will generate code to deserialize the generated types using
85        /// the `quick_xml` crate.
86        const QUICK_XML_DESERIALIZE = 1 << 4;
87
88        /// Combination of [`WITH_NAMESPACE_CONSTANTS`](Self::WITH_NAMESPACE_CONSTANTS),
89        /// [`QUICK_XML_SERIALIZE`](Self::QUICK_XML_SERIALIZE)
90        /// and [`QUICK_XML_DESERIALIZE`](Self::QUICK_XML_DESERIALIZE).
91        const QUICK_XML = Self::WITH_NAMESPACE_CONSTANTS.bits()
92            | Self::QUICK_XML_SERIALIZE.bits()
93            | Self::QUICK_XML_DESERIALIZE.bits();
94
95        /// Implement the [`WithNamespace`](crate::WithNamespace) trait for the generated types.
96        const WITH_NAMESPACE_TRAIT = 1 << 5;
97
98        /// Will generate constants for the different namespace used by the schema.
99        const WITH_NAMESPACE_CONSTANTS = 1 << 6;
100    }
101}
102
103bitflags! {
104    /// Flags to tell the [`Generator`](super::Generator) how to deal with boxed
105    /// types.
106    #[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
107    pub struct BoxFlags: u32 {
108        /// Boxed types will only be used if necessary.
109        ///
110        /// # Examples
111        ///
112        /// Consider the following XML schema:
113        /// ```xml
114        #[doc = include_str!("../../tests/generator/box_flags/schema.xsd")]
115        /// ```
116        ///
117        /// Enable the `AUTO` feature only will result in the following code:
118        /// ```rust
119        #[doc = include_str!("../../tests/generator/box_flags/expected/auto.rs")]
120        /// ```
121        const AUTO = 0;
122
123        /// Elements in a `xs:choice` type will always be boxed.
124        ///
125        /// # Examples
126        ///
127        /// Consider the following XML schema:
128        /// ```xml
129        #[doc = include_str!("../../tests/generator/box_flags/schema.xsd")]
130        /// ```
131        ///
132        /// Enable the `ENUM_ELEMENTS` feature only will result in the following code:
133        /// ```rust
134        #[doc = include_str!("../../tests/generator/box_flags/expected/enum_elements.rs")]
135        /// ```
136        const ENUM_ELEMENTS = 1 << 0;
137
138        /// Elements in a `xs:all` or `xs:sequence` type will always be boxed.
139        ///
140        /// # Examples
141        ///
142        /// Consider the following XML schema:
143        /// ```xml
144        #[doc = include_str!("../../tests/generator/box_flags/schema.xsd")]
145        /// ```
146        ///
147        /// Enable the `STRUCT_ELEMENTS` feature only will result in the following code:
148        /// ```rust
149        #[doc = include_str!("../../tests/generator/box_flags/expected/struct_elements.rs")]
150        /// ```
151        const STRUCT_ELEMENTS = 1 << 1;
152    }
153}
154
155/// Tells the [`Generator`](super::Generator) how to deal with type definitions.
156#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
157pub enum TypedefMode {
158    /// The [`Generator`](super::Generator) will automatically detect if a
159    /// new type struct or a simple type definition should be used
160    /// for a [`Reference`](TypeVariant::Reference) type.
161    ///
162    /// Detecting the correct type automatically depends basically on the
163    /// occurrence of the references type. If the target type is only referenced
164    /// exactly once, a type definition is rendered. If a different
165    /// occurrence is used, it is wrapped in a new type struct because usually
166    /// additional code needs to be implemented for such types.
167    ///
168    /// # Examples
169    ///
170    /// Consider the following XML schema:
171    /// ```xml
172    #[doc = include_str!("../../tests/generator/typedef_mode/schema.xsd")]
173    /// ```
174    ///
175    /// If the typedef mode is set to [`TypedefMode::Auto`] the following code is rendered:
176    /// ```rust
177    #[doc = include_str!("../../tests/generator/typedef_mode/expected/auto.rs")]
178    /// ```
179    #[default]
180    Auto,
181
182    /// The [`Generator`](super::Generator) will always use a simple type definition
183    /// for a [`Reference`](TypeVariant::Reference) type.
184    ///
185    /// # Examples
186    ///
187    /// Consider the following XML schema:
188    /// ```xml
189    #[doc = include_str!("../../tests/generator/typedef_mode/schema.xsd")]
190    /// ```
191    ///
192    /// If the typedef mode is set to [`TypedefMode::Typedef`] the following code is rendered:
193    /// ```rust
194    #[doc = include_str!("../../tests/generator/typedef_mode/expected/typedef.rs")]
195    /// ```
196    Typedef,
197
198    /// The [`Generator`](super::Generator) will always use a new type struct
199    /// for a [`Reference`](TypeVariant::Reference) type.
200    ///
201    /// # Examples
202    ///
203    /// Consider the following XML schema:
204    /// ```xml
205    #[doc = include_str!("../../tests/generator/typedef_mode/schema.xsd")]
206    /// ```
207    ///
208    /// If the typedef mode is set to [`TypedefMode::NewType`] the following code is rendered:
209    /// ```rust
210    #[doc = include_str!("../../tests/generator/typedef_mode/expected/new_type.rs")]
211    /// ```
212    NewType,
213}
214
215/// Tells the [`Generator`](super::Generator) how to generate code for the
216/// [`serde`] crate.
217#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
218pub enum SerdeSupport {
219    /// No code for the [`serde`] crate is generated.
220    ///
221    /// # Examples
222    ///
223    /// Consider the following XML schema:
224    /// ```xml
225    #[doc = include_str!("../../tests/generator/serde_support/schema.xsd")]
226    /// ```
227    ///
228    /// If the serde support mode is set to [`SerdeSupport::None`] the following code is rendered:
229    /// ```rust
230    #[doc = include_str!("../../tests/generator/serde_support/expected/none.rs")]
231    /// ```
232    #[default]
233    None,
234
235    /// Generates code that can be serialized and deserialized using the
236    /// [`serde`] create in combination with the with [`quick_xml`] crate.
237    ///
238    /// # Examples
239    ///
240    /// Consider the following XML schema:
241    /// ```xml
242    #[doc = include_str!("../../tests/generator/serde_support/schema.xsd")]
243    /// ```
244    ///
245    /// If the serde support mode is set to [`SerdeSupport::QuickXml`] the following code is rendered:
246    /// ```rust
247    #[doc = include_str!("../../tests/generator/serde_support/expected/quick_xml.rs")]
248    /// ```
249    QuickXml,
250
251    /// Generates code that can be serialized and deserialized using the
252    /// [`serde`] create in combination with the with [`serde-xml-rs`](https://docs.rs/serde-xml-rs) crate.
253    ///
254    /// # Examples
255    ///
256    /// Consider the following XML schema:
257    /// ```xml
258    #[doc = include_str!("../../tests/generator/serde_support/schema.xsd")]
259    /// ```
260    ///
261    /// If the serde support mode is set to [`SerdeSupport::SerdeXmlRs`] the following code is rendered:
262    /// ```rust
263    #[doc = include_str!("../../tests/generator/serde_support/expected/serde_xml_rs.rs")]
264    /// ```
265    SerdeXmlRs,
266}
267
268impl SerdeSupport {
269    /// Returns `true` if this is equal to [`SerdeSupport::None`], `false` otherwise.
270    #[must_use]
271    pub fn is_none(&self) -> bool {
272        matches!(self, Self::None)
273    }
274
275    /// Returns `false` if this is equal to [`SerdeSupport::None`], `true` otherwise.
276    #[must_use]
277    pub fn is_some(&self) -> bool {
278        !matches!(self, Self::None)
279    }
280}
281
282/* Modules */
283
284#[derive(Default, Debug)]
285pub(super) struct Modules(pub BTreeMap<Option<NamespaceId>, Module>);
286
287impl Modules {
288    pub(super) fn get_mut(&mut self, ns: Option<NamespaceId>) -> &mut Module {
289        self.0.entry(ns).or_default()
290    }
291}
292
293impl Deref for Modules {
294    type Target = BTreeMap<Option<NamespaceId>, Module>;
295
296    fn deref(&self) -> &Self::Target {
297        &self.0
298    }
299}
300
301/* Module */
302
303#[derive(Default, Debug)]
304pub(super) struct Module {
305    pub main: ModuleCode,
306    pub quick_xml_serialize: Option<ModuleCode>,
307    pub quick_xml_deserialize: Option<ModuleCode>,
308}
309
310/* ModuleCode */
311
312#[derive(Default, Debug)]
313pub(super) struct ModuleCode {
314    pub code: TokenStream,
315    pub usings: BTreeSet<String>,
316}
317
318impl ModuleCode {
319    pub(super) fn code(&mut self, code: TokenStream) -> &mut Self {
320        self.code.extend(code);
321
322        self
323    }
324
325    pub(super) fn usings<I>(&mut self, usings: I) -> &mut Self
326    where
327        I: IntoIterator,
328        I::Item: ToString,
329    {
330        for using in usings {
331            self.usings.insert(using.to_string());
332        }
333
334        self
335    }
336}
337
338impl ToTokens for ModuleCode {
339    fn to_tokens(&self, tokens: &mut TokenStream) {
340        let Self { code, usings } = self;
341        let usings = render_usings(usings.iter());
342
343        tokens.extend(quote! {
344            #usings
345            #code
346        });
347    }
348}
349
350/* PendingType */
351
352#[derive(Debug)]
353pub(super) struct PendingType<'types> {
354    pub ty: &'types Type,
355    pub ident: Ident,
356}
357
358/* TypeRef */
359
360#[derive(Debug)]
361pub(super) struct TypeRef {
362    pub ident: Ident,
363    pub type_ident: Ident2,
364    pub module_ident: Option<Ident2>,
365    pub boxed_elements: HashSet<Ident>,
366}
367
368/* TraitInfos */
369
370#[derive(Debug)]
371pub(super) struct TraitInfos(BTreeMap<Ident, TraitInfo>);
372
373impl TraitInfos {
374    #[must_use]
375    pub(super) fn new(types: &Types) -> Self {
376        let mut ret = Self(BTreeMap::new());
377
378        for (base_ident, ty) in types.iter() {
379            let TypeVariant::Dynamic(ai) = &ty.variant else {
380                continue;
381            };
382
383            for type_ident in &ai.derived_types {
384                ret.0
385                    .entry(type_ident.clone())
386                    .or_default()
387                    .traits_all
388                    .insert(base_ident.clone());
389
390                match types.get_variant(type_ident) {
391                    Some(TypeVariant::Dynamic(DynamicInfo {
392                        type_: Some(type_ident),
393                        ..
394                    })) => {
395                        ret.0
396                            .entry(type_ident.clone())
397                            .or_default()
398                            .traits_all
399                            .insert(base_ident.clone());
400                    }
401                    Some(TypeVariant::Reference(ri)) if ri.is_single() => {
402                        ret.0
403                            .entry(ri.type_.clone())
404                            .or_default()
405                            .traits_all
406                            .insert(base_ident.clone());
407                    }
408                    _ => (),
409                }
410            }
411        }
412
413        for ident in ret.0.keys().cloned().collect::<Vec<_>>() {
414            let mut traits_second_level = BTreeSet::new();
415
416            ret.collect_traits(&ident, 0, &mut traits_second_level);
417
418            let info = ret.0.get_mut(&ident).unwrap();
419            info.traits_direct = info
420                .traits_all
421                .difference(&traits_second_level)
422                .cloned()
423                .collect();
424        }
425
426        ret
427    }
428
429    fn collect_traits(
430        &self,
431        ident: &Ident,
432        depth: usize,
433        traits_second_level: &mut BTreeSet<Ident>,
434    ) {
435        if depth > 1 {
436            traits_second_level.insert(ident.clone());
437        }
438
439        if let Some(info) = self.0.get(ident) {
440            for trait_ in &info.traits_all {
441                self.collect_traits(trait_, depth + 1, traits_second_level);
442            }
443        }
444    }
445}
446
447impl Deref for TraitInfos {
448    type Target = BTreeMap<Ident, TraitInfo>;
449
450    fn deref(&self) -> &Self::Target {
451        &self.0
452    }
453}
454
455/* TraitInfo */
456
457#[derive(Default, Debug)]
458pub(super) struct TraitInfo {
459    pub traits_all: BTreeSet<Ident>,
460    pub traits_direct: BTreeSet<Ident>,
461}
462
463/* Occurs */
464
465#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
466pub(super) enum Occurs {
467    #[default]
468    None,
469    Single,
470    Optional,
471    DynamicList,
472    StaticList(usize),
473}
474
475impl Occurs {
476    pub(super) fn from_occurs(min: MinOccurs, max: MaxOccurs) -> Self {
477        match (min, max) {
478            (0, MaxOccurs::Bounded(0)) => Self::None,
479            (1, MaxOccurs::Bounded(1)) => Self::Single,
480            (0, MaxOccurs::Bounded(1)) => Self::Optional,
481            (a, MaxOccurs::Bounded(b)) if a == b => Self::StaticList(a),
482            (_, _) => Self::DynamicList,
483        }
484    }
485
486    pub(super) fn make_type(
487        self,
488        ident: &TokenStream,
489        need_indirection: bool,
490    ) -> Option<TokenStream> {
491        match self {
492            Self::None => None,
493            Self::Single if need_indirection => Some(quote! { Box<#ident> }),
494            Self::Single => Some(quote! { #ident }),
495            Self::Optional if need_indirection => Some(quote! { Option<Box<#ident>> }),
496            Self::Optional => Some(quote! { Option<#ident> }),
497            Self::DynamicList => Some(quote! { Vec<#ident> }),
498            Self::StaticList(sz) if need_indirection => Some(quote! { [Box<#ident>; #sz] }),
499            Self::StaticList(sz) => Some(quote! { [#ident; #sz] }),
500        }
501    }
502
503    pub(super) fn is_some(&self) -> bool {
504        *self != Self::None
505    }
506
507    pub(super) fn is_direct(&self) -> bool {
508        matches!(self, Self::Single | Self::Optional | Self::StaticList(_))
509    }
510}
511
512/* DynTypeTraits */
513
514#[derive(Default, Debug)]
515pub(super) enum DynTypeTraits {
516    #[default]
517    Auto,
518    Custom(Vec<TokenStream>),
519}
520
521/* TypePath */
522
523#[derive(Debug, Clone)]
524pub(super) struct IdentPath {
525    path: Option<ModulePath>,
526    ident: Ident2,
527}
528
529#[derive(Default, Debug, Clone)]
530pub(super) struct ModulePath(pub SmallVec<[Ident2; 2]>);
531
532impl IdentPath {
533    pub(super) fn from_type_ref(type_ref: &TypeRef) -> Self {
534        if type_ref.ident.is_build_in() {
535            Self::from_ident(type_ref.type_ident.clone())
536        } else {
537            Self::from_ident(type_ref.type_ident.clone()).with_path(type_ref.module_ident.clone())
538        }
539    }
540
541    pub(super) fn from_parts<I>(path: I, ident: Ident2) -> Self
542    where
543        I: IntoIterator<Item = Ident2>,
544    {
545        Self::from_ident(ident).with_path(path)
546    }
547
548    pub(super) fn from_ident(ident: Ident2) -> Self {
549        Self { ident, path: None }
550    }
551
552    pub(super) fn with_ident(mut self, ident: Ident2) -> Self {
553        self.ident = ident;
554
555        self
556    }
557
558    pub(super) fn with_path<I>(mut self, path: I) -> Self
559    where
560        I: IntoIterator<Item = Ident2>,
561    {
562        self.path = Some(ModulePath(path.into_iter().collect()));
563
564        self
565    }
566
567    pub(super) fn into_parts(self) -> (Ident2, Option<ModulePath>) {
568        let Self { ident, path } = self;
569
570        (ident, path)
571    }
572
573    pub(super) fn ident(&self) -> &Ident2 {
574        &self.ident
575    }
576
577    pub(super) fn relative_to(&self, dst: &ModulePath) -> TokenStream {
578        let ident = &self.ident;
579
580        let Some(src) = &self.path else {
581            return quote!(#ident);
582        };
583
584        let mut ret = TokenStream::new();
585        let mut src = src.0.iter().fuse();
586        let mut dst = dst.0.iter().fuse();
587
588        macro_rules! push {
589            ($x:expr) => {{
590                let x = $x;
591                if ret.is_empty() {
592                    ret.extend(x)
593                } else {
594                    ret.extend(quote!(::#x))
595                }
596            }};
597        }
598
599        loop {
600            match (src.next(), dst.next()) {
601                (Some(a), Some(b)) if a == b => {}
602                (Some(a), Some(_)) => {
603                    push!(quote!(super));
604                    while dst.next().is_some() {
605                        push!(quote!(super));
606                    }
607
608                    push!(quote!(#a));
609                    for a in src {
610                        push!(quote!(#a));
611                    }
612
613                    push!(quote!(#ident));
614
615                    return ret;
616                }
617                (Some(a), None) => push!(quote!(#a)),
618                (None, Some(_)) => push!(quote!(super)),
619                (None, None) => {
620                    push!(quote!(#ident));
621                    return ret;
622                }
623            }
624        }
625    }
626}
627
628impl FromStr for IdentPath {
629    type Err = ();
630
631    fn from_str(s: &str) -> Result<Self, Self::Err> {
632        let mut ident = None;
633        let mut path = ModulePath::default();
634
635        for part in s.split("::") {
636            let part = part.trim();
637            if part.is_empty() {
638                continue;
639            }
640
641            if let Some(ident) = ident.take() {
642                path.0.push(ident);
643            }
644
645            ident = Some(format_ident!("{part}"));
646        }
647
648        Ok(Self {
649            ident: ident.ok_or(())?,
650            path: Some(path),
651        })
652    }
653}
654
655impl ModulePath {
656    pub(super) fn from_namespace(ns: Option<NamespaceId>, types: &Types) -> Self {
657        let ident = ns
658            .and_then(|id| types.modules.get(&id))
659            .and_then(|module| module.name.as_ref())
660            .map(format_module_ident);
661
662        Self(ident.into_iter().collect())
663    }
664
665    pub(super) fn join(mut self, other: Ident2) -> Self {
666        self.0.push(other);
667
668        self
669    }
670}
671
672impl Deref for ModulePath {
673    type Target = SmallVec<[Ident2; 2]>;
674
675    fn deref(&self) -> &Self::Target {
676        &self.0
677    }
678}
679
680impl DerefMut for ModulePath {
681    fn deref_mut(&mut self) -> &mut Self::Target {
682        &mut self.0
683    }
684}
685
686/* Helper */
687
688pub(super) fn format_field_name(name: &Name, display_name: Option<&str>) -> Cow<'static, str> {
689    if let Some(display_name) = display_name {
690        return Cow::Owned(display_name.to_snake_case());
691    }
692
693    let ident = name
694        .to_type_name(false, None)
695        .as_str()
696        .unwrap()
697        .to_snake_case();
698
699    match KEYWORDS.binary_search_by(|(key, _)| key.cmp(&ident.as_str())) {
700        Ok(idx) => Cow::Borrowed(KEYWORDS[idx].1),
701        Err(_) => {
702            if ident.starts_with(char::is_numeric) {
703                Cow::Owned(format!("_{ident}"))
704            } else {
705                Cow::Owned(ident)
706            }
707        }
708    }
709}
710
711pub(super) fn format_field_ident(name: &Name, display_name: Option<&str>) -> Ident2 {
712    let ident = format_field_name(name, display_name);
713
714    format_ident!("{ident}")
715}
716
717pub(super) fn format_module_ident(name: &Name) -> Ident2 {
718    format_field_ident(name, None)
719}
720
721pub(super) fn format_type_name(name: &Name, display_name: Option<&str>) -> String {
722    if let Some(display_name) = display_name {
723        return display_name.to_pascal_case();
724    }
725
726    let name = name
727        .to_type_name(false, None)
728        .as_str()
729        .unwrap()
730        .to_pascal_case();
731
732    if name.starts_with(char::is_numeric) {
733        format!("_{name}")
734    } else {
735        name
736    }
737}
738
739pub(super) fn format_type_ident(name: &Name, display_name: Option<&str>) -> Ident2 {
740    let ident = format_type_name(name, display_name);
741
742    format_ident!("{ident}")
743}
744
745pub(super) fn format_variant_ident(name: &Name, display_name: Option<&str>) -> Ident2 {
746    format_type_ident(name, display_name)
747}
748
749pub(super) fn format_module(
750    types: &Types,
751    ns: Option<NamespaceId>,
752) -> Result<Option<Ident2>, Error> {
753    let Some(ns) = ns else {
754        return Ok(None);
755    };
756
757    let module = types.modules.get(&ns).ok_or(Error::UnknownNamespace(ns))?;
758    let Some(name) = &module.name else {
759        return Ok(None);
760    };
761
762    Ok(Some(format_module_ident(name)))
763}
764
765pub(super) fn make_type_name(postfixes: &[String], ty: &Type, ident: &Ident) -> Name {
766    match (&ty.variant, &ident.name) {
767        (TypeVariant::Reference(ti), Name::Unnamed { .. }) if ti.type_.name.is_named() => {
768            match Occurs::from_occurs(ti.min_occurs, ti.max_occurs) {
769                Occurs::DynamicList => return Name::new(format!("{}List", ti.type_.name)),
770                Occurs::Optional => return Name::new(format!("{}Opt", ti.type_.name)),
771                _ => (),
772            }
773        }
774        (_, _) => (),
775    };
776
777    let postfix = postfixes
778        .get(ident.type_ as usize)
779        .map_or("", |s| s.as_str());
780
781    match &ident.name {
782        Name::Named(s) if s.ends_with(postfix) => Name::Named(s.clone()),
783        Name::Named(s) => Name::Named(Cow::Owned(format!("{}{postfix}", Name::unify(s)))),
784        name => name.to_type_name(false, None),
785    }
786}
787
788const KEYWORDS: &[(&str, &str)] = &[
789    ("abstract", "abstract_"),
790    ("as", "as_"),
791    ("become", "become_"),
792    ("box", "box_"),
793    ("break", "break_"),
794    ("const", "const_"),
795    ("continue", "continue_"),
796    ("crate", "crate_"),
797    ("do", "do_"),
798    ("else", "else_"),
799    ("enum", "enum_"),
800    ("extern", "extern_"),
801    ("false", "false_"),
802    ("final", "final_"),
803    ("fn", "fn_"),
804    ("for", "for_"),
805    ("if", "if_"),
806    ("impl", "impl_"),
807    ("in", "in_"),
808    ("let", "let_"),
809    ("loop", "loop_"),
810    ("macro", "macro_"),
811    ("match", "match_"),
812    ("mod", "mod_"),
813    ("move", "move_"),
814    ("mut", "mut_"),
815    ("override", "override_"),
816    ("priv", "priv_"),
817    ("pub", "pub_"),
818    ("ref", "ref_"),
819    ("return", "return_"),
820    ("self", "self_"),
821    ("Self", "Self_"),
822    ("static", "static_"),
823    ("struct", "struct_"),
824    ("super", "super_"),
825    ("trait", "trait_"),
826    ("true", "true_"),
827    ("try", "try_"),
828    ("type", "type_"),
829    ("typeof", "typeof_"),
830    ("union", "union_"),
831    ("unsafe", "unsafe_"),
832    ("unsized", "unsized_"),
833    ("use", "use_"),
834    ("virtual", "virtual_"),
835    ("where", "where_"),
836    ("while", "while_"),
837    ("yield", "yield_"),
838];
839
840#[cfg(test)]
841mod tests {
842    use quote::{format_ident, quote};
843
844    use crate::generator::misc::ModulePath;
845
846    use super::IdentPath;
847
848    #[test]
849    #[rustfmt::skip]
850    fn type_path() {
851        let string = IdentPath::from_ident(format_ident!("String"));
852        let my_type = IdentPath::from_parts(
853            [format_ident!("my_module")],
854            format_ident!("MyType"),
855        );
856        let serializer = IdentPath::from_parts(
857            [
858                format_ident!("my_module"),
859                format_ident!("quick_xml_serialize"),
860            ],
861            format_ident!("MyTypeSerializer"),
862        );
863        let deserializer = IdentPath::from_parts(
864            [
865                format_ident!("my_module"),
866                format_ident!("quick_xml_deserialize"),
867            ],
868            format_ident!("MyTypeDeserializer"),
869        );
870
871        let empty_path = ModulePath::default();
872        let module_path = ModulePath::default().join(format_ident!("my_module"));
873        let other_module_path = ModulePath::default().join(format_ident!("other_module"));
874        let serializer_path = module_path.clone().join(format_ident!("quick_xml_serialize"));
875        let deserializer_path = module_path.clone().join(format_ident!("quick_xml_deserialize"));
876
877        macro_rules! test {
878            ($actual:expr, $( $expected:tt )*) => {{
879                let a = $actual.to_string();
880                let b = quote!($( $expected )*).to_string();
881
882                assert_eq!(a, b);
883            }};
884        }
885
886        /* With modules */
887
888        test!(string.relative_to(&empty_path), String);
889        test!(string.relative_to(&module_path), String);
890        test!(string.relative_to(&other_module_path), String);
891        test!(string.relative_to(&serializer_path), String);
892        test!(string.relative_to(&deserializer_path), String);
893
894        test!(my_type.relative_to(&empty_path), my_module::MyType);
895        test!(my_type.relative_to(&module_path), MyType);
896        test!(my_type.relative_to(&other_module_path), super::my_module::MyType);
897        test!(my_type.relative_to(&serializer_path), super::MyType);
898        test!(my_type.relative_to(&deserializer_path), super::MyType);
899
900        test!(serializer.relative_to(&empty_path), my_module::quick_xml_serialize::MyTypeSerializer);
901        test!(serializer.relative_to(&module_path), quick_xml_serialize::MyTypeSerializer);
902        test!(serializer.relative_to(&other_module_path), super::my_module::quick_xml_serialize::MyTypeSerializer);
903        test!(serializer.relative_to(&serializer_path), MyTypeSerializer);
904        test!(serializer.relative_to(&deserializer_path), super::quick_xml_serialize::MyTypeSerializer);
905
906        test!(deserializer.relative_to(&empty_path), my_module::quick_xml_deserialize::MyTypeDeserializer);
907        test!(deserializer.relative_to(&module_path), quick_xml_deserialize::MyTypeDeserializer);
908        test!(deserializer.relative_to(&other_module_path), super::my_module::quick_xml_deserialize::MyTypeDeserializer);
909        test!(deserializer.relative_to(&serializer_path), super::quick_xml_deserialize::MyTypeDeserializer);
910        test!(deserializer.relative_to(&deserializer_path), MyTypeDeserializer);
911    }
912}