fj_core/objects/
object_set.rs

1use std::{collections::BTreeSet, fmt::Debug, iter, slice, vec};
2
3use itertools::Itertools;
4
5use crate::storage::{Handle, HandleWrapper};
6
7/// An ordered set of objects
8///
9/// This is the data structure used by all objects that reference multiple
10/// objects of the same type. It is a set, not containing any duplicate
11/// elements, and it maintains the insertion order of those elements.
12#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
13pub struct ObjectSet<T> {
14    // This is supposed to be a set data structure, so what is that `Vec` doing
15    // here? Well, it's here because we need it to preserve insertion order, but
16    // that doesn't explain why it is here *alone*.
17    //
18    // If you look closely, you'll notice that this is an immutable data
19    // structure (since it is used in objects, and objects themselves are
20    // immutable). We need to make sure there are no duplicates when this is
21    // constructed (see the constructor below), but after that, we're fine.
22    inner: Vec<HandleWrapper<T>>,
23}
24
25impl<T> ObjectSet<T> {
26    /// Create an instance of `ObjectSet` from an iterator over `Handle<T>`
27    ///
28    /// # Panics
29    ///
30    /// Panics, if the iterator contains duplicate `Handle`s.
31    pub fn new(
32        handles: impl IntoIterator<Item = impl Into<HandleWrapper<T>>>,
33    ) -> Self
34    where
35        T: Debug + Ord,
36    {
37        let mut added = BTreeSet::new();
38        let mut inner = Vec::new();
39
40        let handles = handles.into_iter().map(|handle| handle.into());
41
42        for handle in handles {
43            if added.contains(&handle) {
44                panic!(
45                    "Constructing `ObjectSet` with duplicate handle: {:?}",
46                    handle
47                );
48            }
49
50            added.insert(handle.clone());
51            inner.push(handle);
52        }
53
54        Self { inner }
55    }
56
57    /// Return the number of objects in this set
58    pub fn len(&self) -> usize {
59        self.inner.len()
60    }
61
62    /// Indicate whether the set is empty
63    pub fn is_empty(&self) -> bool {
64        self.inner.is_empty()
65    }
66
67    /// Indicate whether the set contains the provided object
68    pub fn contains(&self, object: &Handle<T>) -> bool {
69        self.index_of(object).is_some()
70    }
71
72    /// Return the only item
73    ///
74    /// # Panics
75    ///
76    /// Panics, if there is more than one item.
77    pub fn only(&self) -> &Handle<T> {
78        let mut iter = self.inner.iter();
79        let item = iter
80            .next()
81            .expect("Requested only item, but no items available");
82
83        assert!(
84            iter.next().is_none(),
85            "Requested only item, but more than one available"
86        );
87
88        item
89    }
90
91    /// Return the first item
92    ///
93    /// # Panics
94    ///
95    /// Panics, if there are no items.
96    pub fn first(&self) -> &Handle<T> {
97        self.inner
98            .first()
99            .expect("Requested first item, but no items available")
100    }
101
102    /// Return the n-th item
103    pub fn nth(&self, index: usize) -> Option<&Handle<T>> {
104        self.inner.get(index).map(|wrapper| &wrapper.0)
105    }
106
107    /// Return the n-th item, treating the index space as circular
108    ///
109    /// If the length of `ObjectSet` is `i`, then retrieving the i-th edge using
110    /// this method, is the same as retrieving the 0-th one, and so on.
111    ///
112    /// # Panics
113    ///
114    /// Panics, if `ObjectSet` is empty.
115    pub fn nth_circular(&self, index: usize) -> &Handle<T> {
116        assert!(!self.is_empty(), "`ObjectSet` must not be empty");
117
118        let index = index % self.len();
119        self.nth(index)
120            .expect("Index must be valid, due to modulo above")
121    }
122
123    /// Return the index of the item, if available
124    pub fn index_of(&self, handle: &Handle<T>) -> Option<usize> {
125        self.inner.iter().position(|h| h.id() == handle.id())
126    }
127
128    /// Access the item after the provided one
129    ///
130    /// Returns `None`, if the provided item is not in this iterator.
131    pub fn after(&self, handle: &Handle<T>) -> Option<&Handle<T>> {
132        self.index_of(handle)
133            .map(|index| self.nth_circular(index + 1))
134    }
135
136    /// Access an iterator over the objects
137    pub fn iter(&self) -> ObjectSetIter<T> {
138        self.inner.iter().map(HandleWrapper::as_handle)
139    }
140
141    /// Access an iterator over the neighboring pairs of all contained objects
142    pub fn pairs(&self) -> impl Iterator<Item = (&Handle<T>, &Handle<T>)> {
143        self.iter().circular_tuple_windows()
144    }
145
146    /// Create a new instance in which the provided object has been replaced
147    ///
148    /// Returns `None`, if the provided item is not present.
149    ///
150    /// # Panics
151    ///
152    /// Panics, if the update results in a duplicate item.
153    #[must_use]
154    pub fn replace(
155        &self,
156        original: &Handle<T>,
157        replacements: impl IntoIterator<Item = impl Into<Handle<T>>>,
158    ) -> Option<Self>
159    where
160        T: Debug + Ord,
161    {
162        let mut iter = self.iter().cloned().peekable();
163
164        // Collect all items before the item we want to update.
165        let mut before = Vec::new();
166        loop {
167            let next = match iter.next() {
168                Some(handle) => handle,
169                None => {
170                    // We went through the whole iterator without finding the
171                    // item we were looking for.
172                    return None;
173                }
174            };
175
176            if next.id() == original.id() {
177                break;
178            }
179
180            before.push(next.clone());
181        }
182
183        // What's left in the iterator is what comes after the replaced item.
184        // Let's make that a bit more explicit by renaming the variable.
185        let after = iter;
186
187        Some(
188            before
189                .into_iter()
190                .chain(replacements.into_iter().map(|handle| handle.into()))
191                .chain(after)
192                .collect(),
193        )
194    }
195}
196
197impl<O> FromIterator<Handle<O>> for ObjectSet<O>
198where
199    O: Debug + Ord,
200{
201    fn from_iter<T: IntoIterator<Item = Handle<O>>>(handles: T) -> Self {
202        Self::new(handles)
203    }
204}
205
206impl<'r, T> IntoIterator for &'r ObjectSet<T> {
207    type Item = &'r Handle<T>;
208    type IntoIter = ObjectSetIter<'r, T>;
209
210    fn into_iter(self) -> Self::IntoIter {
211        self.iter()
212    }
213}
214
215impl<T> IntoIterator for ObjectSet<T> {
216    type Item = Handle<T>;
217    type IntoIter = ObjectSetIntoIter<T>;
218
219    fn into_iter(self) -> Self::IntoIter {
220        self.inner.into_iter().map(HandleWrapper::into_handle)
221    }
222}
223
224/// An borrowed iterator over an [`ObjectSet`]
225///
226/// See [`ObjectSet::iter`].
227pub type ObjectSetIter<'r, T> = iter::Map<
228    slice::Iter<'r, HandleWrapper<T>>,
229    fn(&HandleWrapper<T>) -> &Handle<T>,
230>;
231
232/// An owned iterator over an [`ObjectSet`]
233///
234/// You might wonder why we're returning references to `Handle`s here, when
235/// `Handle` already is a kind of reference, and easily cloned.
236///
237/// Most of the time that doesn't make a difference, but there are use cases
238/// where dealing with owned `Handle`s is inconvenient, for example when using
239/// iterator adapters. You can't return a reference to the argument of an
240/// adapter's closure, if you own that argument. You can, if you just reference
241/// the argument.
242pub type ObjectSetIntoIter<T> = iter::Map<
243    vec::IntoIter<HandleWrapper<T>>,
244    fn(HandleWrapper<T>) -> Handle<T>,
245>;
246
247#[cfg(test)]
248mod tests {
249    use std::collections::HashSet;
250
251    use crate::{
252        objects::Cycle, operations::insert::Insert, storage::HandleWrapper,
253        Core,
254    };
255
256    use super::ObjectSet;
257
258    #[test]
259    fn equal_but_not_identical_objects() {
260        let mut core = Core::new();
261
262        let bare_cycle = Cycle::new([]);
263        let cycle_a = bare_cycle.clone().insert(&mut core);
264        let cycle_b = bare_cycle.insert(&mut core);
265
266        let _object_set = ObjectSet::new([cycle_a, cycle_b]);
267        // Nothing more to test. `ObjectSet` panics on duplicate objects, and it
268        // shouldn't do that in this case.
269    }
270
271    #[test]
272    fn object_set_from_handle_wrappers() {
273        let mut core = Core::new();
274
275        let bare_cycle = Cycle::new([]);
276        let cycle_a = HandleWrapper::from(bare_cycle.clone().insert(&mut core));
277        let cycle_b = HandleWrapper::from(bare_cycle.insert(&mut core));
278
279        let _object_set = ObjectSet::new([cycle_a, cycle_b]);
280    }
281
282    #[test]
283    fn object_set_from_deduplicated_hash_set() {
284        let mut core = Core::new();
285
286        let bare_cycle = Cycle::new([]);
287        let cycle_a = HandleWrapper::from(bare_cycle.clone().insert(&mut core));
288        let cycle_b = HandleWrapper::from(bare_cycle.insert(&mut core));
289
290        let _object_set = ObjectSet::new(HashSet::from([cycle_a, cycle_b]));
291    }
292}