Skip to main content

jsonld_syntax/context/definition/
mod.rs

1use super::{TermDefinition, term_definition};
2use crate::{Direction, Keyword, LenientLangTagBuf, Nullable, hash::IndexMap};
3use iri_rs::IriRefBuf;
4
5mod import;
6mod key;
7mod reference;
8mod type_;
9mod version;
10mod vocab;
11
12pub use import::*;
13pub use key::*;
14pub use reference::*;
15pub use type_::*;
16pub use version::*;
17pub use vocab::*;
18
19/// Context definition: the object form of a context entry.
20///
21/// Holds the keyword entries a context may set (`@base`, `@vocab`, …) and the
22/// term definitions it binds.
23#[derive(PartialEq, Eq, Clone, Default, Debug)]
24#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
25pub struct Definition {
26    #[cfg_attr(
27        feature = "serde",
28        serde(rename = "@base", default, deserialize_with = "Nullable::optional", skip_serializing_if = "Option::is_none")
29    )]
30    /// The `@base` entry, setting the base IRI against which relative IRIs are resolved.
31    pub base: Option<Nullable<IriRefBuf>>,
32
33    #[cfg_attr(feature = "serde", serde(rename = "@import", default, skip_serializing_if = "Option::is_none"))]
34    /// The `@import` entry, referencing a context to merge into this one.
35    pub import: Option<IriRefBuf>,
36
37    #[cfg_attr(
38        feature = "serde",
39        serde(rename = "@language", default, deserialize_with = "Nullable::optional", skip_serializing_if = "Option::is_none")
40    )]
41    /// The `@language` entry, tagging string values with a language.
42    pub language: Option<Nullable<LenientLangTagBuf>>,
43
44    #[cfg_attr(
45        feature = "serde",
46        serde(rename = "@direction", default, deserialize_with = "Nullable::optional", skip_serializing_if = "Option::is_none")
47    )]
48    /// The `@direction` entry, setting the base direction of string values.
49    pub direction: Option<Nullable<Direction>>,
50
51    #[cfg_attr(feature = "serde", serde(rename = "@propagate", default, skip_serializing_if = "Option::is_none"))]
52    /// The `@propagate` entry, controlling whether the context survives into node objects.
53    pub propagate: Option<bool>,
54
55    #[cfg_attr(feature = "serde", serde(rename = "@protected", default, skip_serializing_if = "Option::is_none"))]
56    /// The `@protected` entry, forbidding redefinition of the terms defined here.
57    pub protected: Option<bool>,
58
59    #[cfg_attr(feature = "serde", serde(rename = "@type", default, skip_serializing_if = "Option::is_none"))]
60    /// The `@type` entry, setting how the node types of the described documents
61    /// are laid out and whether that setting is protected.
62    pub type_: Option<Type>,
63
64    #[cfg_attr(feature = "serde", serde(rename = "@version", default, skip_serializing_if = "Option::is_none"))]
65    /// The `@version` entry, declaring the processing mode.
66    pub version: Option<Version>,
67
68    #[cfg_attr(
69        feature = "serde",
70        serde(rename = "@vocab", default, deserialize_with = "Nullable::optional", skip_serializing_if = "Option::is_none")
71    )]
72    /// The `@vocab` entry, setting the vocabulary against which terms expand.
73    pub vocab: Option<Nullable<Vocab>>,
74
75    #[cfg_attr(feature = "serde", serde(flatten))]
76    /// Term definitions of this context.
77    pub bindings: Bindings,
78}
79
80impl Definition {
81    /// Creates an empty context definition, with no entry set.
82    #[must_use]
83    pub fn new() -> Self {
84        Self::default()
85    }
86
87    /// Returns the value of the entry with the given key, if that entry is set.
88    ///
89    /// The key may name a keyword entry or a term binding. Keywords that a
90    /// context definition cannot hold, such as `@id`, always yield `None`.
91    pub fn get(&self, key: &KeyOrKeyword) -> Option<EntryValueRef<'_>> {
92        match key {
93            KeyOrKeyword::Keyword(k) => match k {
94                Keyword::Base => self.base.as_ref().map(Nullable::as_ref).map(EntryValueRef::Base),
95                Keyword::Import => self.import.as_ref().map(EntryValueRef::Import),
96                Keyword::Language => self.language.as_ref().map(Nullable::as_ref).map(EntryValueRef::Language),
97                Keyword::Direction => self.direction.map(EntryValueRef::Direction),
98                Keyword::Propagate => self.propagate.map(EntryValueRef::Propagate),
99                Keyword::Protected => self.protected.map(EntryValueRef::Protected),
100                Keyword::Type => self.type_.map(EntryValueRef::Type),
101                Keyword::Version => self.version.map(EntryValueRef::Version),
102                Keyword::Vocab => self.vocab.as_ref().map(Nullable::as_ref).map(EntryValueRef::Vocab),
103                _ => None,
104            },
105            KeyOrKeyword::Key(k) => self.bindings.get(k).map(EntryValueRef::Definition),
106        }
107    }
108
109    /// Returns the term definition bound to the given term, if any.
110    #[must_use]
111    pub fn get_binding(&self, key: &Key) -> Option<Nullable<&TermDefinition>> {
112        self.bindings.get(key)
113    }
114}
115
116/// Term definitions of a context, keyed by term and kept in insertion order.
117#[derive(PartialEq, Eq, Clone, Default, Debug)]
118#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
119#[cfg_attr(feature = "serde", serde(transparent))]
120pub struct Bindings(IndexMap<Key, Nullable<TermDefinition>>);
121
122/// Iterator over the term definitions of a context, in insertion order.
123pub struct BindingsIter<'a>(indexmap::map::Iter<'a, Key, Nullable<TermDefinition>>);
124
125impl<'a> Iterator for BindingsIter<'a> {
126    type Item = (&'a Key, Nullable<&'a TermDefinition>);
127
128    fn next(&mut self) -> Option<Self::Item> {
129        self.0.next().map(|(k, d)| (k, d.as_ref()))
130    }
131
132    fn size_hint(&self) -> (usize, Option<usize>) {
133        self.0.size_hint()
134    }
135}
136
137impl DoubleEndedIterator for BindingsIter<'_> {
138    fn next_back(&mut self) -> Option<Self::Item> {
139        self.0.next_back().map(|(k, d)| (k, d.as_ref()))
140    }
141}
142
143impl ExactSizeIterator for BindingsIter<'_> {}
144
145impl Bindings {
146    /// Binds `key` to `def`, returning the definition it replaced, if any.
147    pub fn insert(&mut self, key: Key, def: Nullable<TermDefinition>) -> Option<Nullable<TermDefinition>> {
148        self.0.insert(key, def)
149    }
150}
151
152impl Bindings {
153    /// Creates an empty set of bindings.
154    #[must_use]
155    pub fn new() -> Self {
156        Self::default()
157    }
158
159    /// Returns the number of bound terms.
160    #[must_use]
161    pub fn len(&self) -> usize {
162        self.0.len()
163    }
164
165    /// Checks whether no term is bound.
166    #[must_use]
167    pub fn is_empty(&self) -> bool {
168        self.0.is_empty()
169    }
170
171    /// Returns the definition bound to the given term, if any.
172    ///
173    /// The result is `Some(Nullable::Null)` for a term the context explicitly
174    /// unsets by binding it to `null`.
175    pub fn get(&self, key: &Key) -> Option<Nullable<&TermDefinition>> {
176        self.0.get(key).map(Nullable::as_ref)
177    }
178
179    /// Returns the `i`th binding in insertion order, if there is one.
180    #[must_use]
181    pub fn get_entry(&self, i: usize) -> Option<(&Key, Nullable<&TermDefinition>)> {
182        self.0.get_index(i).map(|(key, value)| (key, value.as_ref()))
183    }
184
185    /// Returns an iterator over the bindings, in insertion order.
186    #[must_use]
187    pub fn iter(&self) -> BindingsIter<'_> {
188        BindingsIter(self.0.iter())
189    }
190
191    /// Binds `key` to `def`, returning the definition it replaced, if any.
192    ///
193    /// Same as [`insert`](Self::insert).
194    pub fn insert_with(&mut self, key: Key, def: Nullable<TermDefinition>) -> Option<Nullable<TermDefinition>> {
195        self.0.insert(key, def)
196    }
197}
198
199impl IntoIterator for Bindings {
200    type Item = (Key, Nullable<TermDefinition>);
201    type IntoIter = indexmap::map::IntoIter<Key, Nullable<TermDefinition>>;
202
203    fn into_iter(self) -> Self::IntoIter {
204        self.0.into_iter()
205    }
206}
207
208impl FromIterator<(Key, Nullable<TermDefinition>)> for Bindings {
209    fn from_iter<T: IntoIterator<Item = (Key, Nullable<TermDefinition>)>>(iter: T) -> Self {
210        let mut result = Self::new();
211
212        for (key, binding) in iter {
213            result.0.insert(key, binding);
214        }
215
216        result
217    }
218}
219
220/// Fragment of a context definition.
221pub enum FragmentRef<'a> {
222    /// An entry of the definition, key and value together.
223    Entry(EntryRef<'a>),
224
225    /// The key of an entry of the definition.
226    Key(EntryKeyRef<'a>),
227
228    /// The value of an entry of the definition.
229    Value(EntryValueRef<'a>),
230
231    /// A fragment of one of the term definitions.
232    TermDefinitionFragment(term_definition::FragmentRef<'a>),
233}
234
235impl<'a> FragmentRef<'a> {
236    /// Checks whether this fragment is an entry key.
237    #[must_use]
238    pub fn is_key(&self) -> bool {
239        match self {
240            Self::Key(_) => true,
241            Self::TermDefinitionFragment(f) => f.is_key(),
242            _ => false,
243        }
244    }
245
246    /// Checks whether this fragment is an entry, key and value together.
247    #[must_use]
248    pub fn is_entry(&self) -> bool {
249        match self {
250            Self::Entry(_) => true,
251            Self::TermDefinitionFragment(f) => f.is_entry(),
252            _ => false,
253        }
254    }
255
256    /// Checks whether this fragment is a JSON array.
257    #[must_use]
258    pub fn is_array(&self) -> bool {
259        match self {
260            Self::TermDefinitionFragment(i) => i.is_array(),
261            _ => false,
262        }
263    }
264
265    /// Checks whether this fragment is a JSON object.
266    #[must_use]
267    pub fn is_object(&self) -> bool {
268        match self {
269            Self::Value(v) => v.is_object(),
270            Self::TermDefinitionFragment(v) => v.is_object(),
271            _ => false,
272        }
273    }
274
275    /// Returns an iterator over the fragments directly contained in this one.
276    #[must_use]
277    pub fn sub_items(&self) -> SubItems<'a> {
278        match self {
279            Self::Entry(e) => SubItems::Entry(Some(e.key()), Some(Box::new(e.value()))),
280            Self::Key(_) => SubItems::None,
281            Self::Value(v) => SubItems::Value(v.sub_items()),
282            Self::TermDefinitionFragment(f) => SubItems::TermDefinitionFragment(f.sub_fragments()),
283        }
284    }
285}
286
287/// Iterator over the fragments held by a context entry value.
288pub enum EntryValueSubItems<'a> {
289    /// Nothing to iterate over: the value holds no fragment of its own.
290    None,
291    /// The entries of an expanded term definition.
292    TermDefinitionFragment(Box<term_definition::Entries<'a>>),
293}
294
295impl<'a> Iterator for EntryValueSubItems<'a> {
296    type Item = FragmentRef<'a>;
297
298    fn next(&mut self) -> Option<Self::Item> {
299        match self {
300            Self::None => None,
301            Self::TermDefinitionFragment(d) => d.next().map(|e| FragmentRef::TermDefinitionFragment(term_definition::FragmentRef::Entry(e))),
302        }
303    }
304}
305
306/// Iterator over the fragments held by a context definition.
307pub enum SubItems<'a> {
308    /// Nothing to iterate over.
309    None,
310    /// The key and value of an entry, yielded in that order.
311    Entry(Option<EntryKeyRef<'a>>, Option<Box<EntryValueRef<'a>>>),
312    /// The fragments held by an entry value.
313    Value(EntryValueSubItems<'a>),
314    /// The fragments of a term definition.
315    TermDefinitionFragment(term_definition::SubFragments<'a>),
316}
317
318impl<'a> Iterator for SubItems<'a> {
319    type Item = FragmentRef<'a>;
320
321    fn next(&mut self) -> Option<Self::Item> {
322        match self {
323            Self::None => None,
324            Self::Entry(k, v) => k.take().map(FragmentRef::Key).or_else(|| v.take().map(|v| FragmentRef::Value(*v))),
325            Self::Value(d) => d.next(),
326            Self::TermDefinitionFragment(d) => d.next().map(FragmentRef::TermDefinitionFragment),
327        }
328    }
329}
330
331#[cfg(all(test, feature = "serde"))]
332// Test code may panic on failure; that is the point of a test.
333#[allow(clippy::unwrap_used, clippy::expect_used)]
334mod tests {
335    use super::Definition;
336
337    #[test]
338    fn deserialize_null_vocab() {
339        let definition: Definition = jstrict::from_value(jstrict::json!({
340            "@vocab": null
341        }))
342        .unwrap();
343        assert_eq!(definition.vocab, Some(crate::Nullable::Null));
344    }
345
346    #[test]
347    fn deserialize_no_vocab() {
348        let definition: Definition = jstrict::from_value(jstrict::json!({})).unwrap();
349        assert_eq!(definition.vocab, None);
350    }
351}