Skip to main content

oxihuman_core/
var_store.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Variable store: named f32 variables with change tracking.
6
7use std::collections::HashMap;
8
9/// A variable entry.
10#[allow(dead_code)]
11#[derive(Debug, Clone)]
12pub struct VarEntry {
13    pub name: String,
14    pub value: f32,
15    pub default: f32,
16    pub changed: bool,
17}
18
19/// Variable store.
20#[allow(dead_code)]
21#[derive(Debug, Clone, Default)]
22pub struct VarStore {
23    vars: HashMap<String, VarEntry>,
24}
25
26/// Create a new `VarStore`.
27#[allow(dead_code)]
28pub fn new_var_store() -> VarStore {
29    VarStore::default()
30}
31
32/// Declare a variable with a default value.
33#[allow(dead_code)]
34pub fn vs_declare(vs: &mut VarStore, name: &str, default: f32) {
35    vs.vars.entry(name.to_string()).or_insert(VarEntry {
36        name: name.to_string(),
37        value: default,
38        default,
39        changed: false,
40    });
41}
42
43/// Set a variable value.
44#[allow(dead_code)]
45pub fn vs_set(vs: &mut VarStore, name: &str, value: f32) {
46    let entry = vs.vars.entry(name.to_string()).or_insert(VarEntry {
47        name: name.to_string(),
48        value,
49        default: value,
50        changed: false,
51    });
52    entry.changed = (entry.value - value).abs() > f32::EPSILON;
53    entry.value = value;
54}
55
56/// Get a variable value.
57#[allow(dead_code)]
58pub fn vs_get(vs: &VarStore, name: &str) -> Option<f32> {
59    vs.vars.get(name).map(|e| e.value)
60}
61
62/// Whether the variable was changed since last flush.
63#[allow(dead_code)]
64pub fn vs_is_changed(vs: &VarStore, name: &str) -> bool {
65    vs.vars.get(name).is_some_and(|e| e.changed)
66}
67
68/// Reset a variable to its default.
69#[allow(dead_code)]
70pub fn vs_reset(vs: &mut VarStore, name: &str) {
71    if let Some(e) = vs.vars.get_mut(name) {
72        e.value = e.default;
73        e.changed = false;
74    }
75}
76
77/// Flush change flags.
78#[allow(dead_code)]
79pub fn vs_flush(vs: &mut VarStore) {
80    for e in vs.vars.values_mut() {
81        e.changed = false;
82    }
83}
84
85/// Number of declared variables.
86#[allow(dead_code)]
87pub fn vs_len(vs: &VarStore) -> usize {
88    vs.vars.len()
89}
90
91/// Names of all changed variables.
92#[allow(dead_code)]
93pub fn vs_changed_names(vs: &VarStore) -> Vec<String> {
94    vs.vars
95        .values()
96        .filter(|e| e.changed)
97        .map(|e| e.name.clone())
98        .collect()
99}
100
101/// Remove a variable.
102#[allow(dead_code)]
103pub fn vs_remove(vs: &mut VarStore, name: &str) {
104    vs.vars.remove(name);
105}
106
107/// Clear all variables.
108#[allow(dead_code)]
109pub fn vs_clear(vs: &mut VarStore) {
110    vs.vars.clear();
111}
112
113#[cfg(test)]
114mod tests {
115    use super::*;
116
117    #[test]
118    fn test_declare_and_get() {
119        let mut vs = new_var_store();
120        vs_declare(&mut vs, "mass", 1.0);
121        assert!((vs_get(&vs, "mass").expect("should succeed") - 1.0).abs() < 1e-6);
122    }
123
124    #[test]
125    fn test_set_and_get() {
126        let mut vs = new_var_store();
127        vs_set(&mut vs, "speed", 5.0);
128        assert!((vs_get(&vs, "speed").expect("should succeed") - 5.0).abs() < 1e-6);
129    }
130
131    #[test]
132    fn test_change_flag() {
133        let mut vs = new_var_store();
134        vs_declare(&mut vs, "x", 0.0);
135        vs_set(&mut vs, "x", 1.0);
136        assert!(vs_is_changed(&vs, "x"));
137    }
138
139    #[test]
140    fn test_flush_clears_change() {
141        let mut vs = new_var_store();
142        vs_set(&mut vs, "y", 3.0);
143        vs_flush(&mut vs);
144        assert!(!vs_is_changed(&vs, "y"));
145    }
146
147    #[test]
148    fn test_reset_to_default() {
149        let mut vs = new_var_store();
150        vs_declare(&mut vs, "z", 10.0);
151        vs_set(&mut vs, "z", 99.0);
152        vs_reset(&mut vs, "z");
153        assert!((vs_get(&vs, "z").expect("should succeed") - 10.0).abs() < 1e-6);
154    }
155
156    #[test]
157    fn test_changed_names() {
158        let mut vs = new_var_store();
159        vs_declare(&mut vs, "a", 0.0);
160        vs_declare(&mut vs, "b", 0.0);
161        vs_set(&mut vs, "a", 1.0);
162        let changed = vs_changed_names(&vs);
163        assert!(changed.contains(&"a".to_string()));
164        assert!(!changed.contains(&"b".to_string()));
165    }
166
167    #[test]
168    fn test_remove() {
169        let mut vs = new_var_store();
170        vs_declare(&mut vs, "k", 5.0);
171        vs_remove(&mut vs, "k");
172        assert!(vs_get(&vs, "k").is_none());
173    }
174
175    #[test]
176    fn test_len() {
177        let mut vs = new_var_store();
178        vs_declare(&mut vs, "a", 1.0);
179        vs_declare(&mut vs, "b", 2.0);
180        assert_eq!(vs_len(&vs), 2);
181    }
182
183    #[test]
184    fn test_clear() {
185        let mut vs = new_var_store();
186        vs_declare(&mut vs, "x", 1.0);
187        vs_clear(&mut vs);
188        assert_eq!(vs_len(&vs), 0);
189    }
190}