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#[derive_where(Clone, Copy)]
18pub enum TypeRef<'a, T> {
19 Json,
21 Id(&'a T),
23}
24
25impl<'a, T> TypeRef<'a, T> {
26 #[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 #[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#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
49pub enum Literal {
50 Null,
52
53 Boolean(bool),
55
56 Number(NumberBuf),
58
59 String(jstrict::String),
61}
62
63impl Literal {
64 #[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 #[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 #[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 #[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 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 pub fn canonicalize(&mut self) {
115 let mut buffer = ryu_js::Buffer::new();
116 self.canonicalize_with(&mut buffer);
117 }
118}
119
120#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
124pub enum Value<T = IriBuf> {
125 Literal(Literal, Option<T>),
127
128 LangString(LangString),
130
131 Json(jstrict::Value),
133}
134
135impl<T> Value<T> {
136 #[inline(always)]
138 pub fn null() -> Self {
139 Self::Literal(Literal::Null, None)
140 }
141
142 #[inline(always)]
143 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 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 pub fn literal_type(&self) -> Option<&T> {
164 match self {
165 Self::Literal(_, ty) => ty.as_ref(),
166 _ => None,
167 }
168 }
169
170 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 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 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 pub fn as_number(&self) -> Option<&Number> {
206 match self {
207 Value::Literal(lit, _) => lit.as_number(),
208 _ => None,
209 }
210 }
211
212 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 #[inline(always)]
229 pub fn language(&self) -> Option<&LenientLangTag> {
230 match self {
231 Value::LangString(tag) => tag.language(),
232 _ => None,
233 }
234 }
235
236 #[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 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 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 pub fn canonicalize(&mut self) {
325 let mut buffer = ryu_js::Buffer::new();
326 self.canonicalize_with(&mut buffer);
327 }
328
329 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)]
362pub enum EntryRef<'a, T> {
364 Value(ValueEntryRef<'a>),
366 Type(TypeRef<'a, T>),
368 Language(&'a LenientLangTag),
370 Direction(Direction),
372}
373
374impl<'a, T> EntryRef<'a, T> {
375 #[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 #[must_use]
388 pub fn key(&self) -> EntryKey {
389 self.into_key()
390 }
391
392 #[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 #[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)]
416pub enum EntryValueRef<'a, T> {
418 Value(ValueEntryRef<'a>),
420 Type(TypeRef<'a, T>),
422 Language(&'a LenientLangTag),
424 Direction(Direction),
426}
427#[derive(Clone, Copy)]
429pub enum ValueEntryRef<'a> {
430 Literal(&'a Literal),
432 LangString(&'a str),
434 Json(&'a jstrict::Value),
436}
437
438#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
439pub enum EntryKey {
441 Value,
443 Type,
445 Language,
447 Direction,
449}
450
451impl EntryKey {
452 #[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 #[must_use]
465 pub fn as_keyword(&self) -> Keyword {
466 self.into_keyword()
467 }
468
469 #[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 #[must_use]
482 pub fn as_str(&self) -> &'static str {
483 self.into_str()
484 }
485}
486
487#[derive_where(Clone)]
488pub 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
546pub enum FragmentRef<'a, T> {
548 Entry(EntryRef<'a, T>),
550
551 Key(EntryKey),
553
554 Value(EntryValueRef<'a, T>),
556
557 JsonFragment(jstrict::FragmentRef<'a>),
559}
560
561impl<'a, T> FragmentRef<'a, T> {
562 #[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 #[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 #[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 #[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 #[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
616pub enum SubFragments<'a, T> {
618 None(PhantomData<T>),
620 Entry(Option<EntryKey>, Option<EntryValueRef<'a, T>>),
622 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}