1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
//! Xvc Entity Component System allows arbitrary [serializable][Storable] components associated
//! with [entities][XvcEntities] of integers.
//! It's used instead of _object-oriented_ architecture for flexible and maintainable features.
//!
//! In Xvc-ECS, each entity is a plain integer.
//! Components are arbitrary structs implementing [Storable], and [stores][XvcStore] are systems
//! that we use to represent associations between entity and components, and between entities.
#![warn(missing_docs)]
#![forbid(unsafe_code)]
pub mod event;
pub mod hstore;
pub mod r11store;
pub mod r1nstore;
pub mod rmnstore;
pub mod storable;
pub mod vstore;
pub mod xvcstore;

use rand::{rngs, RngCore, SeedableRng};
use std::fmt;
use std::fs;
use std::path::Path;
use std::path::PathBuf;
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::sync::Once;
use std::time::SystemTime;
use std::time::UNIX_EPOCH;

use serde::{Deserialize, Serialize};
use xvc_logging::watch;

use crate::error::{Error as XvcError, Result};

/// Describes an entity in Entity Component System-sense.
///
/// It doesn't have any semantics except being unique for a given entity.
/// Various types of information (components) can be attached to this entity.
/// XvcStore uses the entity as a key for the components.
///
/// It's possible to convert to `(u64, u64)` or `u128` back and forth.
/// Normally, you should use [XvcEntityGenerator] to create entities.
/// It randomizes the first value to be unique and saves the last number across sessions.
/// This changed in 0.5. See https://github.com/iesahin/xvc/issues/198
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Hash)]
pub struct XvcEntity(u64, u64);

impl fmt::Display for XvcEntity {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "({}, {})", self.0, self.1)
    }
}

impl From<(u64, u64)> for XvcEntity {
    fn from(e: (u64, u64)) -> Self {
        Self(e.0, e.1)
    }
}

impl From<u128> for XvcEntity {
    fn from(e: u128) -> Self {
        Self((e >> 64) as u64, e as u64)
    }
}

impl From<XvcEntity> for u128 {
    fn from(e: XvcEntity) -> u128 {
        ((e.0 as u128) << 64) | (e.1 as u128)
    }
}

impl From<XvcEntity> for (u64, u64) {
    fn from(e: XvcEntity) -> (u64, u64) {
        (e.0, e.1)
    }
}

/// Keeps track of the latest [XvcEntity] values, and creates new entities.
///
/// It's thread safe as it uses [AtomicUsize] for the highest entity value.
/// It can save itself to a file, loads from file.
/// Only one instance of this created for each app.
/// It runs load or init functions via [Once] and doesn't allow to load a second instance.

#[derive(Debug)]
pub struct XvcEntityGenerator {
    /// The counter is used to generate the first portion of new entities. It's incremented at every [`next_element`] call.
    counter: AtomicU64,
    /// The random value is used to generate the second portion of new entities. It's generated once at the
    /// initialization of this struct and never changed.
    random: u64,
    /// The counter is saved only if its value is changed.
    dirty: AtomicBool,
}

static INIT: Once = Once::new();

/// Loads the generator from a directory.
///
/// It loads all files from that directory, and selects the highest one.
/// This function can only be used once in a process.
/// You cannot load a second instance of the entity generator, as it will defeat its thread-safe
/// uniqueness purpose.
pub fn load_generator(dir: &Path) -> Result<XvcEntityGenerator> {
    let mut gen: Result<XvcEntityGenerator> = Err(XvcError::CanInitializeOnlyOnce {
        object: "XvcEntityGenerator".to_string(),
    });
    INIT.call_once(|| gen = XvcEntityGenerator::load(dir));
    gen
}

/// Inits a generator for the first time.
///
/// Normally this only be used once an Xvc repository initializes.
/// The starting value for entities is 1.
pub fn init_generator() -> Result<XvcEntityGenerator> {
    let mut gen: Result<XvcEntityGenerator> = Err(XvcError::CanInitializeOnlyOnce {
        object: "XvcEntityGenerator".to_string(),
    });

    INIT.call_once(|| gen = Ok(XvcEntityGenerator::new(1)));
    gen
}

impl Iterator for XvcEntityGenerator {
    type Item = XvcEntity;

    fn next(&mut self) -> Option<Self::Item> {
        Some(self.next_element())
    }
}

impl XvcEntityGenerator {
    fn new(start: u64) -> XvcEntityGenerator {
        let counter = AtomicU64::new(start);
        let mut rng = rngs::StdRng::from_entropy();
        let init_random = rng.next_u64();
        // When we create a new generator from scratch, we need to save it.
        // In the load function, we set this to false, as we don't want to save duplicate values.
        let dirty = AtomicBool::new(true);
        Self {
            dirty,
            counter,
            random: init_random,
        }
    }

    /// Returns the next element by atomically incresing the current value.
    pub fn next_element(&self) -> XvcEntity {
        self.dirty.store(true, Ordering::SeqCst);
        XvcEntity(self.counter.fetch_add(1, Ordering::SeqCst), self.random)
    }

    fn load(dir: &Path) -> Result<XvcEntityGenerator> {
        let path = most_recent_file(dir)?;
        match path {
            Some(path) => {
                let current_val = fs::read_to_string(path)?.parse::<u64>()?;
                // We don't use new here to set the dirty flag to false.
                let counter = AtomicU64::new(current_val);
                let mut rng = rngs::StdRng::from_entropy();
                let init_random = rng.next_u64();
                // When we load a new generator from file, we don't need to save it.
                // In the new function, we set this to true, as we need to save the first value.
                let dirty = AtomicBool::new(false);
                Ok(Self {
                    dirty,
                    counter,
                    random: init_random,
                })
            }
            None => Err(XvcError::CannotRestoreEntityCounter {
                path: dir.as_os_str().to_owned(),
            }),
        }
    }

    /// Saves the current XvcEntity counter to path.
    /// It saves only the first (e.0) part of the entity. The second part is
    /// generated randomly to randomize entities in different invocations of the app.
    pub fn save(&self, dir: &Path) -> Result<()> {
        if self.dirty.load(Ordering::SeqCst) {
            if !dir.exists() {
                fs::create_dir_all(dir)?;
            }
            let path = dir.join(timestamp());
            fs::write(path, format!("{}", self.counter.load(Ordering::SeqCst)))?;
            // We don't need to save again until changed.
            self.dirty.store(false, Ordering::SeqCst);
        }
        Ok(())
    }
}

/// Returns a timestamp string to be used in file names.
/// This is used to generate sortable unique file names in event logs.
pub fn timestamp() -> String {
    let now = SystemTime::now();
    let since = now
        .duration_since(UNIX_EPOCH)
        .expect("Time went backwards!");
    format!("{}", since.as_micros())
}

/// Returns all files in a directory sorted by name.
/// This is used to sort timestamp named files. (See [timestamp]).
/// Store files are loaded in this order to replay the changes across branches.
/// TODO: Add link to book chapter.
pub fn sorted_files(dir: &Path) -> Result<Vec<PathBuf>> {
    if dir.exists() {
        let mut files: Vec<PathBuf> = fs::read_dir(dir)?
            .filter_map(|e| match e {
                Ok(e) => Some(e.path()),
                Err(_) => None,
            })
            .collect();

        files.sort_unstable();
        Ok(files)
    } else {
        fs::create_dir_all(dir)?;
        Ok(vec![])
    }
}

/// This one returns the most recent timestamp named file.
/// It gets files with [sorted_files] and returns the last one if there is one.
/// If there are no files in a directory, this returns `Ok(None)`.
pub fn most_recent_file(dir: &Path) -> Result<Option<PathBuf>> {
    watch!(dir);
    if !dir.exists() {
        return Ok(None);
    }

    let files = sorted_files(dir)?;

    if files.is_empty() {
        Ok(None)
    } else {
        watch!(files);
        Ok(files.get(files.len() - 1).cloned())
    }
}

#[macro_export]
/// Specifies the store name a type is stored.
/// Usually it's the string representation of a type.
/// These strings are used to generate store locations for types.
/// They must be unique across the project.
///
/// ## Example
/// ```rust,ignore
/// persist!(MyType, "my-type");
/// ```
///
/// makes `MyType` to implement [xvc_ecs::PersistentComponent].
/// This trait has a `type_description` function that returns the specified string.
/// The `type_description` string is then used to generate store identifiers like
/// `my-type-bstore.json` or `my-type-another-type-r11store.json`
macro_rules! persist {
    ( $t:ty, $desc:literal ) => {
        impl ::xvc_ecs::Storable for $t {
            fn type_description() -> String {
                $desc.to_string()
            }
        }
    };
}

#[cfg(test)]
mod tests {

    use std::{thread::sleep, time::Duration};

    use super::*;
    use log::LevelFilter;
    use rand;
    use tempdir::TempDir;
    use xvc_logging::setup_logging;

    #[test]
    fn test_init() -> Result<()> {
        let gen = init_generator()?;
        assert_eq!(gen.counter.load(Ordering::SeqCst), 1);
        assert_eq!(gen.next_element().0, 1);
        assert_eq!(gen.next_element().0, 2);
        let gen2 = init_generator();
        assert!(matches!(gen2, Err(XvcError::CanInitializeOnlyOnce { .. })));
        Ok(())
    }

    #[test]
    fn test_load() -> Result<()> {
        setup_logging(Some(LevelFilter::Trace), None);
        let tempdir = TempDir::new("test-xvc-ecs")?;
        let gen_dir = tempdir.path().join("entity-gen");
        fs::create_dir_all(&gen_dir)?;
        let r: u64 = rand::random();
        let gen_file_1 = gen_dir.join(timestamp());
        fs::write(&gen_file_1, format!("{}", r))?;
        sleep(Duration::from_millis(1));
        let gen_file_2 = gen_dir.join(timestamp());
        fs::write(&gen_file_2, format!("{}", r + 1000))?;
        sleep(Duration::from_millis(1));
        let gen_file_3 = gen_dir.join(timestamp());
        fs::write(&gen_file_3, format!("{}", r + 2000))?;
        let gen = XvcEntityGenerator::load(&gen_dir)?;
        assert_eq!(gen.counter.load(Ordering::SeqCst), r + 2000);
        assert_eq!(gen.next_element().0, (r + 2000));
        assert_eq!(gen.next_element().0, (r + 2001));
        assert_eq!(gen.next_element().0, (r + 2002));
        gen.save(&gen_dir)?;
        let new_val = fs::read_to_string(most_recent_file(&gen_dir)?.unwrap())?.parse::<u64>()?;
        assert_eq!(new_val, r + 2003);
        Ok(())
    }

    /// Multiple saves without changed counter should not create new files.
    /// See: https://github.com/iesahin/xvc/issues/185
    #[test]
    fn test_multi_save() -> Result<()> {
        setup_logging(Some(LevelFilter::Trace), None);
        let tempdir = TempDir::new("test-xvc-ecs")?;
        let gen_dir = tempdir.path().join("entity-gen");
        fs::create_dir_all(&gen_dir)?;
        // We use new here to circumvent the singleton check.
        let gen = XvcEntityGenerator::new(10);
        gen.save(&gen_dir)?;
        // It must save the counter at first
        assert!(sorted_files(&gen_dir)?.len() == 1);
        // It must not save the counter if it's not changed
        gen.save(&gen_dir)?;
        assert!(sorted_files(&gen_dir)?.len() == 1);
        // It must save the counter if it's changed
        let _e = gen.next_element();
        gen.save(&gen_dir)?;
        assert!(sorted_files(&gen_dir)?.len() == 2);

        let gen2 = XvcEntityGenerator::load(&gen_dir)?;
        // Don't save if it's not changed after load
        gen.save(&gen_dir)?;
        assert!(sorted_files(&gen_dir)?.len() == 2);
        // Save if it's changed after load
        let _e = gen2.next_element();
        gen2.save(&gen_dir)?;
        assert!(sorted_files(&gen_dir)?.len() == 3);
        // Don't save if it's not changed after save
        gen2.save(&gen_dir)?;
        gen2.save(&gen_dir)?;
        gen2.save(&gen_dir)?;
        gen2.save(&gen_dir)?;
        assert!(sorted_files(&gen_dir)?.len() == 3);

        Ok(())
    }

    #[test]
    fn test_from_to() -> Result<()> {
        let e1 = XvcEntity(1, 2);
        let u1: u128 = e1.into();
        let e2 = XvcEntity::from(u1);
        assert_eq!(e1, e2);
        Ok(())
    }
}