xvc_ecs/ecs/
vstore.rs

1//! [VStore] and its implementation allows [Storable] types to be processed with [std::vector].
2//! It's better suited when duplicate elements with the same `XvcEntity` may occur.
3use super::event::{Event, EventLog};
4use super::hstore::HStore;
5use crate::error::{Error, Result};
6use crate::XvcEntity;
7use crate::{Storable, XvcStore};
8use serde::{Deserialize, Serialize};
9
10use std::collections::HashMap;
11
12use std::fmt::Debug;
13use std::iter::FromIterator;
14use std::ops::Deref;
15use std::path::{Path, PathBuf};
16
17/// This is a tuple-vector for (XvcEntity, T) to enable faster serial access
18#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
19#[serde(bound = "T: Serialize, for<'lt> T: Deserialize<'lt>")]
20pub struct VStore<T>
21where
22    T: Storable,
23{
24    vec: Vec<(XvcEntity, T)>,
25    previous: EventLog<T>,
26    current: EventLog<T>,
27}
28
29impl<T> Default for VStore<T>
30where
31    T: Storable,
32{
33    fn default() -> Self {
34        Self::new()
35    }
36}
37
38impl<T> VStore<T>
39where
40    T: Storable,
41{
42    /// Create an empty [VStore] with empty logs and vector.
43    pub fn new() -> Self {
44        Self {
45            vec: Vec::<(XvcEntity, T)>::new(),
46            current: EventLog::new(),
47            previous: EventLog::new(),
48        }
49    }
50
51    /// Create the vector with the given capacity to reduce later memory expansions.
52    pub fn with_capacity(capacity: usize) -> Self {
53        Self {
54            vec: Vec::<(XvcEntity, T)>::with_capacity(capacity),
55            // TODO: The following may also use with_capacity
56            current: EventLog::new(),
57            previous: EventLog::new(),
58        }
59    }
60
61    fn build_vec(events: &EventLog<T>) -> Vec<(XvcEntity, T)> {
62        let mut map = HashMap::<XvcEntity, T>::new();
63
64        for event in events.iter() {
65            match event {
66                Event::Add { entity, value } => map.insert(*entity, value.clone()),
67                Event::Remove { entity } => map.remove(entity),
68            };
69        }
70
71        Vec::from_iter(map)
72    }
73
74    /// Loads the store from the given `dir`.
75    /// It loads the [event log][EventLog]  and builds ([`XvcEntity`], `T`) by replaying it.
76    pub fn from_dir(dir: &Path) -> Result<Self> {
77        let previous = EventLog::<T>::from_dir(dir)?;
78        let vec = Self::build_vec(&previous);
79
80        Ok(Self {
81            vec,
82            previous,
83            current: EventLog::new(),
84        })
85    }
86
87    /// Saves the event log to a directory.
88    /// It only saves the events added after the last load.
89    pub fn to_dir(&self, dir: &Path) -> Result<()> {
90        self.current.to_dir(dir)
91    }
92
93    /// Get all elements associated with an [`XvcEntity`].
94    ///
95    /// Conventionally each entity is associated with a single component type, but in some cases an
96    /// entity (e.g. a triangle) may be associated with multiple components. (e.g. points.)
97    /// Nevertheless, in such cases, we recommend to use [R1NStore] to mark the relationship
98    /// clearly.
99    pub fn values_of(&self, parent: &XvcEntity) -> Vec<T> {
100        self.vec
101            .iter()
102            .filter_map(|(e, v)| if *e == *parent { Some(v.clone()) } else { None })
103            .collect()
104    }
105
106    /// Insert an entity to the store.
107    /// It also inserts an event to the log.
108    pub fn insert(&mut self, entity: XvcEntity, value: T) {
109        self.current.push_event(Event::Add {
110            entity,
111            value: value.clone(),
112        });
113        self.vec.push((entity, value))
114    }
115
116    /// Convert to an [XvcStore].
117    /// Uses [XvcStore::from_event_logs] to create a new store self.previous and self.current
118    /// clones.
119    pub fn to_store(&self) -> Result<XvcStore<T>> {
120        let store = XvcStore::from_event_logs(self.previous.clone(), self.current.clone());
121        Ok(store)
122    }
123}
124
125impl<T> Deref for VStore<T>
126where
127    T: Storable,
128{
129    type Target = Vec<(XvcEntity, T)>;
130
131    fn deref(&self) -> &Self::Target {
132        &self.vec
133    }
134}
135
136impl<T> From<HStore<T>> for VStore<T>
137where
138    T: Storable,
139{
140    fn from(store: HStore<T>) -> Self {
141        match store.to_vstore() {
142            Ok(vs) => vs,
143            Err(_) => {
144                Error::StoreConversionError.error();
145                Self::new()
146            }
147        }
148    }
149}
150
151impl<T> FromIterator<(XvcEntity, T)> for VStore<T>
152where
153    T: Storable,
154{
155    fn from_iter<I: IntoIterator<Item = (XvcEntity, T)>>(iter: I) -> Self {
156        let mut s = Self::new();
157        for (e, v) in iter {
158            s.insert(e, v.clone());
159        }
160        s
161    }
162}
163
164impl<T> VStore<T>
165where
166    T: Storable,
167{
168    fn vstore_path(store_root: &Path) -> PathBuf {
169        store_root.join(format!("{}-vstore", <T as Storable>::type_description()))
170    }
171
172    /// Loads a store by appending `T`'s type name (see [Storable::type_description]) to
173    /// `store_root.`
174    pub fn load_vstore(store_root: &Path) -> Result<VStore<T>> {
175        let dir = Self::vstore_path(store_root);
176        VStore::<T>::from_dir(&dir)
177    }
178
179    /// Saves a store by appending `T`'s type name (see [Storable::type_description]) to
180    /// `store_root.`
181    pub fn save_vstore(&self, store_root: &Path) -> Result<()> {
182        let dir = Self::vstore_path(store_root);
183        self.to_dir(&dir)
184    }
185}
186
187#[cfg(test)]
188mod test {
189    use super::*;
190
191    #[test]
192    fn new() -> Result<()> {
193        let mut store = VStore::<String>::new();
194        store.insert((0, 12398012938).into(), "0".into());
195        store.insert((1, 12398012938).into(), "1".into());
196        assert_eq!(store.len(), 2);
197
198        assert_eq!(store.vec.pop().unwrap().1, "1".to_string());
199        assert_eq!(store.vec.pop().unwrap().1, "0".to_string());
200        Ok(())
201    }
202}