1mod definition;
3pub mod inverse;
5
6use crate::{Direction, LenientLangTag, LenientLangTagBuf, ProcessingMode, Term, ValidId as Id};
7use contextual::WithContext;
8use iri_rs::IriBuf;
9use jsonld_syntax::{Keyword, KeywordType, Nullable};
10use once_cell::sync::OnceCell;
11use parking_lot::Mutex;
12use rdfx::{BlankIdBuf, vocabulary::Vocabulary};
13use std::{borrow::Borrow, hash::Hash, sync::Arc};
14
15pub use jsonld_syntax::context::{
16 definition::{Key, KeyOrType, Type},
17 term_definition::Nest,
18};
19
20pub use definition::*;
21pub use inverse::InverseContext;
22
23pub enum ContextRef<'a, T = IriBuf, B = BlankIdBuf> {
33 Borrowed(&'a Context<T, B>),
35
36 Owned(Box<Context<T, B>>),
38}
39
40impl<T, B> ContextRef<'_, T, B> {
41 #[inline(always)]
43 pub fn owned(context: Context<T, B>) -> Self {
44 Self::Owned(Box::new(context))
45 }
46
47 #[allow(clippy::should_implement_trait)]
53 #[inline(always)]
54 #[must_use]
55 pub fn as_ref(&self) -> &Context<T, B> {
56 match self {
57 Self::Borrowed(context) => context,
58 Self::Owned(context) => context,
59 }
60 }
61
62 #[inline(always)]
67 #[must_use]
68 pub fn is_owned(&self) -> bool {
69 matches!(self, Self::Owned(_))
70 }
71}
72
73impl<T, B> std::ops::Deref for ContextRef<'_, T, B> {
74 type Target = Context<T, B>;
75
76 #[inline(always)]
77 fn deref(&self) -> &Self::Target {
78 self.as_ref()
79 }
80}
81
82#[derive(Debug, Clone, Copy, thiserror::Error)]
84pub enum InvalidVocab {
85 #[error("vocabulary cannot be a keyword")]
87 Keyword,
88}
89
90#[derive(Debug, Clone, Copy, thiserror::Error)]
92#[error("invalid context key")]
93pub struct InvalidContextKey;
94
95#[derive(Debug, Clone, Copy, thiserror::Error)]
98pub enum InvalidContextError {
99 #[error(transparent)]
100 InvalidVocab(#[from] InvalidVocab),
102
103 #[error(transparent)]
104 InvalidContextKey(#[from] InvalidContextKey),
106
107 #[error("IRI handle does not resolve in the given vocabulary")]
108 UnresolvedIri,
112}
113
114pub type CompactIriKey<T, B> = (Term<T, B>, bool, bool, ProcessingMode);
122
123pub struct CompactIriKeyRef<'a, T, B>(pub &'a Term<T, B>, pub bool, pub bool, pub ProcessingMode);
127
128impl<T: std::hash::Hash, B: std::hash::Hash> std::hash::Hash for CompactIriKeyRef<'_, T, B> {
129 #[inline]
130 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
131 self.0.hash(state);
132 self.1.hash(state);
133 self.2.hash(state);
134 self.3.hash(state);
135 }
136}
137
138impl<T: PartialEq, B: PartialEq> hashbrown::Equivalent<CompactIriKey<T, B>> for CompactIriKeyRef<'_, T, B> {
139 #[inline]
140 fn equivalent(&self, key: &CompactIriKey<T, B>) -> bool {
141 self.0 == &key.0 && self.1 == key.1 && self.2 == key.2 && self.3 == key.3
142 }
143}
144
145pub struct KeywordAliases {
154 aliases: [Box<str>; 13],
155}
156
157impl KeywordAliases {
158 #[must_use]
160 pub fn new(aliases: [Box<str>; 13]) -> Self {
161 Self { aliases }
162 }
163
164 #[inline]
169 #[must_use]
170 pub fn get(&self, k: Keyword) -> &str {
171 match keyword_alias_index(k) {
172 Some(i) => self.aliases[i].as_ref(),
173 None => k.into_str(),
174 }
175 }
176}
177
178pub const CACHED_KEYWORDS: [Keyword; 13] = [
180 Keyword::Id,
181 Keyword::Type,
182 Keyword::Value,
183 Keyword::List,
184 Keyword::Set,
185 Keyword::Graph,
186 Keyword::Index,
187 Keyword::Language,
188 Keyword::Direction,
189 Keyword::Reverse,
190 Keyword::None,
191 Keyword::Included,
192 Keyword::Json,
193];
194
195#[inline]
196fn keyword_aliases_slot(mode: ProcessingMode) -> usize {
197 match mode {
198 ProcessingMode::JsonLd1_0 => 0,
199 ProcessingMode::JsonLd1_1 => 1,
200 }
201}
202
203#[inline]
204fn keyword_alias_index(k: Keyword) -> Option<usize> {
205 Some(match k {
206 Keyword::Id => 0,
207 Keyword::Type => 1,
208 Keyword::Value => 2,
209 Keyword::List => 3,
210 Keyword::Set => 4,
211 Keyword::Graph => 5,
212 Keyword::Index => 6,
213 Keyword::Language => 7,
214 Keyword::Direction => 8,
215 Keyword::Reverse => 9,
216 Keyword::None => 10,
217 Keyword::Included => 11,
218 Keyword::Json => 12,
219 _ => return None,
220 })
221}
222
223type CompactIriCache<T, B> = Mutex<crate::HashMap<CompactIriKey<T, B>, Option<Arc<str>>>>;
224type TermResolutionCache<T, B> = Mutex<crate::HashMap<Box<str>, Arc<Term<T, B>>>>;
225
226pub type PrefixTerm<T, B> = (Key, Arc<Term<T, B>>);
228
229pub struct Context<T = IriBuf, B = BlankIdBuf> {
232 original_base_url: Option<T>,
233 base_iri: Option<T>,
234 vocabulary: Option<Term<T, B>>,
235 default_language: Option<LenientLangTagBuf>,
236 default_base_direction: Option<Direction>,
237 previous_context: Option<Arc<Self>>,
238 definitions: Arc<Definitions<T, B>>,
239 processing_mode: ProcessingMode,
246 inverse: Arc<OnceCell<InverseContext<T, B>>>,
257 prefix_terms: Arc<OnceCell<Vec<PrefixTerm<T, B>>>>,
258 compact_iri_cache: Arc<OnceCell<CompactIriCache<T, B>>>,
259 term_resolution_cache: Arc<OnceCell<TermResolutionCache<T, B>>>,
260 keyword_aliases: Arc<[OnceCell<KeywordAliases>; 2]>,
262}
263
264impl<T, B> Default for Context<T, B> {
265 fn default() -> Self {
266 Self {
267 original_base_url: None,
268 base_iri: None,
269 vocabulary: None,
270 default_language: None,
271 default_base_direction: None,
272 previous_context: None,
273 definitions: Arc::new(Definitions::default()),
274 processing_mode: ProcessingMode::default(),
275 inverse: Arc::new(OnceCell::new()),
276 prefix_terms: Arc::new(OnceCell::new()),
277 compact_iri_cache: Arc::new(OnceCell::new()),
278 term_resolution_cache: Arc::new(OnceCell::new()),
279 keyword_aliases: Arc::new([OnceCell::new(), OnceCell::new()]),
280 }
281 }
282}
283
284pub type DefinitionEntryRef<'a, T = IriBuf, B = BlankIdBuf> = (&'a Key, &'a TermDefinition<T, B>);
286
287impl<T, B> Context<T, B> {
288 pub fn new(base_iri: Option<T>) -> Self
290 where
291 T: Clone,
292 {
293 Self {
294 original_base_url: base_iri.clone(),
295 base_iri,
296 vocabulary: None,
297 default_language: None,
298 default_base_direction: None,
299 previous_context: None,
300 definitions: Arc::new(Definitions::default()),
301 processing_mode: ProcessingMode::default(),
302 inverse: Arc::new(OnceCell::new()),
303 prefix_terms: Arc::new(OnceCell::new()),
304 compact_iri_cache: Arc::new(OnceCell::new()),
305 term_resolution_cache: Arc::new(OnceCell::new()),
306 keyword_aliases: Arc::new([OnceCell::new(), OnceCell::new()]),
307 }
308 }
309
310 #[must_use]
327 pub fn with_private_caches(&self) -> Context<T, B>
328 where
329 T: Clone,
330 B: Clone,
331 {
332 Context {
333 original_base_url: self.original_base_url.clone(),
334 base_iri: self.base_iri.clone(),
335 vocabulary: self.vocabulary.clone(),
336 default_language: self.default_language.clone(),
337 default_base_direction: self.default_base_direction,
338 previous_context: self.previous_context.as_ref().map(|previous| Arc::new(previous.with_private_caches())),
341 definitions: Arc::clone(&self.definitions),
342 processing_mode: self.processing_mode,
343 inverse: Arc::clone(&self.inverse),
344 prefix_terms: Arc::clone(&self.prefix_terms),
345 compact_iri_cache: Arc::new(OnceCell::new()),
346 term_resolution_cache: Arc::new(OnceCell::new()),
347 keyword_aliases: Arc::clone(&self.keyword_aliases),
348 }
349 }
350
351 #[inline(always)]
353 pub fn processing_mode(&self) -> ProcessingMode {
354 self.processing_mode
355 }
356
357 #[inline(always)]
359 pub fn set_processing_mode(&mut self, mode: ProcessingMode) {
360 self.processing_mode = mode;
361 }
362
363 pub fn get<Q>(&self, term: &Q) -> Option<TermDefinitionRef<'_, T, B>>
365 where
366 Key: Borrow<Q>,
367 KeywordType: Borrow<Q>,
368 Q: ?Sized + Hash + Eq,
369 {
370 self.definitions.get(term)
371 }
372
373 pub fn get_normal<Q>(&self, term: &Q) -> Option<&NormalTermDefinition<T, B>>
375 where
376 Key: Borrow<Q>,
377 Q: ?Sized + Hash + Eq,
378 {
379 self.definitions.get_normal(term)
380 }
381
382 pub fn get_type(&self) -> Option<&TypeTermDefinition> {
384 self.definitions.get_type()
385 }
386
387 pub fn contains_term<Q>(&self, term: &Q) -> bool
389 where
390 Key: Borrow<Q>,
391 KeywordType: Borrow<Q>,
392 Q: ?Sized + Hash + Eq,
393 {
394 self.definitions.contains_term(term)
395 }
396
397 pub fn original_base_url(&self) -> Option<&T> {
399 self.original_base_url.as_ref()
400 }
401
402 pub fn base_iri(&self) -> Option<&T> {
404 self.base_iri.as_ref()
405 }
406
407 pub fn vocabulary(&self) -> Option<&Term<T, B>> {
409 match &self.vocabulary {
410 Some(v) => Some(v),
411 None => None,
412 }
413 }
414
415 pub fn default_language(&self) -> Option<&LenientLangTag> {
417 self.default_language.as_ref().map(jsonld_syntax::LenientLangTagBuf::as_lenient_lang_tag_ref)
418 }
419
420 pub fn default_base_direction(&self) -> Option<Direction> {
422 self.default_base_direction
423 }
424
425 pub fn previous_context(&self) -> Option<&Self> {
427 match &self.previous_context {
428 Some(c) => Some(c),
429 None => None,
430 }
431 }
432
433 pub fn definitions_arc_ptr(&self) -> *const Definitions<T, B> {
439 Arc::as_ptr(&self.definitions)
440 }
441
442 pub fn previous_context_arc_ptr(&self) -> *const Self {
445 self.previous_context.as_ref().map_or(std::ptr::null(), Arc::as_ptr)
446 }
447
448 pub fn len(&self) -> usize {
450 self.definitions.len()
451 }
452
453 pub fn is_empty(&self) -> bool {
455 self.definitions.is_empty()
456 }
457
458 pub fn definitions(&self) -> &Definitions<T, B> {
460 &self.definitions
461 }
462
463 pub fn has_protected_items(&self) -> bool {
465 for binding in self.definitions() {
466 if binding.definition().protected() {
467 return true;
468 }
469 }
470
471 false
472 }
473
474 pub fn inverse(&self) -> &InverseContext<T, B>
476 where
477 T: Clone + Hash + Eq,
478 B: Clone + Hash + Eq,
479 {
480 self.inverse.get_or_init(|| self.into())
481 }
482
483 pub fn prefix_terms(&self) -> &[PrefixTerm<T, B>] {
494 self.prefix_terms.get_or_init(|| {
495 self.definitions
496 .iter()
497 .filter_map(|binding| match binding {
498 BindingRef::Normal(key, def) if def.prefix => def.value_arc().map(|value| (*key, value.clone())),
499 _ => None,
500 })
501 .collect()
502 })
503 }
504
505 pub fn compact_iri_cache(&self) -> &CompactIriCache<T, B> {
512 self.compact_iri_cache.get_or_init(|| Mutex::new(crate::HashMap::default()))
513 }
514
515 pub fn term_resolution_cache(&self) -> &TermResolutionCache<T, B> {
523 self.term_resolution_cache.get_or_init(|| Mutex::new(crate::HashMap::default()))
524 }
525
526 pub fn keyword_aliases_or_init<F: FnOnce() -> KeywordAliases>(&self, mode: ProcessingMode, init: F) -> &KeywordAliases {
532 self.keyword_aliases[keyword_aliases_slot(mode)].get_or_init(init)
533 }
534
535 #[inline]
545 fn invalidate_iri_caches(&mut self) {
546 if self.inverse.get().is_some() {
547 self.inverse = Arc::new(OnceCell::new());
548 }
549 if self.compact_iri_cache.get().is_some() {
550 self.compact_iri_cache = Arc::new(OnceCell::new());
551 }
552 if self.term_resolution_cache.get().is_some() {
553 self.term_resolution_cache = Arc::new(OnceCell::new());
554 }
555 if self.keyword_aliases.iter().any(|slot| slot.get().is_some()) {
556 self.keyword_aliases = Arc::new([OnceCell::new(), OnceCell::new()]);
557 }
558 }
559
560 pub fn set_normal(&mut self, key: Key, definition: Option<NormalTermDefinition<T, B>>) -> Option<NormalTermDefinition<T, B>>
562 where
563 T: Clone,
564 B: Clone,
565 {
566 self.invalidate_iri_caches();
567 if self.prefix_terms.get().is_some() {
568 self.prefix_terms = Arc::new(OnceCell::new());
569 }
570 Arc::make_mut(&mut self.definitions).set_normal(key, definition)
571 }
572
573 pub fn set_type(&mut self, type_: Option<TypeTermDefinition>) -> Option<TypeTermDefinition>
575 where
576 T: Clone,
577 B: Clone,
578 {
579 if self.compact_iri_cache.get().is_some() {
580 self.compact_iri_cache = Arc::new(OnceCell::new());
581 }
582 if self.term_resolution_cache.get().is_some() {
583 self.term_resolution_cache = Arc::new(OnceCell::new());
584 }
585 if self.keyword_aliases.iter().any(|slot| slot.get().is_some()) {
586 self.keyword_aliases = Arc::new([OnceCell::new(), OnceCell::new()]);
587 }
588 Arc::make_mut(&mut self.definitions).set_type(type_)
589 }
590
591 pub fn set_base_iri(&mut self, iri: Option<T>) {
593 self.invalidate_iri_caches();
594 self.base_iri = iri;
595 }
596
597 pub fn set_vocabulary(&mut self, vocab: Option<Term<T, B>>) {
599 self.invalidate_iri_caches();
600 self.vocabulary = vocab;
601 }
602
603 pub fn set_default_language(&mut self, lang: Option<LenientLangTagBuf>) {
605 self.invalidate_iri_caches();
606 self.default_language = lang;
607 }
608
609 pub fn set_default_base_direction(&mut self, dir: Option<Direction>) {
611 self.invalidate_iri_caches();
612 self.default_base_direction = dir;
613 }
614
615 pub fn set_previous_context(&mut self, previous: Self) {
617 self.invalidate_iri_caches();
618 self.previous_context = Some(Arc::new(previous));
619 }
620
621 pub fn into_syntax_definition(self, vocabulary: &impl Vocabulary<Iri = T, BlankId = B>) -> Result<jsonld_syntax::context::Definition, InvalidContextError>
627 where
628 T: Clone,
629 B: Clone,
630 {
631 let (bindings, type_) = Arc::unwrap_or_clone(self.definitions).into_parts();
632
633 let vocab = match self.vocabulary {
634 Some(Term::Null) => Some(Nullable::Null),
635 Some(Term::Id(r)) => Some(Nullable::Some(r.with(vocabulary).to_string().into())),
636 Some(Term::Keyword(_)) => return Err(InvalidContextError::InvalidVocab(InvalidVocab::Keyword)),
637 None => None,
638 };
639
640 let base = match self.base_iri {
641 Some(i) => {
642 let iri: iri_rs::IriBuf = vocabulary.iri(&i).ok_or(InvalidContextError::UnresolvedIri)?.into();
645 Some(Nullable::Some(iri.into()))
646 }
647 None => None,
648 };
649
650 Ok(jsonld_syntax::context::Definition {
651 base,
652 import: None,
653 language: self.default_language.map(Nullable::Some),
654 direction: self.default_base_direction.map(Nullable::Some),
655 propagate: None,
656 protected: None,
657 type_: type_.map(TypeTermDefinition::into_syntax_definition),
658 version: None,
659 vocab,
660 bindings: {
661 let mut entries: Vec<_> = bindings.into_iter().collect();
664 entries.sort_unstable_by(|(a, _), (b, _)| a.as_str().cmp(b.as_str()));
665 entries
666 .into_iter()
667 .map(|(key, definition)| definition.into_syntax_definition(vocabulary).map(|d| (key, d)))
668 .collect::<Result<_, _>>()?
669 },
670 })
671 }
672
673 pub fn map_ids<U, C>(self, mut map_iri: impl FnMut(T) -> U, mut map_id: impl FnMut(Id<T, B>) -> Id<U, C>) -> Context<U, C>
676 where
677 T: Clone,
678 B: Clone,
679 {
680 self.map_ids_with(&mut map_iri, &mut map_id)
681 }
682
683 fn map_ids_with<U, C>(self, map_iri: &mut impl FnMut(T) -> U, map_id: &mut impl FnMut(Id<T, B>) -> Id<U, C>) -> Context<U, C>
684 where
685 T: Clone,
686 B: Clone,
687 {
688 Context {
689 original_base_url: self.original_base_url.map(&mut *map_iri),
690 base_iri: self.base_iri.map(&mut *map_iri),
691 vocabulary: self.vocabulary.map(|v| v.map_id(&mut *map_id)),
692 default_language: self.default_language,
693 default_base_direction: self.default_base_direction,
694 previous_context: self.previous_context.map(|c| Arc::new(Arc::unwrap_or_clone(c).map_ids_with(map_iri, map_id))),
695 definitions: Arc::new(Arc::unwrap_or_clone(self.definitions).map_ids(map_iri, map_id)),
696 processing_mode: self.processing_mode,
697 inverse: Arc::new(OnceCell::new()),
698 prefix_terms: Arc::new(OnceCell::new()),
699 compact_iri_cache: Arc::new(OnceCell::new()),
700 term_resolution_cache: Arc::new(OnceCell::new()),
701 keyword_aliases: Arc::new([OnceCell::new(), OnceCell::new()]),
702 }
703 }
704}
705
706pub trait IntoSyntax<T = IriBuf, B = BlankIdBuf> {
708 fn into_syntax(self, vocabulary: &impl Vocabulary<Iri = T, BlankId = B>) -> Result<jsonld_syntax::context::Context, InvalidContextError>;
716}
717
718impl<T, B> IntoSyntax<T, B> for jsonld_syntax::context::Context {
719 fn into_syntax(self, _namespace: &impl Vocabulary<Iri = T, BlankId = B>) -> Result<jsonld_syntax::context::Context, InvalidContextError> {
720 Ok(self)
721 }
722}
723
724impl<T: Clone, B: Clone> IntoSyntax<T, B> for Context<T, B> {
725 fn into_syntax(self, vocabulary: &impl Vocabulary<Iri = T, BlankId = B>) -> Result<jsonld_syntax::context::Context, InvalidContextError> {
726 Ok(jsonld_syntax::context::Context::One(jsonld_syntax::ContextEntry::Definition(
727 self.into_syntax_definition(vocabulary)?,
728 )))
729 }
730}
731
732impl<T: Clone, B: Clone> Clone for Context<T, B> {
733 fn clone(&self) -> Self {
734 Self {
740 original_base_url: self.original_base_url.clone(),
741 base_iri: self.base_iri.clone(),
742 vocabulary: self.vocabulary.clone(),
743 default_language: self.default_language.clone(),
744 default_base_direction: self.default_base_direction,
745 previous_context: self.previous_context.clone(),
746 definitions: Arc::clone(&self.definitions),
747 processing_mode: self.processing_mode,
748 inverse: Arc::clone(&self.inverse),
749 prefix_terms: Arc::clone(&self.prefix_terms),
750 compact_iri_cache: Arc::clone(&self.compact_iri_cache),
751 term_resolution_cache: Arc::clone(&self.term_resolution_cache),
752 keyword_aliases: Arc::clone(&self.keyword_aliases),
753 }
754 }
755}
756
757impl<T: PartialEq, B: PartialEq> PartialEq for Context<T, B> {
758 fn eq(&self, other: &Self) -> bool {
759 self.original_base_url == other.original_base_url
760 && self.base_iri == other.base_iri
761 && self.vocabulary == other.vocabulary
762 && self.default_language == other.default_language
763 && self.default_base_direction == other.default_base_direction
764 && self.processing_mode == other.processing_mode
765 && (Arc::ptr_eq(&self.definitions, &other.definitions) || self.definitions == other.definitions)
768 && self.previous_context == other.previous_context
769 }
770}