json_ld_core/document/
expanded.rs

1use crate::object::{FragmentRef, InvalidExpandedJson, Traverse};
2use crate::{Id, Indexed, IndexedObject, Node, Object, Relabel, TryFromJson};
3use hashbrown::HashMap;
4use indexmap::IndexSet;
5use iref::IriBuf;
6use rdf_types::vocabulary::VocabularyMut;
7use rdf_types::{BlankIdBuf, Generator, Vocabulary};
8use std::collections::HashSet;
9use std::hash::Hash;
10
11/// Result of the document expansion algorithm.
12///
13/// It is just an alias for a set of (indexed) objects.
14#[derive(Debug, Clone)]
15pub struct ExpandedDocument<T = IriBuf, B = BlankIdBuf>(IndexSet<IndexedObject<T, B>>);
16
17impl<T, B> Default for ExpandedDocument<T, B> {
18	#[inline(always)]
19	fn default() -> Self {
20		Self(IndexSet::new())
21	}
22}
23
24impl<T, B> ExpandedDocument<T, B> {
25	#[inline(always)]
26	pub fn new() -> Self {
27		Self::default()
28	}
29
30	#[inline(always)]
31	pub fn len(&self) -> usize {
32		self.0.len()
33	}
34
35	#[inline(always)]
36	pub fn is_empty(&self) -> bool {
37		self.0.is_empty()
38	}
39
40	#[inline(always)]
41	pub fn objects(&self) -> &IndexSet<IndexedObject<T, B>> {
42		&self.0
43	}
44
45	#[inline(always)]
46	pub fn into_objects(self) -> IndexSet<IndexedObject<T, B>> {
47		self.0
48	}
49
50	#[inline(always)]
51	pub fn iter(&self) -> indexmap::set::Iter<'_, IndexedObject<T, B>> {
52		self.0.iter()
53	}
54
55	#[inline(always)]
56	pub fn traverse(&self) -> Traverse<T, B> {
57		Traverse::new(self.iter().map(|o| FragmentRef::IndexedObject(o)))
58	}
59
60	#[inline(always)]
61	pub fn count(&self, f: impl FnMut(&FragmentRef<T, B>) -> bool) -> usize {
62		self.traverse().filter(f).count()
63	}
64
65	/// Give an identifier (`@id`) to every nodes using the given generator to
66	/// generate fresh identifiers for anonymous nodes.
67	#[inline(always)]
68	pub fn identify_all_with<V: Vocabulary<Iri = T, BlankId = B>, G: Generator<V>>(
69		&mut self,
70		vocabulary: &mut V,
71		generator: &mut G,
72	) where
73		T: Eq + Hash,
74		B: Eq + Hash,
75	{
76		let objects = std::mem::take(&mut self.0);
77		for mut object in objects {
78			object.identify_all_with(vocabulary, generator);
79			self.0.insert(object);
80		}
81	}
82
83	/// Give an identifier (`@id`) to every nodes using the given generator to
84	/// generate fresh identifiers for anonymous nodes.
85	#[inline(always)]
86	pub fn identify_all<G: Generator>(&mut self, generator: &mut G)
87	where
88		T: Eq + Hash,
89		B: Eq + Hash,
90		(): Vocabulary<Iri = T, BlankId = B>,
91	{
92		self.identify_all_with(&mut (), generator)
93	}
94
95	/// Give an identifier (`@id`) to every nodes and canonicalize every
96	/// literals using the given generator to generate fresh identifiers for
97	/// anonymous nodes.
98	#[inline(always)]
99	pub fn relabel_and_canonicalize_with<V: Vocabulary<Iri = T, BlankId = B>, G: Generator<V>>(
100		&mut self,
101		vocabulary: &mut V,
102		generator: &mut G,
103	) where
104		T: Clone + Eq + Hash,
105		B: Clone + Eq + Hash,
106	{
107		let objects = std::mem::take(&mut self.0);
108		let mut relabeling = HashMap::new();
109		let mut buffer = ryu_js::Buffer::new();
110		for mut object in objects {
111			object.relabel_with(vocabulary, generator, &mut relabeling);
112			object.canonicalize_with(&mut buffer);
113			self.0.insert(object);
114		}
115	}
116
117	/// Give an identifier (`@id`) to every nodes and canonicalize every
118	/// literals using the given generator to generate fresh identifiers for
119	/// anonymous nodes.
120	#[inline(always)]
121	pub fn relabel_and_canonicalize<G: Generator>(&mut self, generator: &mut G)
122	where
123		T: Clone + Eq + Hash,
124		B: Clone + Eq + Hash,
125		(): Vocabulary<Iri = T, BlankId = B>,
126	{
127		self.relabel_and_canonicalize_with(&mut (), generator)
128	}
129
130	/// Relabels nodes.
131	#[inline(always)]
132	pub fn relabel_with<V: Vocabulary<Iri = T, BlankId = B>, G: Generator<V>>(
133		&mut self,
134		vocabulary: &mut V,
135		generator: &mut G,
136	) where
137		T: Clone + Eq + Hash,
138		B: Clone + Eq + Hash,
139	{
140		let objects = std::mem::take(&mut self.0);
141		let mut relabeling = HashMap::new();
142		for mut object in objects {
143			object.relabel_with(vocabulary, generator, &mut relabeling);
144			self.0.insert(object);
145		}
146	}
147
148	/// Relabels nodes.
149	#[inline(always)]
150	pub fn relabel<G: Generator>(&mut self, generator: &mut G)
151	where
152		T: Clone + Eq + Hash,
153		B: Clone + Eq + Hash,
154		(): Vocabulary<Iri = T, BlankId = B>,
155	{
156		self.relabel_with(&mut (), generator)
157	}
158
159	/// Puts this document literals into canonical form using the given
160	/// `buffer`.
161	///
162	/// The buffer is used to compute the canonical form of numbers.
163	pub fn canonicalize_with(&mut self, buffer: &mut ryu_js::Buffer)
164	where
165		T: Eq + Hash,
166		B: Eq + Hash,
167	{
168		let objects = std::mem::take(&mut self.0);
169		for mut object in objects {
170			object.canonicalize_with(buffer);
171			self.0.insert(object);
172		}
173	}
174
175	/// Puts this document literals into canonical form.
176	pub fn canonicalize(&mut self)
177	where
178		T: Eq + Hash,
179		B: Eq + Hash,
180	{
181		let mut buffer = ryu_js::Buffer::new();
182		self.canonicalize_with(&mut buffer)
183	}
184
185	/// Map the identifiers present in this expanded document (recursively).
186	pub fn map_ids<U, C>(
187		self,
188		mut map_iri: impl FnMut(T) -> U,
189		mut map_id: impl FnMut(Id<T, B>) -> Id<U, C>,
190	) -> ExpandedDocument<U, C>
191	where
192		U: Eq + Hash,
193		C: Eq + Hash,
194	{
195		ExpandedDocument(
196			self.0
197				.into_iter()
198				.map(|i| i.map_inner(|o| o.map_ids(&mut map_iri, &mut map_id)))
199				.collect(),
200		)
201	}
202
203	/// Returns the set of all blank identifiers in the given document.
204	pub fn blank_ids(&self) -> HashSet<&B>
205	where
206		B: Eq + Hash,
207	{
208		self.traverse()
209			.filter_map(|f| f.into_id().and_then(Id::into_blank))
210			.collect()
211	}
212
213	/// Returns the main node object of the document, if any.
214	///
215	/// The main node is the unique top level (root) node object. If multiple
216	/// node objects are on the root, `None` is returned.
217	pub fn main_node(&self) -> Option<&Node<T, B>> {
218		let mut result = None;
219
220		for object in self {
221			if let Object::Node(node) = object.inner() {
222				if result.is_some() {
223					return None;
224				}
225
226				result = Some(&**node)
227			}
228		}
229
230		result
231	}
232
233	/// Consumes the document and returns its main node object, if any.
234	///
235	/// The main node is the unique top level (root) node object. If multiple
236	/// node objects are on the root, `None` is returned.
237	pub fn into_main_node(self) -> Option<Node<T, B>> {
238		let mut result = None;
239
240		for object in self {
241			if let Object::Node(node) = object.into_inner() {
242				if result.is_some() {
243					return None;
244				}
245
246				result = Some(*node)
247			}
248		}
249
250		result
251	}
252}
253
254impl<T: Hash + Eq, B: Hash + Eq> ExpandedDocument<T, B> {
255	#[inline(always)]
256	pub fn insert(&mut self, object: IndexedObject<T, B>) -> bool {
257		self.0.insert(object)
258	}
259}
260
261impl<T: Eq + Hash, B: Eq + Hash> From<Indexed<Node<T, B>>> for ExpandedDocument<T, B> {
262	fn from(value: Indexed<Node<T, B>>) -> Self {
263		let mut result = Self::default();
264
265		result.insert(value.map_inner(Object::node));
266
267		result
268	}
269}
270
271impl<T: Eq + Hash, B: Eq + Hash> TryFromJson<T, B> for ExpandedDocument<T, B> {
272	fn try_from_json_in(
273		vocabulary: &mut impl VocabularyMut<Iri = T, BlankId = B>,
274		value: json_syntax::Value,
275	) -> Result<Self, InvalidExpandedJson> {
276		match value {
277			json_syntax::Value::Array(items) => {
278				let mut result = Self::new();
279
280				for item in items {
281					result.insert(Indexed::try_from_json_in(vocabulary, item)?);
282				}
283
284				Ok(result)
285			}
286			other => Err(InvalidExpandedJson::Unexpected(
287				other.kind(),
288				json_syntax::Kind::Array,
289			)),
290		}
291	}
292}
293
294impl<T: Eq + Hash, B: Eq + Hash> PartialEq for ExpandedDocument<T, B> {
295	/// Comparison between two expanded documents.
296	fn eq(&self, other: &Self) -> bool {
297		self.0.eq(&other.0)
298	}
299}
300
301impl<T: Eq + Hash, B: Eq + Hash> Eq for ExpandedDocument<T, B> {}
302
303impl<T, B> IntoIterator for ExpandedDocument<T, B> {
304	type IntoIter = IntoIter<T, B>;
305	type Item = IndexedObject<T, B>;
306
307	#[inline(always)]
308	fn into_iter(self) -> Self::IntoIter {
309		IntoIter(self.0.into_iter())
310	}
311}
312
313impl<'a, T, B> IntoIterator for &'a ExpandedDocument<T, B> {
314	type IntoIter = indexmap::set::Iter<'a, IndexedObject<T, B>>;
315	type Item = &'a IndexedObject<T, B>;
316
317	#[inline(always)]
318	fn into_iter(self) -> Self::IntoIter {
319		self.iter()
320	}
321}
322pub struct IntoIter<T, B>(indexmap::set::IntoIter<IndexedObject<T, B>>);
323
324impl<T, B> Iterator for IntoIter<T, B> {
325	type Item = IndexedObject<T, B>;
326
327	fn next(&mut self) -> Option<Self::Item> {
328		self.0.next()
329	}
330}
331
332impl<T: Hash + Eq, B: Hash + Eq> FromIterator<IndexedObject<T, B>> for ExpandedDocument<T, B> {
333	fn from_iter<I: IntoIterator<Item = IndexedObject<T, B>>>(iter: I) -> Self {
334		Self(iter.into_iter().collect())
335	}
336}
337
338impl<T: Hash + Eq, B: Hash + Eq> Extend<IndexedObject<T, B>> for ExpandedDocument<T, B> {
339	fn extend<I: IntoIterator<Item = IndexedObject<T, B>>>(&mut self, iter: I) {
340		self.0.extend(iter)
341	}
342}
343
344impl<T, B> From<IndexSet<IndexedObject<T, B>>> for ExpandedDocument<T, B> {
345	fn from(set: IndexSet<IndexedObject<T, B>>) -> Self {
346		Self(set)
347	}
348}