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
184pub 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 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 type_map.set_any(term);
287 lang_map.set_any(term);
288 }
289 Some(typ) => {
290 type_map.set(typ, term)
292 }
293 None => {
294 match (&term_definition.language, &term_definition.direction) {
295 (Some(language), Some(direction)) => {
296 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 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 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}