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#[derive(Educe)]
18#[educe(Clone(bound(false)), Copy(bound(false)))]
19pub enum TypeRef<'a, T> {
20 Json,
22 Id(&'a T),
24}
25
26impl<'a, T> TypeRef<'a, T> {
27 #[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 #[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#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
50pub enum Literal {
51 Null,
53
54 Boolean(bool),
56
57 Number(NumberBuf),
59
60 String(jstrict::String),
62}
63
64impl Literal {
65 #[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 #[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 #[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 #[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 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 pub fn canonicalize(&mut self) {
116 let mut buffer = ryu_js::Buffer::new();
117 self.canonicalize_with(&mut buffer);
118 }
119}
120
121#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
125pub enum Value<T = IriBuf> {
126 Literal(Literal, Option<T>),
128
129 LangString(LangString),
131
132 Json(jstrict::Value),
134}
135
136impl<T> Value<T> {
137 #[inline(always)]
139 pub fn null() -> Self {
140 Self::Literal(Literal::Null, None)
141 }
142
143 #[inline(always)]
144 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 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 pub fn literal_type(&self) -> Option<&T> {
165 match self {
166 Self::Literal(_, ty) => ty.as_ref(),
167 _ => None,
168 }
169 }
170
171 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 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 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 pub fn as_number(&self) -> Option<&Number> {
207 match self {
208 Value::Literal(lit, _) => lit.as_number(),
209 _ => None,
210 }
211 }
212
213 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 #[inline(always)]
230 pub fn language(&self) -> Option<&LenientLangTag> {
231 match self {
232 Value::LangString(tag) => tag.language(),
233 _ => None,
234 }
235 }
236
237 #[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 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 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 pub fn canonicalize(&mut self) {
326 let mut buffer = ryu_js::Buffer::new();
327 self.canonicalize_with(&mut buffer);
328 }
329
330 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)))]
364pub enum EntryRef<'a, T> {
366 Value(ValueEntryRef<'a>),
368 Type(TypeRef<'a, T>),
370 Language(&'a LenientLangTag),
372 Direction(Direction),
374}
375
376impl<'a, T> EntryRef<'a, T> {
377 #[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 #[must_use]
390 pub fn key(&self) -> EntryKey {
391 self.into_key()
392 }
393
394 #[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 #[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)))]
419pub enum EntryValueRef<'a, T> {
421 Value(ValueEntryRef<'a>),
423 Type(TypeRef<'a, T>),
425 Language(&'a LenientLangTag),
427 Direction(Direction),
429}
430#[derive(Clone, Copy)]
432pub enum ValueEntryRef<'a> {
433 Literal(&'a Literal),
435 LangString(&'a str),
437 Json(&'a jstrict::Value),
439}
440
441#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
442pub enum EntryKey {
444 Value,
446 Type,
448 Language,
450 Direction,
452}
453
454impl EntryKey {
455 #[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 #[must_use]
468 pub fn as_keyword(&self) -> Keyword {
469 self.into_keyword()
470 }
471
472 #[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 #[must_use]
485 pub fn as_str(&self) -> &'static str {
486 self.into_str()
487 }
488}
489
490#[derive(Educe)]
491#[educe(Clone)]
492pub 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
550pub enum FragmentRef<'a, T> {
552 Entry(EntryRef<'a, T>),
554
555 Key(EntryKey),
557
558 Value(EntryValueRef<'a, T>),
560
561 JsonFragment(jstrict::FragmentRef<'a>),
563}
564
565impl<'a, T> FragmentRef<'a, T> {
566 #[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 #[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 #[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 #[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 #[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
620pub enum SubFragments<'a, T> {
622 None(PhantomData<T>),
624 Entry(Option<EntryKey>, Option<EntryValueRef<'a, T>>),
626 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}