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