Skip to main content

oxihuman_core/
value_map.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Polymorphic value map: string key -> typed value variant.
6
7use std::collections::HashMap;
8
9/// Supported value types.
10#[allow(dead_code)]
11#[derive(Debug, Clone, PartialEq)]
12pub enum MapVal {
13    Float(f32),
14    Int(i64),
15    Bool(bool),
16    Text(String),
17    Bytes(Vec<u8>),
18}
19
20impl MapVal {
21    /// Return value type name.
22    #[allow(dead_code)]
23    pub fn type_name(&self) -> &'static str {
24        match self {
25            MapVal::Float(_) => "float",
26            MapVal::Int(_) => "int",
27            MapVal::Bool(_) => "bool",
28            MapVal::Text(_) => "text",
29            MapVal::Bytes(_) => "bytes",
30        }
31    }
32}
33
34/// Polymorphic value map.
35#[allow(dead_code)]
36#[derive(Debug, Clone, Default)]
37pub struct ValueMap {
38    inner: HashMap<String, MapVal>,
39}
40
41/// Create a new `ValueMap`.
42#[allow(dead_code)]
43pub fn new_value_map() -> ValueMap {
44    ValueMap::default()
45}
46
47/// Set a float value.
48#[allow(dead_code)]
49pub fn vm_set_float(vm: &mut ValueMap, key: &str, v: f32) {
50    vm.inner.insert(key.to_string(), MapVal::Float(v));
51}
52
53/// Set an int value.
54#[allow(dead_code)]
55pub fn vm_set_int(vm: &mut ValueMap, key: &str, v: i64) {
56    vm.inner.insert(key.to_string(), MapVal::Int(v));
57}
58
59/// Set a bool value.
60#[allow(dead_code)]
61pub fn vm_set_bool(vm: &mut ValueMap, key: &str, v: bool) {
62    vm.inner.insert(key.to_string(), MapVal::Bool(v));
63}
64
65/// Set a text value.
66#[allow(dead_code)]
67pub fn vm_set_text(vm: &mut ValueMap, key: &str, v: &str) {
68    vm.inner
69        .insert(key.to_string(), MapVal::Text(v.to_string()));
70}
71
72/// Get value by key.
73#[allow(dead_code)]
74pub fn vm_get<'a>(vm: &'a ValueMap, key: &str) -> Option<&'a MapVal> {
75    vm.inner.get(key)
76}
77
78/// Get float value.
79#[allow(dead_code)]
80pub fn vm_get_float(vm: &ValueMap, key: &str) -> Option<f32> {
81    vm.inner.get(key).and_then(|v| {
82        if let MapVal::Float(f) = v {
83            Some(*f)
84        } else {
85            None
86        }
87    })
88}
89
90/// Get int value.
91#[allow(dead_code)]
92pub fn vm_get_int(vm: &ValueMap, key: &str) -> Option<i64> {
93    vm.inner.get(key).and_then(|v| {
94        if let MapVal::Int(i) = v {
95            Some(*i)
96        } else {
97            None
98        }
99    })
100}
101
102/// Get bool value.
103#[allow(dead_code)]
104pub fn vm_get_bool(vm: &ValueMap, key: &str) -> Option<bool> {
105    vm.inner.get(key).and_then(|v| {
106        if let MapVal::Bool(b) = v {
107            Some(*b)
108        } else {
109            None
110        }
111    })
112}
113
114/// Remove a key.
115#[allow(dead_code)]
116pub fn vm_remove(vm: &mut ValueMap, key: &str) -> bool {
117    vm.inner.remove(key).is_some()
118}
119
120/// Whether a key exists.
121#[allow(dead_code)]
122pub fn vm_contains(vm: &ValueMap, key: &str) -> bool {
123    vm.inner.contains_key(key)
124}
125
126/// Number of entries.
127#[allow(dead_code)]
128pub fn vm_len(vm: &ValueMap) -> usize {
129    vm.inner.len()
130}
131
132/// Clear all entries.
133#[allow(dead_code)]
134pub fn vm_clear(vm: &mut ValueMap) {
135    vm.inner.clear();
136}
137
138#[cfg(test)]
139mod tests {
140    use super::*;
141
142    #[test]
143    fn test_set_and_get_float() {
144        let mut vm = new_value_map();
145        vm_set_float(&mut vm, "speed", 3.0);
146        assert!((vm_get_float(&vm, "speed").expect("should succeed") - 3.0).abs() < 1e-6);
147    }
148
149    #[test]
150    fn test_set_and_get_int() {
151        let mut vm = new_value_map();
152        vm_set_int(&mut vm, "count", 42);
153        assert_eq!(vm_get_int(&vm, "count"), Some(42));
154    }
155
156    #[test]
157    fn test_set_and_get_bool() {
158        let mut vm = new_value_map();
159        vm_set_bool(&mut vm, "active", true);
160        assert_eq!(vm_get_bool(&vm, "active"), Some(true));
161    }
162
163    #[test]
164    fn test_type_mismatch_returns_none() {
165        let mut vm = new_value_map();
166        vm_set_float(&mut vm, "x", 1.0);
167        assert!(vm_get_int(&vm, "x").is_none());
168    }
169
170    #[test]
171    fn test_remove() {
172        let mut vm = new_value_map();
173        vm_set_bool(&mut vm, "flag", false);
174        assert!(vm_remove(&mut vm, "flag"));
175        assert!(!vm_contains(&vm, "flag"));
176    }
177
178    #[test]
179    fn test_len() {
180        let mut vm = new_value_map();
181        vm_set_float(&mut vm, "a", 1.0);
182        vm_set_int(&mut vm, "b", 2);
183        assert_eq!(vm_len(&vm), 2);
184    }
185
186    #[test]
187    fn test_clear() {
188        let mut vm = new_value_map();
189        vm_set_bool(&mut vm, "x", true);
190        vm_clear(&mut vm);
191        assert_eq!(vm_len(&vm), 0);
192    }
193
194    #[test]
195    fn test_type_name() {
196        assert_eq!(MapVal::Float(1.0).type_name(), "float");
197        assert_eq!(MapVal::Bool(true).type_name(), "bool");
198    }
199
200    #[test]
201    fn test_text_value() {
202        let mut vm = new_value_map();
203        vm_set_text(&mut vm, "name", "Alice");
204        let val = vm_get(&vm, "name").expect("should succeed");
205        assert_eq!(*val, MapVal::Text("Alice".to_string()));
206    }
207}