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}