1use crate::{add_value, compact_iri, compact_property, Error, Options};
2use contextual::WithContext;
3use json_ld_context_processing::{Options as ProcessingOptions, Process, ProcessingMode};
4use json_ld_core::{Container, ContainerKind, Context, Id, Loader, Node, Term, Type};
5use json_ld_syntax::Keyword;
6use mown::Mown;
7use rdf_types::VocabularyMut;
8use std::hash::Hash;
9
10fn optional_string(s: Option<String>) -> json_syntax::Value {
11 s.map(Into::into)
12 .unwrap_or_else(|| json_syntax::Value::Null)
13}
14
15#[allow(clippy::too_many_arguments)]
17pub async fn compact_indexed_node_with<N, L>(
18 vocabulary: &mut N,
19 node: &Node<N::Iri, N::BlankId>,
20 index: Option<&str>,
21 mut active_context: &Context<N::Iri, N::BlankId>,
22 type_scoped_context: &Context<N::Iri, N::BlankId>,
23 active_property: Option<&str>,
24 loader: &L,
25 options: Options,
26) -> Result<json_syntax::Value, Error>
27where
28 N: VocabularyMut,
29 N::Iri: Clone + Hash + Eq,
30 N::BlankId: Clone + Hash + Eq,
31 L: Loader,
32{
33 if !(node.is_empty() && node.id.is_some()) {
38 if let Some(previous_context) = active_context.previous_context() {
40 active_context = previous_context
41 }
42 }
43
44 let mut active_context = Mown::Borrowed(active_context);
48 if let Some(active_property) = active_property {
49 if let Some(active_property_definition) = type_scoped_context.get(active_property) {
50 if let Some(local_context) = active_property_definition.context() {
51 active_context = Mown::Owned(
52 local_context
53 .process_with(
54 vocabulary,
55 active_context.as_ref(),
56 loader,
57 active_property_definition.base_url().cloned(),
58 ProcessingOptions::from(options).with_override(),
59 )
60 .await?
61 .into_processed(),
62 )
63 }
64 }
65 }
66
67 let mut result = json_syntax::Object::default();
69
70 if !node.types().is_empty() {
71 let mut compacted_types = Vec::new();
76 for ty in node.types() {
77 let compacted_ty = compact_iri(
78 vocabulary,
79 type_scoped_context,
80 &ty.clone().into_term(),
81 true,
82 false,
83 options,
84 )?;
85 compacted_types.push(compacted_ty)
86 }
87
88 compacted_types.sort_by(|a, b| a.as_ref().unwrap().cmp(b.as_ref().unwrap()));
89
90 for term in &compacted_types {
91 if let Some(term_definition) = type_scoped_context.get(term.as_ref().unwrap().as_str())
92 {
93 if let Some(local_context) = term_definition.context() {
94 let processing_options = ProcessingOptions::from(options).without_propagation();
95 active_context = Mown::Owned(
96 local_context
97 .process_with(
98 vocabulary,
99 active_context.as_ref(),
100 loader,
101 term_definition.base_url().cloned(),
102 processing_options,
103 )
104 .await?
105 .into_processed(),
106 )
107 }
108 }
109 }
110 }
111
112 let mut expanded_entries: Vec<_> = node.properties().iter().collect();
115 if options.ordered {
116 let vocabulary: &N = vocabulary;
117 expanded_entries.sort_by(|(a, _), (b, _)| {
118 (**a)
119 .with(vocabulary)
120 .as_str()
121 .cmp((**b).with(vocabulary).as_str())
122 })
123 }
124
125 if let Some(id_entry) = &node.id {
127 let id = id_entry.clone().into_term();
128
129 if node.is_empty() {
130 let type_mapping = match active_property {
143 Some(prop) => match active_context.get(prop) {
144 Some(def) => def.typ(),
145 None => None,
146 },
147 None => None,
148 };
149
150 if type_mapping == Some(&Type::Id) {
151 let compacted_value = compact_iri(
152 vocabulary,
153 active_context.as_ref(),
154 &id,
155 false,
156 false,
157 options,
158 )?;
159 return Ok(optional_string(compacted_value));
160 }
161
162 if type_mapping == Some(&Type::Vocab) {
165 let compacted_value = compact_iri(
166 vocabulary,
167 active_context.as_ref(),
168 &id,
169 true,
170 false,
171 options,
172 )?;
173 return Ok(optional_string(compacted_value));
174 }
175 }
176
177 let compacted_value = compact_iri(
180 vocabulary,
181 active_context.as_ref(),
182 &id,
183 false,
184 false,
185 options,
186 )?;
187
188 let alias = compact_iri(
190 vocabulary,
191 active_context.as_ref(),
192 &Term::Keyword(Keyword::Id),
193 true,
194 false,
195 options,
196 )?;
197
198 if let Some(key) = alias {
201 result.insert(key.into(), optional_string(compacted_value));
202 }
203 }
204
205 compact_types(
206 vocabulary,
207 &mut result,
208 node.types.as_deref(),
209 active_context.as_ref(),
210 type_scoped_context,
211 options,
212 )?;
213
214 if let Some(reverse_properties) = node.reverse_properties_entry() {
216 if !reverse_properties.is_empty() {
217 let active_property = "@reverse";
221 if let Some(active_property_definition) = active_context.get(active_property) {
222 if let Some(local_context) = active_property_definition.context() {
223 active_context = Mown::Owned(
224 local_context
225 .process_with(
226 vocabulary,
227 active_context.as_ref(),
228 loader,
229 active_property_definition.base_url().cloned(),
230 ProcessingOptions::from(options).with_override(),
231 )
232 .await?
233 .into_processed(),
234 )
235 }
236 }
237
238 let mut reverse_result = json_syntax::Object::default();
239 for (expanded_property, expanded_value) in reverse_properties.iter() {
240 compact_property(
241 vocabulary,
242 &mut reverse_result,
243 expanded_property.clone().into(),
244 expanded_value.iter(),
245 active_context.as_ref(),
246 loader,
247 true,
248 options,
249 )
250 .await?;
251 }
252
253 let mut reverse_map = json_syntax::Object::default();
255 for (property, mapped_value) in reverse_result.iter_mut() {
256 let mut value = json_syntax::Value::Null;
257 std::mem::swap(&mut value, &mut *mapped_value);
258
259 if let Some(term_definition) = active_context.get(property.as_str()) {
262 if term_definition.reverse_property() {
263 let as_array = term_definition.container().contains(ContainerKind::Set)
266 || !options.compact_arrays;
267
268 add_value(&mut result, property, value, as_array);
270 continue;
271 }
272 }
273
274 reverse_map.insert(property.clone(), value);
275 }
276
277 if !reverse_map.is_empty() {
278 let alias = compact_iri(
280 vocabulary,
281 active_context.as_ref(),
282 &Term::Keyword(Keyword::Reverse),
283 true,
284 false,
285 options,
286 )?;
287
288 result.insert(alias.unwrap().into(), reverse_map.into());
290 }
291 }
292 }
293
294 if let Some(index_entry) = index {
297 let mut index_container = false;
298 if let Some(active_property) = active_property {
299 if let Some(active_property_definition) = active_context.get(active_property) {
300 if active_property_definition
301 .container()
302 .contains(ContainerKind::Index)
303 {
304 index_container = true;
307 }
308 }
309 }
310
311 if !index_container {
312 let alias = compact_iri(
314 vocabulary,
315 active_context.as_ref(),
316 &Term::Keyword(Keyword::Index),
317 true,
318 false,
319 options,
320 )?;
321
322 result.insert(alias.unwrap().into(), index_entry.into());
324 }
325 }
326
327 if let Some(graph_entry) = node.graph_entry() {
328 compact_property(
329 vocabulary,
330 &mut result,
331 Term::Keyword(Keyword::Graph),
332 graph_entry.iter(),
333 active_context.as_ref(),
334 loader,
335 false,
336 options,
337 )
338 .await?
339 }
340
341 for (expanded_property, expanded_value) in expanded_entries {
342 compact_property(
343 vocabulary,
344 &mut result,
345 expanded_property.clone().into(),
346 expanded_value.iter(),
347 active_context.as_ref(),
348 loader,
349 false,
350 options,
351 )
352 .await?
353 }
354
355 if let Some(included_entry) = node.included_entry() {
356 compact_property(
357 vocabulary,
358 &mut result,
359 Term::Keyword(Keyword::Included),
360 included_entry.iter(),
361 active_context.as_ref(),
362 loader,
363 false,
364 options,
365 )
366 .await?
367 }
368
369 Ok(result.into())
370}
371
372fn compact_types<N>(
374 vocabulary: &mut N,
375 result: &mut json_syntax::Object,
376 types: Option<&[Id<N::Iri, N::BlankId>]>,
377 active_context: &Context<N::Iri, N::BlankId>,
378 type_scoped_context: &Context<N::Iri, N::BlankId>,
379 options: Options,
380) -> Result<(), Error>
381where
382 N: VocabularyMut,
383 N::Iri: Clone + Hash + Eq,
384 N::BlankId: Clone + Hash + Eq,
385{
386 if let Some(types) = types {
388 if !types.is_empty() {
389 let compacted_value = if types.len() == 1 {
393 optional_string(compact_iri(
394 vocabulary,
395 type_scoped_context,
396 &types[0].clone().into_term(),
397 true,
398 false,
399 options,
400 )?)
401 } else {
402 let mut compacted_value = Vec::with_capacity(types.len());
405
406 for ty in types.iter() {
408 let ty = ty.clone().into_term();
409
410 let compacted_ty =
412 compact_iri(vocabulary, type_scoped_context, &ty, true, false, options)?;
413
414 compacted_value.push(optional_string(compacted_ty))
416 }
417
418 json_syntax::Value::Array(compacted_value.into_iter().collect())
419 };
420
421 let alias = compact_iri(
423 vocabulary,
424 active_context,
425 &Term::Keyword(Keyword::Type),
426 true,
427 false,
428 options,
429 )?
430 .unwrap();
431
432 let container_mapping = match active_context.get(alias.as_str()) {
436 Some(def) => def.container(),
437 None => Container::None,
438 };
439 let as_array = (options.processing_mode == ProcessingMode::JsonLd1_1
440 && container_mapping.contains(ContainerKind::Set))
441 || !options.compact_arrays;
442
443 add_value(result, &alias, compacted_value, as_array)
445 }
446 }
447
448 Ok(())
449}