Skip to main content

xsd_parser/models/schema/
mod.rs

1//! The `schema` module contains internal representations of XML Schema (XSD) structures.
2//!
3//! This module defines types that model loaded XML schemas, namespaces, and their relationships,
4//! serving as the output of the [`Parser`](crate::Parser) and input to the
5//! [`Interpreter`](crate::Interpreter).
6//!
7//! It manages the resolution of namespaces and tracks schema documents across multiple sources.
8
9pub mod xs;
10
11mod occurs;
12
13use std::collections::btree_map::{BTreeMap, Iter, IterMut};
14use std::ops::Deref;
15
16use url::Url;
17
18use xsd_parser_types::misc::{Namespace, NamespacePrefix};
19
20use self::xs::Schema;
21
22pub use xsd_parser_types::xml::QName;
23
24pub use self::occurs::{MaxOccurs, MinOccurs};
25
26/// Top-level structure for managing loaded XML schema files and associated namespaces.
27///
28/// This type is created and populated by the [`Parser`](crate::Parser), and used
29/// by the [`Interpreter`](crate::Interpreter) to resolve schema components into
30/// meaningful Rust types.
31///
32/// It tracks all loaded schemas, the namespaces they belong to, and which prefixes
33/// are associated with which namespace URIs. Each namespace and schema is assigned
34/// a unique ID (`NamespaceId`, `SchemaId`) to allow efficient lookup and association.
35///
36/// This type supports iterating over loaded schemas and namespaces,
37/// as well as resolving prefixes and namespaces during interpretation.
38#[derive(Debug)]
39pub struct Schemas {
40    pub(crate) schemas: SchemaFiles,
41    pub(crate) namespace_infos: NamespaceInfos,
42
43    pub(crate) known_prefixes: NamespacePrefixes,
44    pub(crate) known_namespaces: Namespaces,
45
46    pub(crate) last_schema_id: usize,
47    pub(crate) last_namespace_id: usize,
48}
49
50/// Contains the information for a specific namespace.
51#[derive(Debug)]
52pub struct NamespaceInfo {
53    /// First used/known prefix of the namespace or `None` if it is unknown.
54    pub prefix: Option<NamespacePrefix>,
55
56    /// URI of the namespace or `None` if it is the global or no namespace.
57    pub namespace: Option<Namespace>,
58
59    /// Schema files associated with this namespace.
60    pub schemas: Vec<SchemaId>,
61
62    /// User defined name to use for module generation for this namespace.
63    pub module_name: Option<String>,
64}
65
66/// Contains information for a specific schema
67#[derive(Debug)]
68pub struct SchemaInfo {
69    /// Name of the schema.
70    pub name: Option<String>,
71
72    /// The actual schema data.
73    pub schema: Schema,
74
75    /// Location the schema was load from.
76    pub location: Option<Url>,
77
78    /// Id of the namespace this schema belongs to.
79    pub(crate) namespace_id: NamespaceId,
80
81    /// Dependencies of this schema, mapping schema paths to their IDs.
82    pub(crate) dependencies: BTreeMap<String, Dependency<SchemaId>>,
83}
84
85/// Represents the different types of dependencies a schema can have on another schema.
86#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
87pub enum Dependency<T> {
88    /// The schema is included by another schema.
89    Include(T),
90
91    /// The schema is imported by another schema.
92    Import(T),
93
94    /// The schema is overridden by another schema.
95    Override(T),
96
97    /// The schema is redefined by another schema.
98    Redefine(T),
99}
100
101/// Represents an unique id for a XML schema.
102#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
103pub struct SchemaId(pub usize);
104
105/// Represents a unique id for a XML namespace.
106#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
107pub struct NamespaceId(pub usize);
108
109/// Map of [`SchemaId`] to [`Schema`]
110pub type SchemaFiles = BTreeMap<SchemaId, SchemaInfo>;
111
112/// Map of [`NamespaceId`] to [`NamespaceInfo`]
113pub type NamespaceInfos = BTreeMap<NamespaceId, NamespaceInfo>;
114
115/// Map of [`Namespace`] to [`NamespaceId`]
116pub type Namespaces = BTreeMap<Option<Namespace>, NamespaceId>;
117
118/// Map of [`NamespacePrefix`] to [`NamespaceId`]
119pub type NamespacePrefixes = BTreeMap<NamespacePrefix, NamespaceId>;
120
121/* Schemas */
122
123impl Schemas {
124    /// Returns an iterator over all schemas stored in this structure.
125    pub fn schemas(&self) -> Iter<'_, SchemaId, SchemaInfo> {
126        self.schemas.iter()
127    }
128
129    /// Returns a mutable iterator over all schemas stored in this structure.
130    pub fn schemas_mut(&mut self) -> IterMut<'_, SchemaId, SchemaInfo> {
131        self.schemas.iter_mut()
132    }
133
134    /// Returns an iterator over all namespace information instances stored
135    /// in this structure.
136    pub fn namespaces(&self) -> Iter<'_, NamespaceId, NamespaceInfo> {
137        self.namespace_infos.iter()
138    }
139
140    /// Returns a reference to a specific schema by using the schema id,
141    /// or `None` if the schema is not known.
142    #[must_use]
143    pub fn get_schema(&self, id: &SchemaId) -> Option<&SchemaInfo> {
144        self.schemas.get(id)
145    }
146
147    /// Returns a mutable reference to a specific schema by using the schema id,
148    /// or `None` if the schema is not known.
149    #[must_use]
150    pub fn get_schema_mut(&mut self, id: &SchemaId) -> Option<&mut SchemaInfo> {
151        self.schemas.get_mut(id)
152    }
153
154    /// Returns a reference to a specific namespace information instance by using
155    /// the namespace id.
156    #[must_use]
157    pub fn get_namespace_info(&self, id: &NamespaceId) -> Option<&NamespaceInfo> {
158        self.namespace_infos.get(id)
159    }
160
161    /// Returns a mutable reference to a specific namespace information instance
162    /// by using the namespace id.
163    #[must_use]
164    pub fn get_namespace_info_mut(&mut self, id: &NamespaceId) -> Option<&mut NamespaceInfo> {
165        self.namespace_infos.get_mut(id)
166    }
167
168    /// Returns a reference to a specific namespace information instance by using
169    /// the namespace URI, or `None` if the schema is not known.
170    #[must_use]
171    pub fn get_namespace_info_by_namespace(
172        &self,
173        ns: &Option<Namespace>,
174    ) -> Option<&NamespaceInfo> {
175        let id = self.resolve_namespace(ns)?;
176
177        self.get_namespace_info(&id)
178    }
179
180    /// Try to resolve the namespace prefix to a namespace id.
181    ///
182    /// Returns the namespace id of the given namespace `prefix`, or `None`.
183    #[must_use]
184    pub fn resolve_prefix(&self, prefix: &NamespacePrefix) -> Option<NamespaceId> {
185        Some(*self.known_prefixes.get(prefix)?)
186    }
187
188    /// Try to resolve the namespace to a namespace id.
189    ///
190    /// Returns the namespace id of the given namespace `ns`, or `None`.
191    #[must_use]
192    pub fn resolve_namespace(&self, ns: &Option<Namespace>) -> Option<NamespaceId> {
193        Some(*self.known_namespaces.get(ns)?)
194    }
195
196    #[must_use]
197    pub(crate) fn next_schema_id(&mut self) -> SchemaId {
198        self.last_schema_id = self.last_schema_id.wrapping_add(1);
199
200        SchemaId(self.last_schema_id)
201    }
202}
203
204impl Default for Schemas {
205    fn default() -> Self {
206        Self {
207            schemas: SchemaFiles::default(),
208            namespace_infos: NamespaceInfos::default(),
209
210            known_prefixes: NamespacePrefixes::default(),
211            known_namespaces: Namespaces::default(),
212
213            last_schema_id: SchemaId::UNKNOWN.0,
214            last_namespace_id: NamespaceId::ANONYMOUS.0,
215        }
216    }
217}
218
219/* NamespaceInfo */
220
221impl NamespaceInfo {
222    /// Create a new [`NamespaceInfo`] instance from the passed `namespace`.
223    #[must_use]
224    pub fn new(namespace: Option<Namespace>) -> Self {
225        Self {
226            prefix: None,
227            namespace,
228            schemas: Vec::new(),
229            module_name: None,
230        }
231    }
232
233    /// Return the name of the namespace.
234    ///
235    /// This is either the [custom name](Self::module_name) or the namespace
236    /// [`prefix`](Self::prefix).
237    #[must_use]
238    pub fn name(&self) -> Option<String> {
239        self.module_name
240            .clone()
241            .or_else(|| self.prefix.as_ref().map(ToString::to_string))
242    }
243}
244
245/* SchemaInfo */
246
247impl SchemaInfo {
248    /// Get the id of the namespace this schema belongs to.
249    #[must_use]
250    pub fn namespace_id(&self) -> NamespaceId {
251        self.namespace_id
252    }
253
254    /// Get the dependencies of this schema, mapping schema paths to their IDs.
255    #[must_use]
256    pub fn dependencies(&self) -> &BTreeMap<String, Dependency<SchemaId>> {
257        &self.dependencies
258    }
259
260    /// Returns `true` if this schema depends on the given schema id, `false` otherwise.
261    #[must_use]
262    pub fn depends_on(&self, schema_id: &SchemaId) -> bool {
263        self.dependencies
264            .values()
265            .any(|id| id.as_ref() == schema_id)
266    }
267}
268
269/* Dependency */
270
271impl<T> Dependency<T> {
272    /// Maps the inner value of this dependency to another type using the provided function `f`.
273    pub fn map<F, U>(self, f: F) -> Dependency<U>
274    where
275        F: FnOnce(T) -> U,
276    {
277        match self {
278            Dependency::Include(x) => Dependency::Include(f(x)),
279            Dependency::Import(x) => Dependency::Import(f(x)),
280            Dependency::Override(x) => Dependency::Override(f(x)),
281            Dependency::Redefine(x) => Dependency::Redefine(f(x)),
282        }
283    }
284}
285
286impl<T> Deref for Dependency<T> {
287    type Target = T;
288
289    fn deref(&self) -> &Self::Target {
290        match self {
291            Dependency::Include(x)
292            | Dependency::Import(x)
293            | Dependency::Override(x)
294            | Dependency::Redefine(x) => x,
295        }
296    }
297}
298
299impl<T> AsRef<T> for Dependency<T> {
300    fn as_ref(&self) -> &T {
301        match self {
302            Dependency::Include(x)
303            | Dependency::Import(x)
304            | Dependency::Override(x)
305            | Dependency::Redefine(x) => x,
306        }
307    }
308}
309
310/* SchemaId */
311
312impl SchemaId {
313    /// Represents the unknown [`SchemaId`]
314    pub const UNKNOWN: Self = Self(0);
315
316    /// Returns `true` if this schema id is unknown, `false` otherwise.
317    #[must_use]
318    pub fn is_unknown(&self) -> bool {
319        self.eq(&Self::UNKNOWN)
320    }
321
322    /// Returns `other` if this schema id is unknown, `self` otherwise.
323    #[inline]
324    #[must_use]
325    pub fn or(self, other: Self) -> Self {
326        if self.is_unknown() {
327            other
328        } else {
329            self
330        }
331    }
332}
333
334/* NamespaceId */
335
336impl NamespaceId {
337    /// Represents the unknown [`NamespaceId`]
338    pub const UNKNOWN: Self = Self(0);
339
340    /// Represents the anonymous [`NamespaceId`]
341    pub const ANONYMOUS: Self = Self(1);
342
343    /// Returns `true` if this namespace id is unknown, `false` otherwise.
344    #[inline]
345    #[must_use]
346    pub fn is_unknown(&self) -> bool {
347        self.eq(&Self::UNKNOWN)
348    }
349
350    /// Returns `true` if this namespace id is anonymous, `false` otherwise.
351    #[inline]
352    #[must_use]
353    pub fn is_anonymous(&self) -> bool {
354        self.eq(&Self::ANONYMOUS)
355    }
356
357    /// Returns `other` if this namespace id is unknown, `self` otherwise.
358    #[inline]
359    #[must_use]
360    pub fn or(self, other: Self) -> Self {
361        if self.is_unknown() {
362            other
363        } else {
364            self
365        }
366    }
367}