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