json_ld_core/context/
mod.rs

1//! Context processing algorithm and related types.
2mod definition;
3pub mod inverse;
4
5use crate::{Direction, LenientLangTag, LenientLangTagBuf, Term};
6use contextual::WithContext;
7use iref::IriBuf;
8use json_ld_syntax::{KeywordType, Nullable};
9use once_cell::sync::OnceCell;
10use rdf_types::{BlankIdBuf, Id, Vocabulary};
11use std::borrow::Borrow;
12use std::hash::Hash;
13
14pub use json_ld_syntax::context::{
15	definition::{Key, KeyOrType, Type},
16	term_definition::Nest,
17};
18
19pub use definition::*;
20pub use inverse::InverseContext;
21
22/// Processed JSON-LD context.
23///
24/// Represents the result of the [context processing algorithm][1] implemented
25/// by the [`json-ld-context-processing`] crate.
26///
27/// [1]: <https://www.w3.org/TR/json-ld11-api/#context-processing-algorithm>
28/// [`json-ld-context-processing`]: <https://crates.io/crates/json-ld-context-processing>
29pub struct Context<T = IriBuf, B = BlankIdBuf> {
30	original_base_url: Option<T>,
31	base_iri: Option<T>,
32	vocabulary: Option<Term<T, B>>,
33	default_language: Option<LenientLangTagBuf>,
34	default_base_direction: Option<Direction>,
35	previous_context: Option<Box<Self>>,
36	definitions: Definitions<T, B>,
37	inverse: OnceCell<InverseContext<T, B>>,
38}
39
40impl<T, B> Default for Context<T, B> {
41	fn default() -> Self {
42		Self {
43			original_base_url: None,
44			base_iri: None,
45			vocabulary: None,
46			default_language: None,
47			default_base_direction: None,
48			previous_context: None,
49			definitions: Definitions::default(),
50			inverse: OnceCell::default(),
51		}
52	}
53}
54
55pub type DefinitionEntryRef<'a, T = IriBuf, B = BlankIdBuf> = (&'a Key, &'a TermDefinition<T, B>);
56
57impl<T, B> Context<T, B> {
58	/// Create a new context with the given base IRI.
59	pub fn new(base_iri: Option<T>) -> Self
60	where
61		T: Clone,
62	{
63		Self {
64			original_base_url: base_iri.clone(),
65			base_iri,
66			vocabulary: None,
67			default_language: None,
68			default_base_direction: None,
69			previous_context: None,
70			definitions: Definitions::default(),
71			inverse: OnceCell::default(),
72		}
73	}
74
75	/// Returns a reference to the given `term` definition, if any.
76	pub fn get<Q>(&self, term: &Q) -> Option<TermDefinitionRef<T, B>>
77	where
78		Key: Borrow<Q>,
79		KeywordType: Borrow<Q>,
80		Q: ?Sized + Hash + Eq,
81	{
82		self.definitions.get(term)
83	}
84
85	/// Returns a reference to the given `term` normal definition, if any.
86	pub fn get_normal<Q>(&self, term: &Q) -> Option<&NormalTermDefinition<T, B>>
87	where
88		Key: Borrow<Q>,
89		Q: ?Sized + Hash + Eq,
90	{
91		self.definitions.get_normal(term)
92	}
93
94	/// Returns a reference to the `@type` definition, if any.
95	pub fn get_type(&self) -> Option<&TypeTermDefinition> {
96		self.definitions.get_type()
97	}
98
99	/// Checks if the given `term` is defined.
100	pub fn contains_term<Q>(&self, term: &Q) -> bool
101	where
102		Key: Borrow<Q>,
103		KeywordType: Borrow<Q>,
104		Q: ?Sized + Hash + Eq,
105	{
106		self.definitions.contains_term(term)
107	}
108
109	/// Returns the original base URL of the context.
110	pub fn original_base_url(&self) -> Option<&T> {
111		self.original_base_url.as_ref()
112	}
113
114	/// Returns the base IRI of the context.
115	pub fn base_iri(&self) -> Option<&T> {
116		self.base_iri.as_ref()
117	}
118
119	/// Returns the `@vocab` value, if any.
120	pub fn vocabulary(&self) -> Option<&Term<T, B>> {
121		match &self.vocabulary {
122			Some(v) => Some(v),
123			None => None,
124		}
125	}
126
127	/// Returns the default `@language` value.
128	pub fn default_language(&self) -> Option<&LenientLangTag> {
129		self.default_language
130			.as_ref()
131			.map(|tag| tag.as_lenient_lang_tag_ref())
132	}
133
134	/// Returns the default `@direction` value.
135	pub fn default_base_direction(&self) -> Option<Direction> {
136		self.default_base_direction
137	}
138
139	/// Returns a reference to the previous context.
140	pub fn previous_context(&self) -> Option<&Self> {
141		match &self.previous_context {
142			Some(c) => Some(c),
143			None => None,
144		}
145	}
146
147	/// Returns the number of terms defined.
148	pub fn len(&self) -> usize {
149		self.definitions.len()
150	}
151
152	/// Checks if no terms are defined.
153	pub fn is_empty(&self) -> bool {
154		self.definitions.is_empty()
155	}
156
157	/// Returns a handle to the term definitions.
158	pub fn definitions(&self) -> &Definitions<T, B> {
159		&self.definitions
160	}
161
162	/// Checks if the context has a protected definition.
163	pub fn has_protected_items(&self) -> bool {
164		for binding in self.definitions() {
165			if binding.definition().protected() {
166				return true;
167			}
168		}
169
170		false
171	}
172
173	/// Returns the inverse of this context.
174	pub fn inverse(&self) -> &InverseContext<T, B>
175	where
176		T: Clone + Hash + Eq,
177		B: Clone + Hash + Eq,
178	{
179		self.inverse.get_or_init(|| self.into())
180	}
181
182	/// Sets the normal definition for the given term `key`.
183	pub fn set_normal(
184		&mut self,
185		key: Key,
186		definition: Option<NormalTermDefinition<T, B>>,
187	) -> Option<NormalTermDefinition<T, B>> {
188		self.inverse.take();
189		self.definitions.set_normal(key, definition)
190	}
191
192	/// Sets the `@type` definition.
193	pub fn set_type(&mut self, type_: Option<TypeTermDefinition>) -> Option<TypeTermDefinition> {
194		self.definitions.set_type(type_)
195	}
196
197	/// Sets the base IRI.
198	pub fn set_base_iri(&mut self, iri: Option<T>) {
199		self.inverse.take();
200		self.base_iri = iri
201	}
202
203	/// Sets the `@vocab` value.
204	pub fn set_vocabulary(&mut self, vocab: Option<Term<T, B>>) {
205		self.inverse.take();
206		self.vocabulary = vocab;
207	}
208
209	/// Sets the default `@language` value.
210	pub fn set_default_language(&mut self, lang: Option<LenientLangTagBuf>) {
211		self.inverse.take();
212		self.default_language = lang;
213	}
214
215	/// Sets the default `@direction` value.
216	pub fn set_default_base_direction(&mut self, dir: Option<Direction>) {
217		self.inverse.take();
218		self.default_base_direction = dir;
219	}
220
221	/// Sets the previous context.
222	pub fn set_previous_context(&mut self, previous: Self) {
223		self.inverse.take();
224		self.previous_context = Some(Box::new(previous))
225	}
226
227	/// Converts this context into its syntactic definition.
228	pub fn into_syntax_definition(
229		self,
230		vocabulary: &impl Vocabulary<Iri = T, BlankId = B>,
231	) -> json_ld_syntax::context::Definition {
232		let (bindings, type_) = self.definitions.into_parts();
233
234		json_ld_syntax::context::Definition {
235			base: self
236				.base_iri
237				.map(|i| Nullable::Some(vocabulary.iri(&i).unwrap().to_owned().into())),
238			import: None,
239			language: self.default_language.map(Nullable::Some),
240			direction: self.default_base_direction.map(Nullable::Some),
241			propagate: None,
242			protected: None,
243			type_: type_.map(TypeTermDefinition::into_syntax_definition),
244			version: None,
245			vocab: self.vocabulary.map(|v| match v {
246				Term::Null => Nullable::Null,
247				Term::Id(r) => Nullable::Some(r.with(vocabulary).to_string().into()),
248				Term::Keyword(_) => panic!("invalid vocab"),
249			}),
250			bindings: bindings
251				.into_iter()
252				.map(|(key, definition)| (key, definition.into_syntax_definition(vocabulary)))
253				.collect(),
254		}
255	}
256
257	pub fn map_ids<U, C>(
258		self,
259		mut map_iri: impl FnMut(T) -> U,
260		mut map_id: impl FnMut(Id<T, B>) -> Id<U, C>,
261	) -> Context<U, C> {
262		self.map_ids_with(&mut map_iri, &mut map_id)
263	}
264
265	fn map_ids_with<U, C>(
266		self,
267		map_iri: &mut impl FnMut(T) -> U,
268		map_id: &mut impl FnMut(Id<T, B>) -> Id<U, C>,
269	) -> Context<U, C> {
270		Context {
271			original_base_url: self.original_base_url.map(&mut *map_iri),
272			base_iri: self.base_iri.map(&mut *map_iri),
273			vocabulary: self.vocabulary.map(|v| v.map_id(&mut *map_id)),
274			default_language: self.default_language,
275			default_base_direction: self.default_base_direction,
276			previous_context: self
277				.previous_context
278				.map(|c| Box::new((*c).map_ids_with(map_iri, map_id))),
279			definitions: self.definitions.map_ids(map_iri, map_id),
280			inverse: OnceCell::new(),
281		}
282	}
283}
284
285/// Context fragment to syntax method.
286pub trait IntoSyntax<T = IriBuf, B = BlankIdBuf> {
287	fn into_syntax(
288		self,
289		vocabulary: &impl Vocabulary<Iri = T, BlankId = B>,
290	) -> json_ld_syntax::context::Context;
291}
292
293impl<T, B> IntoSyntax<T, B> for json_ld_syntax::context::Context {
294	fn into_syntax(
295		self,
296		_namespace: &impl Vocabulary<Iri = T, BlankId = B>,
297	) -> json_ld_syntax::context::Context {
298		self
299	}
300}
301
302impl<T, B: Clone> IntoSyntax<T, B> for Context<T, B> {
303	fn into_syntax(
304		self,
305		vocabulary: &impl Vocabulary<Iri = T, BlankId = B>,
306	) -> json_ld_syntax::context::Context {
307		json_ld_syntax::context::Context::One(json_ld_syntax::ContextEntry::Definition(
308			self.into_syntax_definition(vocabulary),
309		))
310	}
311}
312
313impl<T: Clone, B: Clone> Clone for Context<T, B> {
314	fn clone(&self) -> Self {
315		Self {
316			original_base_url: self.original_base_url.clone(),
317			base_iri: self.base_iri.clone(),
318			vocabulary: self.vocabulary.clone(),
319			default_language: self.default_language.clone(),
320			default_base_direction: self.default_base_direction,
321			previous_context: self.previous_context.clone(),
322			definitions: self.definitions.clone(),
323			inverse: OnceCell::default(),
324		}
325	}
326}
327
328impl<T: PartialEq, B: PartialEq> PartialEq for Context<T, B> {
329	fn eq(&self, other: &Self) -> bool {
330		self.original_base_url == other.original_base_url
331			&& self.base_iri == other.base_iri
332			&& self.vocabulary == other.vocabulary
333			&& self.default_language == other.default_language
334			&& self.default_base_direction == other.default_base_direction
335			&& self.previous_context == other.previous_context
336	}
337}