Skip to main content

jsonld_syntax/context/definition/
mod.rs

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