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