json_ld_core/context/
inverse.rs

1use iref::IriBuf;
2
3use super::BindingRef;
4use super::Context;
5use super::Key;
6use crate::{Container, Direction, LenientLangTag, LenientLangTagBuf, Nullable, Term, Type};
7use std::cmp::Ordering;
8use std::collections::HashMap;
9use std::fmt;
10use std::hash::Hash;
11
12#[derive(Clone, PartialEq, Eq)]
13pub enum TypeSelection<T = IriBuf> {
14	Reverse,
15	Any,
16	Type(Type<T>),
17}
18
19impl<T: fmt::Debug> fmt::Debug for TypeSelection<T> {
20	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
21		match self {
22			TypeSelection::Reverse => write!(f, "Reverse"),
23			TypeSelection::Any => write!(f, "Any"),
24			TypeSelection::Type(ty) => write!(f, "Type({ty:?})"),
25		}
26	}
27}
28
29struct InverseType<T> {
30	reverse: Option<Key>,
31	any: Option<Key>,
32	map: HashMap<Type<T>, Key>,
33}
34
35impl<T> InverseType<T> {
36	fn select(&self, selection: TypeSelection<T>) -> Option<&Key>
37	where
38		T: Hash + Eq,
39	{
40		match selection {
41			TypeSelection::Reverse => self.reverse.as_ref(),
42			TypeSelection::Any => self.any.as_ref(),
43			TypeSelection::Type(ty) => self.map.get(&ty),
44		}
45	}
46
47	fn set_any(&mut self, term: &Key) {
48		if self.any.is_none() {
49			self.any = Some(term.clone())
50		}
51	}
52
53	fn set_none(&mut self, term: &Key)
54	where
55		T: Clone + Hash + Eq,
56	{
57		self.set(&Type::None, term)
58	}
59
60	fn set(&mut self, ty: &Type<T>, term: &Key)
61	where
62		T: Clone + Hash + Eq,
63	{
64		if !self.map.contains_key(ty) {
65			self.map.insert(ty.clone(), term.clone());
66		}
67	}
68}
69
70type LangDir = Nullable<(Option<LenientLangTagBuf>, Option<Direction>)>;
71
72struct InverseLang {
73	any: Option<Key>,
74	map: HashMap<LangDir, Key>,
75}
76
77#[derive(Clone, Copy, PartialEq, Eq, Debug)]
78pub enum LangSelection<'a> {
79	Any,
80	Lang(Nullable<(Option<&'a LenientLangTag>, Option<Direction>)>),
81}
82
83impl InverseLang {
84	fn select(&self, selection: LangSelection) -> Option<&Key> {
85		match selection {
86			LangSelection::Any => self.any.as_ref(),
87			LangSelection::Lang(lang_dir) => {
88				let lang_dir = lang_dir.map(|(l, d)| (l.map(|l| l.to_owned()), d));
89				self.map.get(&lang_dir)
90			}
91		}
92	}
93
94	fn set_any(&mut self, term: &Key) {
95		if self.any.is_none() {
96			self.any = Some(term.clone())
97		}
98	}
99
100	fn set_none(&mut self, term: &Key) {
101		self.set(Nullable::Some((None, None)), term)
102	}
103
104	fn set(
105		&mut self,
106		lang_dir: Nullable<(Option<&LenientLangTag>, Option<Direction>)>,
107		term: &Key,
108	) {
109		let lang_dir = lang_dir.map(|(l, d)| (l.map(|l| l.to_owned()), d));
110		self.map.entry(lang_dir).or_insert_with(|| term.clone());
111	}
112}
113
114struct InverseContainer<T> {
115	language: InverseLang,
116	typ: InverseType<T>,
117	any: Any,
118}
119
120struct Any {
121	none: Key,
122}
123
124impl<T> InverseContainer<T> {
125	pub fn new(term: &Key) -> InverseContainer<T> {
126		InverseContainer {
127			language: InverseLang {
128				any: None,
129				map: HashMap::new(),
130			},
131			typ: InverseType {
132				reverse: None,
133				any: None,
134				map: HashMap::new(),
135			},
136			any: Any { none: term.clone() },
137		}
138	}
139}
140
141pub struct InverseDefinition<T> {
142	map: HashMap<Container, InverseContainer<T>>,
143}
144
145impl<T> InverseDefinition<T> {
146	fn new() -> InverseDefinition<T> {
147		InverseDefinition {
148			map: HashMap::new(),
149		}
150	}
151
152	fn get(&self, container: &Container) -> Option<&InverseContainer<T>> {
153		self.map.get(container)
154	}
155
156	fn contains(&self, container: &Container) -> bool {
157		self.map.contains_key(container)
158	}
159
160	fn reference_mut<F: FnOnce() -> InverseContainer<T>>(
161		&mut self,
162		container: &Container,
163		insert: F,
164	) -> &mut InverseContainer<T> {
165		if !self.contains(container) {
166			self.map.insert(*container, insert());
167		}
168		self.map.get_mut(container).unwrap()
169	}
170
171	pub fn select(&self, containers: &[Container], selection: &Selection<T>) -> Option<&Key>
172	where
173		T: Clone + Hash + Eq,
174	{
175		for container in containers {
176			if let Some(type_lang_map) = self.get(container) {
177				match selection {
178					Selection::Any => return Some(&type_lang_map.any.none),
179					Selection::Type(preferred_values) => {
180						for item in preferred_values {
181							if let Some(term) = type_lang_map.typ.select(item.clone()) {
182								return Some(term);
183							}
184						}
185					}
186					Selection::Lang(preferred_values) => {
187						for item in preferred_values {
188							if let Some(term) = type_lang_map.language.select(*item) {
189								return Some(term);
190							}
191						}
192					}
193				}
194			}
195		}
196
197		None
198	}
199}
200
201/// Inverse context.
202pub struct InverseContext<T, B> {
203	map: HashMap<Term<T, B>, InverseDefinition<T>>,
204}
205
206pub enum Selection<'a, T> {
207	Any,
208	Type(Vec<TypeSelection<T>>),
209	Lang(Vec<LangSelection<'a>>),
210}
211
212impl<'a, T: fmt::Debug> fmt::Debug for Selection<'a, T> {
213	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
214		match self {
215			Selection::Any => write!(f, "Any"),
216			Selection::Type(s) => write!(f, "Type({s:?})"),
217			Selection::Lang(s) => write!(f, "Lang({s:?})"),
218		}
219	}
220}
221
222impl<T, B> InverseContext<T, B> {
223	pub fn new() -> Self {
224		InverseContext {
225			map: HashMap::new(),
226		}
227	}
228}
229
230impl<T: Hash + Eq, B: Hash + Eq> InverseContext<T, B> {
231	pub fn contains(&self, term: &Term<T, B>) -> bool {
232		self.map.contains_key(term)
233	}
234
235	pub fn insert(&mut self, term: Term<T, B>, value: InverseDefinition<T>) {
236		self.map.insert(term, value);
237	}
238
239	pub fn get(&self, term: &Term<T, B>) -> Option<&InverseDefinition<T>> {
240		self.map.get(term)
241	}
242
243	pub fn get_mut(&mut self, term: &Term<T, B>) -> Option<&mut InverseDefinition<T>> {
244		self.map.get_mut(term)
245	}
246
247	fn reference_mut<F: FnOnce() -> InverseDefinition<T>>(
248		&mut self,
249		term: &Term<T, B>,
250		insert: F,
251	) -> &mut InverseDefinition<T>
252	where
253		T: Clone,
254		B: Clone,
255	{
256		if !self.contains(term) {
257			self.insert(term.clone(), insert());
258		}
259		self.map.get_mut(term).unwrap()
260	}
261
262	pub fn select(
263		&self,
264		var: &Term<T, B>,
265		containers: &[Container],
266		selection: &Selection<T>,
267	) -> Option<&Key>
268	where
269		T: Clone,
270	{
271		match self.get(var) {
272			Some(container_map) => container_map.select(containers, selection),
273			None => None,
274		}
275	}
276}
277
278impl<T, B> Default for InverseContext<T, B> {
279	fn default() -> Self {
280		Self::new()
281	}
282}
283
284impl<'a, T: Clone + Hash + Eq, B: Clone + Hash + Eq> From<&'a Context<T, B>>
285	for InverseContext<T, B>
286{
287	fn from(context: &'a Context<T, B>) -> Self {
288		let mut result = InverseContext::new();
289
290		let mut definitions: Vec<_> = context.definitions().iter().collect();
291		definitions.sort_by(|a, b| {
292			let a = a.term().as_str();
293			let b = b.term().as_str();
294			let ord = a.len().cmp(&b.len());
295			if ord == Ordering::Equal {
296				a.cmp(b)
297			} else {
298				ord
299			}
300		});
301
302		for binding in definitions {
303			if let BindingRef::Normal(term, term_definition) = binding {
304				if let Some(var) = term_definition.value.as_ref() {
305					let container = &term_definition.container;
306					let container_map = result.reference_mut(var, InverseDefinition::new);
307					let type_lang_map =
308						container_map.reference_mut(container, || InverseContainer::new(term));
309
310					let type_map = &mut type_lang_map.typ;
311					let lang_map = &mut type_lang_map.language;
312
313					if term_definition.reverse_property {
314						// If the term definition indicates that the term represents a reverse property:
315						if type_map.reverse.is_none() {
316							type_map.reverse = Some(term.clone())
317						}
318					} else {
319						match &term_definition.typ {
320							Some(Type::None) => {
321								// Otherwise, if term definition has a type mapping which is @none:
322								type_map.set_any(term);
323								lang_map.set_any(term);
324							}
325							Some(typ) => {
326								// Otherwise, if term definition has a type mapping:
327								type_map.set(typ, term)
328							}
329							None => {
330								match (&term_definition.language, &term_definition.direction) {
331									(Some(language), Some(direction)) => {
332										// Otherwise, if term definition has both a language mapping
333										// and a direction mapping:
334										match (language, direction) {
335											(
336												Nullable::Some(language),
337												Nullable::Some(direction),
338											) => lang_map.set(
339												Nullable::Some((
340													Some(language.as_lenient_lang_tag_ref()),
341													Some(*direction),
342												)),
343												term,
344											),
345											(Nullable::Some(language), Nullable::Null) => lang_map
346												.set(
347													Nullable::Some((
348														Some(language.as_lenient_lang_tag_ref()),
349														None,
350													)),
351													term,
352												),
353											(Nullable::Null, Nullable::Some(direction)) => lang_map
354												.set(
355													Nullable::Some((None, Some(*direction))),
356													term,
357												),
358											(Nullable::Null, Nullable::Null) => {
359												lang_map.set(Nullable::Null, term)
360											}
361										}
362									}
363									(Some(language), None) => {
364										// Otherwise, if term definition has a language mapping (might
365										// be null):
366										match language {
367											Nullable::Some(language) => lang_map.set(
368												Nullable::Some((
369													Some(language.as_lenient_lang_tag_ref()),
370													None,
371												)),
372												term,
373											),
374											Nullable::Null => lang_map.set(Nullable::Null, term),
375										}
376									}
377									(None, Some(direction)) => {
378										// Otherwise, if term definition has a direction mapping (might
379										// be null):
380										match direction {
381											Nullable::Some(direction) => lang_map.set(
382												Nullable::Some((None, Some(*direction))),
383												term,
384											),
385											Nullable::Null => {
386												lang_map.set(Nullable::Some((None, None)), term)
387											}
388										}
389									}
390									(None, None) => {
391										lang_map.set(
392											Nullable::Some((
393												context.default_language(),
394												context.default_base_direction(),
395											)),
396											term,
397										);
398										lang_map.set_none(term);
399										type_map.set_none(term);
400									}
401								}
402							}
403						}
404					}
405				}
406			}
407		}
408
409		result
410	}
411}