Skip to main content

thedes_entity/
compact.rs

1use std::{
2    cmp,
3    collections::{BinaryHeap, HashMap},
4    fmt,
5};
6
7use serde::{Deserialize, Serialize};
8use thiserror::Error;
9
10#[derive(Debug, Clone, Copy, Error)]
11#[error("Identifier {0} is not short")]
12pub struct NonShortId(pub Id);
13
14#[derive(Debug, Clone, Copy, Error)]
15#[error("Identifier {0} is not tiny")]
16pub struct NonTinyId(pub Id);
17
18#[derive(Debug, Clone, Copy, Error)]
19#[error("Invalid identifier {0}")]
20pub struct InvalidId(pub Id);
21
22#[derive(Debug, Clone, Copy, Error)]
23#[error("Invalid index {index} out of length {len}")]
24pub struct InvalidIndex {
25    pub index: usize,
26    pub len: usize,
27}
28
29#[derive(Debug, Clone, Copy, Error)]
30pub enum InvalidIndexAs<E> {
31    #[error(transparent)]
32    OutOfBounds(#[from] InvalidIndex),
33    #[error("id is not convertible")]
34    IdConversion(#[source] E),
35}
36
37#[derive(
38    Debug,
39    Clone,
40    Copy,
41    PartialEq,
42    Eq,
43    PartialOrd,
44    Ord,
45    Hash,
46    Default,
47    Serialize,
48    Deserialize,
49)]
50#[serde(transparent)]
51pub struct Id(u32);
52
53impl fmt::Display for Id {
54    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55        write!(f, "{}", self.0)
56    }
57}
58
59impl From<ShortId> for Id {
60    fn from(id: ShortId) -> Self {
61        Self(id.0.into())
62    }
63}
64
65impl From<TinyId> for Id {
66    fn from(id: TinyId) -> Self {
67        Self(id.0.into())
68    }
69}
70
71#[derive(
72    Debug,
73    Clone,
74    Copy,
75    PartialEq,
76    Eq,
77    PartialOrd,
78    Ord,
79    Hash,
80    Default,
81    Serialize,
82    Deserialize,
83)]
84#[serde(transparent)]
85pub struct ShortId(u16);
86
87impl fmt::Display for ShortId {
88    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89        write!(f, "{}", self.0)
90    }
91}
92
93impl From<TinyId> for ShortId {
94    fn from(id: TinyId) -> Self {
95        Self(id.0.into())
96    }
97}
98
99impl TryFrom<Id> for ShortId {
100    type Error = NonShortId;
101
102    fn try_from(id: Id) -> Result<Self, Self::Error> {
103        match id.0.try_into() {
104            Ok(bits) => Ok(Self(bits)),
105            Err(_) => Err(NonShortId(id)),
106        }
107    }
108}
109
110#[derive(
111    Debug,
112    Clone,
113    Copy,
114    PartialEq,
115    Eq,
116    PartialOrd,
117    Ord,
118    Hash,
119    Default,
120    Serialize,
121    Deserialize,
122)]
123#[serde(transparent)]
124pub struct TinyId(u8);
125
126impl TryFrom<Id> for TinyId {
127    type Error = NonTinyId;
128
129    fn try_from(id: Id) -> Result<Self, Self::Error> {
130        match id.0.try_into() {
131            Ok(bits) => Ok(Self(bits)),
132            Err(_) => Err(NonTinyId(id)),
133        }
134    }
135}
136
137impl TryFrom<ShortId> for TinyId {
138    type Error = NonTinyId;
139
140    fn try_from(id: ShortId) -> Result<Self, Self::Error> {
141        match id.0.try_into() {
142            Ok(bits) => Ok(Self(bits)),
143            Err(_) => Err(NonTinyId(id.into())),
144        }
145    }
146}
147
148impl fmt::Display for TinyId {
149    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150        write!(f, "{}", self.0)
151    }
152}
153
154#[derive(Debug, Clone, Serialize, Deserialize)]
155pub struct Registry<T> {
156    primary: Vec<(Id, T)>,
157    secondary_pos: HashMap<Id, usize>,
158    secondary_neg: BinaryHeap<cmp::Reverse<Id>>,
159}
160
161impl<T> Registry<T> {
162    pub fn new() -> Self {
163        Self {
164            primary: Vec::new(),
165            secondary_pos: HashMap::new(),
166            secondary_neg: BinaryHeap::new(),
167        }
168    }
169
170    pub fn len(&self) -> usize {
171        self.primary.len()
172    }
173
174    pub fn create(&mut self, data: T) -> Id {
175        let id = self.pop_secondary_neg();
176        self.create_with_id(data, id);
177        id
178    }
179
180    pub fn create_as<I, E>(&mut self, data: T) -> Result<I, E>
181    where
182        Id: TryInto<I, Error = E>,
183    {
184        let id = self.pop_secondary_neg();
185        match id.try_into() {
186            Ok(converted_id) => {
187                self.create_with_id(data, id);
188                Ok(converted_id)
189            },
190            Err(error) => {
191                self.push_secondary_neg(id);
192                Err(error)
193            },
194        }
195    }
196
197    pub fn remove(&mut self, id: impl Into<Id>) -> Result<T, InvalidId> {
198        let id = id.into();
199        let index =
200            self.secondary_pos.get(&id).copied().ok_or(InvalidId(id))?;
201        let last_index = self.primary.len() - 1;
202        if index != last_index {
203            self.primary.swap(index, last_index);
204            let (id, _) = self.primary[index];
205            self.secondary_pos.insert(id, index);
206        }
207        let (_, item) = self.primary.pop().expect("inconsistent tables");
208        self.push_secondary_neg(id);
209        Ok(item)
210    }
211
212    pub fn id_to_index(&self, id: impl Into<Id>) -> Result<usize, InvalidId> {
213        let id = id.into();
214        self.secondary_pos.get(&id).copied().ok_or(InvalidId(id))
215    }
216
217    pub fn get_by_id(&self, id: impl Into<Id>) -> Result<&T, InvalidId> {
218        let index = self.id_to_index(id)?;
219        Ok(&self.primary[index].1)
220    }
221
222    pub fn get_by_id_mut(
223        &mut self,
224        id: impl Into<Id>,
225    ) -> Result<&mut T, InvalidId> {
226        let index = self.id_to_index(id)?;
227        Ok(&mut self.primary[index].1)
228    }
229
230    pub fn get_by_index(&self, index: usize) -> Result<(Id, &T), InvalidIndex> {
231        let error = InvalidIndex { index, len: self.primary.len() };
232        let (id, value) = self.primary.get(index).ok_or(error)?;
233        Ok((*id, value))
234    }
235
236    pub fn get_by_index_mut(
237        &mut self,
238        index: usize,
239    ) -> Result<(Id, &mut T), InvalidIndex> {
240        let error = InvalidIndex { index, len: self.primary.len() };
241        let (id, value) = self.primary.get_mut(index).ok_or(error)?;
242        Ok((*id, value))
243    }
244
245    pub fn get_by_index_as<I, E>(
246        &self,
247        index: usize,
248    ) -> Result<(I, &T), InvalidIndexAs<E>>
249    where
250        Id: TryInto<I, Error = E>,
251    {
252        let (id, data) = self.get_by_index(index)?;
253        let converted_id =
254            id.try_into().map_err(InvalidIndexAs::IdConversion)?;
255        Ok((converted_id, data))
256    }
257
258    pub fn get_by_index_mut_as<I, E>(
259        &mut self,
260        index: usize,
261    ) -> Result<(I, &mut T), InvalidIndexAs<E>>
262    where
263        Id: TryInto<I, Error = E>,
264    {
265        let (id, data) = self.get_by_index_mut(index)?;
266        let converted_id =
267            id.try_into().map_err(InvalidIndexAs::IdConversion)?;
268        Ok((converted_id, data))
269    }
270
271    pub fn iter<'a>(
272        &'a self,
273    ) -> impl DoubleEndedIterator<Item = (Id, &'a T)> + 'a + Send + Sync
274    where
275        T: Send + Sync,
276    {
277        self.primary.iter().map(|(id, elem)| (*id, elem))
278    }
279
280    pub fn iter_mut<'a>(
281        &'a mut self,
282    ) -> impl DoubleEndedIterator<Item = (Id, &'a mut T)> + 'a + Send + Sync
283    where
284        T: Send + Sync,
285    {
286        self.primary.iter_mut().map(|(id, elem)| (*id, elem))
287    }
288
289    pub fn into_iter<'a>(
290        self,
291    ) -> impl DoubleEndedIterator<Item = (Id, T)> + 'a + Send + Sync
292    where
293        T: Send + Sync + 'a,
294    {
295        self.primary.into_iter().map(|(id, elem)| (id, elem))
296    }
297
298    fn create_with_id(&mut self, data: T, id: Id) {
299        let index = self.primary.len();
300        self.primary.push((id, data));
301        self.secondary_pos.insert(id, index);
302    }
303
304    fn pop_secondary_neg(&mut self) -> Id {
305        match self.secondary_neg.pop() {
306            Some(cmp::Reverse(id)) => id,
307            None => Id(self.len().try_into().expect("too much ids")),
308        }
309    }
310
311    fn push_secondary_neg(&mut self, id: Id) {
312        self.secondary_neg.push(cmp::Reverse(id));
313    }
314}