Skip to main content

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    Name, Naming, TypeIdent,
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<TypeIdent, 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>(
81        &'a self,
82        ident: &'a TypeIdent,
83    ) -> Option<(&'a TypeIdent, &'a MetaType)> {
84        let mut visit = Vec::new();
85
86        get_resolved_impl(self, ident, &mut visit)
87    }
88
89    /// Get the type of the passed `ident` with all single type references resolved.
90    ///
91    /// Like [`get_resolved`](Self::get_resolved), but instead of returning the identifier and
92    /// the type it will return only the resolved type.
93    #[must_use]
94    pub fn get_resolved_type<'a>(&'a self, ident: &'a TypeIdent) -> Option<&'a MetaType> {
95        self.get_resolved(ident).map(|(_ident, ty)| ty)
96    }
97
98    /// Get the type ident of the passed `ident` with all single type references resolved.
99    ///
100    /// Like [`get_resolved`](Self::get_resolved), but instead of returning the identifier and
101    /// the type it will return only the identifier of the resolved type.
102    #[must_use]
103    pub fn get_resolved_ident<'a>(&'a self, ident: &'a TypeIdent) -> Option<&'a TypeIdent> {
104        self.get_resolved(ident).map(|(ident, _ty)| ident)
105    }
106
107    /// Return the [`MetaTypeVariant`] of corresponding type for the passed identifier.
108    ///
109    /// This is a shorthand for `self.get(ident).map(|ty| &type.variant)`.
110    #[inline]
111    #[must_use]
112    pub fn get_variant(&self, ident: &TypeIdent) -> Option<&MetaTypeVariant> {
113        self.items.get(ident).map(|ty| &ty.variant)
114    }
115
116    /// Return the [`MetaTypeVariant`] of corresponding type for the passed identifier.
117    ///
118    /// This is a shorthand for `self.get_mut(ident).map(|ty| &type.variant)`.
119    #[inline]
120    #[must_use]
121    pub fn get_variant_mut(&mut self, ident: &TypeIdent) -> Option<&mut MetaTypeVariant> {
122        self.items.get_mut(ident).map(|ty| &mut ty.variant)
123    }
124}
125
126impl Default for MetaTypes {
127    fn default() -> Self {
128        Self {
129            items: Default::default(),
130            modules: Default::default(),
131            schemas: Default::default(),
132            naming: Box::new(Naming::default()),
133        }
134    }
135}
136
137impl ModuleMeta {
138    /// Get the name or the prefix of the module.
139    #[must_use]
140    pub fn name(&self) -> Option<&Name> {
141        self.name.as_ref().or(self.prefix.as_ref())
142    }
143
144    /// Get the prefix or the name of the module.
145    #[must_use]
146    pub fn prefix(&self) -> Option<&Name> {
147        self.prefix.as_ref().or(self.name.as_ref())
148    }
149}
150
151fn get_resolved_impl<'a>(
152    types: &'a MetaTypes,
153    ident: &'a TypeIdent,
154    visited: &mut Vec<&'a TypeIdent>,
155) -> Option<(&'a TypeIdent, &'a MetaType)> {
156    if visited.contains(&ident) {
157        let chain = visited
158            .iter()
159            .map(ToString::to_string)
160            .chain(Some(ident.to_string()))
161            .collect::<Vec<_>>()
162            .join(" >> ");
163
164        tracing::debug!("Detected type reference loop: {chain}");
165
166        return None;
167    }
168
169    let ty = types.items.get(ident)?;
170
171    match &ty.variant {
172        MetaTypeVariant::Reference(x) if x.is_simple() => {
173            visited.push(ident);
174
175            let ret = match get_resolved_impl(types, &x.type_, visited) {
176                None => Some((ident, ty)),
177                Some((ident, ty)) => Some((ident, ty)),
178            };
179
180            visited.pop();
181
182            ret
183        }
184        _ => Some((ident, ty)),
185    }
186}