stable_id/
sparse_entities.rs

1use std::{
2    hash::Hash,
3    ops::{Index, IndexMut},
4};
5
6use stable_id_traits::Successor;
7
8use super::SparseEntities;
9
10impl<IndexT, DataT> SparseEntities<IndexT, DataT>
11where
12    IndexT: Successor + Clone + Copy + Hash + Eq + Default,
13{
14    pub fn len(&self) -> usize {
15        self.data.len()
16    }
17
18    pub fn is_empty(&self) -> bool {
19        self.data.is_empty()
20    }
21
22    pub fn get(&self, index: IndexT) -> Option<&DataT> {
23        self.data.get(&index)
24    }
25
26    pub fn get_mut(&mut self, index: IndexT) -> Option<&mut DataT> {
27        self.data.get_mut(&index)
28    }
29
30    /** Panic if index is invalid */
31    pub fn remove(&mut self, index: IndexT) -> DataT {
32        self.data.remove(&index).expect("id is not value")
33    }
34
35    pub fn alloc(&mut self, data: DataT) -> IndexT {
36        let next_id = self.seq.next_value();
37        self.data.insert(next_id, data);
38
39        next_id
40    }
41
42    pub fn iter(&self) -> impl Iterator<Item = (IndexT, &DataT)> {
43        self.data
44            .iter()
45            .map(|(virtual_id, data)| (*virtual_id, data))
46    }
47
48    pub fn iter_mut(&mut self) -> impl Iterator<Item = (IndexT, &mut DataT)> {
49        self.data
50            .iter_mut()
51            .map(|(virtual_id, data)| (*virtual_id, data))
52    }
53}
54
55impl<IndexT, DataT> IntoIterator for SparseEntities<IndexT, DataT>
56where
57    IndexT: Successor + Clone + Copy + Default + Hash + Eq,
58{
59    type Item = (IndexT, DataT);
60
61    type IntoIter = std::collections::hash_map::IntoIter<IndexT, DataT>;
62
63    fn into_iter(self) -> Self::IntoIter {
64        self.data.into_iter()
65    }
66}
67
68impl<IndexT, DataT> Default for SparseEntities<IndexT, DataT>
69where
70    IndexT: Default,
71{
72    fn default() -> Self {
73        Self {
74            data: Default::default(),
75            seq: Default::default(),
76        }
77    }
78}
79
80impl<IndexT, DataT> Index<IndexT> for SparseEntities<IndexT, DataT>
81where
82    IndexT: Successor + Clone + Copy + Hash + Eq + Default,
83{
84    type Output = DataT;
85
86    fn index(&self, index: IndexT) -> &Self::Output {
87        self.get(index).expect("element not exist")
88    }
89}
90
91impl<IndexT, DataT> IndexMut<IndexT> for SparseEntities<IndexT, DataT>
92where
93    IndexT: Successor + Clone + Copy + Hash + Eq + Default,
94{
95    fn index_mut(&mut self, index: IndexT) -> &mut Self::Output {
96        self.get_mut(index).expect("element not exist")
97    }
98}
99
100#[cfg(test)]
101mod tests {
102    use std::collections::HashMap;
103
104    use crate::SparseEntities;
105
106    #[test]
107    fn access_out_of_bound() {
108        let mut entities = SparseEntities::default();
109        entities.alloc(1232);
110        assert_eq!(entities.get(312u16), None);
111    }
112
113    #[test]
114    #[should_panic(expected = "element not exist")]
115    fn access_out_of_bound_mut() {
116        let mut entities = SparseEntities::default();
117        entities.alloc(1232);
118        entities[312u16] = 3333;
119    }
120
121    #[test]
122    fn normal() {
123        let mut entities = SparseEntities::default();
124
125        fn check_all(entities: &SparseEntities<usize, &str>) {
126            entities
127                .iter()
128                .for_each(|(id, data)| assert_eq!(entities[id], *data));
129        }
130
131        vec!["1", "2", "3", "4", "5"]
132            .into_iter()
133            .fold(HashMap::new(), |mut acc, data| {
134                acc.insert(entities.alloc(data), data);
135                acc
136            })
137            .into_iter()
138            .for_each(|(id, data)| assert_eq!(entities[id], data));
139
140        entities.remove(1);
141        check_all(&entities);
142
143        entities.remove(4);
144        check_all(&entities);
145
146        entities.remove(3);
147        check_all(&entities);
148
149        entities.remove(2);
150        assert_eq!(entities.len(), 1);
151        check_all(&entities);
152
153        entities.remove(0);
154        assert!(entities.is_empty());
155        check_all(&entities);
156    }
157}