fusabi_host/
value.rs

1//! Fusabi Value type and basic operations.
2
3use std::collections::HashMap;
4use std::fmt;
5
6/// The type of a Fusabi value.
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
8pub enum ValueType {
9    /// Null/nil value.
10    Null,
11    /// Boolean value.
12    Bool,
13    /// Integer value.
14    Int,
15    /// Floating point value.
16    Float,
17    /// String value.
18    String,
19    /// List/array value.
20    List,
21    /// Map/object value.
22    Map,
23    /// Function value.
24    Function,
25    /// Bytes/binary data.
26    Bytes,
27    /// Error value.
28    Error,
29}
30
31impl fmt::Display for ValueType {
32    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33        match self {
34            ValueType::Null => write!(f, "null"),
35            ValueType::Bool => write!(f, "bool"),
36            ValueType::Int => write!(f, "int"),
37            ValueType::Float => write!(f, "float"),
38            ValueType::String => write!(f, "string"),
39            ValueType::List => write!(f, "list"),
40            ValueType::Map => write!(f, "map"),
41            ValueType::Function => write!(f, "function"),
42            ValueType::Bytes => write!(f, "bytes"),
43            ValueType::Error => write!(f, "error"),
44        }
45    }
46}
47
48/// A Fusabi runtime value.
49///
50/// This is a representation of values that can be passed between
51/// the host and Fusabi scripts.
52#[derive(Debug, Clone, PartialEq)]
53pub enum Value {
54    /// Null/nil value.
55    Null,
56    /// Boolean value.
57    Bool(bool),
58    /// Integer value (64-bit signed).
59    Int(i64),
60    /// Floating point value (64-bit).
61    Float(f64),
62    /// UTF-8 string value.
63    String(String),
64    /// Ordered list of values.
65    List(Vec<Value>),
66    /// Key-value map (string keys).
67    Map(HashMap<String, Value>),
68    /// Opaque function reference (not directly usable by host).
69    Function(FunctionRef),
70    /// Binary data.
71    Bytes(Vec<u8>),
72    /// Error value with message.
73    Error(String),
74}
75
76/// An opaque reference to a Fusabi function.
77#[derive(Debug, Clone, PartialEq)]
78pub struct FunctionRef {
79    /// Internal identifier.
80    pub(crate) id: u64,
81    /// Function name if known.
82    pub(crate) name: Option<String>,
83}
84
85impl FunctionRef {
86    /// Get the function name if available.
87    pub fn name(&self) -> Option<&str> {
88        self.name.as_deref()
89    }
90}
91
92impl Value {
93    /// Get the type of this value.
94    pub fn value_type(&self) -> ValueType {
95        match self {
96            Value::Null => ValueType::Null,
97            Value::Bool(_) => ValueType::Bool,
98            Value::Int(_) => ValueType::Int,
99            Value::Float(_) => ValueType::Float,
100            Value::String(_) => ValueType::String,
101            Value::List(_) => ValueType::List,
102            Value::Map(_) => ValueType::Map,
103            Value::Function(_) => ValueType::Function,
104            Value::Bytes(_) => ValueType::Bytes,
105            Value::Error(_) => ValueType::Error,
106        }
107    }
108
109    /// Returns true if this is a null value.
110    pub fn is_null(&self) -> bool {
111        matches!(self, Value::Null)
112    }
113
114    /// Returns true if this is an error value.
115    pub fn is_error(&self) -> bool {
116        matches!(self, Value::Error(_))
117    }
118
119    /// Try to get as a bool.
120    pub fn as_bool(&self) -> Option<bool> {
121        match self {
122            Value::Bool(b) => Some(*b),
123            _ => None,
124        }
125    }
126
127    /// Try to get as an integer.
128    pub fn as_int(&self) -> Option<i64> {
129        match self {
130            Value::Int(i) => Some(*i),
131            _ => None,
132        }
133    }
134
135    /// Try to get as a float.
136    pub fn as_float(&self) -> Option<f64> {
137        match self {
138            Value::Float(f) => Some(*f),
139            Value::Int(i) => Some(*i as f64),
140            _ => None,
141        }
142    }
143
144    /// Try to get as a string.
145    pub fn as_str(&self) -> Option<&str> {
146        match self {
147            Value::String(s) => Some(s.as_str()),
148            _ => None,
149        }
150    }
151
152    /// Try to get as a list.
153    pub fn as_list(&self) -> Option<&[Value]> {
154        match self {
155            Value::List(l) => Some(l.as_slice()),
156            _ => None,
157        }
158    }
159
160    /// Try to get as a map.
161    pub fn as_map(&self) -> Option<&HashMap<String, Value>> {
162        match self {
163            Value::Map(m) => Some(m),
164            _ => None,
165        }
166    }
167
168    /// Try to get as bytes.
169    pub fn as_bytes(&self) -> Option<&[u8]> {
170        match self {
171            Value::Bytes(b) => Some(b.as_slice()),
172            _ => None,
173        }
174    }
175
176    /// Get the error message if this is an error value.
177    pub fn as_error(&self) -> Option<&str> {
178        match self {
179            Value::Error(e) => Some(e.as_str()),
180            _ => None,
181        }
182    }
183
184    /// Create a null value.
185    pub fn null() -> Self {
186        Value::Null
187    }
188
189    /// Create an error value.
190    pub fn error(msg: impl Into<String>) -> Self {
191        Value::Error(msg.into())
192    }
193}
194
195impl Default for Value {
196    fn default() -> Self {
197        Value::Null
198    }
199}
200
201impl fmt::Display for Value {
202    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203        match self {
204            Value::Null => write!(f, "null"),
205            Value::Bool(b) => write!(f, "{}", b),
206            Value::Int(i) => write!(f, "{}", i),
207            Value::Float(fl) => write!(f, "{}", fl),
208            Value::String(s) => write!(f, "\"{}\"", s),
209            Value::List(l) => {
210                write!(f, "[")?;
211                for (i, v) in l.iter().enumerate() {
212                    if i > 0 {
213                        write!(f, ", ")?;
214                    }
215                    write!(f, "{}", v)?;
216                }
217                write!(f, "]")
218            }
219            Value::Map(m) => {
220                write!(f, "{{")?;
221                for (i, (k, v)) in m.iter().enumerate() {
222                    if i > 0 {
223                        write!(f, ", ")?;
224                    }
225                    write!(f, "\"{}\": {}", k, v)?;
226                }
227                write!(f, "}}")
228            }
229            Value::Function(fr) => {
230                if let Some(name) = &fr.name {
231                    write!(f, "<function {}>", name)
232                } else {
233                    write!(f, "<function>")
234                }
235            }
236            Value::Bytes(b) => write!(f, "<bytes len={}>", b.len()),
237            Value::Error(e) => write!(f, "<error: {}>", e),
238        }
239    }
240}
241
242// Conversion implementations
243impl From<bool> for Value {
244    fn from(b: bool) -> Self {
245        Value::Bool(b)
246    }
247}
248
249impl From<i64> for Value {
250    fn from(i: i64) -> Self {
251        Value::Int(i)
252    }
253}
254
255impl From<i32> for Value {
256    fn from(i: i32) -> Self {
257        Value::Int(i as i64)
258    }
259}
260
261impl From<f64> for Value {
262    fn from(f: f64) -> Self {
263        Value::Float(f)
264    }
265}
266
267impl From<f32> for Value {
268    fn from(f: f32) -> Self {
269        Value::Float(f as f64)
270    }
271}
272
273impl From<String> for Value {
274    fn from(s: String) -> Self {
275        Value::String(s)
276    }
277}
278
279impl From<&str> for Value {
280    fn from(s: &str) -> Self {
281        Value::String(s.to_string())
282    }
283}
284
285// Note: Vec<Value> -> Value is handled by the generic impl<T: IntoValue> From<Vec<T>> for Value
286// in convert.rs, since Value implements IntoValue
287
288impl From<HashMap<String, Value>> for Value {
289    fn from(m: HashMap<String, Value>) -> Self {
290        Value::Map(m)
291    }
292}
293
294impl From<Vec<u8>> for Value {
295    fn from(b: Vec<u8>) -> Self {
296        Value::Bytes(b)
297    }
298}
299
300impl<T: Into<Value>> From<Option<T>> for Value {
301    fn from(opt: Option<T>) -> Self {
302        match opt {
303            Some(v) => v.into(),
304            None => Value::Null,
305        }
306    }
307}
308
309#[cfg(test)]
310mod tests {
311    use super::*;
312
313    #[test]
314    fn test_value_types() {
315        assert_eq!(Value::Null.value_type(), ValueType::Null);
316        assert_eq!(Value::Bool(true).value_type(), ValueType::Bool);
317        assert_eq!(Value::Int(42).value_type(), ValueType::Int);
318        assert_eq!(Value::Float(3.14).value_type(), ValueType::Float);
319        assert_eq!(Value::String("hello".into()).value_type(), ValueType::String);
320        assert_eq!(Value::List(vec![]).value_type(), ValueType::List);
321        assert_eq!(Value::Map(HashMap::new()).value_type(), ValueType::Map);
322    }
323
324    #[test]
325    fn test_value_accessors() {
326        assert!(Value::Null.is_null());
327        assert!(!Value::Bool(true).is_null());
328
329        assert_eq!(Value::Bool(true).as_bool(), Some(true));
330        assert_eq!(Value::Int(42).as_bool(), None);
331
332        assert_eq!(Value::Int(42).as_int(), Some(42));
333        assert_eq!(Value::Float(3.14).as_float(), Some(3.14));
334        assert_eq!(Value::Int(42).as_float(), Some(42.0));
335
336        assert_eq!(Value::String("test".into()).as_str(), Some("test"));
337    }
338
339    #[test]
340    fn test_value_from_impls() {
341        let v: Value = true.into();
342        assert_eq!(v, Value::Bool(true));
343
344        let v: Value = 42i64.into();
345        assert_eq!(v, Value::Int(42));
346
347        let v: Value = "hello".into();
348        assert_eq!(v, Value::String("hello".into()));
349
350        let v: Value = None::<i64>.into();
351        assert!(v.is_null());
352
353        let v: Value = Some(42i64).into();
354        assert_eq!(v, Value::Int(42));
355    }
356
357    #[test]
358    fn test_value_display() {
359        assert_eq!(format!("{}", Value::Null), "null");
360        assert_eq!(format!("{}", Value::Bool(true)), "true");
361        assert_eq!(format!("{}", Value::Int(42)), "42");
362        assert_eq!(format!("{}", Value::String("test".into())), "\"test\"");
363    }
364}