Skip to main content

json_ld/algorithms/flattening/node_map/
mod.rs

1use crate::{
2	ConflictingIndexes, JsonLdBacktraceBuilder, JsonLdExpandedDocument, JsonLdFlattenedDocument,
3	JsonLdIndexed, JsonLdIndexedNode, JsonLdIndexedObject, JsonLdLenient, JsonLdLocated,
4	JsonLdNode, JsonLdObject, JsonLdSourceFile,
5};
6use educe::Educe;
7use json_syntax::tracing::JsonBacktraceBuf;
8use rdf_syntax::{Generator, Id};
9use std::collections::{HashMap, HashSet};
10
11mod builder;
12
13use builder::NodeMapBuilder;
14
15pub type NodeMapExtendError = Box<JsonLdLocated<ConflictingIndexes>>;
16
17pub type Parts = (
18	JsonLdNodeMapGraph,
19	HashMap<JsonLdLenient<Id>, JsonLdNodeMapGraph>,
20);
21
22impl JsonLdExpandedDocument {
23	pub fn generate_node_map_with(
24		&self,
25		generator: impl Generator,
26		location: JsonLdBacktraceBuilder<'_>,
27	) -> Result<JsonLdNodeMap, NodeMapExtendError> {
28		let mut builder = NodeMapBuilder::new(generator);
29
30		for (i, object) in self.iter().enumerate() {
31			builder.extend_node_map(object, None, location.array_index(i))?;
32		}
33
34		Ok(builder.end())
35	}
36}
37
38/// Node identifier to node definition map.
39#[derive(Educe)]
40#[educe(Default)]
41pub struct JsonLdNodeMap {
42	graphs: HashMap<JsonLdLenient<Id>, JsonLdNodeMapGraph>,
43	default_graph: JsonLdNodeMapGraph,
44}
45
46impl JsonLdNodeMap {
47	pub fn new() -> Self {
48		Self {
49			graphs: HashMap::new(),
50			default_graph: JsonLdNodeMapGraph::new(),
51		}
52	}
53
54	pub fn into_parts(self) -> Parts {
55		(self.default_graph, self.graphs)
56	}
57
58	pub fn iter(&self) -> Iter<'_> {
59		Iter {
60			default_graph: Some(&self.default_graph),
61			graphs: self.graphs.iter(),
62		}
63	}
64
65	pub fn iter_named(
66		&self,
67	) -> std::collections::hash_map::Iter<'_, JsonLdLenient<Id>, JsonLdNodeMapGraph> {
68		self.graphs.iter()
69	}
70
71	pub fn graph(&self, id: Option<&JsonLdLenient<Id>>) -> Option<&JsonLdNodeMapGraph> {
72		match id {
73			Some(id) => self.graphs.get(id),
74			None => Some(&self.default_graph),
75		}
76	}
77
78	pub fn graph_mut(&mut self, id: Option<&JsonLdLenient<Id>>) -> Option<&mut JsonLdNodeMapGraph> {
79		match id {
80			Some(id) => self.graphs.get_mut(id),
81			None => Some(&mut self.default_graph),
82		}
83	}
84
85	pub fn declare_graph(&mut self, id: JsonLdLenient<Id>) {
86		if let std::collections::hash_map::Entry::Vacant(entry) = self.graphs.entry(id) {
87			entry.insert(JsonLdNodeMapGraph::new());
88		}
89	}
90
91	/// Merge all the graphs into a single `JsonLdNodeMapGraph`.
92	///
93	/// The order in which graphs are merged is not defined.
94	pub fn merge(self) -> JsonLdNodeMapGraph {
95		let mut result = self.default_graph;
96
97		for (_, graph) in self.graphs {
98			result.merge_with(graph)
99		}
100
101		result
102	}
103
104	pub fn flatten(self, ordered: bool) -> JsonLdFlattenedDocument {
105		let (mut default_graph, named_graphs) = self.into_parts();
106
107		let mut named_graphs: Vec<_> = named_graphs.into_iter().collect();
108		if ordered {
109			named_graphs.sort_by(|a, b| a.0.as_str().cmp(b.0.as_str()));
110		}
111
112		for (graph_id, graph) in named_graphs {
113			let entry = default_graph.declare_node(graph_id, None).ok().unwrap();
114			let mut nodes: Vec<_> = graph.into_nodes().collect();
115			if ordered {
116				nodes.sort_by(|a, b| {
117					a.id.as_ref()
118						.unwrap()
119						.as_str()
120						.cmp(b.id.as_ref().unwrap().as_str())
121				});
122			}
123			entry.set_graph_entry(Some(
124				nodes.into_iter().filter_map(filter_sub_graph).collect(),
125			));
126		}
127
128		let mut nodes: Vec<_> = default_graph
129			.into_nodes()
130			.filter_map(filter_graph)
131			.collect();
132
133		if ordered {
134			nodes.sort_by(|a, b| {
135				a.id.as_ref()
136					.unwrap()
137					.as_str()
138					.cmp(b.id.as_ref().unwrap().as_str())
139			});
140		}
141
142		nodes
143	}
144
145	pub fn flatten_unordered(self) -> HashSet<JsonLdIndexedNode> {
146		let (mut default_graph, named_graphs) = self.into_parts();
147
148		for (graph_id, graph) in named_graphs {
149			let entry = default_graph.declare_node(graph_id, None).ok().unwrap();
150			entry.set_graph_entry(Some(
151				graph.into_nodes().filter_map(filter_sub_graph).collect(),
152			));
153		}
154
155		default_graph
156			.into_nodes()
157			.filter_map(filter_graph)
158			.collect()
159	}
160}
161
162pub struct Iter<'a> {
163	default_graph: Option<&'a JsonLdNodeMapGraph>,
164	graphs: std::collections::hash_map::Iter<'a, JsonLdLenient<Id>, JsonLdNodeMapGraph>,
165}
166
167impl<'a> Iterator for Iter<'a> {
168	type Item = (Option<&'a JsonLdLenient<Id>>, &'a JsonLdNodeMapGraph);
169
170	fn next(&mut self) -> Option<Self::Item> {
171		match self.default_graph.take() {
172			Some(default_graph) => Some((None, default_graph)),
173			None => self.graphs.next().map(|(id, graph)| (Some(id), graph)),
174		}
175	}
176}
177
178impl<'a> IntoIterator for &'a JsonLdNodeMap {
179	type Item = (Option<&'a JsonLdLenient<Id>>, &'a JsonLdNodeMapGraph);
180	type IntoIter = Iter<'a>;
181
182	fn into_iter(self) -> Self::IntoIter {
183		self.iter()
184	}
185}
186
187pub struct IntoIter {
188	default_graph: Option<JsonLdNodeMapGraph>,
189	graphs: std::collections::hash_map::IntoIter<JsonLdLenient<Id>, JsonLdNodeMapGraph>,
190}
191
192impl Iterator for IntoIter {
193	type Item = (Option<JsonLdLenient<Id>>, JsonLdNodeMapGraph);
194
195	fn next(&mut self) -> Option<Self::Item> {
196		match self.default_graph.take() {
197			Some(default_graph) => Some((None, default_graph)),
198			None => self.graphs.next().map(|(id, graph)| (Some(id), graph)),
199		}
200	}
201}
202
203impl IntoIterator for JsonLdNodeMap {
204	type Item = (Option<JsonLdLenient<Id>>, JsonLdNodeMapGraph);
205	type IntoIter = IntoIter;
206
207	fn into_iter(self) -> Self::IntoIter {
208		IntoIter {
209			default_graph: Some(self.default_graph),
210			graphs: self.graphs.into_iter(),
211		}
212	}
213}
214
215/// Entry in a [`JsonLdNodeMapGraph`], pairing an indexed node with the optional
216/// source location of its `@index` value.
217pub struct JsonLdNodeMapGraphEntry {
218	pub node: JsonLdIndexedNode,
219	/// Location of the `@index` value that was declared for this node, if known.
220	pub index_location: Option<JsonBacktraceBuf<JsonLdSourceFile>>,
221}
222
223impl JsonLdNodeMapGraphEntry {
224	fn new(node: JsonLdIndexedNode) -> Self {
225		Self {
226			node,
227			index_location: None,
228		}
229	}
230}
231
232impl std::ops::Deref for JsonLdNodeMapGraphEntry {
233	type Target = JsonLdIndexedNode;
234	fn deref(&self) -> &Self::Target {
235		&self.node
236	}
237}
238
239impl std::ops::DerefMut for JsonLdNodeMapGraphEntry {
240	fn deref_mut(&mut self) -> &mut Self::Target {
241		&mut self.node
242	}
243}
244
245#[derive(Educe)]
246#[educe(Default)]
247pub struct JsonLdNodeMapGraph {
248	nodes: HashMap<JsonLdLenient<Id>, JsonLdNodeMapGraphEntry>,
249}
250
251impl JsonLdNodeMapGraph {
252	pub fn new() -> Self {
253		Self {
254			nodes: HashMap::new(),
255		}
256	}
257}
258
259pub type DeclareNodeResult<'a> = Result<&'a mut JsonLdNodeMapGraphEntry, NodeMapExtendError>;
260
261impl JsonLdNodeMapGraph {
262	pub fn contains(&self, id: &JsonLdLenient<Id>) -> bool {
263		self.nodes.contains_key(id)
264	}
265
266	pub fn get(&self, id: &JsonLdLenient<Id>) -> Option<&JsonLdNodeMapGraphEntry> {
267		self.nodes.get(id)
268	}
269
270	pub fn get_mut(&mut self, id: &JsonLdLenient<Id>) -> Option<&mut JsonLdNodeMapGraphEntry> {
271		self.nodes.get_mut(id)
272	}
273
274	/// Declares a node in the graph.
275	///
276	/// `index` pairs the `@index` string with its source location in the
277	/// expanded document (if known).
278	pub fn declare_node(
279		&mut self,
280		id: JsonLdLenient<Id>,
281		index: Option<(&str, JsonBacktraceBuf<JsonLdSourceFile>)>,
282	) -> DeclareNodeResult<'_> {
283		if let Some(entry) = self.nodes.get_mut(&id) {
284			match (entry.index(), index) {
285				(Some(entry_index), Some((new_index, new_location))) => {
286					if entry_index != new_index {
287						let defined_location = entry.index_location.clone().unwrap_or_default();
288						return Err(Box::new(JsonLdLocated::new(
289							ConflictingIndexes {
290								node_id: id,
291								defined_index: JsonLdLocated::new(
292									entry_index.to_string(),
293									defined_location,
294								),
295								conflicting_index: new_index.to_string(),
296							},
297							new_location,
298						)));
299					}
300				}
301				(None, Some((new_index, new_location))) => {
302					entry.set_index(Some(new_index.to_owned()));
303					entry.index_location = Some(new_location);
304				}
305				_ => (),
306			}
307		} else {
308			let (index_str, index_location) = match index {
309				Some((s, loc)) => (Some(s.to_owned()), Some(loc)),
310				None => (None, None),
311			};
312			self.nodes.insert(
313				id.clone(),
314				JsonLdNodeMapGraphEntry {
315					node: JsonLdIndexed::new(JsonLdNode::new_with_id(Some(id.clone())), index_str),
316					index_location,
317				},
318			);
319		}
320
321		Ok(self.nodes.get_mut(&id).unwrap())
322	}
323
324	/// Merge this graph with `other`.
325	///
326	/// This calls [`merge_node`](Self::merge_node) with every node of `other`.
327	pub fn merge_with(&mut self, other: Self) {
328		for (_, entry) in other {
329			self.merge_node(entry.node)
330		}
331	}
332
333	/// Merge the given `node` into the graph.
334	///
335	/// The `node` must has an identifier, or this function will have no effect.
336	/// If there is already a node with the same identifier:
337	/// - The index of `node`, if any, overrides the previously existing index.
338	/// - The list of `node` types is concatenated after the preexisting types.
339	/// - The graph and imported values are overridden.
340	/// - Properties and reverse properties are merged.
341	pub fn merge_node(&mut self, node: JsonLdIndexedNode) {
342		let (node, index) = node.into_parts();
343
344		if let Some(id) = &node.id {
345			if let Some(entry) = self.nodes.get_mut(id) {
346				if let Some(index) = index {
347					entry.set_index(Some(index))
348				}
349			} else {
350				self.nodes.insert(
351					id.clone(),
352					JsonLdNodeMapGraphEntry::new(JsonLdIndexed::new(
353						JsonLdNode::new_with_id(Some(id.clone())),
354						index,
355					)),
356				);
357			}
358
359			let flat_node = self.nodes.get_mut(id).unwrap();
360
361			if let Some(types) = node.types {
362				flat_node.types.get_or_insert_default().extend(types);
363			}
364
365			flat_node.set_graph_entry(node.graph);
366			flat_node.set_included(node.included);
367			flat_node.properties_mut().extend_unique(node.properties);
368
369			if let Some(props) = node.reverse_properties {
370				flat_node
371					.reverse_properties_or_default()
372					.extend_unique(props);
373			}
374		}
375	}
376
377	pub fn nodes(&self) -> NodeMapGraphNodes<'_> {
378		self.nodes.values()
379	}
380
381	pub fn into_nodes(self) -> IntoNodeMapGraphNodes {
382		self.nodes.into_values()
383	}
384}
385
386pub type NodeMapGraphNodes<'a> =
387	std::collections::hash_map::Values<'a, JsonLdLenient<Id>, JsonLdNodeMapGraphEntry>;
388pub type IntoNodeMapGraphNodes =
389	std::collections::hash_map::IntoValues<JsonLdLenient<Id>, JsonLdNodeMapGraphEntry>;
390
391impl IntoIterator for JsonLdNodeMapGraph {
392	type Item = (JsonLdLenient<Id>, JsonLdNodeMapGraphEntry);
393	type IntoIter =
394		std::collections::hash_map::IntoIter<JsonLdLenient<Id>, JsonLdNodeMapGraphEntry>;
395
396	fn into_iter(self) -> Self::IntoIter {
397		self.nodes.into_iter()
398	}
399}
400
401impl<'a> IntoIterator for &'a JsonLdNodeMapGraph {
402	type Item = (&'a JsonLdLenient<Id>, &'a JsonLdNodeMapGraphEntry);
403	type IntoIter =
404		std::collections::hash_map::Iter<'a, JsonLdLenient<Id>, JsonLdNodeMapGraphEntry>;
405
406	fn into_iter(self) -> Self::IntoIter {
407		self.nodes.iter()
408	}
409}
410
411fn filter_graph(entry: JsonLdNodeMapGraphEntry) -> Option<JsonLdIndexedNode> {
412	if entry.index().is_none() && entry.is_empty() {
413		None
414	} else {
415		Some(entry.node)
416	}
417}
418
419fn filter_sub_graph(mut entry: JsonLdNodeMapGraphEntry) -> Option<JsonLdIndexedObject> {
420	if entry.index().is_none() && entry.properties().is_empty() {
421		None
422	} else {
423		entry.set_graph_entry(None);
424		entry.set_included(None);
425		entry.set_reverse_properties(None);
426		Some(entry.node.map_inner(JsonLdObject::node))
427	}
428}