json_ld_core/object/
value.rs

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