json_ld_core/object/node/
properties.rs

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