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///
72/// [`Clone`] duplicates the inner map. Two [`Runner`](crate::runtime::Runner) instances
73/// that need the same variables must share one storage (for example
74/// `Arc<Mutex<HashMapStorage>>`) or use a custom [`VariableStorage`] implementation.
75#[derive(Debug, Clone, Default)]
76#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
77pub struct HashMapStorage {
78    map: HashMap<String, Value>,
79}
80
81impl HashMapStorage {
82    /// Creates an empty store.
83    #[must_use]
84    pub fn new() -> Self {
85        Self::default()
86    }
87}
88
89impl VariableStorage for HashMapStorage {
90    fn get(&self, name: &str) -> Option<Value> {
91        self.map.get(name).cloned()
92    }
93
94    fn set(&mut self, name: &str, value: Value) {
95        self.map.insert(name.to_owned(), value);
96    }
97
98    fn get_ref(&self, name: &str) -> Option<Cow<'_, Value>> {
99        self.map.get(name).map(Cow::Borrowed)
100    }
101
102    fn all_variables(&self) -> Vec<(String, Value)> {
103        self.map
104            .iter()
105            .map(|(k, v)| (k.clone(), v.clone()))
106            .collect()
107    }
108}
109
110#[cfg(test)]
111#[path = "storage_tests.rs"]
112mod tests;