json_ld_core/context/
definition.rs

1use super::{IntoSyntax, Nest};
2use crate::{Container, Direction, LenientLangTagBuf, Nullable, Term, Type};
3use contextual::WithContext;
4use iref::IriBuf;
5use json_ld_syntax::{
6	context::{
7		definition::{Key, TypeContainer},
8		term_definition::Index,
9	},
10	KeywordType,
11};
12use rdf_types::{vocabulary::IriVocabulary, BlankIdBuf, Id, Vocabulary};
13use std::collections::HashMap;
14use std::hash::Hash;
15use std::{borrow::Borrow, fmt};
16
17/// Term binding.
18pub enum Binding<T = IriBuf, B = BlankIdBuf> {
19	/// Normal term definition.
20	Normal(Key, NormalTermDefinition<T, B>),
21
22	/// `@type` term definition.
23	Type(TypeTermDefinition),
24}
25
26/// Term binding reference.
27pub enum BindingRef<'a, T, B> {
28	/// Normal term definition.
29	Normal(&'a Key, &'a NormalTermDefinition<T, B>),
30
31	/// `@type` term definition.
32	Type(&'a TypeTermDefinition),
33}
34
35pub enum BindingTerm<'a> {
36	Normal(&'a Key),
37	Type,
38}
39
40impl<'a> BindingTerm<'a> {
41	pub fn as_str(&self) -> &'a str {
42		match self {
43			Self::Normal(key) => key.as_str(),
44			Self::Type => "@type",
45		}
46	}
47}
48
49impl<'a> fmt::Display for BindingTerm<'a> {
50	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51		self.as_str().fmt(f)
52	}
53}
54
55impl<'a, T, B> BindingRef<'a, T, B> {
56	/// Returns a reference to the bound term.
57	pub fn term(&self) -> BindingTerm<'a> {
58		match self {
59			Self::Normal(key, _) => BindingTerm::Normal(key),
60			Self::Type(_) => BindingTerm::Type,
61		}
62	}
63
64	/// Returns a reference to the bound term definition.
65	pub fn definition(&self) -> TermDefinitionRef<'a, T, B> {
66		match self {
67			Self::Normal(_, d) => TermDefinitionRef::Normal(d),
68			Self::Type(d) => TermDefinitionRef::Type(d),
69		}
70	}
71}
72
73/// Context term definitions.
74#[derive(Clone)]
75pub struct Definitions<T, B> {
76	normal: HashMap<Key, NormalTermDefinition<T, B>>,
77	type_: Option<TypeTermDefinition>,
78}
79
80impl<T, B> Default for Definitions<T, B> {
81	fn default() -> Self {
82		Self {
83			normal: HashMap::new(),
84			type_: None,
85		}
86	}
87}
88
89impl<T, B> Definitions<T, B> {
90	#[allow(clippy::type_complexity)]
91	pub fn into_parts(
92		self,
93	) -> (
94		HashMap<Key, NormalTermDefinition<T, B>>,
95		Option<TypeTermDefinition>,
96	) {
97		(self.normal, self.type_)
98	}
99
100	/// Returns the number of defined terms.
101	pub fn len(&self) -> usize {
102		if self.type_.is_some() {
103			self.normal.len() + 1
104		} else {
105			self.normal.len()
106		}
107	}
108
109	/// Checks if no terms are defined.
110	pub fn is_empty(&self) -> bool {
111		self.type_.is_none() && self.normal.is_empty()
112	}
113
114	/// Returns a reference to the definition of the given `term`, if any.
115	pub fn get<Q>(&self, term: &Q) -> Option<TermDefinitionRef<T, B>>
116	where
117		Q: ?Sized + Hash + Eq,
118		Key: Borrow<Q>,
119		KeywordType: Borrow<Q>,
120	{
121		if KeywordType.borrow() == term {
122			self.type_.as_ref().map(TermDefinitionRef::Type)
123		} else {
124			self.normal.get(term).map(TermDefinitionRef::Normal)
125		}
126	}
127
128	/// Returns a reference to the normal definition of the given `term`, if any.
129	pub fn get_normal<Q>(&self, term: &Q) -> Option<&NormalTermDefinition<T, B>>
130	where
131		Q: ?Sized + Hash + Eq,
132		Key: Borrow<Q>,
133	{
134		self.normal.get(term)
135	}
136
137	/// Returns a reference to the `@type` definition, if any.
138	pub fn get_type(&self) -> Option<&TypeTermDefinition> {
139		self.type_.as_ref()
140	}
141
142	pub fn contains_term<Q>(&self, term: &Q) -> bool
143	where
144		Q: ?Sized + Hash + Eq,
145		Key: Borrow<Q>,
146		KeywordType: Borrow<Q>,
147	{
148		if KeywordType.borrow() == term {
149			self.type_.is_some()
150		} else {
151			self.normal.contains_key(term)
152		}
153	}
154
155	/// Inserts the given `binding`.
156	pub fn insert(&mut self, binding: Binding<T, B>) -> Option<TermDefinition<T, B>> {
157		match binding {
158			Binding::Normal(key, definition) => self
159				.insert_normal(key, definition)
160				.map(TermDefinition::Normal),
161			Binding::Type(definition) => self.insert_type(definition).map(TermDefinition::Type),
162		}
163	}
164
165	/// Defines the given normal term.
166	pub fn insert_normal(
167		&mut self,
168		term: Key,
169		definition: NormalTermDefinition<T, B>,
170	) -> Option<NormalTermDefinition<T, B>> {
171		self.normal.insert(term, definition)
172	}
173
174	/// Inserts the given `@type` definition.
175	pub fn insert_type(&mut self, definition: TypeTermDefinition) -> Option<TypeTermDefinition> {
176		std::mem::replace(&mut self.type_, Some(definition))
177	}
178
179	/// Sets the given `term` normal definition.
180	pub fn set_normal(
181		&mut self,
182		term: Key,
183		definition: Option<NormalTermDefinition<T, B>>,
184	) -> Option<NormalTermDefinition<T, B>> {
185		match definition {
186			Some(d) => self.normal.insert(term, d),
187			None => self.normal.remove(&term),
188		}
189	}
190
191	/// Sets the given `@type` definition.
192	pub fn set_type(
193		&mut self,
194		definition: Option<TypeTermDefinition>,
195	) -> Option<TypeTermDefinition> {
196		std::mem::replace(&mut self.type_, definition)
197	}
198
199	/// Returns an iterator over the term definitions.
200	pub fn iter(&self) -> Iter<T, B> {
201		Iter {
202			type_: self.type_.as_ref(),
203			normal: self.normal.iter(),
204		}
205	}
206
207	pub fn map_ids<U, C>(
208		self,
209		mut map_iri: impl FnMut(T) -> U,
210		mut map_id: impl FnMut(Id<T, B>) -> Id<U, C>,
211	) -> Definitions<U, C> {
212		Definitions {
213			normal: self
214				.normal
215				.into_iter()
216				.map(|(key, d)| (key, d.map_ids(&mut map_iri, &mut map_id)))
217				.collect(),
218			type_: self.type_,
219		}
220	}
221}
222
223pub struct Iter<'a, T, B> {
224	type_: Option<&'a TypeTermDefinition>,
225	normal: std::collections::hash_map::Iter<'a, Key, NormalTermDefinition<T, B>>,
226}
227
228impl<'a, T, B> Iterator for Iter<'a, T, B> {
229	type Item = BindingRef<'a, T, B>;
230
231	fn next(&mut self) -> Option<Self::Item> {
232		self.type_
233			.take()
234			.map(BindingRef::Type)
235			.or_else(|| self.normal.next().map(|(k, d)| BindingRef::Normal(k, d)))
236	}
237}
238
239impl<'a, T, B> IntoIterator for &'a Definitions<T, B> {
240	type Item = BindingRef<'a, T, B>;
241	type IntoIter = Iter<'a, T, B>;
242
243	fn into_iter(self) -> Self::IntoIter {
244		self.iter()
245	}
246}
247
248pub struct IntoIter<T, B> {
249	type_: Option<TypeTermDefinition>,
250	normal: std::collections::hash_map::IntoIter<Key, NormalTermDefinition<T, B>>,
251}
252
253impl<T, B> Iterator for IntoIter<T, B> {
254	type Item = Binding<T, B>;
255
256	fn next(&mut self) -> Option<Self::Item> {
257		self.type_
258			.take()
259			.map(Binding::Type)
260			.or_else(|| self.normal.next().map(|(k, d)| Binding::Normal(k, d)))
261	}
262}
263
264impl<T, B> IntoIterator for Definitions<T, B> {
265	type Item = Binding<T, B>;
266	type IntoIter = IntoIter<T, B>;
267
268	fn into_iter(self) -> Self::IntoIter {
269		IntoIter {
270			type_: self.type_,
271			normal: self.normal.into_iter(),
272		}
273	}
274}
275
276/// `@type` term definition.
277///
278/// Such definition compared to a [`NormalTermDefinition`] can only contain
279/// a `@container` and `@protected` value.
280#[derive(PartialEq, Eq, Clone)]
281pub struct TypeTermDefinition {
282	/// Type container.
283	pub container: TypeContainer,
284
285	/// Protection flag.
286	pub protected: bool,
287}
288
289impl Default for TypeTermDefinition {
290	fn default() -> Self {
291		Self {
292			container: TypeContainer::Set,
293			protected: false,
294		}
295	}
296}
297
298impl TypeTermDefinition {
299	pub fn modulo_protected_field(&self) -> ModuloProtected<&Self> {
300		ModuloProtected(self)
301	}
302
303	pub fn into_syntax_definition(self) -> json_ld_syntax::context::definition::Type {
304		json_ld_syntax::context::definition::Type {
305			container: self.container,
306			protected: if self.protected { Some(true) } else { None },
307		}
308	}
309}
310
311/// Term definition.
312#[derive(PartialEq, Eq, Clone)]
313pub enum TermDefinition<T, B> {
314	/// `@type` term definition.
315	Type(TypeTermDefinition),
316
317	/// Normal term definition.
318	Normal(NormalTermDefinition<T, B>),
319}
320
321impl<T, B> TermDefinition<T, B> {
322	pub fn as_ref(&self) -> TermDefinitionRef<T, B> {
323		match self {
324			Self::Type(t) => TermDefinitionRef::Type(t),
325			Self::Normal(n) => TermDefinitionRef::Normal(n),
326		}
327	}
328
329	pub fn modulo_protected_field(&self) -> ModuloProtected<TermDefinitionRef<T, B>> {
330		ModuloProtected(self.as_ref())
331	}
332
333	pub fn value(&self) -> Option<&Term<T, B>> {
334		match self {
335			Self::Type(_) => None,
336			Self::Normal(d) => d.value.as_ref(),
337		}
338	}
339
340	pub fn prefix(&self) -> bool {
341		match self {
342			Self::Type(_) => false,
343			Self::Normal(d) => d.prefix,
344		}
345	}
346
347	pub fn protected(&self) -> bool {
348		match self {
349			Self::Type(d) => d.protected,
350			Self::Normal(d) => d.protected,
351		}
352	}
353
354	pub fn reverse_property(&self) -> bool {
355		match self {
356			Self::Type(_) => false,
357			Self::Normal(d) => d.reverse_property,
358		}
359	}
360
361	pub fn base_url(&self) -> Option<&T> {
362		match self {
363			Self::Type(_) => None,
364			Self::Normal(d) => d.base_url.as_ref(),
365		}
366	}
367
368	pub fn context(&self) -> Option<&json_ld_syntax::context::Context> {
369		match self {
370			Self::Type(_) => None,
371			Self::Normal(d) => d.context.as_deref(),
372		}
373	}
374
375	pub fn container(&self) -> Container {
376		match self {
377			Self::Type(d) => d.container.into(),
378			Self::Normal(d) => d.container,
379		}
380	}
381
382	pub fn direction(&self) -> Option<Nullable<Direction>> {
383		match self {
384			Self::Type(_) => None,
385			Self::Normal(d) => d.direction,
386		}
387	}
388
389	pub fn index(&self) -> Option<&Index> {
390		match self {
391			Self::Type(_) => None,
392			Self::Normal(d) => d.index.as_ref(),
393		}
394	}
395
396	pub fn language(&self) -> Option<Nullable<&LenientLangTagBuf>> {
397		match self {
398			Self::Type(_) => None,
399			Self::Normal(d) => d.language.as_ref().map(Nullable::as_ref),
400		}
401	}
402
403	pub fn nest(&self) -> Option<&Nest> {
404		match self {
405			Self::Type(_) => None,
406			Self::Normal(d) => d.nest.as_ref(),
407		}
408	}
409
410	pub fn typ(&self) -> Option<&Type<T>> {
411		match self {
412			Self::Type(_) => None,
413			Self::Normal(d) => d.typ.as_ref(),
414		}
415	}
416}
417
418/// Term definition reference.
419#[derive(PartialEq, Eq)]
420pub enum TermDefinitionRef<'a, T = IriBuf, B = BlankIdBuf> {
421	/// `@type` definition.
422	Type(&'a TypeTermDefinition),
423
424	/// Normal definition.
425	Normal(&'a NormalTermDefinition<T, B>),
426}
427
428impl<'a, T, B> TermDefinitionRef<'a, T, B> {
429	pub fn modulo_protected_field(&self) -> ModuloProtected<Self> {
430		ModuloProtected(*self)
431	}
432
433	pub fn value(&self) -> Option<&'a Term<T, B>> {
434		match self {
435			Self::Type(_) => None,
436			Self::Normal(d) => d.value.as_ref(),
437		}
438	}
439
440	pub fn prefix(&self) -> bool {
441		match self {
442			Self::Type(_) => false,
443			Self::Normal(d) => d.prefix,
444		}
445	}
446
447	pub fn protected(&self) -> bool {
448		match self {
449			Self::Type(d) => d.protected,
450			Self::Normal(d) => d.protected,
451		}
452	}
453
454	pub fn reverse_property(&self) -> bool {
455		match self {
456			Self::Type(_) => false,
457			Self::Normal(d) => d.reverse_property,
458		}
459	}
460
461	pub fn base_url(&self) -> Option<&'a T> {
462		match self {
463			Self::Type(_) => None,
464			Self::Normal(d) => d.base_url.as_ref(),
465		}
466	}
467
468	pub fn context(&self) -> Option<&'a json_ld_syntax::context::Context> {
469		match self {
470			Self::Type(_) => None,
471			Self::Normal(d) => d.context.as_deref(),
472		}
473	}
474
475	pub fn container(&self) -> Container {
476		match self {
477			Self::Type(d) => d.container.into(),
478			Self::Normal(d) => d.container,
479		}
480	}
481
482	pub fn direction(&self) -> Option<Nullable<Direction>> {
483		match self {
484			Self::Type(_) => None,
485			Self::Normal(d) => d.direction,
486		}
487	}
488
489	pub fn index(&self) -> Option<&'a Index> {
490		match self {
491			Self::Type(_) => None,
492			Self::Normal(d) => d.index.as_ref(),
493		}
494	}
495
496	pub fn language(&self) -> Option<Nullable<&'a LenientLangTagBuf>> {
497		match self {
498			Self::Type(_) => None,
499			Self::Normal(d) => d.language.as_ref().map(Nullable::as_ref),
500		}
501	}
502
503	pub fn nest(&self) -> Option<&'a Nest> {
504		match self {
505			Self::Type(_) => None,
506			Self::Normal(d) => d.nest.as_ref(),
507		}
508	}
509
510	pub fn typ(&self) -> Option<&'a Type<T>> {
511		match self {
512			Self::Type(_) => None,
513			Self::Normal(d) => d.typ.as_ref(),
514		}
515	}
516}
517
518impl<'a, T, B> Clone for TermDefinitionRef<'a, T, B> {
519	fn clone(&self) -> Self {
520		*self
521	}
522}
523
524impl<'a, T, B> Copy for TermDefinitionRef<'a, T, B> {}
525
526// A term definition.
527#[derive(PartialEq, Eq, Clone)]
528pub struct NormalTermDefinition<T = IriBuf, B = BlankIdBuf> {
529	// IRI mapping.
530	pub value: Option<Term<T, B>>,
531
532	// Prefix flag.
533	pub prefix: bool,
534
535	// Protected flag.
536	pub protected: bool,
537
538	// Reverse property flag.
539	pub reverse_property: bool,
540
541	// Optional base URL.
542	pub base_url: Option<T>,
543
544	// Optional context.
545	pub context: Option<Box<json_ld_syntax::context::Context>>,
546
547	// Container mapping.
548	pub container: Container,
549
550	// Optional direction mapping.
551	pub direction: Option<Nullable<Direction>>,
552
553	// Optional index mapping.
554	pub index: Option<Index>,
555
556	// Optional language mapping.
557	pub language: Option<Nullable<LenientLangTagBuf>>,
558
559	// Optional nest value.
560	pub nest: Option<Nest>,
561
562	// Optional type mapping.
563	pub typ: Option<Type<T>>,
564}
565
566impl<T, B> NormalTermDefinition<T, B> {
567	pub fn modulo_protected_field(&self) -> ModuloProtected<&Self> {
568		ModuloProtected(self)
569	}
570
571	pub fn base_url(&self) -> Option<&T> {
572		self.base_url.as_ref()
573	}
574
575	pub fn into_syntax_definition(
576		self,
577		vocabulary: &impl Vocabulary<Iri = T, BlankId = B>,
578	) -> Nullable<json_ld_syntax::context::TermDefinition> {
579		use json_ld_syntax::context::term_definition::{Id, Type as SyntaxType, TypeKeyword};
580
581		fn term_into_id<T, B>(
582			vocabulary: &impl Vocabulary<Iri = T, BlankId = B>,
583			term: Term<T, B>,
584		) -> Nullable<Id> {
585			match term {
586				Term::Null => Nullable::Null,
587				Term::Keyword(k) => Nullable::Some(Id::Keyword(k)),
588				Term::Id(r) => Nullable::Some(Id::Term(r.with(vocabulary).to_string())),
589			}
590		}
591
592		fn term_into_key<T, B>(
593			vocabulary: &impl Vocabulary<Iri = T, BlankId = B>,
594			term: Term<T, B>,
595		) -> Key {
596			match term {
597				Term::Null => panic!("invalid key"),
598				Term::Keyword(k) => k.to_string().into(),
599				Term::Id(r) => r.with(vocabulary).to_string().into(),
600			}
601		}
602
603		fn type_into_syntax<T>(
604			vocabulary: &impl IriVocabulary<Iri = T>,
605			ty: Type<T>,
606		) -> SyntaxType {
607			match ty {
608				Type::Id => SyntaxType::Keyword(TypeKeyword::Id),
609				Type::Json => SyntaxType::Keyword(TypeKeyword::Json),
610				Type::None => SyntaxType::Keyword(TypeKeyword::None),
611				Type::Vocab => SyntaxType::Keyword(TypeKeyword::Vocab),
612				Type::Iri(t) => SyntaxType::Term(vocabulary.iri(&t).unwrap().to_string()),
613			}
614		}
615
616		let (id, reverse) = if self.reverse_property {
617			(None, self.value.map(|t| term_into_key(vocabulary, t)))
618		} else {
619			(self.value.map(|t| term_into_id(vocabulary, t)), None)
620		};
621
622		let container = self.container.into_syntax();
623
624		json_ld_syntax::context::term_definition::Expanded {
625			id,
626			type_: self
627				.typ
628				.map(|t| Nullable::Some(type_into_syntax(vocabulary, t))),
629			context: self.context.map(|e| Box::new(e.into_syntax(vocabulary))),
630			reverse,
631			index: self.index.clone(),
632			language: self.language,
633			direction: self.direction,
634			container: container.map(Nullable::Some),
635			nest: self.nest.clone(),
636			prefix: if self.prefix { Some(true) } else { None },
637			propagate: None,
638			protected: if self.protected { Some(true) } else { None },
639		}
640		.simplify()
641	}
642
643	fn map_ids<U, C>(
644		self,
645		mut map_iri: impl FnMut(T) -> U,
646		map_id: impl FnOnce(Id<T, B>) -> Id<U, C>,
647	) -> NormalTermDefinition<U, C> {
648		NormalTermDefinition {
649			value: self.value.map(|t| t.map_id(map_id)),
650			prefix: self.prefix,
651			protected: self.protected,
652			reverse_property: self.reverse_property,
653			base_url: self.base_url.map(&mut map_iri),
654			context: self.context,
655			container: self.container,
656			direction: self.direction,
657			index: self.index,
658			language: self.language,
659			nest: self.nest,
660			typ: self.typ.map(|t| t.map(map_iri)),
661		}
662	}
663}
664
665impl<T, B> Default for NormalTermDefinition<T, B> {
666	fn default() -> NormalTermDefinition<T, B> {
667		NormalTermDefinition {
668			value: None,
669			prefix: false,
670			protected: false,
671			reverse_property: false,
672			base_url: None,
673			typ: None,
674			language: None,
675			direction: None,
676			context: None,
677			nest: None,
678			index: None,
679			container: Container::new(),
680		}
681	}
682}
683
684/// Wrapper to consider a term definition without the `@protected` flag.
685pub struct ModuloProtected<T>(T);
686
687impl<'a, 'b, T: PartialEq, B: PartialEq> PartialEq<ModuloProtected<&'b NormalTermDefinition<T, B>>>
688	for ModuloProtected<&'a NormalTermDefinition<T, B>>
689{
690	fn eq(&self, other: &ModuloProtected<&'b NormalTermDefinition<T, B>>) -> bool {
691		// NOTE we ignore the `protected` flag.
692		self.0.prefix == other.0.prefix
693			&& self.0.reverse_property == other.0.reverse_property
694			&& self.0.language == other.0.language
695			&& self.0.direction == other.0.direction
696			&& self.0.nest == other.0.nest
697			&& self.0.index == other.0.index
698			&& self.0.container == other.0.container
699			&& self.0.base_url == other.0.base_url
700			&& self.0.value == other.0.value
701			&& self.0.typ == other.0.typ
702			&& self.0.context == other.0.context
703	}
704}
705
706impl<'a, T: Eq, B: Eq> Eq for ModuloProtected<&'a NormalTermDefinition<T, B>> {}
707
708impl<'a, 'b> PartialEq<ModuloProtected<&'b TypeTermDefinition>>
709	for ModuloProtected<&'a TypeTermDefinition>
710{
711	fn eq(&self, other: &ModuloProtected<&'b TypeTermDefinition>) -> bool {
712		// NOTE we ignore the `protected` flag.
713		self.0.container == other.0.container
714	}
715}
716
717impl<'a> Eq for ModuloProtected<&'a TypeTermDefinition> {}
718
719impl<'a, 'b, T: PartialEq, B: PartialEq> PartialEq<ModuloProtected<TermDefinitionRef<'b, T, B>>>
720	for ModuloProtected<TermDefinitionRef<'a, T, B>>
721{
722	fn eq(&self, other: &ModuloProtected<TermDefinitionRef<'b, T, B>>) -> bool {
723		// NOTE we ignore the `protected` flag.
724		self.0.prefix() == other.0.prefix()
725			&& self.0.reverse_property() == other.0.reverse_property()
726			&& self.0.language() == other.0.language()
727			&& self.0.direction() == other.0.direction()
728			&& self.0.nest() == other.0.nest()
729			&& self.0.index() == other.0.index()
730			&& self.0.container() == other.0.container()
731			&& self.0.base_url() == other.0.base_url()
732			&& self.0.value() == other.0.value()
733			&& self.0.typ() == other.0.typ()
734			&& self.0.context() == other.0.context()
735	}
736}
737
738impl<'a, T: Eq, B: Eq> Eq for ModuloProtected<TermDefinitionRef<'a, T, B>> {}