xsd_parser/models/meta/
types.rs

1use std::collections::BTreeMap;
2
3use xsd_parser_types::misc::Namespace;
4
5use crate::models::{
6    schema::{NamespaceId, SchemaId},
7    Ident, Name, Naming,
8};
9use crate::traits::{NameBuilder as NameBuilderTrait, Naming as NamingTrait};
10
11use super::{MetaType, MetaTypeVariant};
12
13/// Intermediate representation of all resolved type and module metadata.
14///
15/// This structure is created by the [`Interpreter`](crate::Interpreter)
16/// from the parsed [`Schemas`](crate::Schemas). It contains the full set of
17/// interpreted types, along with module-to-namespace mappings.
18///
19/// This type acts as the canonical source of truth for all derived type definitions,
20/// which can be passed to the [`Optimizer`](crate::Optimizer) for post-processing,
21/// or used directly by the code [`Generator`](crate::Generator).
22#[derive(Debug)]
23pub struct MetaTypes {
24    /// Map of the different types.
25    pub items: BTreeMap<Ident, MetaType>,
26
27    /// Map of the different namespaces.
28    pub modules: BTreeMap<NamespaceId, ModuleMeta>,
29
30    /// Map of the different schemas.
31    pub schemas: BTreeMap<SchemaId, SchemaMeta>,
32
33    /// Trait to control how name generation is done in `xsd_parser`.
34    pub naming: Box<dyn NamingTrait>,
35}
36
37/// Represents a module used by type information in the [`MetaTypes`] structure.
38#[derive(Debug)]
39pub struct ModuleMeta {
40    /// Name of the module.
41    pub name: Option<Name>,
42
43    /// Prefix of the modules namespace.
44    pub prefix: Option<Name>,
45
46    /// Namespace of the module.
47    pub namespace: Option<Namespace>,
48
49    /// Id of the namespace the module was created from.
50    pub namespace_id: NamespaceId,
51
52    /// Number of schemas assigned to this module/namespace.
53    pub schema_count: usize,
54}
55
56/// Represents a schema used by type information in the [`MetaTypes`] structure.
57#[derive(Debug)]
58pub struct SchemaMeta {
59    /// Name of the schema.
60    pub name: Option<Name>,
61
62    /// Id of the namespace this schema belongs to.
63    pub namespace: NamespaceId,
64}
65
66impl MetaTypes {
67    /// Create a new boxed [`NameBuilder`](NameBuilderTrait), that can be used to build type names.
68    #[must_use]
69    pub fn name_builder(&self) -> Box<dyn NameBuilderTrait> {
70        self.naming.builder()
71    }
72
73    /// Get the identifier and the type of the passed `ident` with all single
74    /// type references resolved.
75    ///
76    /// Tries to find the type specified by the passed `ident` and resolve simple
77    /// type definitions to the very base type. If the type could not be found `None`
78    /// is returned.
79    #[must_use]
80    pub fn get_resolved<'a>(&'a self, ident: &'a Ident) -> Option<(&'a Ident, &'a MetaType)> {
81        let mut visit = Vec::new();
82
83        get_resolved_impl(self, ident, &mut visit)
84    }
85
86    /// Get the type of the passed `ident` with all single type references resolved.
87    ///
88    /// Like [`get_resolved`](Self::get_resolved), but instead of returning the identifier and
89    /// the type it will return only the resolved type.
90    #[must_use]
91    pub fn get_resolved_type<'a>(&'a self, ident: &'a Ident) -> Option<&'a MetaType> {
92        self.get_resolved(ident).map(|(_ident, ty)| ty)
93    }
94
95    /// Get the type ident of the passed `ident` with all single type references resolved.
96    ///
97    /// Like [`get_resolved`](Self::get_resolved), but instead of returning the identifier and
98    /// the type it will return only the identifier of the resolved type.
99    #[must_use]
100    pub fn get_resolved_ident<'a>(&'a self, ident: &'a Ident) -> Option<&'a Ident> {
101        self.get_resolved(ident).map(|(ident, _ty)| ident)
102    }
103
104    /// Return the [`MetaTypeVariant`] of corresponding type for the passed identifier.
105    ///
106    /// This is a shorthand for `self.get(ident).map(|ty| &type.variant)`.
107    #[inline]
108    #[must_use]
109    pub fn get_variant(&self, ident: &Ident) -> Option<&MetaTypeVariant> {
110        self.items.get(ident).map(|ty| &ty.variant)
111    }
112
113    /// Return the [`MetaTypeVariant`] of corresponding type for the passed identifier.
114    ///
115    /// This is a shorthand for `self.get_mut(ident).map(|ty| &type.variant)`.
116    #[inline]
117    #[must_use]
118    pub fn get_variant_mut(&mut self, ident: &Ident) -> Option<&mut MetaTypeVariant> {
119        self.items.get_mut(ident).map(|ty| &mut ty.variant)
120    }
121}
122
123impl Default for MetaTypes {
124    fn default() -> Self {
125        Self {
126            items: Default::default(),
127            modules: Default::default(),
128            schemas: Default::default(),
129            naming: Box::new(Naming::default()),
130        }
131    }
132}
133
134impl ModuleMeta {
135    /// Get the name or the prefix of the module.
136    #[must_use]
137    pub fn name(&self) -> Option<&Name> {
138        self.name.as_ref().or(self.prefix.as_ref())
139    }
140
141    /// Get the prefix or the name of the module.
142    #[must_use]
143    pub fn prefix(&self) -> Option<&Name> {
144        self.prefix.as_ref().or(self.name.as_ref())
145    }
146}
147
148fn get_resolved_impl<'a>(
149    types: &'a MetaTypes,
150    ident: &'a Ident,
151    visited: &mut Vec<&'a Ident>,
152) -> Option<(&'a Ident, &'a MetaType)> {
153    if visited.contains(&ident) {
154        let chain = visited
155            .iter()
156            .map(ToString::to_string)
157            .chain(Some(ident.to_string()))
158            .collect::<Vec<_>>()
159            .join(" >> ");
160
161        tracing::debug!("Detected type reference loop: {chain}");
162
163        return None;
164    }
165
166    let ty = types.items.get(ident)?;
167
168    match &ty.variant {
169        MetaTypeVariant::Reference(x) if x.is_simple() => {
170            visited.push(ident);
171
172            let ret = match get_resolved_impl(types, &x.type_, visited) {
173                None => Some((ident, ty)),
174                Some((ident, ty)) => Some((ident, ty)),
175            };
176
177            visited.pop();
178
179            ret
180        }
181        _ => Some((ident, ty)),
182    }
183}