1#![warn(missing_docs)]
9#![forbid(unsafe_code)]
10pub mod event;
11pub mod hstore;
12pub mod r11store;
13pub mod r1nstore;
14pub mod rmnstore;
15pub mod storable;
16pub mod vstore;
17pub mod xvcstore;
18
19use rand::{rngs, RngCore, SeedableRng};
20use std::fmt;
21use std::fs;
22use std::path::Path;
23use std::path::PathBuf;
24use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
25use std::sync::Once;
26use std::time::SystemTime;
27use std::time::UNIX_EPOCH;
28
29use serde::{Deserialize, Serialize};
30
31use crate::error::{Error as XvcError, Result};
32
33#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Hash)]
44pub struct XvcEntity(u64, u64);
45
46impl fmt::Display for XvcEntity {
47    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48        write!(f, "({}, {})", self.0, self.1)
49    }
50}
51
52impl From<(u64, u64)> for XvcEntity {
53    fn from(e: (u64, u64)) -> Self {
54        Self(e.0, e.1)
55    }
56}
57
58impl From<u128> for XvcEntity {
59    fn from(e: u128) -> Self {
60        Self((e >> 64) as u64, e as u64)
61    }
62}
63
64impl From<XvcEntity> for u128 {
65    fn from(e: XvcEntity) -> u128 {
66        ((e.0 as u128) << 64) | (e.1 as u128)
67    }
68}
69
70impl From<XvcEntity> for (u64, u64) {
71    fn from(e: XvcEntity) -> (u64, u64) {
72        (e.0, e.1)
73    }
74}
75
76#[derive(Debug)]
84pub struct XvcEntityGenerator {
85    counter: AtomicU64,
87    random: u64,
90    dirty: AtomicBool,
92}
93
94static INIT: Once = Once::new();
95
96pub fn load_generator(dir: &Path) -> Result<XvcEntityGenerator> {
103    let mut gen: Result<XvcEntityGenerator> = Err(XvcError::CanInitializeOnlyOnce {
104        object: "XvcEntityGenerator".to_string(),
105    });
106    INIT.call_once(|| gen = XvcEntityGenerator::load(dir));
107    gen
108}
109
110pub fn init_generator() -> Result<XvcEntityGenerator> {
115    let mut gen: Result<XvcEntityGenerator> = Err(XvcError::CanInitializeOnlyOnce {
116        object: "XvcEntityGenerator".to_string(),
117    });
118
119    INIT.call_once(|| gen = Ok(XvcEntityGenerator::new(1)));
120    gen
121}
122
123impl Iterator for XvcEntityGenerator {
124    type Item = XvcEntity;
125
126    fn next(&mut self) -> Option<Self::Item> {
127        Some(self.next_element())
128    }
129}
130
131impl XvcEntityGenerator {
132    fn new(start: u64) -> XvcEntityGenerator {
133        let counter = AtomicU64::new(start);
134        let mut rng = rngs::StdRng::from_entropy();
135        let init_random = rng.next_u64();
136        let dirty = AtomicBool::new(true);
139        Self {
140            dirty,
141            counter,
142            random: init_random,
143        }
144    }
145
146    pub fn next_element(&self) -> XvcEntity {
148        self.dirty.store(true, Ordering::SeqCst);
149        XvcEntity(self.counter.fetch_add(1, Ordering::SeqCst), self.random)
150    }
151
152    fn load(dir: &Path) -> Result<XvcEntityGenerator> {
153        let path = most_recent_file(dir)?;
154        match path {
155            Some(path) => {
156                let current_val = fs::read_to_string(path)?.parse::<u64>()?;
157                let counter = AtomicU64::new(current_val);
159                let mut rng = rngs::StdRng::from_entropy();
160                let init_random = rng.next_u64();
161                let dirty = AtomicBool::new(false);
164                Ok(Self {
165                    dirty,
166                    counter,
167                    random: init_random,
168                })
169            }
170            None => Err(XvcError::CannotRestoreEntityCounter {
171                path: dir.as_os_str().to_owned(),
172            }),
173        }
174    }
175
176    pub fn save(&self, dir: &Path) -> Result<()> {
180        if self.dirty.load(Ordering::SeqCst) {
181            if !dir.exists() {
182                fs::create_dir_all(dir)?;
183            }
184            let path = dir.join(timestamp());
185            fs::write(path, format!("{}", self.counter.load(Ordering::SeqCst)))?;
186            self.dirty.store(false, Ordering::SeqCst);
188        }
189        Ok(())
190    }
191}
192
193pub fn timestamp() -> String {
196    let now = SystemTime::now();
197    let since = now
198        .duration_since(UNIX_EPOCH)
199        .expect("Time went backwards!");
200    format!("{}", since.as_micros())
201}
202
203pub fn sorted_files(dir: &Path) -> Result<Vec<PathBuf>> {
208    if dir.exists() {
209        let mut files: Vec<PathBuf> = fs::read_dir(dir)?
210            .filter_map(|e| match e {
211                Ok(e) => Some(e.path()),
212                Err(_) => None,
213            })
214            .collect();
215
216        files.sort_unstable();
217        Ok(files)
218    } else {
219        fs::create_dir_all(dir)?;
220        Ok(vec![])
221    }
222}
223
224pub fn most_recent_file(dir: &Path) -> Result<Option<PathBuf>> {
228    if !dir.exists() {
229        return Ok(None);
230    }
231
232    let files = sorted_files(dir)?;
233
234    if files.is_empty() {
235        Ok(None)
236    } else {
237        Ok(files.last().cloned())
238    }
239}
240
241#[macro_export]
242macro_rules! persist {
257    ( $t:ty, $desc:literal ) => {
258        impl $crate::Storable for $t {
259            fn type_description() -> String {
260                $desc.to_string()
261            }
262        }
263    };
264}
265
266#[cfg(test)]
267mod tests {
268
269    use std::{thread::sleep, time::Duration};
270
271    use super::*;
272    use log::LevelFilter;
273    use rand;
274    use tempdir::TempDir;
275    use xvc_logging::setup_logging;
276
277    #[test]
278    fn test_init() -> Result<()> {
279        let gen = init_generator()?;
280        assert_eq!(gen.counter.load(Ordering::SeqCst), 1);
281        assert_eq!(gen.next_element().0, 1);
282        assert_eq!(gen.next_element().0, 2);
283        let gen2 = init_generator();
284        assert!(matches!(gen2, Err(XvcError::CanInitializeOnlyOnce { .. })));
285        Ok(())
286    }
287
288    #[test]
289    fn test_load() -> Result<()> {
290        setup_logging(Some(LevelFilter::Trace), None);
291        let tempdir = TempDir::new("test-xvc-ecs")?;
292        let gen_dir = tempdir.path().join("entity-gen");
293        fs::create_dir_all(&gen_dir)?;
294        let r: u64 = rand::random();
295        let gen_file_1 = gen_dir.join(timestamp());
296        fs::write(gen_file_1, format!("{}", r))?;
297        sleep(Duration::from_millis(1));
298        let gen_file_2 = gen_dir.join(timestamp());
299        fs::write(gen_file_2, format!("{}", r + 1000))?;
300        sleep(Duration::from_millis(1));
301        let gen_file_3 = gen_dir.join(timestamp());
302        fs::write(gen_file_3, format!("{}", r + 2000))?;
303        let gen = XvcEntityGenerator::load(&gen_dir)?;
304        assert_eq!(gen.counter.load(Ordering::SeqCst), r + 2000);
305        assert_eq!(gen.next_element().0, (r + 2000));
306        assert_eq!(gen.next_element().0, (r + 2001));
307        assert_eq!(gen.next_element().0, (r + 2002));
308        gen.save(&gen_dir)?;
309        let new_val = fs::read_to_string(most_recent_file(&gen_dir)?.unwrap())?.parse::<u64>()?;
310        assert_eq!(new_val, r + 2003);
311        Ok(())
312    }
313
314    #[test]
317    fn test_multi_save() -> Result<()> {
318        setup_logging(Some(LevelFilter::Trace), None);
319        let tempdir = TempDir::new("test-xvc-ecs")?;
320        let gen_dir = tempdir.path().join("entity-gen");
321        fs::create_dir_all(&gen_dir)?;
322        let gen = XvcEntityGenerator::new(10);
324        gen.save(&gen_dir)?;
325        assert!(sorted_files(&gen_dir)?.len() == 1);
327        gen.save(&gen_dir)?;
329        assert!(sorted_files(&gen_dir)?.len() == 1);
330        let _e = gen.next_element();
332        gen.save(&gen_dir)?;
333        assert!(sorted_files(&gen_dir)?.len() == 2);
334
335        let gen2 = XvcEntityGenerator::load(&gen_dir)?;
336        gen.save(&gen_dir)?;
338        assert!(sorted_files(&gen_dir)?.len() == 2);
339        let _e = gen2.next_element();
341        gen2.save(&gen_dir)?;
342        assert!(sorted_files(&gen_dir)?.len() == 3);
343        gen2.save(&gen_dir)?;
345        gen2.save(&gen_dir)?;
346        gen2.save(&gen_dir)?;
347        gen2.save(&gen_dir)?;
348        assert!(sorted_files(&gen_dir)?.len() == 3);
349
350        Ok(())
351    }
352
353    #[test]
354    fn test_from_to() -> Result<()> {
355        let e1 = XvcEntity(1, 2);
356        let u1: u128 = e1.into();
357        let e2 = XvcEntity::from(u1);
358        assert_eq!(e1, e2);
359        Ok(())
360    }
361}