oxirs_core/jsonld/flattening/
node_map.rs1use super::{FlatteningError, FlatteningOptions, JsonLdValue};
11use indexmap::IndexMap;
12
13#[derive(Debug, Default)]
23pub struct BlankNodeIdMapper {
24 mapping: IndexMap<String, String>,
25 counter: u64,
26}
27
28impl BlankNodeIdMapper {
29 pub fn new() -> Self {
31 Self::default()
32 }
33
34 pub fn map(&mut self, original: &str) -> String {
39 if let Some(mapped) = self.mapping.get(original) {
40 return mapped.clone();
41 }
42 let new_id = format!("_:b{}", self.counter);
43 self.counter += 1;
44 self.mapping.insert(original.to_string(), new_id.clone());
45 new_id
46 }
47
48 pub fn reset(&mut self) {
52 self.mapping.clear();
53 self.counter = 0;
54 }
55}
56
57#[derive(Debug, Clone, Default)]
63pub struct NodeObject {
64 pub id: String,
66 pub types: Vec<String>,
68 pub properties: IndexMap<String, Vec<JsonLdValue>>,
70}
71
72impl NodeObject {
73 pub fn new(id: impl Into<String>) -> Self {
75 Self {
76 id: id.into(),
77 ..Default::default()
78 }
79 }
80
81 pub fn merge_property(&mut self, property: &str, value: JsonLdValue) {
85 let values = self.properties.entry(property.to_string()).or_default();
86 merge_value(values, value);
87 }
88}
89
90#[derive(Debug, Clone, Default)]
96pub struct GraphNodeMap {
97 pub nodes: IndexMap<String, NodeObject>,
99}
100
101impl GraphNodeMap {
102 pub fn new() -> Self {
104 Self::default()
105 }
106
107 pub fn get_or_create(&mut self, id: &str) -> &mut NodeObject {
109 self.nodes
110 .entry(id.to_string())
111 .or_insert_with(|| NodeObject::new(id))
112 }
113}
114
115#[derive(Debug, Clone, Default)]
121pub struct NodeMap {
122 pub graphs: IndexMap<String, GraphNodeMap>,
124}
125
126impl NodeMap {
127 pub fn new() -> Self {
129 let mut nm = Self::default();
130 nm.graphs
131 .insert("@default".to_string(), GraphNodeMap::new());
132 nm
133 }
134
135 pub fn get_or_create_graph(&mut self, graph_name: &str) -> &mut GraphNodeMap {
137 self.graphs.entry(graph_name.to_string()).or_default()
138 }
139
140 pub fn default_graph(&self) -> &GraphNodeMap {
142 self.graphs
143 .get("@default")
144 .expect("@default graph always present")
145 }
146
147 pub fn default_graph_mut(&mut self) -> &mut GraphNodeMap {
149 self.graphs
150 .get_mut("@default")
151 .expect("@default graph always present")
152 }
153}
154
155pub fn generate_node_map(
172 expanded: &[JsonLdValue],
173 options: &FlatteningOptions,
174) -> Result<NodeMap, FlatteningError> {
175 let mut node_map = NodeMap::new();
176 let mut blank_mapper = BlankNodeIdMapper::new();
177
178 for element in expanded {
179 generate_node_map_element(
180 element,
181 &mut node_map,
182 "@default",
183 None,
184 None,
185 &mut blank_mapper,
186 options,
187 )?;
188 }
189
190 Ok(node_map)
191}
192
193#[allow(clippy::too_many_arguments)]
209pub fn generate_node_map_element(
210 element: &JsonLdValue,
211 node_map: &mut NodeMap,
212 active_graph: &str,
213 active_subject: Option<&str>,
214 active_property: Option<&str>,
215 blank_mapper: &mut BlankNodeIdMapper,
216 _options: &FlatteningOptions,
217) -> Result<(), FlatteningError> {
218 match element {
219 JsonLdValue::Array(items) => {
221 for item in items {
222 generate_node_map_element(
223 item,
224 node_map,
225 active_graph,
226 active_subject,
227 active_property,
228 blank_mapper,
229 _options,
230 )?;
231 }
232 return Ok(());
233 }
234
235 JsonLdValue::Null | JsonLdValue::Bool(_) | JsonLdValue::Number(_) | JsonLdValue::Str(_) => {
237 if let (Some(subj), Some(prop)) = (active_subject, active_property) {
239 let graph = node_map.get_or_create_graph(active_graph);
240 graph
241 .get_or_create(subj)
242 .merge_property(prop, element.clone());
243 }
244 return Ok(());
245 }
246
247 JsonLdValue::Object(_) => {} }
249
250 let obj = match element {
251 JsonLdValue::Object(m) => m,
252 _ => unreachable!("handled above"),
253 };
254
255 if obj.contains_key("@value") {
257 if let (Some(subj), Some(prop)) = (active_subject, active_property) {
258 let graph = node_map.get_or_create_graph(active_graph);
259 graph
260 .get_or_create(subj)
261 .merge_property(prop, element.clone());
262 }
263 return Ok(());
264 }
265
266 if obj.contains_key("@list") {
268 let list_items = match obj.get("@list") {
270 Some(JsonLdValue::Array(items)) => items.clone(),
271 _ => Vec::new(),
272 };
273
274 let mut processed: Vec<JsonLdValue> = Vec::new();
276 for item in &list_items {
277 let item_copy = resolve_node_reference(item, node_map, active_graph, blank_mapper);
278 processed.push(item_copy);
279 }
280
281 let list_obj = {
282 let mut m: IndexMap<String, JsonLdValue> = IndexMap::new();
283 m.insert("@list".to_string(), JsonLdValue::Array(processed));
284 JsonLdValue::Object(m)
285 };
286
287 if let (Some(subj), Some(prop)) = (active_subject, active_property) {
288 let graph = node_map.get_or_create_graph(active_graph);
289 graph.get_or_create(subj).merge_property(prop, list_obj);
290 }
291 return Ok(());
292 }
293
294 let id = get_or_assign_id(obj, blank_mapper);
296
297 node_map
299 .get_or_create_graph(active_graph)
300 .get_or_create(&id);
301
302 if let (Some(subj), Some(prop)) = (active_subject, active_property) {
304 let ref_obj = {
305 let mut m: IndexMap<String, JsonLdValue> = IndexMap::new();
306 m.insert("@id".to_string(), JsonLdValue::Str(id.clone()));
307 JsonLdValue::Object(m)
308 };
309 let graph = node_map.get_or_create_graph(active_graph);
310 graph.get_or_create(subj).merge_property(prop, ref_obj);
311 }
312
313 if let Some(graph_val) = obj.get("@graph") {
315 {
317 let def_graph = node_map.default_graph_mut();
318 def_graph.get_or_create(&id);
319 }
320 let graph_items = match graph_val {
322 JsonLdValue::Array(items) => items.clone(),
323 single => vec![single.clone()],
324 };
325 for item in &graph_items {
326 generate_node_map_element(item, node_map, &id, None, None, blank_mapper, _options)?;
327 }
328 }
329
330 if let Some(type_val) = obj.get("@type") {
332 let type_iris: Vec<String> = match type_val {
333 JsonLdValue::Array(items) => items
334 .iter()
335 .filter_map(|v| v.as_str())
336 .map(|s| {
337 if s.starts_with("_:") {
338 blank_mapper.map(s)
339 } else {
340 s.to_string()
341 }
342 })
343 .collect(),
344 JsonLdValue::Str(s) => {
345 let mapped = if s.starts_with("_:") {
346 blank_mapper.map(s)
347 } else {
348 s.to_string()
349 };
350 vec![mapped]
351 }
352 _ => Vec::new(),
353 };
354
355 let graph = node_map.get_or_create_graph(active_graph);
356 let node = graph.get_or_create(&id);
357 for t in type_iris {
358 if !node.types.contains(&t) {
359 node.types.push(t.clone());
360 }
361 merge_value(
362 node.properties.entry("@type".to_string()).or_default(),
363 JsonLdValue::Str(t),
364 );
365 }
366 }
367
368 if let Some(JsonLdValue::Object(rev_obj)) = obj.get("@reverse") {
370 for (rev_prop, rev_val) in rev_obj {
371 let rev_items: Vec<JsonLdValue> = match rev_val {
372 JsonLdValue::Array(a) => a.clone(),
373 single => vec![single.clone()],
374 };
375 for rev_item in &rev_items {
376 let rev_item_id = match rev_item {
378 JsonLdValue::Object(m) => get_or_assign_id(m, blank_mapper),
379 _ => continue,
380 };
381 node_map
383 .get_or_create_graph(active_graph)
384 .get_or_create(&rev_item_id);
385 let ref_obj = {
387 let mut m: IndexMap<String, JsonLdValue> = IndexMap::new();
388 m.insert("@id".to_string(), JsonLdValue::Str(id.clone()));
389 JsonLdValue::Object(m)
390 };
391 let graph = node_map.get_or_create_graph(active_graph);
392 graph
393 .get_or_create(&rev_item_id)
394 .merge_property(rev_prop, ref_obj);
395 }
396 }
397 }
398
399 for (prop, value) in obj {
401 match prop.as_str() {
403 "@id" | "@type" | "@graph" | "@reverse" | "@context" => continue,
404 _ => {}
405 }
406
407 let values: Vec<JsonLdValue> = match value {
408 JsonLdValue::Array(a) => a.clone(),
409 single => vec![single.clone()],
410 };
411
412 for v in values {
413 generate_node_map_element(
414 &v,
415 node_map,
416 active_graph,
417 Some(&id),
418 Some(prop),
419 blank_mapper,
420 _options,
421 )?;
422 }
423 }
424
425 Ok(())
426}
427
428pub fn node_map_to_flat_array(node_map: &NodeMap, ordered: bool) -> Vec<JsonLdValue> {
442 let default_graph = node_map.default_graph();
443
444 let mut subjects: Vec<&str> = default_graph.nodes.keys().map(String::as_str).collect();
445 if ordered {
446 subjects.sort_unstable();
447 }
448
449 let mut result: Vec<JsonLdValue> = Vec::with_capacity(subjects.len());
450
451 for subj_id in subjects {
452 let node_obj = &default_graph.nodes[subj_id];
453 let mut map: IndexMap<String, JsonLdValue> = IndexMap::new();
454
455 map.insert("@id".to_string(), JsonLdValue::Str(subj_id.to_string()));
456
457 if !node_obj.types.is_empty() {
459 let types: Vec<JsonLdValue> = node_obj
460 .types
461 .iter()
462 .map(|t| JsonLdValue::Str(t.clone()))
463 .collect();
464 map.insert("@type".to_string(), JsonLdValue::Array(types));
465 }
466
467 let mut props: Vec<&str> = node_obj.properties.keys().map(String::as_str).collect();
469 if ordered {
470 props.sort_unstable();
471 }
472 for prop in props {
473 if prop == "@type" {
474 continue;
475 }
476 let values = &node_obj.properties[prop];
477 if !values.is_empty() {
478 map.insert(prop.to_string(), JsonLdValue::Array(values.clone()));
479 }
480 }
481
482 if node_map.graphs.contains_key(subj_id) && subj_id != "@default" {
484 let inner_graph = &node_map.graphs[subj_id];
485 let mut inner_subjects: Vec<&str> =
486 inner_graph.nodes.keys().map(String::as_str).collect();
487 if ordered {
488 inner_subjects.sort_unstable();
489 }
490 let inner_array: Vec<JsonLdValue> = inner_subjects
491 .iter()
492 .map(|s| {
493 let inner_node = &inner_graph.nodes[*s];
494 serialise_node_object(inner_node, ordered)
495 })
496 .collect();
497 map.insert("@graph".to_string(), JsonLdValue::Array(inner_array));
498 }
499
500 result.push(JsonLdValue::Object(map));
501 }
502
503 result
504}
505
506fn serialise_node_object(node: &NodeObject, ordered: bool) -> JsonLdValue {
512 let mut map: IndexMap<String, JsonLdValue> = IndexMap::new();
513
514 map.insert("@id".to_string(), JsonLdValue::Str(node.id.clone()));
515
516 if !node.types.is_empty() {
517 let types: Vec<JsonLdValue> = node
518 .types
519 .iter()
520 .map(|t| JsonLdValue::Str(t.clone()))
521 .collect();
522 map.insert("@type".to_string(), JsonLdValue::Array(types));
523 }
524
525 let mut props: Vec<&str> = node.properties.keys().map(String::as_str).collect();
526 if ordered {
527 props.sort_unstable();
528 }
529 for prop in props {
530 if prop == "@type" {
531 continue;
532 }
533 let values = &node.properties[prop];
534 if !values.is_empty() {
535 map.insert(prop.to_string(), JsonLdValue::Array(values.clone()));
536 }
537 }
538
539 JsonLdValue::Object(map)
540}
541
542fn get_or_assign_id(
548 obj: &IndexMap<String, JsonLdValue>,
549 blank_mapper: &mut BlankNodeIdMapper,
550) -> String {
551 match obj.get("@id") {
552 Some(JsonLdValue::Str(id)) => {
553 if id.starts_with("_:") {
554 blank_mapper.map(id)
555 } else {
556 id.clone()
557 }
558 }
559 _ => {
560 let anon = format!("_:anon_{}", blank_mapper.counter);
562 blank_mapper.map(&anon)
563 }
564 }
565}
566
567fn resolve_node_reference(
573 value: &JsonLdValue,
574 node_map: &mut NodeMap,
575 active_graph: &str,
576 blank_mapper: &mut BlankNodeIdMapper,
577) -> JsonLdValue {
578 match value {
579 JsonLdValue::Object(m) if !m.contains_key("@value") && !m.contains_key("@list") => {
580 let id = get_or_assign_id(m, blank_mapper);
581 node_map
582 .get_or_create_graph(active_graph)
583 .get_or_create(&id);
584 let mut ref_map: IndexMap<String, JsonLdValue> = IndexMap::new();
585 ref_map.insert("@id".to_string(), JsonLdValue::Str(id));
586 JsonLdValue::Object(ref_map)
587 }
588 other => other.clone(),
589 }
590}
591
592pub fn merge_value(into: &mut Vec<JsonLdValue>, value: JsonLdValue) {
598 if into.contains(&value) {
600 return;
601 }
602 into.push(value);
603}