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