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 namespace;
12mod namespace_prefix;
13mod occurs;
14mod qname;
15
16use std::borrow::Cow;
17use std::collections::btree_map::{BTreeMap, Entry, Iter, IterMut};
18
19use self::xs::Schema;
20
21pub use namespace::Namespace;
22pub use namespace_prefix::NamespacePrefix;
23pub use occurs::{MaxOccurs, MinOccurs};
24pub use qname::QName;
25use url::Url;
26
27/// Top-level structure for managing loaded XML schema files and associated namespaces.
28///
29/// This type is created and populated by the [`Parser`](crate::Parser), and used
30/// by the [`Interpreter`](crate::Interpreter) to resolve schema components into
31/// meaningful Rust types.
32///
33/// It tracks all loaded schemas, the namespaces they belong to, and which prefixes
34/// are associated with which namespace URIs. Each namespace and schema is assigned
35/// a unique ID (`NamespaceId`, `SchemaId`) to allow efficient lookup and association.
36///
37/// This type supports iterating over loaded schemas and namespaces,
38/// as well as resolving prefixes and namespaces during interpretation.
39#[derive(Default, Debug)]
40pub struct Schemas {
41    schemas: SchemaFiles,
42    namespace_infos: NamespaceInfos,
43
44    known_prefixes: NamespacePrefixes,
45    known_namespaces: Namespaces,
46
47    next_schema_id: usize,
48    next_namespace_id: usize,
49}
50
51/// Contains the information for a specific namespace.
52#[derive(Debug)]
53pub struct NamespaceInfo {
54    /// First used/known prefix of the namespace or `None` if it is unknown.
55    pub prefix: Option<NamespacePrefix>,
56
57    /// URI of the namespace or `None` if it is the global or no namespace.
58    pub namespace: Option<Namespace>,
59
60    /// Schema files associated with this namespace.
61    pub schemas: Vec<SchemaId>,
62
63    /// User defined name to use for module generation for this namespace.
64    pub module_name: Option<String>,
65}
66
67/// Contains information for a specific schema
68#[derive(Debug)]
69pub struct SchemaInfo {
70    /// Name of the schema.
71    pub name: Option<String>,
72
73    /// The actual schema data.
74    pub schema: Schema,
75
76    /// Location the schema was load from.
77    pub location: Option<Url>,
78
79    /// Id of the namespace this schema belongs to.
80    namespace_id: NamespaceId,
81}
82
83/// Represents an unique id for a XML schema.
84#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
85pub struct SchemaId(pub usize);
86
87/// Represents a unique id for a XML namespace.
88#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
89pub struct NamespaceId(pub usize);
90
91/// Map of [`SchemaId`] to [`Schema`]
92pub type SchemaFiles = BTreeMap<SchemaId, SchemaInfo>;
93
94/// Map of [`NamespaceId`] to [`NamespaceInfo`]
95pub type NamespaceInfos = BTreeMap<NamespaceId, NamespaceInfo>;
96
97/// Map of [`Namespace`] to [`NamespaceId`]
98pub type Namespaces = BTreeMap<Option<Namespace>, NamespaceId>;
99
100/// Map of [`NamespacePrefix`] to [`NamespaceId`]
101pub type NamespacePrefixes = BTreeMap<NamespacePrefix, NamespaceId>;
102
103/* Schemas */
104
105impl Schemas {
106    /// Add a new schema to the schemas structure.
107    ///
108    /// # Errors
109    ///
110    /// Will just forward the errors from [`get_or_create_namespace_info_mut`](Self::get_or_create_namespace_info_mut).
111    pub fn add_schema(
112        &mut self,
113        prefix: Option<NamespacePrefix>,
114        namespace: Option<Namespace>,
115        name: Option<String>,
116        schema: Schema,
117        location: Option<Url>,
118    ) {
119        let schema_id = SchemaId(self.next_schema_id);
120        let (namespace_id, namespace_info) =
121            self.get_or_create_namespace_info_mut(prefix, namespace);
122        namespace_info.schemas.push(schema_id);
123        self.next_schema_id = self.next_schema_id.wrapping_add(1);
124
125        match self.schemas.entry(schema_id) {
126            Entry::Vacant(e) => e.insert(SchemaInfo {
127                name,
128                schema,
129                location,
130                namespace_id,
131            }),
132            Entry::Occupied(_) => crate::unreachable!(),
133        };
134    }
135
136    /// Get a mutable reference to a [`NamespaceInfo`] or create a new one if needed.
137    pub fn get_or_create_namespace_info_mut(
138        &mut self,
139        prefix: Option<NamespacePrefix>,
140        namespace: Option<Namespace>,
141    ) -> (NamespaceId, &mut NamespaceInfo) {
142        let (ns, id) = match self.known_namespaces.entry(namespace) {
143            Entry::Occupied(e) => {
144                let id = *e.get();
145                let ns = self.namespace_infos.get_mut(&id).unwrap();
146
147                (ns, id)
148            }
149            Entry::Vacant(e) => {
150                let id = NamespaceId(self.next_namespace_id);
151                self.next_namespace_id = self.next_namespace_id.wrapping_add(1);
152
153                let namespace = e.key().clone();
154                e.insert(id);
155
156                let ns = match self.namespace_infos.entry(id) {
157                    Entry::Vacant(e) => e.insert(NamespaceInfo::new(namespace)),
158                    Entry::Occupied(_) => crate::unreachable!(),
159                };
160
161                (ns, id)
162            }
163        };
164
165        if let Some(mut prefix) = prefix {
166            if matches!(self.known_prefixes.get(&prefix), Some(x) if *x != id) {
167                let ext = format!("_{}", id.0);
168                let ext = ext.as_bytes();
169
170                let mut p = prefix.0.into_owned();
171                p.extend_from_slice(ext);
172
173                prefix = NamespacePrefix(Cow::Owned(p));
174            }
175
176            self.known_prefixes.insert(prefix.clone(), id);
177
178            if ns.prefix.is_none() {
179                ns.prefix = Some(prefix);
180            }
181        }
182
183        (id, ns)
184    }
185
186    /// Returns an iterator over all schemas stored in this structure.
187    pub fn schemas(&self) -> Iter<'_, SchemaId, SchemaInfo> {
188        self.schemas.iter()
189    }
190
191    /// Returns a mutable iterator over all schemas stored in this structure.
192    pub fn schemas_mut(&mut self) -> IterMut<'_, SchemaId, SchemaInfo> {
193        self.schemas.iter_mut()
194    }
195
196    /// Returns an iterator over all namespace information instances stored
197    /// in this structure.
198    pub fn namespaces(&self) -> Iter<'_, NamespaceId, NamespaceInfo> {
199        self.namespace_infos.iter()
200    }
201
202    /// Returns a reference to a specific schema by using the schema id,
203    /// or `None` if the schema is not known.
204    #[must_use]
205    pub fn get_schema(&self, id: &SchemaId) -> Option<&SchemaInfo> {
206        self.schemas.get(id)
207    }
208
209    /// Returns a mutable reference to a specific schema by using the schema id,
210    /// or `None` if the schema is not known.
211    #[must_use]
212    pub fn get_schema_mut(&mut self, id: &SchemaId) -> Option<&mut SchemaInfo> {
213        self.schemas.get_mut(id)
214    }
215
216    /// Returns a reference to a specific namespace information instance by using
217    /// the namespace id.
218    #[must_use]
219    pub fn get_namespace_info(&self, id: &NamespaceId) -> Option<&NamespaceInfo> {
220        self.namespace_infos.get(id)
221    }
222
223    /// Returns a mutable reference to a specific namespace information instance
224    /// by using the namespace id.
225    #[must_use]
226    pub fn get_namespace_info_mut(&mut self, id: &NamespaceId) -> Option<&mut NamespaceInfo> {
227        self.namespace_infos.get_mut(id)
228    }
229
230    /// Returns a reference to a specific namespace information instance by using
231    /// the namespace URI, or `None` if the schema is not known.
232    #[must_use]
233    pub fn get_namespace_info_by_namespace(
234        &self,
235        ns: &Option<Namespace>,
236    ) -> Option<&NamespaceInfo> {
237        let id = self.resolve_namespace(ns)?;
238
239        self.get_namespace_info(&id)
240    }
241
242    /// Try to resolve the namespace prefix to a namespace id.
243    ///
244    /// Returns the namespace id of the given namespace `prefix`, or `None`.
245    #[must_use]
246    pub fn resolve_prefix(&self, prefix: &NamespacePrefix) -> Option<NamespaceId> {
247        Some(*self.known_prefixes.get(prefix)?)
248    }
249
250    /// Try to resolve the namespace to a namespace id.
251    ///
252    /// Returns the namespace id of the given namespace `ns`, or `None`.
253    #[must_use]
254    pub fn resolve_namespace(&self, ns: &Option<Namespace>) -> Option<NamespaceId> {
255        Some(*self.known_namespaces.get(ns)?)
256    }
257}
258
259/* NamespaceInfo */
260
261impl NamespaceInfo {
262    /// Create a new [`NamespaceInfo`] instance from the passed `namespace`.
263    #[must_use]
264    pub fn new(namespace: Option<Namespace>) -> Self {
265        Self {
266            prefix: None,
267            namespace,
268            schemas: Vec::new(),
269            module_name: None,
270        }
271    }
272}
273
274/* SchemaInfo */
275
276impl SchemaInfo {
277    /// Get the id of the namespace this schema belongs to.
278    #[must_use]
279    pub fn namespace_id(&self) -> NamespaceId {
280        self.namespace_id
281    }
282}