Skip to main content

jsonld_syntax/context/
mod.rs

1use iri_rs::{Iri, IriRef, IriRefBuf};
2use smallvec::SmallVec;
3
4/// Context definitions and their bindings.
5pub mod definition;
6mod print;
7/// Term definitions, simple and expanded.
8pub mod term_definition;
9mod try_from_json;
10
11pub use definition::Definition;
12pub use term_definition::TermDefinition;
13pub use try_from_json::{InvalidContext, MAX_CONTEXT_DEPTH};
14
15/// JSON-LD Context.
16///
17/// Can represent a single context entry, or a list of context entries.
18#[derive(PartialEq, Eq, Clone, Debug)]
19#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
20#[cfg_attr(feature = "serde", serde(untagged))]
21#[allow(clippy::large_enum_variant)]
22pub enum Context {
23    /// A single entry, written on its own.
24    One(ContextEntry),
25    /// Several entries, written as a JSON array.
26    Many(Vec<ContextEntry>),
27}
28
29impl Default for Context {
30    fn default() -> Self {
31        Self::Many(Vec::new())
32    }
33}
34
35impl Context {
36    /// Creates a new context with a single entry.
37    #[must_use]
38    pub fn one(context: ContextEntry) -> Self {
39        Self::One(context)
40    }
41
42    /// Creates the `null` context.
43    #[must_use]
44    pub fn null() -> Self {
45        Self::one(ContextEntry::Null)
46    }
47
48    /// Creates a new context with a single IRI-reference entry.
49    #[must_use]
50    pub fn iri_ref(iri_ref: IriRefBuf) -> Self {
51        Self::one(ContextEntry::IriRef(iri_ref))
52    }
53
54    /// Creates a new context with a single context definition entry.
55    #[must_use]
56    pub fn definition(def: Definition) -> Self {
57        Self::one(ContextEntry::Definition(def))
58    }
59}
60
61impl Context {
62    /// Returns the number of entries of this context, which is 1 for a single
63    /// entry.
64    #[must_use]
65    pub fn len(&self) -> usize {
66        match self {
67            Self::One(_) => 1,
68            Self::Many(l) => l.len(),
69        }
70    }
71
72    /// Checks whether this context has no entry, which only an empty array can.
73    #[must_use]
74    pub fn is_empty(&self) -> bool {
75        match self {
76            Self::One(_) => false,
77            Self::Many(l) => l.is_empty(),
78        }
79    }
80
81    /// Returns the entries of this context as a slice.
82    #[must_use]
83    pub fn as_slice(&self) -> &[ContextEntry] {
84        match self {
85            Self::One(c) => std::slice::from_ref(c),
86            Self::Many(list) => list,
87        }
88    }
89
90    /// Checks whether this context is a single context definition, written as
91    /// a JSON object.
92    #[must_use]
93    pub fn is_object(&self) -> bool {
94        match self {
95            Self::One(c) => c.is_object(),
96            Self::Many(_) => false,
97        }
98    }
99
100    /// Checks whether this context is written as a JSON array.
101    #[must_use]
102    pub fn is_array(&self) -> bool {
103        matches!(self, Self::Many(_))
104    }
105
106    /// Returns a depth-first iterator over this context and every fragment it
107    /// contains, itself included.
108    #[must_use]
109    pub fn traverse(&self) -> Traverse<'_> {
110        match self {
111            Self::One(c) => Traverse::new(FragmentRef::Context(c)),
112            Self::Many(m) => Traverse::new(FragmentRef::ContextArray(m)),
113        }
114    }
115
116    /// Returns an iterator over the entries of this context, in order.
117    pub fn iter(&self) -> std::slice::Iter<'_, ContextEntry> {
118        self.as_slice().iter()
119    }
120}
121
122/// Owning iterator over the entries of a context.
123///
124/// The single-entry variant boxes its entry. A [`ContextEntry`] holding a
125/// [`Definition`] is large, and inline it would set the size of the whole
126/// iterator — paid by the [`Many`](Self::Many) variant too, whose own payload is
127/// a [`Vec`] iterator. [`Option<Box<_>>`] is null-pointer-optimised, so no
128/// allocation happens once the entry has been yielded.
129pub enum IntoIter {
130    /// The entry of a single-entry context, until it has been yielded.
131    One(Option<Box<ContextEntry>>),
132    /// The remaining entries of an array of contexts.
133    Many(std::vec::IntoIter<ContextEntry>),
134}
135
136impl Iterator for IntoIter {
137    type Item = ContextEntry;
138
139    fn next(&mut self) -> Option<Self::Item> {
140        match self {
141            Self::One(t) => t.take().map(|t| *t),
142            Self::Many(t) => t.next(),
143        }
144    }
145}
146
147impl IntoIterator for Context {
148    type Item = ContextEntry;
149    type IntoIter = IntoIter;
150
151    fn into_iter(self) -> Self::IntoIter {
152        match self {
153            Self::One(t) => IntoIter::One(Some(Box::new(t))),
154            Self::Many(t) => IntoIter::Many(t.into_iter()),
155        }
156    }
157}
158
159impl<'a> IntoIterator for &'a Context {
160    type IntoIter = std::slice::Iter<'a, ContextEntry>;
161    type Item = &'a ContextEntry;
162
163    fn into_iter(self) -> Self::IntoIter {
164        self.iter()
165    }
166}
167
168impl From<ContextEntry> for Context {
169    fn from(c: ContextEntry) -> Self {
170        Self::One(c)
171    }
172}
173
174impl From<IriRefBuf> for Context {
175    fn from(i: IriRefBuf) -> Self {
176        Self::One(ContextEntry::IriRef(i))
177    }
178}
179
180impl<'a> From<IriRef<&'a str>> for Context {
181    fn from(i: IriRef<&'a str>) -> Self {
182        Self::One(ContextEntry::IriRef(i.into()))
183    }
184}
185
186impl From<iri_rs::IriBuf> for Context {
187    fn from(i: iri_rs::IriBuf) -> Self {
188        Self::One(ContextEntry::IriRef(i.into()))
189    }
190}
191
192impl<'a> From<Iri<&'a str>> for Context {
193    fn from(i: Iri<&'a str>) -> Self {
194        Self::One(ContextEntry::IriRef(IriRefBuf::from(IriRef::<&str>::from(i))))
195    }
196}
197
198impl From<Definition> for Context {
199    fn from(c: Definition) -> Self {
200        Self::One(ContextEntry::Definition(c))
201    }
202}
203
204/// Single entry of a `@context`.
205#[derive(PartialEq, Eq, Clone, Debug)]
206#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(untagged))]
207pub enum ContextEntry {
208    /// The `null` value, which clears the active context.
209    Null,
210    /// An IRI reference to a remote context, to be dereferenced and loaded.
211    IriRef(IriRefBuf),
212    /// An inline context definition.
213    Definition(Definition),
214}
215
216impl ContextEntry {
217    fn sub_items(&self) -> ContextSubFragments<'_> {
218        match self {
219            Self::Definition(d) => ContextSubFragments::Definition(Box::new(d.iter())),
220            _ => ContextSubFragments::None,
221        }
222    }
223
224    /// Checks whether this entry is an inline context definition, written as a
225    /// JSON object.
226    #[must_use]
227    pub fn is_object(&self) -> bool {
228        matches!(self, Self::Definition(_))
229    }
230}
231
232impl From<IriRefBuf> for ContextEntry {
233    fn from(i: IriRefBuf) -> Self {
234        ContextEntry::IriRef(i)
235    }
236}
237
238impl<'a> From<IriRef<&'a str>> for ContextEntry {
239    fn from(i: IriRef<&'a str>) -> Self {
240        ContextEntry::IriRef(i.into())
241    }
242}
243
244impl From<iri_rs::IriBuf> for ContextEntry {
245    fn from(i: iri_rs::IriBuf) -> Self {
246        ContextEntry::IriRef(i.into())
247    }
248}
249
250impl<'a> From<Iri<&'a str>> for ContextEntry {
251    fn from(i: Iri<&'a str>) -> Self {
252        ContextEntry::IriRef(IriRefBuf::from(IriRef::<&str>::from(i)))
253    }
254}
255
256impl From<Definition> for ContextEntry {
257    fn from(c: Definition) -> Self {
258        ContextEntry::Definition(c)
259    }
260}
261
262/// Fragment of a context value: any JSON value reachable inside a `@context`.
263pub enum FragmentRef<'a> {
264    /// An array of context entries.
265    ContextArray(&'a [ContextEntry]),
266
267    /// A single context entry.
268    Context(&'a ContextEntry),
269
270    /// A fragment of a context definition.
271    DefinitionFragment(definition::FragmentRef<'a>),
272}
273
274impl<'a> FragmentRef<'a> {
275    /// Checks whether this fragment is a JSON array.
276    #[must_use]
277    pub fn is_array(&self) -> bool {
278        match self {
279            Self::ContextArray(_) => true,
280            Self::DefinitionFragment(i) => i.is_array(),
281            Self::Context(_) => false,
282        }
283    }
284
285    /// Checks whether this fragment is a JSON object.
286    #[must_use]
287    pub fn is_object(&self) -> bool {
288        match self {
289            Self::Context(c) => c.is_object(),
290            Self::DefinitionFragment(i) => i.is_object(),
291            Self::ContextArray(_) => false,
292        }
293    }
294
295    /// Returns an iterator over the fragments directly contained in this one.
296    #[must_use]
297    pub fn sub_items(&self) -> SubFragments<'a> {
298        match self {
299            Self::ContextArray(a) => SubFragments::ContextArray(a.iter()),
300            Self::Context(c) => SubFragments::Context(c.sub_items()),
301            Self::DefinitionFragment(d) => SubFragments::Definition(Box::new(d.sub_items())),
302        }
303    }
304}
305
306/// Iterator over the fragments held by a single context entry.
307pub enum ContextSubFragments<'a> {
308    /// Nothing to iterate over: the entry is `null` or an IRI reference.
309    None,
310    /// The entries of a context definition.
311    Definition(Box<definition::Entries<'a>>),
312}
313
314impl<'a> Iterator for ContextSubFragments<'a> {
315    type Item = FragmentRef<'a>;
316
317    fn next(&mut self) -> Option<Self::Item> {
318        match self {
319            Self::None => None,
320            Self::Definition(e) => e.next().map(|e| FragmentRef::DefinitionFragment(definition::FragmentRef::Entry(e))),
321        }
322    }
323}
324
325/// Iterator over the fragments held by a context.
326pub enum SubFragments<'a> {
327    /// The items of an array of contexts.
328    ContextArray(std::slice::Iter<'a, ContextEntry>),
329    /// The fragments of a single context entry.
330    Context(ContextSubFragments<'a>),
331    /// The fragments of a context definition.
332    Definition(Box<definition::SubItems<'a>>),
333}
334
335impl<'a> Iterator for SubFragments<'a> {
336    type Item = FragmentRef<'a>;
337
338    fn next(&mut self) -> Option<Self::Item> {
339        match self {
340            Self::ContextArray(a) => a.next().map(FragmentRef::Context),
341            Self::Context(i) => i.next(),
342            Self::Definition(i) => i.next().map(FragmentRef::DefinitionFragment),
343        }
344    }
345}
346
347/// Depth-first iterator over a context and everything it contains.
348pub struct Traverse<'a> {
349    stack: SmallVec<[FragmentRef<'a>; 8]>,
350}
351
352impl<'a> Traverse<'a> {
353    pub(crate) fn new(item: FragmentRef<'a>) -> Self {
354        let mut stack = SmallVec::new();
355        stack.push(item);
356        Self { stack }
357    }
358}
359
360impl<'a> Iterator for Traverse<'a> {
361    type Item = FragmentRef<'a>;
362
363    fn next(&mut self) -> Option<Self::Item> {
364        match self.stack.pop() {
365            Some(item) => {
366                self.stack.extend(item.sub_items());
367                Some(item)
368            }
369            None => None,
370        }
371    }
372}
373
374/// Context document.
375///
376/// A context document is a JSON-LD document containing an object with a single
377/// `@context` entry.
378#[derive(PartialEq, Eq, Clone, Debug)]
379#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
380pub struct ContextDocument {
381    #[cfg_attr(feature = "serde", serde(rename = "@context"))]
382    /// The `@context` entry, the sole entry of the document.
383    pub context: Context,
384}