Skip to main content

ion_core/
value.rs

1use indexmap::IndexMap;
2use serde_json;
3use std::collections::HashMap;
4use std::fmt;
5use std::sync::atomic::{AtomicU64, Ordering};
6use std::sync::{Arc, Mutex};
7
8use crate::ast::{Param, Stmt};
9#[cfg(feature = "vm")]
10use crate::bytecode::Chunk;
11
12static NEXT_FN_ID: AtomicU64 = AtomicU64::new(1);
13
14/// Runtime value representation.
15#[derive(Debug, Clone)]
16pub enum Value {
17    Int(i64),
18    Float(f64),
19    Bool(bool),
20    Str(String),
21    Bytes(Vec<u8>),
22    List(Vec<Value>),
23    Dict(IndexMap<String, Value>),
24    Tuple(Vec<Value>),
25    Option(Option<Box<Value>>),
26    Result(Result<Box<Value>, Box<Value>>),
27    Fn(IonFn),
28    BuiltinFn(String, BuiltinFn),
29    /// Closure-backed builtin. Unlike `BuiltinFn` (a bare `fn` pointer),
30    /// this variant can capture host-side state — e.g. a
31    /// `tokio::runtime::Handle` for async host calls, a database
32    /// connection pool, or shared counters.
33    ///
34    /// Register via `Engine::register_closure`.
35    BuiltinClosure(String, BuiltinClosureFn),
36    /// Ordered set of unique values
37    Set(Vec<Value>),
38    /// Host-injected struct: `TypeName { field: val, ... }`
39    HostStruct {
40        type_name: String,
41        fields: IndexMap<String, Value>,
42    },
43    /// Host-injected enum variant: `EnumName::Variant` or `EnumName::Variant(data)`
44    HostEnum {
45        enum_name: String,
46        variant: String,
47        data: Vec<Value>,
48    },
49    /// Async task handle (concurrency feature)
50    #[cfg(feature = "concurrency")]
51    Task(std::sync::Arc<dyn crate::async_rt::TaskHandle>),
52    /// Channel sender/receiver pair
53    #[cfg(feature = "concurrency")]
54    Channel(crate::async_rt::ChannelEnd),
55    /// Shared mutable reference cell for closure state
56    Cell(Arc<Mutex<Value>>),
57    /// Lazy integer range (start..end or start..=end)
58    Range {
59        start: i64,
60        end: i64,
61        inclusive: bool,
62    },
63    Unit,
64}
65
66/// A function value.
67#[derive(Debug, Clone)]
68pub struct IonFn {
69    pub fn_id: u64,
70    pub name: String,
71    pub params: Vec<Param>,
72    pub body: Vec<Stmt>,
73    /// Captured environment for closures
74    pub captures: HashMap<String, Value>,
75}
76
77impl IonFn {
78    pub fn new(
79        name: String,
80        params: Vec<Param>,
81        body: Vec<Stmt>,
82        captures: HashMap<String, Value>,
83    ) -> Self {
84        Self {
85            fn_id: NEXT_FN_ID.fetch_add(1, Ordering::Relaxed),
86            name,
87            params,
88            body,
89            captures,
90        }
91    }
92}
93
94/// Precompiled function chunk, keyed by fn_id.
95#[cfg(feature = "vm")]
96pub type FnChunkCache = HashMap<u64, Chunk>;
97
98/// A built-in function: Rust-side callback.
99pub type BuiltinFn = fn(&[Value]) -> Result<Value, String>;
100
101/// Wrapper around a closure-backed builtin so `Value` can still derive
102/// `Debug`. `dyn Fn` doesn't implement `Debug`; we print a placeholder.
103#[derive(Clone)]
104pub struct BuiltinClosureFn(
105    pub Arc<dyn Fn(&[Value]) -> Result<Value, String> + Send + Sync>,
106);
107
108impl fmt::Debug for BuiltinClosureFn {
109    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110        write!(f, "<closure>")
111    }
112}
113
114impl BuiltinClosureFn {
115    pub fn new<F>(func: F) -> Self
116    where
117        F: Fn(&[Value]) -> Result<Value, String> + Send + Sync + 'static,
118    {
119        Self(Arc::new(func))
120    }
121
122    pub fn call(&self, args: &[Value]) -> Result<Value, String> {
123        (self.0)(args)
124    }
125}
126
127impl Value {
128    pub fn type_name(&self) -> &'static str {
129        match self {
130            Value::Int(_) => ion_static_str!("int"),
131            Value::Float(_) => ion_static_str!("float"),
132            Value::Bool(_) => ion_static_str!("bool"),
133            Value::Str(_) => ion_static_str!("string"),
134            Value::Bytes(_) => ion_static_str!("bytes"),
135            Value::List(_) => ion_static_str!("list"),
136            Value::Dict(_) => ion_static_str!("dict"),
137            Value::Tuple(_) => ion_static_str!("tuple"),
138            Value::Set(_) => ion_static_str!("set"),
139            Value::Option(_) => ion_static_str!("Option"),
140            Value::Result(_) => ion_static_str!("Result"),
141            Value::Fn(_) => ion_static_str!("fn"),
142            Value::BuiltinFn(_, _) => ion_static_str!("builtin_fn"),
143            Value::BuiltinClosure(_, _) => ion_static_str!("builtin_fn"),
144            Value::HostStruct { .. } => ion_static_str!("struct"),
145            Value::HostEnum { .. } => ion_static_str!("enum"),
146            #[cfg(feature = "concurrency")]
147            Value::Task(_) => ion_static_str!("Task"),
148            #[cfg(feature = "concurrency")]
149            Value::Channel(_) => ion_static_str!("Channel"),
150            Value::Cell(_) => ion_static_str!("cell"),
151            Value::Range { .. } => ion_static_str!("range"),
152            Value::Unit => ion_static_str!("()"),
153        }
154    }
155
156    pub fn is_truthy(&self) -> bool {
157        match self {
158            Value::Bool(b) => *b,
159            Value::Int(n) => *n != 0,
160            Value::Bytes(b) => !b.is_empty(),
161            Value::Option(None) => false,
162            Value::Unit => false,
163            _ => true,
164        }
165    }
166
167    pub fn as_int(&self) -> Option<i64> {
168        match self {
169            Value::Int(n) => Some(*n),
170            _ => None,
171        }
172    }
173
174    pub fn as_float(&self) -> Option<f64> {
175        match self {
176            Value::Float(n) => Some(*n),
177            Value::Int(n) => Some(*n as f64),
178            _ => None,
179        }
180    }
181
182    pub fn as_str(&self) -> Option<&str> {
183        match self {
184            Value::Str(s) => Some(s),
185            _ => None,
186        }
187    }
188
189    pub fn as_bool(&self) -> Option<bool> {
190        match self {
191            Value::Bool(b) => Some(*b),
192            _ => None,
193        }
194    }
195
196    /// Materialize a range into a list of ints.
197    pub fn range_to_list(start: i64, end: i64, inclusive: bool) -> Vec<Value> {
198        if inclusive {
199            (start..=end).map(Value::Int).collect()
200        } else {
201            (start..end).map(Value::Int).collect()
202        }
203    }
204
205    /// Length of a range without materializing.
206    pub fn range_len(start: i64, end: i64, inclusive: bool) -> i64 {
207        if inclusive {
208            (end - start + 1).max(0)
209        } else {
210            (end - start).max(0)
211        }
212    }
213}
214
215impl fmt::Display for Value {
216    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
217        match self {
218            Value::Int(n) => write!(f, "{}", n),
219            Value::Float(n) => {
220                if *n == n.floor() && n.is_finite() {
221                    write!(f, "{:.1}", n)
222                } else {
223                    write!(f, "{}", n)
224                }
225            }
226            Value::Bool(b) => write!(f, "{}", b),
227            Value::Str(s) => write!(f, "{}", s),
228            Value::Bytes(bytes) => {
229                write!(f, "b\"")?;
230                for &b in bytes {
231                    match b {
232                        b'\\' => write!(f, "\\\\")?,
233                        b'"' => write!(f, "\\\"")?,
234                        b'\n' => write!(f, "\\n")?,
235                        b'\t' => write!(f, "\\t")?,
236                        b'\r' => write!(f, "\\r")?,
237                        0x20..=0x7e => write!(f, "{}", b as char)?,
238                        _ => write!(f, "\\x{:02x}", b)?,
239                    }
240                }
241                write!(f, "\"")
242            }
243            Value::List(items) => {
244                write!(f, "[")?;
245                for (i, item) in items.iter().enumerate() {
246                    if i > 0 {
247                        write!(f, ", ")?;
248                    }
249                    write!(f, "{}", item)?;
250                }
251                write!(f, "]")
252            }
253            Value::Dict(map) => {
254                write!(f, "#{{")?;
255                for (i, (k, v)) in map.iter().enumerate() {
256                    if i > 0 {
257                        write!(f, ", ")?;
258                    }
259                    write!(f, "\"{}\": {}", k, v)?;
260                }
261                write!(f, "}}")
262            }
263            Value::Tuple(items) => {
264                write!(f, "(")?;
265                for (i, item) in items.iter().enumerate() {
266                    if i > 0 {
267                        write!(f, ", ")?;
268                    }
269                    write!(f, "{}", item)?;
270                }
271                if items.len() == 1 {
272                    write!(f, ",")?;
273                }
274                write!(f, ")")
275            }
276            Value::Set(items) => {
277                write!(f, "set{{")?;
278                for (i, item) in items.iter().enumerate() {
279                    if i > 0 {
280                        write!(f, ", ")?;
281                    }
282                    write!(f, "{}", item)?;
283                }
284                write!(f, "}}")
285            }
286            Value::Option(opt) => match opt {
287                Some(v) => write!(f, "Some({})", v),
288                None => write!(f, "None"),
289            },
290            Value::Result(res) => match res {
291                Ok(v) => write!(f, "Ok({})", v),
292                Err(e) => write!(f, "Err({})", e),
293            },
294            Value::Fn(func) => write!(f, "<fn {}>", func.name),
295            Value::BuiltinFn(name, _) => write!(f, "<builtin {}>", name),
296            Value::BuiltinClosure(name, _) => write!(f, "<builtin {}>", name),
297            #[cfg(feature = "concurrency")]
298            Value::Task(_) => write!(f, "<Task>"),
299            #[cfg(feature = "concurrency")]
300            Value::Channel(ch) => match ch {
301                crate::async_rt::ChannelEnd::Sender(_) => write!(f, "<ChannelTx>"),
302                crate::async_rt::ChannelEnd::Receiver(_) => write!(f, "<ChannelRx>"),
303            },
304            Value::HostStruct { type_name, fields } => {
305                write!(f, "{} {{ ", type_name)?;
306                for (i, (k, v)) in fields.iter().enumerate() {
307                    if i > 0 {
308                        write!(f, ", ")?;
309                    }
310                    write!(f, "{}: {}", k, v)?;
311                }
312                write!(f, " }}")
313            }
314            Value::HostEnum {
315                enum_name,
316                variant,
317                data,
318            } => {
319                write!(f, "{}::{}", enum_name, variant)?;
320                if !data.is_empty() {
321                    write!(f, "(")?;
322                    for (i, v) in data.iter().enumerate() {
323                        if i > 0 {
324                            write!(f, ", ")?;
325                        }
326                        write!(f, "{}", v)?;
327                    }
328                    write!(f, ")")?;
329                }
330                Ok(())
331            }
332            Value::Cell(cell) => {
333                let inner = cell.lock().unwrap();
334                write!(f, "cell({})", *inner)
335            }
336            Value::Range {
337                start,
338                end,
339                inclusive,
340            } => {
341                if *inclusive {
342                    write!(f, "{}..={}", start, end)
343                } else {
344                    write!(f, "{}..{}", start, end)
345                }
346            }
347            Value::Unit => write!(f, "()"),
348        }
349    }
350}
351
352impl PartialEq for Value {
353    fn eq(&self, other: &Self) -> bool {
354        match (self, other) {
355            (Value::Int(a), Value::Int(b)) => a == b,
356            (Value::Float(a), Value::Float(b)) => a == b,
357            (Value::Int(a), Value::Float(b)) => (*a as f64) == *b,
358            (Value::Float(a), Value::Int(b)) => *a == (*b as f64),
359            (Value::Bool(a), Value::Bool(b)) => a == b,
360            (Value::Str(a), Value::Str(b)) => a == b,
361            (Value::Bytes(a), Value::Bytes(b)) => a == b,
362            (Value::List(a), Value::List(b)) => a == b,
363            (Value::Tuple(a), Value::Tuple(b)) => a == b,
364            (Value::Dict(a), Value::Dict(b)) => a == b,
365            (Value::Set(a), Value::Set(b)) => a.len() == b.len() && a.iter().all(|v| b.contains(v)),
366            (Value::Option(a), Value::Option(b)) => a == b,
367            (Value::Result(Ok(a)), Value::Result(Ok(b))) => a == b,
368            (Value::Result(Err(a)), Value::Result(Err(b))) => a == b,
369            (
370                Value::HostStruct {
371                    type_name: a_name,
372                    fields: a_fields,
373                },
374                Value::HostStruct {
375                    type_name: b_name,
376                    fields: b_fields,
377                },
378            ) => a_name == b_name && a_fields == b_fields,
379            (
380                Value::HostEnum {
381                    enum_name: a_en,
382                    variant: a_v,
383                    data: a_d,
384                },
385                Value::HostEnum {
386                    enum_name: b_en,
387                    variant: b_v,
388                    data: b_d,
389                },
390            ) => a_en == b_en && a_v == b_v && a_d == b_d,
391            (Value::Cell(a), Value::Cell(b)) => Arc::ptr_eq(a, b),
392            (
393                Value::Range {
394                    start: s1,
395                    end: e1,
396                    inclusive: i1,
397                },
398                Value::Range {
399                    start: s2,
400                    end: e2,
401                    inclusive: i2,
402                },
403            ) => s1 == s2 && e1 == e2 && i1 == i2,
404            (Value::Unit, Value::Unit) => true,
405            (Value::Option(None), Value::Unit) => false,
406            // Task and Channel are not comparable
407            _ => false,
408        }
409    }
410}
411
412// ---- Serde JSON conversions ----
413
414impl Value {
415    /// Convert an Ion Value to a serde_json::Value.
416    pub fn to_json(&self) -> serde_json::Value {
417        match self {
418            Value::Int(n) => serde_json::Value::Number((*n).into()),
419            Value::Float(n) => serde_json::Number::from_f64(*n)
420                .map(serde_json::Value::Number)
421                .unwrap_or(serde_json::Value::Null),
422            Value::Bool(b) => serde_json::Value::Bool(*b),
423            Value::Str(s) => serde_json::Value::String(s.clone()),
424            Value::List(items) => {
425                serde_json::Value::Array(items.iter().map(|v| v.to_json()).collect())
426            }
427            Value::Dict(map) => {
428                let obj: serde_json::Map<String, serde_json::Value> =
429                    map.iter().map(|(k, v)| (k.clone(), v.to_json())).collect();
430                serde_json::Value::Object(obj)
431            }
432            Value::Tuple(items) => {
433                serde_json::Value::Array(items.iter().map(|v| v.to_json()).collect())
434            }
435            Value::Set(items) => {
436                serde_json::Value::Array(items.iter().map(|v| v.to_json()).collect())
437            }
438            Value::Option(Some(v)) => v.to_json(),
439            Value::Option(None) | Value::Unit => serde_json::Value::Null,
440            Value::Result(Ok(v)) => v.to_json(),
441            Value::Result(Err(v)) => {
442                let mut map = serde_json::Map::new();
443                map.insert("error".to_string(), v.to_json());
444                serde_json::Value::Object(map)
445            }
446            Value::HostStruct { fields, .. } => {
447                let obj: serde_json::Map<String, serde_json::Value> = fields
448                    .iter()
449                    .map(|(k, v)| (k.clone(), v.to_json()))
450                    .collect();
451                serde_json::Value::Object(obj)
452            }
453            Value::HostEnum {
454                enum_name,
455                variant,
456                data,
457            } => {
458                let mut map = serde_json::Map::new();
459                map.insert(
460                    "_type".to_string(),
461                    serde_json::Value::String(format!("{}::{}", enum_name, variant)),
462                );
463                if !data.is_empty() {
464                    map.insert(
465                        "data".to_string(),
466                        serde_json::Value::Array(data.iter().map(|v| v.to_json()).collect()),
467                    );
468                }
469                serde_json::Value::Object(map)
470            }
471            #[cfg(feature = "concurrency")]
472            Value::Task(_) | Value::Channel(_) => serde_json::Value::Null,
473            Value::Cell(cell) => cell.lock().unwrap().to_json(),
474            Value::Bytes(b) => {
475                let hex: String = b.iter().map(|byte| format!("{:02x}", byte)).collect();
476                serde_json::Value::String(hex)
477            }
478            Value::Range {
479                start,
480                end,
481                inclusive,
482            } => serde_json::Value::Array(
483                Value::range_to_list(*start, *end, *inclusive)
484                    .iter()
485                    .map(|v| v.to_json())
486                    .collect(),
487            ),
488            Value::Fn(_) | Value::BuiltinFn(_, _) | Value::BuiltinClosure(_, _) => {
489                serde_json::Value::Null
490            }
491        }
492    }
493
494    /// Encode an Ion Value to MessagePack bytes.
495    #[cfg(feature = "msgpack")]
496    pub fn to_msgpack(&self) -> Result<Vec<u8>, String> {
497        let mp = self.to_msgpack_value();
498        let mut buf = Vec::new();
499        rmpv::encode::write_value(&mut buf, &mp)
500            .map_err(|e| format!("{}{}", ion_str!("msgpack_encode error: "), e))?;
501        Ok(buf)
502    }
503
504    /// Decode MessagePack bytes to an Ion Value.
505    #[cfg(feature = "msgpack")]
506    pub fn from_msgpack(data: &[u8]) -> Result<Value, String> {
507        let mut cursor = std::io::Cursor::new(data);
508        let mp = rmpv::decode::read_value(&mut cursor)
509            .map_err(|e| format!("{}{}", ion_str!("msgpack_decode error: "), e))?;
510        Ok(Self::from_msgpack_value(mp))
511    }
512
513    #[cfg(feature = "msgpack")]
514    fn to_msgpack_value(&self) -> rmpv::Value {
515        match self {
516            Value::Int(n) => rmpv::Value::Integer((*n).into()),
517            Value::Float(n) => rmpv::Value::F64(*n),
518            Value::Bool(b) => rmpv::Value::Boolean(*b),
519            Value::Str(s) => rmpv::Value::String(s.clone().into()),
520            Value::Bytes(b) => rmpv::Value::Binary(b.clone()),
521            Value::List(items) => {
522                rmpv::Value::Array(items.iter().map(|v| v.to_msgpack_value()).collect())
523            }
524            Value::Dict(map) => {
525                let pairs: Vec<(rmpv::Value, rmpv::Value)> = map
526                    .iter()
527                    .map(|(k, v)| (rmpv::Value::String(k.clone().into()), v.to_msgpack_value()))
528                    .collect();
529                rmpv::Value::Map(pairs)
530            }
531            Value::Tuple(items) => {
532                rmpv::Value::Array(items.iter().map(|v| v.to_msgpack_value()).collect())
533            }
534            Value::Set(items) => {
535                rmpv::Value::Array(items.iter().map(|v| v.to_msgpack_value()).collect())
536            }
537            Value::Option(Some(v)) => v.to_msgpack_value(),
538            Value::Option(None) | Value::Unit => rmpv::Value::Nil,
539            Value::Result(Ok(v)) => v.to_msgpack_value(),
540            Value::Result(Err(v)) => {
541                let pairs = vec![(rmpv::Value::String("error".into()), v.to_msgpack_value())];
542                rmpv::Value::Map(pairs)
543            }
544            Value::HostStruct { fields, .. } => {
545                let pairs: Vec<(rmpv::Value, rmpv::Value)> = fields
546                    .iter()
547                    .map(|(k, v)| (rmpv::Value::String(k.clone().into()), v.to_msgpack_value()))
548                    .collect();
549                rmpv::Value::Map(pairs)
550            }
551            Value::HostEnum {
552                enum_name,
553                variant,
554                data,
555            } => {
556                let mut pairs = vec![(
557                    rmpv::Value::String("_type".into()),
558                    rmpv::Value::String(format!("{}::{}", enum_name, variant).into()),
559                )];
560                if !data.is_empty() {
561                    pairs.push((
562                        rmpv::Value::String("data".into()),
563                        rmpv::Value::Array(data.iter().map(|v| v.to_msgpack_value()).collect()),
564                    ));
565                }
566                rmpv::Value::Map(pairs)
567            }
568            #[cfg(feature = "concurrency")]
569            Value::Task(_) | Value::Channel(_) => rmpv::Value::Nil,
570            Value::Cell(cell) => cell.lock().unwrap().to_msgpack_value(),
571            Value::Range {
572                start,
573                end,
574                inclusive,
575            } => rmpv::Value::Array(
576                Value::range_to_list(*start, *end, *inclusive)
577                    .iter()
578                    .map(|v| v.to_msgpack_value())
579                    .collect(),
580            ),
581            Value::Fn(_) | Value::BuiltinFn(_, _) | Value::BuiltinClosure(_, _) => {
582                rmpv::Value::Nil
583            }
584        }
585    }
586
587    #[cfg(feature = "msgpack")]
588    fn from_msgpack_value(mp: rmpv::Value) -> Value {
589        match mp {
590            rmpv::Value::Nil => Value::Option(None),
591            rmpv::Value::Boolean(b) => Value::Bool(b),
592            rmpv::Value::Integer(n) => {
593                if let Some(i) = n.as_i64() {
594                    Value::Int(i)
595                } else if let Some(u) = n.as_u64() {
596                    Value::Int(u as i64)
597                } else {
598                    Value::Int(0)
599                }
600            }
601            rmpv::Value::F32(f) => Value::Float(f as f64),
602            rmpv::Value::F64(f) => Value::Float(f),
603            rmpv::Value::String(s) => Value::Str(s.into_str().unwrap_or_default().to_string()),
604            rmpv::Value::Binary(b) => Value::Bytes(b),
605            rmpv::Value::Array(arr) => {
606                Value::List(arr.into_iter().map(Self::from_msgpack_value).collect())
607            }
608            rmpv::Value::Map(pairs) => {
609                let dict: IndexMap<String, Value> = pairs
610                    .into_iter()
611                    .filter_map(|(k, v)| {
612                        let key = match k {
613                            rmpv::Value::String(s) => s.into_str().map(|s| s.to_string()),
614                            _ => None,
615                        };
616                        key.map(|k| (k, Self::from_msgpack_value(v)))
617                    })
618                    .collect();
619                Value::Dict(dict)
620            }
621            rmpv::Value::Ext(_, data) => Value::Bytes(data),
622        }
623    }
624
625    /// Convert a serde_json::Value to an Ion Value.
626    pub fn from_json(json: serde_json::Value) -> Value {
627        match json {
628            serde_json::Value::Null => Value::Option(None),
629            serde_json::Value::Bool(b) => Value::Bool(b),
630            serde_json::Value::Number(n) => {
631                if let Some(i) = n.as_i64() {
632                    Value::Int(i)
633                } else if let Some(f) = n.as_f64() {
634                    Value::Float(f)
635                } else {
636                    Value::Int(0)
637                }
638            }
639            serde_json::Value::String(s) => Value::Str(s),
640            serde_json::Value::Array(arr) => {
641                Value::List(arr.into_iter().map(Value::from_json).collect())
642            }
643            serde_json::Value::Object(map) => {
644                let dict: IndexMap<String, Value> = map
645                    .into_iter()
646                    .map(|(k, v)| (k, Value::from_json(v)))
647                    .collect();
648                Value::Dict(dict)
649            }
650        }
651    }
652}