Skip to main content

jsonld_core/object/
value.rs

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