sdml_core/store.rs
1/*!
2This module provides a trait for module *stores*, and an implementation for in-memory caches.
3
4# Example
5
6```
7use sdml_core::model::identifiers::Identifier;
8use sdml_core::store::{InMemoryModuleCache, ModuleStore};
9use std::str::FromStr;
10
11let store = InMemoryModuleCache::with_stdlib();
12
13let xml_schema_module = Identifier::from_str("xsd").unwrap();
14
15assert!(store.contains(&xml_schema_module));
16```
17
18*/
19
20use crate::config::{builtin_library_cache, is_library_module};
21use crate::model::definitions::Definition;
22use crate::model::identifiers::{Identifier, IdentifierReference, QualifiedIdentifier};
23use crate::model::modules::Module;
24use crate::model::HasName;
25use crate::stdlib::get_library_module_implementation;
26use std::collections::HashMap;
27use url::Url;
28
29// ------------------------------------------------------------------------------------------------
30// Public Types
31// ------------------------------------------------------------------------------------------------
32
33///
34/// A trait for any type that /stores/ modules and can retrieve them by name and by URI.
35///
36pub trait ModuleStore {
37 ///
38 /// Return the number of modules in the store.
39 ///
40 fn len(&self) -> usize;
41
42 ///
43 /// Return `true` if there are no modules in this store, else `false`.
44 ///
45 fn is_empty(&self) -> bool {
46 self.len() == 0
47 }
48
49 ///
50 /// Returns `true` if the loader's cache contains a module with the name `name`, else `false`.
51 ///
52 fn contains(&self, name: &Identifier) -> bool;
53
54 ///
55 /// Returns `true` if the loader's cache contains a module with the base URI `uri`, else `false`.
56 ///
57 fn contains_by_uri(&self, uri: &Url) -> bool {
58 let name = self.uri_to_module_name(uri).cloned();
59 if let Some(name) = name {
60 self.contains(&name)
61 } else {
62 false
63 }
64 }
65
66 ///
67 /// Returns a reference to the `Module` identified by `name` if the store contains it;
68 /// else `None`.
69 ///
70 fn get(&self, name: &Identifier) -> Option<&Module>;
71
72 ///
73 /// Returns a mutable reference to the `Module` identified by `name` if the store contains it;
74 /// else `None`.
75 ///
76 fn get_mut(&mut self, name: &Identifier) -> Option<&mut Module>;
77
78 ///
79 /// Returns a reference to the `Module` identified by `uri` if the store contains it;
80 /// else `None`.
81 ///
82 fn get_by_uri(&self, uri: &Url) -> Option<&Module> {
83 self.uri_to_module_name(uri).and_then(|name| self.get(name))
84 }
85
86 ///
87 /// Returns a mutable reference to the `Module` identified by `uri` if the store contains it;
88 /// else `None`.
89 ///
90 fn get_by_uri_mut(&mut self, uri: &Url) -> Option<&mut Module> {
91 let name = self.uri_to_module_name(uri).cloned();
92 if let Some(name) = name {
93 self.get_mut(&name)
94 } else {
95 None
96 }
97 }
98
99 ///
100 /// Return an iterator over all modules in this store. This may be an expensive operation if
101 /// modules only exist in some backing store.
102 ///
103 fn modules(&self) -> impl Iterator<Item = &Module>;
104
105 ///
106 /// Return an iterator over the names of the modules in this store.
107 ///
108 fn module_names(&self) -> impl Iterator<Item = &Identifier>;
109
110 ///
111 /// Insert `module` into the store.
112 ///
113 fn insert(&mut self, module: Module);
114
115 ///
116 /// Remove any module identified by `name`.
117 ///
118 fn remove(&mut self, name: &Identifier) -> bool;
119
120 ///
121 /// Remove any module identified by `uri`.
122 ///
123 fn remove_by_uri(&mut self, uri: &Url) -> bool {
124 let name = self.uri_to_module_name(uri).cloned();
125 if let Some(name) = name {
126 self.remove(&name)
127 } else {
128 false
129 }
130 }
131
132 ///
133 /// Return the module name corresponding to the provided `url` if it exists, or else `None`.
134 ///
135 fn uri_to_module_name(&self, url: &Url) -> Option<&Identifier>;
136
137 ///
138 /// Return the module URI corresponding to the provided `name` if it exists, or else `None`.
139 ///
140 fn module_name_to_uri(&self, name: &Identifier) -> Option<&Url>;
141
142 ///
143 /// Given a qualified identifier, find the named module or return `None`, then find the named
144 /// member in the found module or return `None`.
145 ///
146 /// # Example
147 ///
148 /// ```
149 /// use sdml_core::model::identifiers::QualifiedIdentifier;
150 /// use sdml_core::store::{InMemoryModuleCache, ModuleStore};
151 /// use std::str::FromStr;
152 ///
153 /// let cache = InMemoryModuleCache::with_stdlib();
154 /// let name = QualifiedIdentifier::from_str("xsd:integer").unwrap();
155 /// let integer = cache.resolve(&name).unwrap();
156 /// println!("{integer:?}");
157 /// ```
158 ///
159 fn resolve(&self, definition: &QualifiedIdentifier) -> Option<&Definition> {
160 if let Some(module) = self.get(definition.module()) {
161 module.resolve_local(definition.member())
162 } else {
163 None
164 }
165 }
166
167 ///
168 /// If `definition` is a `QualifiedIdentifier` this is the same as `resolve`; however, if
169 /// `definition` is an `Identifier` then look for definition in the module named
170 /// `in_module`.
171 ///
172 /// # Example
173 ///
174 /// ```
175 /// use sdml_core::model::identifiers::{Identifier, IdentifierReference};
176 /// use sdml_core::store::{InMemoryModuleCache, ModuleStore};
177 /// use std::str::FromStr;
178 ///
179 /// let cache = InMemoryModuleCache::with_stdlib();
180 /// let default_module = Identifier::from_str("xsd").unwrap();
181 /// let name = IdentifierReference::from_str("integer").unwrap();
182 /// let integer = cache.resolve_or_in(&name, &default_module).unwrap();
183 /// println!("{integer:?}");
184 /// ```
185 ///
186 fn resolve_or_in(
187 &self,
188 definition: &IdentifierReference,
189 in_module: &Identifier,
190 ) -> Option<&Definition> {
191 match definition {
192 IdentifierReference::Identifier(v) => self.resolve(&v.with_module(in_module.clone())),
193 IdentifierReference::QualifiedIdentifier(v) => self.resolve(v),
194 }
195 }
196}
197
198///
199/// An implementation of [`ModuleStore`] that has no persistence it simply acts as an in-process
200/// cache.
201///
202#[derive(Clone, Debug)]
203pub struct InMemoryModuleCache {
204 uri_map: HashMap<Url, Identifier>,
205 modules: HashMap<Identifier, Module>,
206}
207
208// ------------------------------------------------------------------------------------------------
209// Implementations
210// ------------------------------------------------------------------------------------------------
211
212impl ModuleStore for InMemoryModuleCache {
213 fn len(&self) -> usize {
214 self.modules.len()
215 }
216
217 fn contains(&self, name: &Identifier) -> bool {
218 self.modules.contains_key(name) || is_library_module(name)
219 }
220
221 fn contains_by_uri(&self, uri: &Url) -> bool {
222 self.uri_map.contains_key(uri)
223 }
224
225 fn get(&self, name: &Identifier) -> Option<&Module> {
226 if let Some(module) = get_library_module_implementation(name) {
227 Some(module)
228 } else {
229 self.modules.get(name)
230 }
231 }
232
233 fn get_mut(&mut self, name: &Identifier) -> Option<&mut Module> {
234 self.modules.get_mut(name)
235 }
236
237 fn get_by_uri(&self, uri: &Url) -> Option<&Module> {
238 match self.uri_map.get(uri) {
239 Some(name) => self.get(name),
240 _ => None,
241 }
242 }
243
244 fn get_by_uri_mut(&mut self, uri: &Url) -> Option<&mut Module> {
245 let name = self.uri_map.get_mut(uri).map(|n| n.clone());
246 match name {
247 Some(name) => self.get_mut(&name),
248 _ => None,
249 }
250 }
251
252 fn modules(&self) -> impl Iterator<Item = &Module> {
253 self.modules.values()
254 }
255
256 fn module_names(&self) -> impl Iterator<Item = &Identifier> {
257 self.modules.keys()
258 }
259
260 fn insert(&mut self, module: Module) {
261 if let Some(base_uri) = module.base_uri() {
262 self.uri_map
263 .insert(base_uri.value().clone(), module.name().clone());
264 }
265 self.modules.insert(module.name().clone(), module);
266 }
267
268 fn remove(&mut self, name: &Identifier) -> bool {
269 if self.modules.remove(name).is_some() {
270 self.uri_map.retain(|_, v| v == name);
271 true
272 } else {
273 false
274 }
275 }
276
277 fn remove_by_uri(&mut self, uri: &Url) -> bool {
278 if let Some(name) = self.uri_map.remove(uri) {
279 self.modules.remove(&name);
280 true
281 } else {
282 false
283 }
284 }
285
286 fn uri_to_module_name(&self, url: &Url) -> Option<&Identifier> {
287 self.uri_map.get(url)
288 }
289
290 fn module_name_to_uri(&self, id: &Identifier) -> Option<&Url> {
291 self.modules
292 .get(id)
293 .map(|module| module.base_uri().map(|hv| hv.value()))
294 .unwrap_or_default()
295 }
296}
297
298impl InMemoryModuleCache {
299 pub fn empty() -> Self {
300 Self {
301 uri_map: Default::default(),
302 modules: Default::default(),
303 }
304 }
305
306 pub fn with_stdlib() -> Self {
307 builtin_library_cache()
308 }
309
310 ///
311 /// Builder-like function to add a module to a newly constructed cache.
312 ///
313 pub fn with(self, module: Module) -> Self {
314 let mut self_mut = self;
315 self_mut.insert(module);
316 self_mut
317 }
318}