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;