Skip to main content

rexlang_core/
json.rs

1use crate::{EngineError, Heap, Pointer};
2use rexlang_ast::expr::{Symbol, sym};
3use rexlang_typesystem::{AdtDecl, BuiltinTypeId, Type, TypeKind, TypeSystem};
4use serde_json::{Map, Number, Value};
5use std::collections::BTreeMap;
6
7#[derive(Clone, Debug)]
8pub struct EnumPatch {
9    /// Variant name (kept as `enum_name` for backward compatibility).
10    pub enum_name: String,
11    pub discriminant: i64,
12}
13
14/// Options for handling enum JSON encodings.
15///
16/// Some enums are encoded as integers, while others are encoded as strings, and the type
17/// information alone is not always sufficient to recover which encoding should be used. For this
18/// reason, callers can whitelist ADT names that should be represented as integers in JSON. This
19/// applies to both encoding and decoding.
20///
21/// Additionally, older generated type metadata may have missing/incorrect discriminants. To keep
22/// JSON conversion behavior aligned with external serde-based encodings, callers can provide
23/// per-variant integer patches via [`EnumPatch`].
24#[derive(Clone, Default, Debug)]
25pub struct JsonOptions {
26    pub int_enums: BTreeMap<String, Vec<EnumPatch>>,
27}
28
29impl JsonOptions {
30    /// Register the name of an ADT enum that should be encoded as integers, rather than strings.
31    ///
32    /// This only has an effect for unit variants.
33    pub fn add_int_enum(&mut self, name: &str) {
34        self.int_enums.insert(name.to_string(), vec![]);
35    }
36
37    /// Register an ADT enum for integer encoding, with explicit discriminant patches.
38    ///
39    /// This only has an effect for unit variants.
40    pub fn add_int_enum_with_patches(&mut self, name: &str, patches: Vec<EnumPatch>) {
41        self.int_enums.insert(name.to_string(), patches);
42    }
43}
44
45/// Convert a JSON [`Value`] into a typed Rex runtime value (`Pointer`) allocated on `heap`.
46///
47/// The conversion is compatible with serde-style JSON representations for Rex-compatible Rust
48/// types, but operates entirely via runtime Rex types (`Type`) plus the evaluator heap.
49///
50/// This is intended for cases where Rust compile-time types are not available and only runtime
51/// type information exists.
52///
53/// Important behavior:
54/// - JSON arrays targeting `Array a` become Rex runtime arrays (`Value::Array`).
55/// - JSON arrays targeting tuple types become tuples.
56/// - JSON arrays targeting `List a` become list ADTs (`Cons`/`Empty`).
57pub fn json_to_rex(
58    heap: &Heap,
59    json: &Value,
60    want: &Type,
61    ts: &TypeSystem,
62    opts: &JsonOptions,
63) -> Result<Pointer, EngineError> {
64    match want.as_ref() {
65        TypeKind::Var(tv) => Err(error(format!(
66            "cannot decode JSON into unresolved type variable t{}",
67            tv.id
68        ))),
69        TypeKind::Con(con) => json_to_pointer_for_con(heap, json, &con.name, &[], ts, opts),
70        TypeKind::App(_, _) => {
71            let (head, args) = decompose_type_app(want);
72            if let TypeKind::Con(con) = head.as_ref() {
73                json_to_pointer_for_con(heap, json, &con.name, &args, ts, opts)
74            } else {
75                Err(error(format!("unsupported applied type {}", want)))
76            }
77        }
78        TypeKind::Fun(_, _) => Err(error("cannot decode JSON into function type".to_string())),
79        TypeKind::Tuple(items) => match json {
80            Value::Array(values) if values.len() == items.len() => {
81                let mut out = Vec::with_capacity(values.len());
82                for (value, item_ty) in values.iter().zip(items.iter()) {
83                    out.push(json_to_rex(heap, value, item_ty, ts, opts)?);
84                }
85                heap.alloc_tuple(out)
86            }
87            _ => Err(type_mismatch_json(json, want)),
88        },
89        TypeKind::Record(fields) => match json {
90            Value::Object(entries) => {
91                let mut out = BTreeMap::new();
92                for (k, t) in fields {
93                    let j = entries.get(k.as_ref()).unwrap_or(&Value::Null);
94                    out.insert(k.clone(), json_to_rex(heap, j, t, ts, opts)?);
95                }
96                heap.alloc_dict(out)
97            }
98            _ => Err(type_mismatch_json(json, want)),
99        },
100    }
101}
102
103/// Convert a typed Rex runtime value (`Pointer`) to a JSON [`Value`].
104///
105/// The conversion is compatible with serde-style JSON representations for Rex-compatible Rust
106/// types, but operates on runtime values stored in the evaluator heap.
107pub fn rex_to_json(
108    heap: &Heap,
109    pointer: &Pointer,
110    want: &Type,
111    ts: &TypeSystem,
112    opts: &JsonOptions,
113) -> Result<Value, EngineError> {
114    match want.as_ref() {
115        TypeKind::Var(tv) => Err(error(format!(
116            "cannot encode unresolved type variable t{} to JSON",
117            tv.id
118        ))),
119        TypeKind::Con(con) => pointer_to_json_for_con(heap, pointer, &con.name, &[], ts, opts),
120        TypeKind::App(_, _) => {
121            let (head, args) = decompose_type_app(want);
122            if let TypeKind::Con(con) = head.as_ref() {
123                pointer_to_json_for_con(heap, pointer, &con.name, &args, ts, opts)
124            } else {
125                Err(error(format!("unsupported applied type {}", want)))
126            }
127        }
128        TypeKind::Fun(_, _) => Err(error("cannot encode function value to JSON".to_string())),
129        TypeKind::Tuple(item_types) => {
130            let values = heap.pointer_as_tuple(pointer)?;
131            if values.len() != item_types.len() {
132                return Err(type_mismatch_pointer(heap, pointer, want));
133            }
134            let mut out = Vec::with_capacity(values.len());
135            for (p, t) in values.iter().zip(item_types.iter()) {
136                out.push(rex_to_json(heap, p, t, ts, opts)?);
137            }
138            Ok(Value::Array(out))
139        }
140        TypeKind::Record(fields) => {
141            let entries = heap.pointer_as_dict(pointer)?;
142            if entries.len() != fields.len() {
143                return Err(type_mismatch_pointer(heap, pointer, want));
144            }
145            let mut out = Map::new();
146            for (k, t) in fields {
147                let p = entries
148                    .get(k)
149                    .ok_or_else(|| type_mismatch_pointer(heap, pointer, want))?;
150                out.insert(k.to_string(), rex_to_json(heap, p, t, ts, opts)?);
151            }
152            Ok(Value::Object(out))
153        }
154    }
155}
156
157fn json_to_pointer_for_con(
158    heap: &Heap,
159    json: &Value,
160    con_name: &Symbol,
161    con_args: &[Type],
162    ts: &TypeSystem,
163    opts: &JsonOptions,
164) -> Result<Pointer, EngineError> {
165    match (con_name.as_ref(), con_args) {
166        ("bool", []) => match json {
167            Value::Bool(v) => heap.alloc_bool(*v),
168            _ => Err(type_mismatch_json(
169                json,
170                &Type::builtin(BuiltinTypeId::Bool),
171            )),
172        },
173
174        ("u8", []) => {
175            let v = json_u64(json)?;
176            u8::try_from(v)
177                .map_err(|_| error(format!("value {} out of range for u8", v)))
178                .and_then(|x| heap.alloc_u8(x))
179        }
180        ("u16", []) => {
181            let v = json_u64(json)?;
182            u16::try_from(v)
183                .map_err(|_| error(format!("value {} out of range for u16", v)))
184                .and_then(|x| heap.alloc_u16(x))
185        }
186        ("u32", []) => {
187            let v = json_u64(json)?;
188            u32::try_from(v)
189                .map_err(|_| error(format!("value {} out of range for u32", v)))
190                .and_then(|x| heap.alloc_u32(x))
191        }
192        ("u64", []) => heap.alloc_u64(json_u64(json)?),
193
194        ("i8", []) => {
195            let v = json_i64(json)?;
196            i8::try_from(v)
197                .map_err(|_| error(format!("value {} out of range for i8", v)))
198                .and_then(|x| heap.alloc_i8(x))
199        }
200        ("i16", []) => {
201            let v = json_i64(json)?;
202            i16::try_from(v)
203                .map_err(|_| error(format!("value {} out of range for i16", v)))
204                .and_then(|x| heap.alloc_i16(x))
205        }
206        ("i32", []) => {
207            let v = json_i64(json)?;
208            i32::try_from(v)
209                .map_err(|_| error(format!("value {} out of range for i32", v)))
210                .and_then(|x| heap.alloc_i32(x))
211        }
212        ("i64", []) => heap.alloc_i64(json_i64(json)?),
213
214        ("f32", []) => heap.alloc_f32(json_f64(json)? as f32),
215        ("f64", []) => heap.alloc_f64(json_f64(json)?),
216
217        ("string", []) => match json {
218            Value::String(s) => heap.alloc_string(s.clone()),
219            _ => Err(type_mismatch_json(
220                json,
221                &Type::builtin(BuiltinTypeId::String),
222            )),
223        },
224        ("uuid", []) => {
225            let u = serde_json::from_value(json.clone())
226                .map_err(|e| error(format!("invalid uuid JSON: {e}")))?;
227            heap.alloc_uuid(u)
228        }
229        ("datetime", []) => {
230            let dt = serde_json::from_value(json.clone())
231                .map_err(|e| error(format!("invalid datetime JSON: {e}")))?;
232            heap.alloc_datetime(dt)
233        }
234
235        ("Option", [inner]) => match json {
236            Value::Null => heap.alloc_adt(sym("None"), vec![]),
237            _ => {
238                let inner_ptr = json_to_rex(heap, json, inner, ts, opts)?;
239                heap.alloc_adt(sym("Some"), vec![inner_ptr])
240            }
241        },
242
243        // Internal argument order is Result err ok.
244        ("Result", [err_t, ok_t]) => match json {
245            Value::Object(obj) if obj.len() == 1 => {
246                if let Some(v) = obj.get("Ok") {
247                    let p = json_to_rex(heap, v, ok_t, ts, opts)?;
248                    heap.alloc_adt(sym("Ok"), vec![p])
249                } else if let Some(v) = obj.get("Err") {
250                    let p = json_to_rex(heap, v, err_t, ts, opts)?;
251                    heap.alloc_adt(sym("Err"), vec![p])
252                } else {
253                    Err(error(format!(
254                        "expected {{Ok:..}} or {{Err:..}}, got {}",
255                        json
256                    )))
257                }
258            }
259            _ => Err(error(format!("expected result object JSON, got {}", json))),
260        },
261
262        // IMPORTANT: JSON arrays map to runtime arrays, not lists.
263        ("Array", [elem_t]) => match json {
264            Value::Array(items) => {
265                let mut out = Vec::with_capacity(items.len());
266                for item in items {
267                    out.push(json_to_rex(heap, item, elem_t, ts, opts)?);
268                }
269                heap.alloc_array(out)
270            }
271            _ => Err(error(format!(
272                "expected array JSON for Array, got {}",
273                json
274            ))),
275        },
276
277        ("List", [elem_t]) => match json {
278            Value::Array(items) => {
279                let mut out = Vec::with_capacity(items.len());
280                for item in items {
281                    out.push(json_to_rex(heap, item, elem_t, ts, opts)?);
282                }
283                let mut list = heap.alloc_adt(sym("Empty"), vec![])?;
284                for p in out.into_iter().rev() {
285                    list = heap.alloc_adt(sym("Cons"), vec![p, list])?;
286                }
287                Ok(list)
288            }
289            _ => Err(error(format!("expected array JSON for List, got {}", json))),
290        },
291
292        ("Dict", [elem_t]) => match json {
293            Value::Object(obj) => {
294                let mut out = BTreeMap::new();
295                for (k, v) in obj {
296                    out.insert(sym(k), json_to_rex(heap, v, elem_t, ts, opts)?);
297                }
298                heap.alloc_dict(out)
299            }
300            _ => Err(error(format!(
301                "expected object JSON for Dict, got {}",
302                json
303            ))),
304        },
305
306        _ => json_to_pointer_for_adt(heap, json, con_name, con_args, ts, opts),
307    }
308}
309
310fn pointer_to_json_for_con(
311    heap: &Heap,
312    pointer: &Pointer,
313    con_name: &Symbol,
314    con_args: &[Type],
315    ts: &TypeSystem,
316    opts: &JsonOptions,
317) -> Result<Value, EngineError> {
318    match (con_name.as_ref(), con_args) {
319        ("bool", []) => Ok(Value::Bool(heap.pointer_as_bool(pointer)?)),
320        ("u8", []) => Ok(Value::Number(
321            u64::from(heap.pointer_as_u8(pointer)?).into(),
322        )),
323        ("u16", []) => Ok(Value::Number(
324            u64::from(heap.pointer_as_u16(pointer)?).into(),
325        )),
326        ("u32", []) => Ok(Value::Number(
327            u64::from(heap.pointer_as_u32(pointer)?).into(),
328        )),
329        ("u64", []) => Ok(Value::Number(heap.pointer_as_u64(pointer)?.into())),
330        ("i8", []) => Ok(Value::Number(
331            i64::from(heap.pointer_as_i8(pointer)?).into(),
332        )),
333        ("i16", []) => Ok(Value::Number(
334            i64::from(heap.pointer_as_i16(pointer)?).into(),
335        )),
336        ("i32", []) => Ok(Value::Number(
337            i64::from(heap.pointer_as_i32(pointer)?).into(),
338        )),
339        ("i64", []) => Ok(Value::Number(heap.pointer_as_i64(pointer)?.into())),
340        ("f32", []) => Number::from_f64(f64::from(heap.pointer_as_f32(pointer)?))
341            .map(Value::Number)
342            .ok_or_else(|| error("invalid f32 value for JSON".to_string())),
343        ("f64", []) => Number::from_f64(heap.pointer_as_f64(pointer)?)
344            .map(Value::Number)
345            .ok_or_else(|| error("invalid f64 value for JSON".to_string())),
346        ("string", []) => Ok(Value::String(heap.pointer_as_string(pointer)?)),
347        ("uuid", []) => serde_json::to_value(heap.pointer_as_uuid(pointer)?)
348            .map_err(|e| error(format!("failed to serialize uuid: {e}"))),
349        ("datetime", []) => serde_json::to_value(heap.pointer_as_datetime(pointer)?)
350            .map_err(|e| error(format!("failed to serialize datetime: {e}"))),
351
352        ("Option", [inner_t]) => {
353            let (tag, args) = heap.pointer_as_adt(pointer)?;
354            match (tag.as_ref(), args.as_slice()) {
355                ("None", []) => Ok(Value::Null),
356                ("Some", [x]) => rex_to_json(heap, x, inner_t, ts, opts),
357                _ => Err(type_mismatch_pointer(
358                    heap,
359                    pointer,
360                    &Type::app(Type::builtin(BuiltinTypeId::Option), inner_t.clone()),
361                )),
362            }
363        }
364
365        // Internal argument order is Result err ok.
366        ("Result", [err_t, ok_t]) => {
367            let (tag, args) = heap.pointer_as_adt(pointer)?;
368            match (tag.as_ref(), args.as_slice()) {
369                ("Ok", [x]) => {
370                    let mut out = Map::new();
371                    out.insert("Ok".to_string(), rex_to_json(heap, x, ok_t, ts, opts)?);
372                    Ok(Value::Object(out))
373                }
374                ("Err", [x]) => {
375                    let mut out = Map::new();
376                    out.insert("Err".to_string(), rex_to_json(heap, x, err_t, ts, opts)?);
377                    Ok(Value::Object(out))
378                }
379                _ => Err(type_mismatch_pointer(
380                    heap,
381                    pointer,
382                    &Type::app(
383                        Type::app(Type::builtin(BuiltinTypeId::Result), err_t.clone()),
384                        ok_t.clone(),
385                    ),
386                )),
387            }
388        }
389
390        ("Array", [elem_t]) => {
391            let items = heap.pointer_as_array(pointer)?;
392            let mut out = Vec::with_capacity(items.len());
393            for item in &items {
394                out.push(rex_to_json(heap, item, elem_t, ts, opts)?);
395            }
396            Ok(Value::Array(out))
397        }
398
399        ("List", [elem_t]) => {
400            let items = list_to_vec(heap, pointer)?;
401            let mut out = Vec::with_capacity(items.len());
402            for item in &items {
403                out.push(rex_to_json(heap, item, elem_t, ts, opts)?);
404            }
405            Ok(Value::Array(out))
406        }
407
408        ("Dict", [elem_t]) => {
409            let entries = heap.pointer_as_dict(pointer)?;
410            let mut out = Map::new();
411            for (k, v) in &entries {
412                out.insert(k.to_string(), rex_to_json(heap, v, elem_t, ts, opts)?);
413            }
414            Ok(Value::Object(out))
415        }
416
417        _ => pointer_to_json_for_adt(heap, pointer, con_name, con_args, ts, opts),
418    }
419}
420
421fn json_to_pointer_for_adt(
422    heap: &Heap,
423    json: &Value,
424    adt_name: &Symbol,
425    type_args: &[Type],
426    ts: &TypeSystem,
427    opts: &JsonOptions,
428) -> Result<Pointer, EngineError> {
429    let adt = ts
430        .adts
431        .get(adt_name)
432        .ok_or_else(|| error(format!("unknown ADT `{}`", adt_name)))?;
433    let subst = adt_subst(adt, type_args)?;
434
435    if adt.variants.len() == 1 {
436        let v = &adt.variants[0];
437        let arg_types = instantiate_types(&v.args, &subst);
438        return decode_direct_variant(heap, json, &v.name, &arg_types, ts, opts);
439    }
440
441    let enum_name = adt.name.to_string();
442    let enum_like = adt.variants.iter().all(|v| v.args.is_empty());
443    if enum_like {
444        if opts.int_enums.contains_key(&enum_name) {
445            if let Value::Number(n) = json
446                && let Some(i) = n.as_i64()
447            {
448                for (idx, v) in adt.variants.iter().enumerate() {
449                    if variant_discriminant(&enum_name, &v.name, idx, opts) == Some(i) {
450                        return heap.alloc_adt(v.name.clone(), vec![]);
451                    }
452                }
453            }
454            return Err(error(format!(
455                "expected integer enum JSON for `{}`, got {}",
456                enum_name, json
457            )));
458        }
459        if let Value::String(tag) = json {
460            if let Some(v) = adt.variants.iter().find(|v| v.name.as_ref() == tag) {
461                return heap.alloc_adt(v.name.clone(), vec![]);
462            }
463            return Err(error(format!(
464                "unknown enum tag `{}` for `{}`",
465                tag, enum_name
466            )));
467        }
468        return Err(error(format!(
469            "expected enum string JSON for `{}`, got {}",
470            enum_name, json
471        )));
472    }
473
474    if let Value::String(tag) = json
475        && let Some(v) = adt
476            .variants
477            .iter()
478            .find(|v| v.args.is_empty() && v.name.as_ref() == tag)
479    {
480        return heap.alloc_adt(v.name.clone(), vec![]);
481    }
482
483    if let Value::Object(obj) = json
484        && obj.len() == 1
485    {
486        let Some((tag, payload)) = obj.iter().next() else {
487            return Err(error(format!(
488                "expected ADT JSON representation for `{}`; got {}",
489                adt_name, json
490            )));
491        };
492        if let Some(v) = adt.variants.iter().find(|v| v.name.as_ref() == tag) {
493            let arg_types = instantiate_types(&v.args, &subst);
494            return decode_wrapped_variant(heap, payload, &v.name, &arg_types, ts, opts);
495        }
496    }
497
498    Err(error(format!(
499        "expected ADT JSON representation for `{}`; got {}",
500        adt_name, json
501    )))
502}
503
504fn pointer_to_json_for_adt(
505    heap: &Heap,
506    pointer: &Pointer,
507    adt_name: &Symbol,
508    type_args: &[Type],
509    ts: &TypeSystem,
510    opts: &JsonOptions,
511) -> Result<Value, EngineError> {
512    let adt = ts
513        .adts
514        .get(adt_name)
515        .ok_or_else(|| error(format!("unknown ADT `{}`", adt_name)))?;
516    let subst = adt_subst(adt, type_args)?;
517
518    let (tag, args) = heap.pointer_as_adt(pointer)?;
519    let v = adt.variants.iter().find(|v| v.name == tag).ok_or_else(|| {
520        error(format!(
521            "constructor `{}` is not in ADT `{}`",
522            tag, adt_name
523        ))
524    })?;
525    let arg_types = instantiate_types(&v.args, &subst);
526    if args.len() != arg_types.len() {
527        return Err(error(format!(
528            "constructor `{}` expected {} args, got {}",
529            tag,
530            arg_types.len(),
531            args.len()
532        )));
533    }
534
535    if adt.variants.len() == 1 {
536        return encode_direct_variant(heap, &tag, &args, &arg_types, ts, opts);
537    }
538
539    let enum_name = adt.name.to_string();
540    let enum_like = adt.variants.iter().all(|v| v.args.is_empty());
541    if enum_like && args.is_empty() {
542        if opts.int_enums.contains_key(&enum_name) {
543            let idx = adt
544                .variants
545                .iter()
546                .position(|v| v.name == tag)
547                .ok_or_else(|| error(format!("missing enum variant `{}`", tag)))?;
548            let d = variant_discriminant(&enum_name, &tag, idx, opts).ok_or_else(|| {
549                error(format!(
550                    "missing integer discriminant for enum `{}` variant `{}`",
551                    enum_name, tag
552                ))
553            })?;
554            return Ok(Value::Number(d.into()));
555        }
556        return Ok(Value::String(tag.to_string()));
557    }
558
559    if args.is_empty() {
560        return Ok(Value::String(tag.to_string()));
561    }
562
563    let payload = encode_wrapped_variant(heap, &args, &arg_types, ts, opts)?;
564    let mut out = Map::new();
565    out.insert(tag.to_string(), payload);
566    Ok(Value::Object(out))
567}
568
569fn decode_direct_variant(
570    heap: &Heap,
571    json: &Value,
572    ctor: &Symbol,
573    arg_types: &[Type],
574    ts: &TypeSystem,
575    opts: &JsonOptions,
576) -> Result<Pointer, EngineError> {
577    match arg_types {
578        [] => match json {
579            Value::Null => heap.alloc_adt(ctor.clone(), vec![]),
580            Value::String(tag) if tag == ctor.as_ref() => heap.alloc_adt(ctor.clone(), vec![]),
581            _ => Err(error(format!(
582                "expected null or `{}` for unit constructor, got {}",
583                ctor, json
584            ))),
585        },
586        [t0] => {
587            let p = json_to_rex(heap, json, t0, ts, opts)?;
588            heap.alloc_adt(ctor.clone(), vec![p])
589        }
590        _ => match json {
591            Value::Array(items) if items.len() == arg_types.len() => {
592                let mut args = Vec::with_capacity(items.len());
593                for (item, t) in items.iter().zip(arg_types.iter()) {
594                    args.push(json_to_rex(heap, item, t, ts, opts)?);
595                }
596                heap.alloc_adt(ctor.clone(), args)
597            }
598            _ => Err(error(format!(
599                "expected array payload for constructor `{}`, got {}",
600                ctor, json
601            ))),
602        },
603    }
604}
605
606fn decode_wrapped_variant(
607    heap: &Heap,
608    payload: &Value,
609    ctor: &Symbol,
610    arg_types: &[Type],
611    ts: &TypeSystem,
612    opts: &JsonOptions,
613) -> Result<Pointer, EngineError> {
614    match arg_types {
615        [] => heap.alloc_adt(ctor.clone(), vec![]),
616        [t0] => {
617            let p = json_to_rex(heap, payload, t0, ts, opts)?;
618            heap.alloc_adt(ctor.clone(), vec![p])
619        }
620        _ => match payload {
621            Value::Array(items) if items.len() == arg_types.len() => {
622                let mut args = Vec::with_capacity(items.len());
623                for (item, t) in items.iter().zip(arg_types.iter()) {
624                    args.push(json_to_rex(heap, item, t, ts, opts)?);
625                }
626                heap.alloc_adt(ctor.clone(), args)
627            }
628            _ => Err(error(format!(
629                "expected array payload for constructor `{}`, got {}",
630                ctor, payload
631            ))),
632        },
633    }
634}
635
636fn encode_direct_variant(
637    heap: &Heap,
638    ctor: &Symbol,
639    args: &[Pointer],
640    arg_types: &[Type],
641    ts: &TypeSystem,
642    opts: &JsonOptions,
643) -> Result<Value, EngineError> {
644    match arg_types {
645        [] => Ok(Value::String(ctor.to_string())),
646        [t0] => rex_to_json(heap, &args[0], t0, ts, opts),
647        _ => {
648            let mut out = Vec::with_capacity(args.len());
649            for (arg, t) in args.iter().zip(arg_types.iter()) {
650                out.push(rex_to_json(heap, arg, t, ts, opts)?);
651            }
652            Ok(Value::Array(out))
653        }
654    }
655}
656
657fn encode_wrapped_variant(
658    heap: &Heap,
659    args: &[Pointer],
660    arg_types: &[Type],
661    ts: &TypeSystem,
662    opts: &JsonOptions,
663) -> Result<Value, EngineError> {
664    match arg_types {
665        [] => Ok(Value::Null),
666        [t0] => rex_to_json(heap, &args[0], t0, ts, opts),
667        _ => {
668            let mut out = Vec::with_capacity(args.len());
669            for (arg, t) in args.iter().zip(arg_types.iter()) {
670                out.push(rex_to_json(heap, arg, t, ts, opts)?);
671            }
672            Ok(Value::Array(out))
673        }
674    }
675}
676
677fn decompose_type_app(typ: &Type) -> (Type, Vec<Type>) {
678    let mut args = Vec::new();
679    let mut cur = typ.clone();
680    while let TypeKind::App(f, a) = cur.as_ref() {
681        args.push(a.clone());
682        cur = f.clone();
683    }
684    args.reverse();
685    (cur, args)
686}
687
688fn adt_subst(adt: &AdtDecl, type_args: &[Type]) -> Result<BTreeMap<usize, Type>, EngineError> {
689    if adt.params.len() != type_args.len() {
690        return Err(error(format!(
691            "ADT `{}` expects {} type args, got {}",
692            adt.name,
693            adt.params.len(),
694            type_args.len()
695        )));
696    }
697    let mut subst = BTreeMap::new();
698    for (param, arg) in adt.params.iter().zip(type_args.iter()) {
699        subst.insert(param.var.id, arg.clone());
700    }
701    Ok(subst)
702}
703
704fn instantiate_types(ts: &[Type], subst: &BTreeMap<usize, Type>) -> Vec<Type> {
705    ts.iter().map(|t| instantiate_type(t, subst)).collect()
706}
707
708fn instantiate_type(t: &Type, subst: &BTreeMap<usize, Type>) -> Type {
709    match t.as_ref() {
710        TypeKind::Var(tv) => subst.get(&tv.id).cloned().unwrap_or_else(|| t.clone()),
711        TypeKind::Con(_) => t.clone(),
712        TypeKind::App(f, a) => Type::app(instantiate_type(f, subst), instantiate_type(a, subst)),
713        TypeKind::Fun(a, b) => Type::fun(instantiate_type(a, subst), instantiate_type(b, subst)),
714        TypeKind::Tuple(xs) => Type::tuple(xs.iter().map(|x| instantiate_type(x, subst)).collect()),
715        TypeKind::Record(fields) => Type::record(
716            fields
717                .iter()
718                .map(|(k, v)| (k.clone(), instantiate_type(v, subst)))
719                .collect(),
720        ),
721    }
722}
723
724fn variant_discriminant(
725    enum_type_name: &str,
726    variant_name: &Symbol,
727    idx: usize,
728    opts: &JsonOptions,
729) -> Option<i64> {
730    let patches = opts.int_enums.get(enum_type_name)?;
731    for p in patches {
732        if p.enum_name == variant_name.as_ref() {
733            return Some(p.discriminant);
734        }
735    }
736    Some(idx as i64)
737}
738
739fn list_to_vec(heap: &Heap, pointer: &Pointer) -> Result<Vec<Pointer>, EngineError> {
740    let mut out = Vec::new();
741    let mut cur = *pointer;
742    loop {
743        let (tag, args) = heap.pointer_as_adt(&cur)?;
744        if tag.as_ref() == "Empty" && args.is_empty() {
745            return Ok(out);
746        }
747        if tag.as_ref() == "Cons" && args.len() == 2 {
748            out.push(args[0]);
749            cur = args[1];
750            continue;
751        }
752        return Err(error(format!(
753            "expected list ADT chain (Cons/Empty), found constructor `{}`",
754            tag
755        )));
756    }
757}
758
759fn json_u64(json: &Value) -> Result<u64, EngineError> {
760    match json {
761        Value::Number(n) => n
762            .as_u64()
763            .ok_or_else(|| error(format!("expected unsigned integer JSON, got {}", json))),
764        _ => Err(error(format!(
765            "expected unsigned integer JSON, got {}",
766            json
767        ))),
768    }
769}
770
771fn json_i64(json: &Value) -> Result<i64, EngineError> {
772    match json {
773        Value::Number(n) => n
774            .as_i64()
775            .ok_or_else(|| error(format!("expected signed integer JSON, got {}", json))),
776        _ => Err(error(format!("expected signed integer JSON, got {}", json))),
777    }
778}
779
780fn json_f64(json: &Value) -> Result<f64, EngineError> {
781    match json {
782        Value::Number(n) => n
783            .as_f64()
784            .ok_or_else(|| error(format!("expected floating-point JSON, got {}", json))),
785        _ => Err(error(format!("expected floating-point JSON, got {}", json))),
786    }
787}
788
789fn error(msg: String) -> EngineError {
790    EngineError::Custom(msg)
791}
792
793fn type_mismatch_json(json: &Value, want: &Type) -> EngineError {
794    error(format!(
795        "JSON value {} does not match Rex type {}",
796        json, want
797    ))
798}
799
800fn type_mismatch_pointer(heap: &Heap, pointer: &Pointer, want: &Type) -> EngineError {
801    match heap.type_name(pointer) {
802        Ok(got) => error(format!(
803            "Rex value of runtime kind `{}` does not match Rex type {}",
804            got, want
805        )),
806        Err(e) => e,
807    }
808}