Skip to main content

jsonld_core/object/node/
reverse_properties.rs

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