bones_schema/alloc/
type_datas.rs

1use append_only_vec::AppendOnlyVec;
2
3use crate::prelude::*;
4
5/// A `TypeMap`-like structure, that does not allow removing entries or updating exisintg
6/// entries.
7///
8/// This structure doesn't require a mutable reference to insert records
9#[derive(Debug)]
10// TODO: Evaluate the possibility of using a `OnceMap` instead of an `AppendOnlyVec` for `TypeDatas`.
11// The only reason we would use a `OnceMap` would be to improve the lookup performance and avoid
12// having to iterate over the entire vec each time we need to find a type data with a particular
13// schema. Since there are not usually a large number of type datas for any single type, this
14// probably isn't a concern, but maybe we should do some benchmarking.
15pub struct TypeDatas(AppendOnlyVec<SchemaBox>);
16impl Default for TypeDatas {
17    fn default() -> Self {
18        Self(AppendOnlyVec::new())
19    }
20}
21impl Clone for TypeDatas {
22    fn clone(&self) -> Self {
23        let clone = TypeDatas::default();
24        for entry in self.0.iter() {
25            clone.insert(entry.clone()).unwrap();
26        }
27        clone
28    }
29}
30
31impl TypeDatas {
32    /// Insert data into the store.
33    pub fn insert<T: HasSchema>(&self, data: T) -> Result<(), TypeDataAlreadyInserted> {
34        self.insert_box(SchemaBox::new(data))
35    }
36
37    /// Insert boxed data into the store.
38    pub fn insert_box(&self, data: SchemaBox) -> Result<(), TypeDataAlreadyInserted> {
39        let schema = data.schema();
40        for entry in self.0.iter() {
41            if entry.schema() == schema {
42                return Err(TypeDataAlreadyInserted(schema));
43            }
44        }
45        self.0.push(data);
46        Ok(())
47    }
48
49    /// Borrow data from the store, if it exists.
50    pub fn get<T: HasSchema>(&self) -> Option<&T> {
51        let id = T::schema().id();
52        for data in self.0.iter() {
53            if data.schema().id() == id {
54                return Some(data.cast_ref());
55            }
56        }
57        None
58    }
59
60    /// Borrow data from the store, if it exists.
61    pub fn get_ref(&self, id: SchemaId) -> Option<SchemaRef<'_>> {
62        for data in self.0.iter() {
63            if data.schema().id() == id {
64                return Some(data.as_ref());
65            }
66        }
67        None
68    }
69
70    /// Iterate over type datas.
71    pub fn iter(&self) -> impl DoubleEndedIterator<Item = &SchemaBox> {
72        self.0.iter()
73    }
74}
75
76/// Error type for [`TypeDatas`]
77#[derive(Debug)]
78pub struct TypeDataAlreadyInserted(&'static Schema);
79
80impl std::fmt::Display for TypeDataAlreadyInserted {
81    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
82        f.write_fmt(format_args!(
83            "Type data already contains an entry of type: {}",
84            self.0.full_name,
85        ))
86    }
87}
88impl std::error::Error for TypeDataAlreadyInserted {}
89
90#[cfg(test)]
91mod test {
92    use super::*;
93
94    #[test]
95    fn smoke() {
96        let tds = TypeDatas::default();
97
98        tds.insert(String::from("hi")).unwrap();
99        assert_eq!(Some("hi"), tds.get::<String>().map(|x| x.as_str()));
100
101        tds.insert(7u32).unwrap();
102        assert_eq!(Some(&7), tds.get::<u32>());
103
104        let result = tds.insert(String::from("bye"));
105        assert!(matches!(result, Err(TypeDataAlreadyInserted(_))));
106    }
107}