1mod 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
22pub 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 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 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 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 pub fn get_type(&self) -> Option<&TypeTermDefinition> {
96 self.definitions.get_type()
97 }
98
99 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 pub fn original_base_url(&self) -> Option<&T> {
111 self.original_base_url.as_ref()
112 }
113
114 pub fn base_iri(&self) -> Option<&T> {
116 self.base_iri.as_ref()
117 }
118
119 pub fn vocabulary(&self) -> Option<&Term<T, B>> {
121 match &self.vocabulary {
122 Some(v) => Some(v),
123 None => None,
124 }
125 }
126
127 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 pub fn default_base_direction(&self) -> Option<Direction> {
136 self.default_base_direction
137 }
138
139 pub fn previous_context(&self) -> Option<&Self> {
141 match &self.previous_context {
142 Some(c) => Some(c),
143 None => None,
144 }
145 }
146
147 pub fn len(&self) -> usize {
149 self.definitions.len()
150 }
151
152 pub fn is_empty(&self) -> bool {
154 self.definitions.is_empty()
155 }
156
157 pub fn definitions(&self) -> &Definitions<T, B> {
159 &self.definitions
160 }
161
162 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 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 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 pub fn set_type(&mut self, type_: Option<TypeTermDefinition>) -> Option<TypeTermDefinition> {
194 self.definitions.set_type(type_)
195 }
196
197 pub fn set_base_iri(&mut self, iri: Option<T>) {
199 self.inverse.take();
200 self.base_iri = iri
201 }
202
203 pub fn set_vocabulary(&mut self, vocab: Option<Term<T, B>>) {
205 self.inverse.take();
206 self.vocabulary = vocab;
207 }
208
209 pub fn set_default_language(&mut self, lang: Option<LenientLangTagBuf>) {
211 self.inverse.take();
212 self.default_language = lang;
213 }
214
215 pub fn set_default_base_direction(&mut self, dir: Option<Direction>) {
217 self.inverse.take();
218 self.default_base_direction = dir;
219 }
220
221 pub fn set_previous_context(&mut self, previous: Self) {
223 self.inverse.take();
224 self.previous_context = Some(Box::new(previous))
225 }
226
227 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
285pub 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}