Skip to main content

yog_storage/
store.rs

1//! A handle to a store: typed values by key.
2
3use std::io;
4use std::path::Path;
5
6use yog_data::Schema;
7use yog_sync::{Arc, Mutex};
8
9use crate::Scope;
10use crate::shelf::{self, Shelf};
11
12/// A mod's store of a scope: values of any [`Schema`] type by key, kept in
13/// memory and written when it's flushed. Handles to one store share it.
14#[derive(Clone)]
15pub struct Store {
16    shelf: Arc<Mutex<Shelf>>,
17}
18
19impl Store {
20    /// `mod_id`'s store of `scope` under `root` (the game's folder or the world's).
21    pub fn open(root: &Path, mod_id: &str, scope: Scope<'_>) -> Self {
22        Store {
23            shelf: shelf::file(scope.path(root, mod_id)),
24        }
25    }
26
27    /// `mod_id`'s session store: kept while the game runs, never written.
28    pub fn session(mod_id: &str) -> Self {
29        Store {
30            shelf: shelf::session(mod_id),
31        }
32    }
33
34    /// The value at `key`, if there's one of type `T`.
35    pub fn get<T: Schema>(&self, key: &str) -> Option<T> {
36        yog_data::read_value(self.shelf.lock().values.get(key)?).ok()
37    }
38
39    /// The value at `key`, or `T`'s default.
40    pub fn get_or_default<T: Schema + Default>(&self, key: &str) -> T {
41        self.get(key).unwrap_or_default()
42    }
43
44    /// `value` at `key`.
45    pub fn set<T: Schema>(&self, key: &str, value: &T) {
46        let mut shelf = self.shelf.lock();
47        shelf.values.insert(key, value.write());
48        shelf.dirty = true;
49    }
50
51    /// The record at `key`: bytes kept as they were given ([`Store::set_record`]),
52    /// never decoded — what a Yog Script object's state is.
53    pub fn record(&self, key: &str) -> Option<Vec<u8>> {
54        match self.shelf.lock().values.get(key)? {
55            yog_data::Value::Bytes(bytes) => Some(bytes.clone()),
56            _ => None,
57        }
58    }
59
60    /// `bytes` at `key` as a record: kept and written as they are (its
61    /// owner's codec reads them back).
62    pub fn set_record(&self, key: &str, bytes: &[u8]) {
63        let mut shelf = self.shelf.lock();
64        shelf.dirty = true;
65        // The record already there keeps its buffer: no allocation once
66        // it's grown to the record's size.
67        if let Some(yog_data::Value::Bytes(old)) = shelf.values.get_mut(key) {
68            old.clear();
69            old.extend_from_slice(bytes);
70            return;
71        }
72        shelf
73            .values
74            .insert(key, yog_data::Value::Bytes(bytes.to_vec()));
75    }
76
77    /// Takes out every value whose key starts with `prefix`; how many.
78    pub fn remove_under(&self, prefix: &str) -> usize {
79        let mut shelf = self.shelf.lock();
80        let before = shelf.values.len();
81        // One pass, whatever how many go.
82        shelf.values.retain(|k, _| !k.starts_with(prefix));
83        let removed = before - shelf.values.len();
84        shelf.dirty |= removed > 0;
85        removed
86    }
87
88    /// The value at `key` changed by `change` (its default when there's none); what `change` returns.
89    pub fn update<T: Schema + Default, R>(&self, key: &str, change: impl FnOnce(&mut T) -> R) -> R {
90        let mut value = self.get_or_default(key);
91        let out = change(&mut value);
92        self.set(key, &value);
93        out
94    }
95
96    /// Takes the value at `key` out; whether there was one.
97    pub fn remove(&self, key: &str) -> bool {
98        let mut shelf = self.shelf.lock();
99        let removed = shelf.values.remove(key).is_some();
100        shelf.dirty |= removed;
101        removed
102    }
103
104    pub fn contains(&self, key: &str) -> bool {
105        self.shelf.lock().values.get(key).is_some()
106    }
107
108    /// Every key, in the order they were first set.
109    pub fn keys(&self) -> Vec<String> {
110        self.shelf.lock().values.keys().map(str::to_owned).collect()
111    }
112
113    /// Writes it now if it changed (else it's written with every store, see [`crate::flush_all`]).
114    pub fn flush(&self) -> io::Result<()> {
115        self.shelf.lock().flush()
116    }
117}