Skip to main content

jsonld_core/object/
value.rs

1use crate::{Direction, LangString, LenientLangTag, object};
2use derive_where::derive_where;
3use iri_rs::{Iri, IriBuf};
4use jsonld_syntax::{IntoJsonWithContext, Keyword};
5use jstrict::{Number, NumberBuf};
6use rdfx::vocabulary::{IriVocabulary, IriVocabularyMut};
7use std::{hash::Hash, marker::PhantomData};
8
9use super::InvalidExpandedJson;
10
11/// Value type reference.
12// `bound(false)`: every variant payload is a shared reference or a `Copy` value
13// type, so this borrow type is unconditionally `Clone + Copy` regardless of `T`.
14// A plain `derive` would instead add a bound per field type,
15// which makes the impl conditional and breaks `&self` methods that consume
16// `self` by copy.
17#[derive_where(Clone, Copy)]
18pub enum TypeRef<'a, T> {
19    /// A JSON literal.
20    Json,
21    /// A type given by an IRI.
22    Id(&'a T),
23}
24
25impl<'a, T> TypeRef<'a, T> {
26    /// Converts this value type into the more general term type used by
27    /// context definitions.
28    #[must_use]
29    pub fn as_syntax_type(&self) -> crate::Type<&'a T> {
30        match self {
31            Self::Json => crate::Type::Json,
32            Self::Id(id) => crate::Type::Iri(id),
33        }
34    }
35
36    /// Returns the node identifier naming this type, or `None` for the
37    /// `@json` type, which names a literal rather than a node.
38    #[must_use]
39    pub fn into_reference<B>(self) -> Option<crate::id::Ref<'a, T, B>> {
40        match self {
41            Self::Json => None,
42            Self::Id(t) => Some(crate::id::Ref::Iri(t)),
43        }
44    }
45}
46
47/// Literal value.
48#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
49pub enum Literal {
50    /// The `null` value.
51    Null,
52
53    /// Boolean value.
54    Boolean(bool),
55
56    /// Number.
57    Number(NumberBuf),
58
59    /// String.
60    String(jstrict::String),
61}
62
63impl Literal {
64    /// Returns this value as a string if it is one.
65    #[inline(always)]
66    #[must_use]
67    pub fn as_str(&self) -> Option<&str> {
68        match self {
69            Literal::String(s) => Some(s.as_ref()),
70            _ => None,
71        }
72    }
73
74    /// Returns this value as a boolean if it is one.
75    #[inline(always)]
76    #[must_use]
77    pub fn as_bool(&self) -> Option<bool> {
78        match self {
79            Literal::Boolean(b) => Some(*b),
80            _ => None,
81        }
82    }
83
84    /// Returns this value as a number if it is one.
85    #[inline(always)]
86    #[must_use]
87    pub fn as_number(&self) -> Option<&Number> {
88        match self {
89            Literal::Number(n) => Some(n),
90            _ => None,
91        }
92    }
93
94    /// Converts the literal into the equivalent JSON value.
95    #[must_use]
96    pub fn into_json(self) -> jstrict::Value {
97        match self {
98            Self::Null => jstrict::Value::Null,
99            Self::Boolean(b) => jstrict::Value::Boolean(b),
100            Self::Number(n) => jstrict::Value::Number(n),
101            Self::String(s) => jstrict::Value::String(s),
102        }
103    }
104
105    /// Puts the literal into canonical form, using the given `buffer` to
106    /// render numbers. Only numbers have a non-trivial canonical form.
107    pub fn canonicalize_with(&mut self, buffer: &mut ryu_js::Buffer) {
108        if let Self::Number(n) = self {
109            *n = NumberBuf::from_number(n.canonical_with(buffer));
110        }
111    }
112
113    /// Puts this literal into canonical form.
114    pub fn canonicalize(&mut self) {
115        let mut buffer = ryu_js::Buffer::new();
116        self.canonicalize_with(&mut buffer);
117    }
118}
119
120/// Value object.
121///
122/// Either a typed literal value, or an internationalized language string.
123#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
124pub enum Value<T = IriBuf> {
125    /// Typed literal value.
126    Literal(Literal, Option<T>),
127
128    /// Language tagged string.
129    LangString(LangString),
130
131    /// JSON literal value.
132    Json(jstrict::Value),
133}
134
135impl<T> Value<T> {
136    /// Creates a `null` value object.
137    #[inline(always)]
138    pub fn null() -> Self {
139        Self::Literal(Literal::Null, None)
140    }
141
142    #[inline(always)]
143    /// Returns this value as a string slice.
144    pub fn as_str(&self) -> Option<&str> {
145        match self {
146            Value::Literal(lit, _) => lit.as_str(),
147            Value::LangString(str) => Some(str.as_str()),
148            Value::Json(_) => None,
149        }
150    }
151
152    #[inline(always)]
153    /// Returns the literal and its type, if this is a typed literal value.
154    pub fn as_literal(&self) -> Option<(&Literal, Option<&T>)> {
155        match self {
156            Self::Literal(lit, ty) => Some((lit, ty.as_ref())),
157            _ => None,
158        }
159    }
160
161    /// Returns the `@type` IRI of this value, if it is a typed literal with
162    /// one.
163    pub fn literal_type(&self) -> Option<&T> {
164        match self {
165            Self::Literal(_, ty) => ty.as_ref(),
166            _ => None,
167        }
168    }
169
170    /// Sets the `@type` IRI of a typed literal value, returning the type it
171    /// replaced.
172    ///
173    /// Has no effect and returns `None` if the value is not a typed literal.
174    pub fn set_literal_type(&mut self, mut ty: Option<T>) -> Option<T> {
175        match self {
176            Self::Literal(_, old_ty) => {
177                std::mem::swap(old_ty, &mut ty);
178                ty
179            }
180            _ => None,
181        }
182    }
183
184    /// Replaces the `@type` IRI of a typed literal value with the result of
185    /// `f`.
186    ///
187    /// Has no effect if the value is not a typed literal.
188    pub fn map_literal_type<F: FnOnce(Option<T>) -> Option<T>>(&mut self, f: F) {
189        if let Self::Literal(_, ty) = self {
190            *ty = f(ty.take());
191        }
192    }
193
194    #[inline(always)]
195    /// Returns the value as a boolean, if it is a literal holding one.
196    pub fn as_bool(&self) -> Option<bool> {
197        match self {
198            Value::Literal(lit, _) => lit.as_bool(),
199            _ => None,
200        }
201    }
202
203    #[inline(always)]
204    /// Returns the value as a number, if it is a literal holding one.
205    pub fn as_number(&self) -> Option<&Number> {
206        match self {
207            Value::Literal(lit, _) => lit.as_number(),
208            _ => None,
209        }
210    }
211
212    /// Returns the `@type` of the value, if it has one.
213    ///
214    /// JSON literal values report [`TypeRef::Json`]; language-tagged strings
215    /// have no type and report `None`.
216    pub fn typ(&self) -> Option<TypeRef<'_, T>> {
217        match self {
218            Value::Literal(_, Some(ty)) => Some(TypeRef::Id(ty)),
219            Value::Json(_) => Some(TypeRef::Json),
220            _ => None,
221        }
222    }
223
224    /// Returns the `@language` of the value.
225    ///
226    /// Returns `None` if the value is not a language-tagged string, or is one
227    /// carrying only a `@direction`.
228    #[inline(always)]
229    pub fn language(&self) -> Option<&LenientLangTag> {
230        match self {
231            Value::LangString(tag) => tag.language(),
232            _ => None,
233        }
234    }
235
236    /// Returns the `@direction` of the value.
237    ///
238    /// Returns `None` if the value is not a language-tagged string, or is one
239    /// carrying only a `@language`.
240    #[inline(always)]
241    pub fn direction(&self) -> Option<Direction> {
242        match self {
243            Value::LangString(str) => str.direction(),
244            _ => None,
245        }
246    }
247
248    #[inline(always)]
249    /// Returns an iterator over the entries of the JSON representation of
250    /// the value object.
251    pub fn entries(&self) -> Entries<'_, T> {
252        match self {
253            Self::Literal(l, ty) => Entries {
254                value: Some(ValueEntryRef::Literal(l)),
255                type_: ty.as_ref().map(TypeRef::Id),
256                language: None,
257                direction: None,
258            },
259            Self::LangString(l) => Entries {
260                value: Some(ValueEntryRef::LangString(l.as_str())),
261                type_: None,
262                language: l.language(),
263                direction: l.direction(),
264            },
265            Self::Json(j) => Entries {
266                value: Some(ValueEntryRef::Json(j)),
267                type_: Some(TypeRef::Json),
268                language: None,
269                direction: None,
270            },
271        }
272    }
273
274    pub(crate) fn try_from_json_object_in(
275        vocabulary: &mut impl IriVocabularyMut<Iri = T>,
276        mut object: jstrict::Object,
277        value_entry: jstrict::object::Entry,
278    ) -> Result<Self, InvalidExpandedJson> {
279        if let Some(type_entry) = object.remove_unique("@type").map_err(InvalidExpandedJson::duplicate_key)? {
280            match type_entry.value {
281                jstrict::Value::String(ty) => match ty.as_str() {
282                    "@json" => Ok(Self::Json(value_entry.value)),
283                    iri => match Iri::parse(iri) {
284                        Ok(iri) => {
285                            let ty = vocabulary.insert(iri);
286                            let lit = value_entry.value.try_into()?;
287                            Ok(Self::Literal(lit, Some(ty)))
288                        }
289                        Err(_) => Err(InvalidExpandedJson::InvalidValueType),
290                    },
291                },
292                _ => Err(InvalidExpandedJson::InvalidValueType),
293            }
294        } else {
295            let language = object
296                .remove_unique("@language")
297                .map_err(InvalidExpandedJson::duplicate_key)?
298                .map(jstrict::object::Entry::into_value);
299            let direction = object
300                .remove_unique("@direction")
301                .map_err(InvalidExpandedJson::duplicate_key)?
302                .map(jstrict::object::Entry::into_value);
303
304            if language.is_some() || direction.is_some() {
305                Ok(Self::LangString(LangString::try_from_json(object, value_entry.value, language, direction)?))
306            } else {
307                let lit = value_entry.value.try_into()?;
308                Ok(Self::Literal(lit, None))
309            }
310        }
311    }
312
313    /// Puts the literal held by this value object into canonical form, using
314    /// the given `buffer` to render numbers.
315    pub fn canonicalize_with(&mut self, buffer: &mut ryu_js::Buffer) {
316        match self {
317            Self::Json(json) => json.canonicalize_with(buffer),
318            Self::Literal(l, _) => l.canonicalize_with(buffer),
319            Self::LangString(_) => (),
320        }
321    }
322
323    /// Puts the literal held by this value object into canonical form.
324    pub fn canonicalize(&mut self) {
325        let mut buffer = ryu_js::Buffer::new();
326        self.canonicalize_with(&mut buffer);
327    }
328
329    /// Rewrites the `@type` IRI of the value, if it has one, with the given
330    /// function.
331    pub fn map_ids<U>(self, map_iri: impl FnOnce(T) -> U) -> Value<U> {
332        match self {
333            Self::Literal(l, type_) => Value::Literal(l, type_.map(map_iri)),
334            Self::LangString(s) => Value::LangString(s),
335            Self::Json(json) => Value::Json(json),
336        }
337    }
338}
339
340impl TryFrom<jstrict::Value> for Literal {
341    type Error = InvalidExpandedJson;
342
343    fn try_from(value: jstrict::Value) -> Result<Self, Self::Error> {
344        match value {
345            jstrict::Value::Null => Ok(Self::Null),
346            jstrict::Value::Boolean(b) => Ok(Self::Boolean(b)),
347            jstrict::Value::Number(n) => Ok(Self::Number(n)),
348            jstrict::Value::String(s) => Ok(Self::String(s)),
349            _ => Err(InvalidExpandedJson::InvalidLiteral),
350        }
351    }
352}
353
354impl<T, B> object::Any<T, B> for Value<T> {
355    #[inline(always)]
356    fn as_ref(&self) -> object::Ref<'_, T, B> {
357        object::Ref::Value(self)
358    }
359}
360
361#[derive_where(Clone, Copy)]
362/// Entry of a value object, key and value together.
363pub enum EntryRef<'a, T> {
364    /// The `@value` entry, holding the literal value.
365    Value(ValueEntryRef<'a>),
366    /// The `@type` entry, giving the type of the node or the values.
367    Type(TypeRef<'a, T>),
368    /// The `@language` entry, tagging string values with a language.
369    Language(&'a LenientLangTag),
370    /// The `@direction` entry, setting the base direction of string values.
371    Direction(Direction),
372}
373
374impl<'a, T> EntryRef<'a, T> {
375    /// Consumes this `EntryRef`, returning its key.
376    #[must_use]
377    pub fn into_key(self) -> EntryKey {
378        match self {
379            Self::Value(_) => EntryKey::Value,
380            Self::Type(_) => EntryKey::Type,
381            Self::Language(_) => EntryKey::Language,
382            Self::Direction(_) => EntryKey::Direction,
383        }
384    }
385
386    /// Returns the key of this `EntryRef`.
387    #[must_use]
388    pub fn key(&self) -> EntryKey {
389        self.into_key()
390    }
391
392    /// Consumes this `EntryRef`, returning its value.
393    #[must_use]
394    pub fn into_value(self) -> EntryValueRef<'a, T> {
395        match self {
396            Self::Value(v) => EntryValueRef::Value(v),
397            Self::Type(v) => EntryValueRef::Type(v),
398            Self::Language(v) => EntryValueRef::Language(v),
399            Self::Direction(v) => EntryValueRef::Direction(v),
400        }
401    }
402
403    /// Returns the value of this `EntryRef`.
404    #[must_use]
405    pub fn value(&self) -> EntryValueRef<'a, T> {
406        match self {
407            Self::Value(v) => EntryValueRef::Value(*v),
408            Self::Type(v) => EntryValueRef::Type(*v),
409            Self::Language(v) => EntryValueRef::Language(v),
410            Self::Direction(v) => EntryValueRef::Direction(*v),
411        }
412    }
413}
414
415#[derive_where(Clone, Copy)]
416/// Value of a value object entry.
417pub enum EntryValueRef<'a, T> {
418    /// The `@value` entry, holding the literal value.
419    Value(ValueEntryRef<'a>),
420    /// The `@type` entry, giving the type of the node or the values.
421    Type(TypeRef<'a, T>),
422    /// The `@language` entry, tagging string values with a language.
423    Language(&'a LenientLangTag),
424    /// The `@direction` entry, setting the base direction of string values.
425    Direction(Direction),
426}
427/// Value held by the `@value` entry.
428#[derive(Clone, Copy)]
429pub enum ValueEntryRef<'a> {
430    /// An RDF literal.
431    Literal(&'a Literal),
432    /// A language-tagged string.
433    LangString(&'a str),
434    /// A JSON literal.
435    Json(&'a jstrict::Value),
436}
437
438#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
439/// Key of a value object entry.
440pub enum EntryKey {
441    /// The `@value` entry, holding the literal value.
442    Value,
443    /// The `@type` entry, giving the type of the node or the values.
444    Type,
445    /// The `@language` entry, tagging string values with a language.
446    Language,
447    /// The `@direction` entry, setting the base direction of string values.
448    Direction,
449}
450
451impl EntryKey {
452    /// Returns the JSON-LD keyword this key stands for.
453    #[must_use]
454    pub fn into_keyword(self) -> Keyword {
455        match self {
456            Self::Value => Keyword::Value,
457            Self::Type => Keyword::Type,
458            Self::Language => Keyword::Language,
459            Self::Direction => Keyword::Direction,
460        }
461    }
462
463    /// Returns the JSON-LD keyword this key stands for.
464    #[must_use]
465    pub fn as_keyword(&self) -> Keyword {
466        self.into_keyword()
467    }
468
469    /// Returns the key as a string slice.
470    #[must_use]
471    pub fn into_str(&self) -> &'static str {
472        match self {
473            Self::Value => "@value",
474            Self::Type => "@type",
475            Self::Language => "@language",
476            Self::Direction => "@direction",
477        }
478    }
479
480    /// Returns this value as a string slice.
481    #[must_use]
482    pub fn as_str(&self) -> &'static str {
483        self.into_str()
484    }
485}
486
487#[derive_where(Clone)]
488/// Iterator over the entries of a value object.
489pub struct Entries<'a, T> {
490    value: Option<ValueEntryRef<'a>>,
491    type_: Option<TypeRef<'a, T>>,
492    language: Option<&'a LenientLangTag>,
493    direction: Option<Direction>,
494}
495
496impl<'a, T> Iterator for Entries<'a, T> {
497    type Item = EntryRef<'a, T>;
498
499    fn size_hint(&self) -> (usize, Option<usize>) {
500        let mut len = 0;
501
502        if self.value.is_some() {
503            len += 1;
504        }
505
506        if self.type_.is_some() {
507            len += 1;
508        }
509
510        if self.language.is_some() {
511            len += 1;
512        }
513
514        if self.direction.is_some() {
515            len += 1;
516        }
517
518        (len, Some(len))
519    }
520
521    fn next(&mut self) -> Option<Self::Item> {
522        self.value.take().map(EntryRef::Value).or_else(|| {
523            self.type_.take().map(EntryRef::Type).or_else(|| {
524                self.language
525                    .take()
526                    .map(EntryRef::Language)
527                    .or_else(|| self.direction.take().map(EntryRef::Direction))
528            })
529        })
530    }
531}
532
533impl<T> ExactSizeIterator for Entries<'_, T> {}
534
535impl<T> DoubleEndedIterator for Entries<'_, T> {
536    fn next_back(&mut self) -> Option<Self::Item> {
537        self.direction.take().map(EntryRef::Direction).or_else(|| {
538            self.language
539                .take()
540                .map(EntryRef::Language)
541                .or_else(|| self.type_.take().map(EntryRef::Type).or_else(|| self.value.take().map(EntryRef::Value)))
542        })
543    }
544}
545
546/// Reference to any fragment that can appear in a value object.
547pub enum FragmentRef<'a, T> {
548    /// Value object entry.
549    Entry(EntryRef<'a, T>),
550
551    /// Value object entry key.
552    Key(EntryKey),
553
554    /// Value object entry value.
555    Value(EntryValueRef<'a, T>),
556
557    /// JSON fragment in a "@json" typed value.
558    JsonFragment(jstrict::FragmentRef<'a>),
559}
560
561impl<'a, T> FragmentRef<'a, T> {
562    /// Returns the IRI this fragment stands for, if it is one.
563    #[must_use]
564    pub fn into_iri(self) -> Option<&'a T> {
565        match self {
566            Self::Value(EntryValueRef::Type(TypeRef::Id(id))) => Some(id),
567            _ => None,
568        }
569    }
570
571    /// Returns the IRI this fragment stands for, if it is one.
572    #[must_use]
573    pub fn as_iri(&self) -> Option<&'a T> {
574        match self {
575            Self::Value(EntryValueRef::Type(TypeRef::Id(id))) => Some(id),
576            _ => None,
577        }
578    }
579
580    /// Checks whether this fragment renders as a JSON array.
581    #[must_use]
582    pub fn is_json_array(&self) -> bool {
583        match self {
584            Self::Value(EntryValueRef::Value(ValueEntryRef::Json(json))) => json.is_array(),
585            Self::JsonFragment(json) => json.is_array(),
586            _ => false,
587        }
588    }
589
590    /// Checks whether this fragment renders as a JSON object.
591    #[must_use]
592    pub fn is_json_object(&self) -> bool {
593        match self {
594            Self::Value(EntryValueRef::Value(ValueEntryRef::Json(json))) => json.is_object(),
595            Self::JsonFragment(json) => json.is_object(),
596            _ => false,
597        }
598    }
599
600    /// Returns an iterator over the fragments directly contained in this one.
601    #[must_use]
602    pub fn sub_fragments(&self) -> SubFragments<'a, T> {
603        match self {
604            Self::Entry(e) => SubFragments::Entry(Some(e.key()), Some(e.value())),
605            Self::Value(EntryValueRef::Value(ValueEntryRef::Json(json))) => match json {
606                jstrict::Value::Array(a) => SubFragments::JsonFragment(jstrict::SubFragments::Array(a.iter())),
607                jstrict::Value::Object(o) => SubFragments::JsonFragment(jstrict::SubFragments::Object(o.iter())),
608                _ => SubFragments::None(PhantomData),
609            },
610            Self::JsonFragment(f) => SubFragments::JsonFragment(f.sub_fragments()),
611            _ => SubFragments::None(PhantomData),
612        }
613    }
614}
615
616/// Iterator over the fragments held by a value object.
617pub enum SubFragments<'a, T> {
618    /// No value.
619    None(PhantomData<T>),
620    /// An object entry.
621    Entry(Option<EntryKey>, Option<EntryValueRef<'a, T>>),
622    /// A fragment of a JSON literal.
623    JsonFragment(jstrict::SubFragments<'a>),
624}
625
626impl<'a, T: 'a> Iterator for SubFragments<'a, T> {
627    type Item = FragmentRef<'a, T>;
628
629    fn next(&mut self) -> Option<Self::Item> {
630        match self {
631            Self::None(_) => None,
632            Self::Entry(k, v) => k.take().map(FragmentRef::Key).or_else(|| v.take().map(FragmentRef::Value)),
633            Self::JsonFragment(f) => f.next().map(|v| FragmentRef::JsonFragment(v)),
634        }
635    }
636}
637
638impl<T, N: IriVocabulary<Iri = T>> IntoJsonWithContext<N> for Value<T> {
639    fn into_json_with(self, vocabulary: &N) -> jstrict::Value {
640        let mut obj = jstrict::Object::new();
641
642        let value = match self {
643            Self::Literal(lit, ty) => {
644                if let Some(ty) = ty {
645                    let ty_str = vocabulary.iri(&ty).map_or("<unresolved iri>", iri_rs::Iri::into_inner);
646                    obj.insert("@type".into(), ty_str.into());
647                }
648
649                lit.into_json()
650            }
651            Self::LangString(s) => {
652                if let Some(language) = s.language() {
653                    obj.insert("@language".into(), language.as_str().into());
654                }
655
656                if let Some(direction) = s.direction() {
657                    obj.insert("@direction".into(), direction.as_str().into());
658                }
659
660                s.as_str().into()
661            }
662            Self::Json(json) => {
663                obj.insert("@type".into(), "@json".into());
664
665                json
666            }
667        };
668
669        obj.insert("@value".into(), value);
670        obj.into()
671    }
672}