Skip to main content

bubbles/value/
storage.rs

1//! [`VariableStorage`] trait and the default [`HashMapStorage`] implementation.
2
3use std::borrow::Cow;
4use std::collections::HashMap;
5
6use super::Value;
7
8/// Pluggable variable storage consumed by the runner.
9///
10/// Implement this trait to back variables with your game's own data model
11/// (e.g. an ECS component, a database row, or a save-file entry).
12///
13/// # Example
14///
15/// ```rust
16/// use bubbles::{HashMapStorage, Value, VariableStorage};
17///
18/// let mut s = HashMapStorage::new();
19/// s.set("$score", Value::Number(10.0));
20/// assert_eq!(s.get("$score"), Some(Value::Number(10.0)));
21/// assert_eq!(s.get("$missing"), None);
22/// ```
23pub trait VariableStorage {
24    /// Returns the current value of `name`, or `None` if the variable has not been set.
25    ///
26    /// This is the ergonomic read path: it always returns an owned [`Value`],
27    /// cloning if the backing store holds one by reference.  New impls are
28    /// encouraged to override [`get_ref`](Self::get_ref) as well so hot
29    /// expression-evaluation paths can avoid cloning [`Value::Text`].
30    fn get(&self, name: &str) -> Option<Value>;
31    /// Stores `value` under `name`, replacing any previous value.
32    fn set(&mut self, name: &str, value: Value);
33
34    /// Returns a reference to the current value of `name`, or `None` if the
35    /// variable has not been set.
36    ///
37    /// The runner prefers this over [`get`](Self::get) during expression
38    /// evaluation so string variables can be observed without an allocation.
39    /// The default implementation simply forwards to [`get`](Self::get) and
40    /// wraps the result in [`Cow::Owned`], so existing implementations keep
41    /// working unchanged.  Stores that already own their values (such as
42    /// [`HashMapStorage`]) should override this to return [`Cow::Borrowed`].
43    fn get_ref(&self, name: &str) -> Option<Cow<'_, Value>> {
44        self.get(name).map(Cow::Owned)
45    }
46
47    /// Returns every `(variable_name, value)` pair this storage currently holds.
48    ///
49    /// Intended for debug overlays, save editors, and tests. The default
50    /// implementation returns an empty vector; override it when you can
51    /// enumerate variables (as [`HashMapStorage`] does).
52    fn all_variables(&self) -> Vec<(String, Value)> {
53        Vec::new()
54    }
55}
56
57/// Default in-memory variable store backed by a [`HashMap`].
58///
59/// # Example
60///
61/// ```rust
62/// use bubbles::{HashMapStorage, Value, VariableStorage};
63///
64/// let mut storage = HashMapStorage::new();
65/// storage.set("$hp", Value::Number(100.0));
66/// storage.set("$name", Value::Text("Hero".into()));
67///
68/// assert_eq!(storage.get("$hp"), Some(Value::Number(100.0)));
69/// assert_eq!(storage.get("$name"), Some(Value::Text("Hero".into())));
70/// ```
71#[derive(Debug, Clone, Default)]
72#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
73pub struct HashMapStorage {
74    map: HashMap<String, Value>,
75}
76
77impl HashMapStorage {
78    /// Creates an empty store.
79    #[must_use]
80    pub fn new() -> Self {
81        Self::default()
82    }
83}
84
85impl VariableStorage for HashMapStorage {
86    fn get(&self, name: &str) -> Option<Value> {
87        self.map.get(name).cloned()
88    }
89
90    fn set(&mut self, name: &str, value: Value) {
91        self.map.insert(name.to_owned(), value);
92    }
93
94    fn get_ref(&self, name: &str) -> Option<Cow<'_, Value>> {
95        self.map.get(name).map(Cow::Borrowed)
96    }
97
98    fn all_variables(&self) -> Vec<(String, Value)> {
99        self.map
100            .iter()
101            .map(|(k, v)| (k.clone(), v.clone()))
102            .collect()
103    }
104}
105
106#[cfg(test)]
107#[path = "storage_tests.rs"]
108mod tests;