Skip to main content

ion_core/
value.rs

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