json_ld_core/object/node/
reverse_properties.rs

1use super::{Multiset, Nodes};
2use crate::{
3	object::{InvalidExpandedJson, TryFromJson, TryFromJsonObject},
4	Id, IndexedNode,
5};
6use contextual::WithContext;
7use educe::Educe;
8use indexmap::IndexMap;
9use iref::IriBuf;
10use json_ld_syntax::IntoJsonWithContext;
11use rdf_types::{BlankIdBuf, Vocabulary, VocabularyMut};
12use std::hash::{Hash, Hasher};
13
14pub type ReversePropertyNodes<T = IriBuf, B = BlankIdBuf> = Multiset<IndexedNode<T, B>>;
15
16/// Reverse properties of a node object, and their associated nodes.
17#[derive(Educe, Debug, Clone)]
18#[educe(
19	PartialEq(bound = "T: Eq + Hash, B: Eq + Hash"),
20	Eq(bound = "T: Eq + Hash, B: Eq + Hash")
21)]
22pub struct ReverseProperties<T = IriBuf, B = BlankIdBuf>(
23	IndexMap<Id<T, B>, ReversePropertyNodes<T, B>>,
24);
25
26impl<T, B> Default for ReverseProperties<T, B> {
27	fn default() -> Self {
28		Self::new()
29	}
30}
31
32impl<T, B> ReverseProperties<T, B> {
33	/// Creates an empty map.
34	pub fn new() -> Self {
35		Self(IndexMap::new())
36	}
37
38	/// Returns the number of reverse properties.
39	#[inline(always)]
40	pub fn len(&self) -> usize {
41		self.0.len()
42	}
43
44	/// Checks if there are no defined reverse properties.
45	#[inline(always)]
46	pub fn is_empty(&self) -> bool {
47		self.0.is_empty()
48	}
49
50	/// Returns an iterator over the reverse properties and their associated nodes.
51	#[inline(always)]
52	pub fn iter(&self) -> Iter<'_, T, B> {
53		Iter {
54			inner: self.0.iter(),
55		}
56	}
57
58	/// Returns an iterator over the reverse properties with a mutable reference to their associated nodes.
59	#[inline(always)]
60	pub fn iter_mut(&mut self) -> IterMut<'_, T, B> {
61		self.0.iter_mut()
62	}
63
64	/// Removes all reverse properties.
65	#[inline(always)]
66	pub fn clear(&mut self) {
67		self.0.clear()
68	}
69}
70
71impl<T: Eq + Hash, B: Eq + Hash> ReverseProperties<T, B> {
72	/// Checks if the given reverse property is associated to any node.
73	#[inline(always)]
74	pub fn contains<Q: ?Sized + Hash + indexmap::Equivalent<Id<T, B>>>(&self, prop: &Q) -> bool {
75		self.0.get(prop).is_some()
76	}
77
78	/// Returns an iterator over all the nodes associated to the given reverse property.
79	#[inline(always)]
80	pub fn get<'a, Q: ?Sized + Hash + indexmap::Equivalent<Id<T, B>>>(
81		&self,
82		prop: &Q,
83	) -> Nodes<T, B>
84	where
85		T: 'a,
86	{
87		match self.0.get(prop) {
88			Some(values) => Nodes::new(Some(values.iter())),
89			None => Nodes::new(None),
90		}
91	}
92
93	/// Get one of the nodes associated to the given reverse property.
94	///
95	/// If multiple nodes are found, there are no guaranties on which node will be returned.
96	#[inline(always)]
97	pub fn get_any<'a, Q: ?Sized + Hash + indexmap::Equivalent<Id<T, B>>>(
98		&self,
99		prop: &Q,
100	) -> Option<&IndexedNode<T, B>>
101	where
102		T: 'a,
103	{
104		match self.0.get(prop) {
105			Some(values) => values.iter().next(),
106			None => None,
107		}
108	}
109
110	/// Associate the given node to the given reverse property.
111	#[inline(always)]
112	pub fn insert(&mut self, prop: Id<T, B>, value: IndexedNode<T, B>) {
113		if let Some(node_values) = self.0.get_mut(&prop) {
114			node_values.insert(value);
115		} else {
116			self.0.insert(prop, Multiset::singleton(value));
117		}
118	}
119
120	/// Associate the given node to the given reverse property, unless it is already.
121	#[inline(always)]
122	pub fn insert_unique(&mut self, prop: Id<T, B>, value: IndexedNode<T, B>) {
123		if let Some(node_values) = self.0.get_mut(&prop) {
124			if node_values.iter().all(|v| !v.equivalent(&value)) {
125				node_values.insert(value)
126			}
127		} else {
128			self.0.insert(prop, Multiset::singleton(value));
129		}
130	}
131
132	/// Associate all the given nodes to the given reverse property.
133	#[inline(always)]
134	pub fn insert_all<Objects: IntoIterator<Item = IndexedNode<T, B>>>(
135		&mut self,
136		prop: Id<T, B>,
137		values: Objects,
138	) {
139		if let Some(node_values) = self.0.get_mut(&prop) {
140			node_values.extend(values);
141		} else {
142			self.0.insert(prop, values.into_iter().collect());
143		}
144	}
145
146	/// Associate all the given nodes to the given reverse property, unless it is already.
147	#[inline(always)]
148	pub fn insert_all_unique<Nodes: IntoIterator<Item = IndexedNode<T, B>>>(
149		&mut self,
150		prop: Id<T, B>,
151		values: Nodes,
152	) {
153		if let Some(node_values) = self.0.get_mut(&prop) {
154			for value in values {
155				if node_values.iter().all(|v| !v.equivalent(&value)) {
156					node_values.insert(value)
157				}
158			}
159		} else {
160			let values = values.into_iter();
161			let mut node_values: ReversePropertyNodes<T, B> =
162				Multiset::with_capacity(values.size_hint().0);
163			for value in values {
164				if node_values.iter().all(|v| !v.equivalent(&value)) {
165					node_values.insert(value)
166				}
167			}
168
169			self.0.insert(prop, node_values);
170		}
171	}
172
173	pub fn set(&mut self, prop: Id<T, B>, values: ReversePropertyNodes<T, B>) {
174		self.0.insert(prop, values);
175	}
176
177	pub fn extend_unique<N>(&mut self, iter: impl IntoIterator<Item = (Id<T, B>, N)>)
178	where
179		N: IntoIterator<Item = IndexedNode<T, B>>,
180	{
181		for (prop, values) in iter {
182			self.insert_all_unique(prop, values)
183		}
184	}
185
186	/// Removes and returns all the values associated to the given reverse property.
187	#[inline(always)]
188	pub fn remove(&mut self, prop: &Id<T, B>) -> Option<ReversePropertyNodes<T, B>> {
189		self.0.swap_remove(prop)
190	}
191}
192
193impl<T: Eq + Hash, B: Eq + Hash, N> FromIterator<(Id<T, B>, N)> for ReverseProperties<T, B>
194where
195	N: IntoIterator<Item = IndexedNode<T, B>>,
196{
197	fn from_iter<I: IntoIterator<Item = (Id<T, B>, N)>>(iter: I) -> Self {
198		let mut result = Self::default();
199		for (id, values) in iter {
200			result.insert_all(id, values);
201		}
202		result
203	}
204}
205
206impl<T: Eq + Hash, B: Eq + Hash> TryFromJson<T, B> for ReverseProperties<T, B> {
207	fn try_from_json_in(
208		vocabulary: &mut impl VocabularyMut<Iri = T, BlankId = B>,
209		value: json_syntax::Value,
210	) -> Result<Self, InvalidExpandedJson> {
211		match value {
212			json_syntax::Value::Object(object) => Self::try_from_json_object_in(vocabulary, object),
213			_ => Err(InvalidExpandedJson::InvalidObject),
214		}
215	}
216}
217
218impl<T: Eq + Hash, B: Eq + Hash> TryFromJsonObject<T, B> for ReverseProperties<T, B> {
219	fn try_from_json_object_in(
220		vocabulary: &mut impl VocabularyMut<Iri = T, BlankId = B>,
221		object: json_syntax::Object,
222	) -> Result<Self, InvalidExpandedJson> {
223		let mut result = Self::new();
224
225		for entry in object {
226			let prop = Id::from_string_in(vocabulary, entry.key.to_string());
227			let nodes: Vec<IndexedNode<T, B>> = Vec::try_from_json_in(vocabulary, entry.value)?;
228			result.insert_all(prop, nodes)
229		}
230
231		Ok(result)
232	}
233}
234
235impl<T: Hash, B: Hash> Hash for ReverseProperties<T, B> {
236	#[inline(always)]
237	fn hash<H: Hasher>(&self, h: &mut H) {
238		crate::utils::hash_map(&self.0, h)
239	}
240}
241
242impl<T: Eq + Hash, B: Eq + Hash> Extend<(Id<T, B>, Vec<IndexedNode<T, B>>)>
243	for ReverseProperties<T, B>
244{
245	fn extend<I>(&mut self, iter: I)
246	where
247		I: IntoIterator<Item = (Id<T, B>, Vec<IndexedNode<T, B>>)>,
248	{
249		for (prop, values) in iter {
250			self.insert_all(prop, values)
251		}
252	}
253}
254
255/// Tuple type representing a reverse binding in a node object,
256/// associating a reverse property to some nodes.
257pub type ReverseBinding<T, B> = (Id<T, B>, ReversePropertyNodes<T, B>);
258
259/// Tuple type representing a reference to a reverse binding in a node object,
260/// associating a reverse property to some nodes.
261pub type ReverseBindingRef<'a, T, B> = (&'a Id<T, B>, &'a [IndexedNode<T, B>]);
262
263/// Tuple type representing a mutable reference to a reverse binding in a node object,
264/// associating a reverse property to some nodes, with a mutable access to the nodes.
265pub type ReverseBindingMut<'a, T, B> = (&'a Id<T, B>, &'a mut ReversePropertyNodes<T, B>);
266
267impl<T, B> IntoIterator for ReverseProperties<T, B> {
268	type Item = ReverseBinding<T, B>;
269	type IntoIter = IntoIter<T, B>;
270
271	#[inline(always)]
272	fn into_iter(self) -> Self::IntoIter {
273		self.0.into_iter()
274	}
275}
276
277impl<'a, T, B> IntoIterator for &'a ReverseProperties<T, B> {
278	type Item = ReverseBindingRef<'a, T, B>;
279	type IntoIter = Iter<'a, T, B>;
280
281	#[inline(always)]
282	fn into_iter(self) -> Self::IntoIter {
283		self.iter()
284	}
285}
286
287impl<'a, T, B> IntoIterator for &'a mut ReverseProperties<T, B> {
288	type Item = ReverseBindingMut<'a, T, B>;
289	type IntoIter = IterMut<'a, T, B>;
290
291	#[inline(always)]
292	fn into_iter(self) -> Self::IntoIter {
293		self.iter_mut()
294	}
295}
296
297/// Iterator over the reverse properties of a node.
298///
299/// It is created by the [`ReverseProperties::into_iter`] function.
300pub type IntoIter<T, B> = indexmap::map::IntoIter<Id<T, B>, ReversePropertyNodes<T, B>>;
301
302/// Iterator over the reverse properties of a node.
303///
304/// It is created by the [`ReverseProperties::iter`] function.
305#[derive(Educe)]
306#[educe(Clone)]
307pub struct Iter<'a, T, B> {
308	inner: indexmap::map::Iter<'a, Id<T, B>, ReversePropertyNodes<T, B>>,
309}
310
311impl<'a, T, B> Iterator for Iter<'a, T, B> {
312	type Item = ReverseBindingRef<'a, T, B>;
313
314	#[inline(always)]
315	fn size_hint(&self) -> (usize, Option<usize>) {
316		self.inner.size_hint()
317	}
318
319	#[inline(always)]
320	fn next(&mut self) -> Option<Self::Item> {
321		self.inner
322			.next()
323			.map(|(property, objects)| (property, objects.as_slice()))
324	}
325}
326
327impl<'a, T, B> ExactSizeIterator for Iter<'a, T, B> {}
328
329impl<'a, T, B> std::iter::FusedIterator for Iter<'a, T, B> {}
330
331/// Iterator over the reverse properties of a node, giving a mutable reference
332/// to the associated nodes.
333///
334/// It is created by the [`ReverseProperties::iter_mut`] function.
335pub type IterMut<'a, T, B> = indexmap::map::IterMut<'a, Id<T, B>, ReversePropertyNodes<T, B>>;
336
337impl<T, B, N: Vocabulary<Iri = T, BlankId = B>> IntoJsonWithContext<N> for ReverseProperties<T, B> {
338	fn into_json_with(self, vocabulary: &N) -> json_syntax::Value {
339		let mut obj = json_syntax::Object::new();
340
341		for (prop, nodes) in self {
342			obj.insert(
343				prop.with(vocabulary).to_string().into(),
344				nodes.into_json_with(vocabulary),
345			);
346		}
347
348		obj.into()
349	}
350}