Skip to main content

jsonld_core/object/node/
properties.rs

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