Skip to main content

json_ld/core/context/
inverse.rs

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