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        ("Promise", [_inner]) => {
244            let promise_id =
245                json_to_rex(heap, json, &Type::builtin(BuiltinTypeId::Uuid), ts, opts)?;
246            heap.alloc_adt(sym("Promise"), vec![promise_id])
247        }
248
249        // Internal argument order is Result err ok.
250        ("Result", [err_t, ok_t]) => match json {
251            Value::Object(obj) if obj.len() == 1 => {
252                if let Some(v) = obj.get("Ok") {
253                    let p = json_to_rex(heap, v, ok_t, ts, opts)?;
254                    heap.alloc_adt(sym("Ok"), vec![p])
255                } else if let Some(v) = obj.get("Err") {
256                    let p = json_to_rex(heap, v, err_t, ts, opts)?;
257                    heap.alloc_adt(sym("Err"), vec![p])
258                } else {
259                    Err(error(format!(
260                        "expected {{Ok:..}} or {{Err:..}}, got {}",
261                        json
262                    )))
263                }
264            }
265            _ => Err(error(format!("expected result object JSON, got {}", json))),
266        },
267
268        // IMPORTANT: JSON arrays map to runtime arrays, not lists.
269        ("Array", [elem_t]) => match json {
270            Value::Array(items) => {
271                let mut out = Vec::with_capacity(items.len());
272                for item in items {
273                    out.push(json_to_rex(heap, item, elem_t, ts, opts)?);
274                }
275                heap.alloc_array(out)
276            }
277            _ => Err(error(format!(
278                "expected array JSON for Array, got {}",
279                json
280            ))),
281        },
282
283        ("List", [elem_t]) => match json {
284            Value::Array(items) => {
285                let mut out = Vec::with_capacity(items.len());
286                for item in items {
287                    out.push(json_to_rex(heap, item, elem_t, ts, opts)?);
288                }
289                let mut list = heap.alloc_adt(sym("Empty"), vec![])?;
290                for p in out.into_iter().rev() {
291                    list = heap.alloc_adt(sym("Cons"), vec![p, list])?;
292                }
293                Ok(list)
294            }
295            _ => Err(error(format!("expected array JSON for List, got {}", json))),
296        },
297
298        ("Dict", [elem_t]) => match json {
299            Value::Object(obj) => {
300                let mut out = BTreeMap::new();
301                for (k, v) in obj {
302                    out.insert(sym(k), json_to_rex(heap, v, elem_t, ts, opts)?);
303                }
304                heap.alloc_dict(out)
305            }
306            _ => Err(error(format!(
307                "expected object JSON for Dict, got {}",
308                json
309            ))),
310        },
311
312        _ => json_to_pointer_for_adt(heap, json, con_name, con_args, ts, opts),
313    }
314}
315
316fn pointer_to_json_for_con(
317    heap: &Heap,
318    pointer: &Pointer,
319    con_name: &Symbol,
320    con_args: &[Type],
321    ts: &TypeSystem,
322    opts: &JsonOptions,
323) -> Result<Value, EngineError> {
324    match (con_name.as_ref(), con_args) {
325        ("bool", []) => Ok(Value::Bool(heap.pointer_as_bool(pointer)?)),
326        ("u8", []) => Ok(Value::Number(
327            u64::from(heap.pointer_as_u8(pointer)?).into(),
328        )),
329        ("u16", []) => Ok(Value::Number(
330            u64::from(heap.pointer_as_u16(pointer)?).into(),
331        )),
332        ("u32", []) => Ok(Value::Number(
333            u64::from(heap.pointer_as_u32(pointer)?).into(),
334        )),
335        ("u64", []) => Ok(Value::Number(heap.pointer_as_u64(pointer)?.into())),
336        ("i8", []) => Ok(Value::Number(
337            i64::from(heap.pointer_as_i8(pointer)?).into(),
338        )),
339        ("i16", []) => Ok(Value::Number(
340            i64::from(heap.pointer_as_i16(pointer)?).into(),
341        )),
342        ("i32", []) => Ok(Value::Number(
343            i64::from(heap.pointer_as_i32(pointer)?).into(),
344        )),
345        ("i64", []) => Ok(Value::Number(heap.pointer_as_i64(pointer)?.into())),
346        ("f32", []) => Number::from_f64(f64::from(heap.pointer_as_f32(pointer)?))
347            .map(Value::Number)
348            .ok_or_else(|| error("invalid f32 value for JSON".to_string())),
349        ("f64", []) => Number::from_f64(heap.pointer_as_f64(pointer)?)
350            .map(Value::Number)
351            .ok_or_else(|| error("invalid f64 value for JSON".to_string())),
352        ("string", []) => Ok(Value::String(heap.pointer_as_string(pointer)?)),
353        ("uuid", []) => serde_json::to_value(heap.pointer_as_uuid(pointer)?)
354            .map_err(|e| error(format!("failed to serialize uuid: {e}"))),
355        ("datetime", []) => serde_json::to_value(heap.pointer_as_datetime(pointer)?)
356            .map_err(|e| error(format!("failed to serialize datetime: {e}"))),
357
358        ("Option", [inner_t]) => {
359            let (tag, args) = heap.pointer_as_adt(pointer)?;
360            match (tag.as_ref(), args.as_slice()) {
361                ("None", []) => Ok(Value::Null),
362                ("Some", [x]) => rex_to_json(heap, x, inner_t, ts, opts),
363                _ => Err(type_mismatch_pointer(
364                    heap,
365                    pointer,
366                    &Type::app(Type::builtin(BuiltinTypeId::Option), inner_t.clone()),
367                )),
368            }
369        }
370
371        ("Promise", [inner_t]) => {
372            let (tag, args) = heap.pointer_as_adt(pointer)?;
373            match (tag.as_ref(), args.as_slice()) {
374                ("Promise", [promise_id]) => rex_to_json(
375                    heap,
376                    promise_id,
377                    &Type::builtin(BuiltinTypeId::Uuid),
378                    ts,
379                    opts,
380                ),
381                _ => Err(type_mismatch_pointer(
382                    heap,
383                    pointer,
384                    &Type::app(Type::builtin(BuiltinTypeId::Promise), inner_t.clone()),
385                )),
386            }
387        }
388
389        // Internal argument order is Result err ok.
390        ("Result", [err_t, ok_t]) => {
391            let (tag, args) = heap.pointer_as_adt(pointer)?;
392            match (tag.as_ref(), args.as_slice()) {
393                ("Ok", [x]) => {
394                    let mut out = Map::new();
395                    out.insert("Ok".to_string(), rex_to_json(heap, x, ok_t, ts, opts)?);
396                    Ok(Value::Object(out))
397                }
398                ("Err", [x]) => {
399                    let mut out = Map::new();
400                    out.insert("Err".to_string(), rex_to_json(heap, x, err_t, ts, opts)?);
401                    Ok(Value::Object(out))
402                }
403                _ => Err(type_mismatch_pointer(
404                    heap,
405                    pointer,
406                    &Type::app(
407                        Type::app(Type::builtin(BuiltinTypeId::Result), err_t.clone()),
408                        ok_t.clone(),
409                    ),
410                )),
411            }
412        }
413
414        ("Array", [elem_t]) => {
415            let items = heap.pointer_as_array(pointer)?;
416            let mut out = Vec::with_capacity(items.len());
417            for item in &items {
418                out.push(rex_to_json(heap, item, elem_t, ts, opts)?);
419            }
420            Ok(Value::Array(out))
421        }
422
423        ("List", [elem_t]) => {
424            let items = list_to_vec(heap, pointer)?;
425            let mut out = Vec::with_capacity(items.len());
426            for item in &items {
427                out.push(rex_to_json(heap, item, elem_t, ts, opts)?);
428            }
429            Ok(Value::Array(out))
430        }
431
432        ("Dict", [elem_t]) => {
433            let entries = heap.pointer_as_dict(pointer)?;
434            let mut out = Map::new();
435            for (k, v) in &entries {
436                out.insert(k.to_string(), rex_to_json(heap, v, elem_t, ts, opts)?);
437            }
438            Ok(Value::Object(out))
439        }
440
441        _ => pointer_to_json_for_adt(heap, pointer, con_name, con_args, ts, opts),
442    }
443}
444
445fn json_to_pointer_for_adt(
446    heap: &Heap,
447    json: &Value,
448    adt_name: &Symbol,
449    type_args: &[Type],
450    ts: &TypeSystem,
451    opts: &JsonOptions,
452) -> Result<Pointer, EngineError> {
453    let adt = ts
454        .adts
455        .get(adt_name)
456        .ok_or_else(|| error(format!("unknown ADT `{}`", adt_name)))?;
457    let subst = adt_subst(adt, type_args)?;
458
459    if adt.variants.len() == 1 {
460        let v = &adt.variants[0];
461        let arg_types = instantiate_types(&v.args, &subst);
462        return decode_direct_variant(heap, json, &v.name, &arg_types, ts, opts);
463    }
464
465    let enum_name = adt.name.to_string();
466    let enum_like = adt.variants.iter().all(|v| v.args.is_empty());
467    if enum_like {
468        if opts.int_enums.contains_key(&enum_name) {
469            if let Value::Number(n) = json
470                && let Some(i) = n.as_i64()
471            {
472                for (idx, v) in adt.variants.iter().enumerate() {
473                    if variant_discriminant(&enum_name, &v.name, idx, opts) == Some(i) {
474                        return heap.alloc_adt(v.name.clone(), vec![]);
475                    }
476                }
477            }
478            return Err(error(format!(
479                "expected integer enum JSON for `{}`, got {}",
480                enum_name, json
481            )));
482        }
483        if let Value::String(tag) = json {
484            if let Some(v) = adt.variants.iter().find(|v| v.name.as_ref() == tag) {
485                return heap.alloc_adt(v.name.clone(), vec![]);
486            }
487            return Err(error(format!(
488                "unknown enum tag `{}` for `{}`",
489                tag, enum_name
490            )));
491        }
492        return Err(error(format!(
493            "expected enum string JSON for `{}`, got {}",
494            enum_name, json
495        )));
496    }
497
498    if let Value::String(tag) = json
499        && let Some(v) = adt
500            .variants
501            .iter()
502            .find(|v| v.args.is_empty() && v.name.as_ref() == tag)
503    {
504        return heap.alloc_adt(v.name.clone(), vec![]);
505    }
506
507    if let Value::Object(obj) = json
508        && obj.len() == 1
509    {
510        let Some((tag, payload)) = obj.iter().next() else {
511            return Err(error(format!(
512                "expected ADT JSON representation for `{}`; got {}",
513                adt_name, json
514            )));
515        };
516        if let Some(v) = adt.variants.iter().find(|v| v.name.as_ref() == tag) {
517            let arg_types = instantiate_types(&v.args, &subst);
518            return decode_wrapped_variant(heap, payload, &v.name, &arg_types, ts, opts);
519        }
520    }
521
522    Err(error(format!(
523        "expected ADT JSON representation for `{}`; got {}",
524        adt_name, json
525    )))
526}
527
528fn pointer_to_json_for_adt(
529    heap: &Heap,
530    pointer: &Pointer,
531    adt_name: &Symbol,
532    type_args: &[Type],
533    ts: &TypeSystem,
534    opts: &JsonOptions,
535) -> Result<Value, EngineError> {
536    let adt = ts
537        .adts
538        .get(adt_name)
539        .ok_or_else(|| error(format!("unknown ADT `{}`", adt_name)))?;
540    let subst = adt_subst(adt, type_args)?;
541
542    let (tag, args) = heap.pointer_as_adt(pointer)?;
543    let v = adt.variants.iter().find(|v| v.name == tag).ok_or_else(|| {
544        error(format!(
545            "constructor `{}` is not in ADT `{}`",
546            tag, adt_name
547        ))
548    })?;
549    let arg_types = instantiate_types(&v.args, &subst);
550    if args.len() != arg_types.len() {
551        return Err(error(format!(
552            "constructor `{}` expected {} args, got {}",
553            tag,
554            arg_types.len(),
555            args.len()
556        )));
557    }
558
559    if adt.variants.len() == 1 {
560        return encode_direct_variant(heap, &tag, &args, &arg_types, ts, opts);
561    }
562
563    let enum_name = adt.name.to_string();
564    let enum_like = adt.variants.iter().all(|v| v.args.is_empty());
565    if enum_like && args.is_empty() {
566        if opts.int_enums.contains_key(&enum_name) {
567            let idx = adt
568                .variants
569                .iter()
570                .position(|v| v.name == tag)
571                .ok_or_else(|| error(format!("missing enum variant `{}`", tag)))?;
572            let d = variant_discriminant(&enum_name, &tag, idx, opts).ok_or_else(|| {
573                error(format!(
574                    "missing integer discriminant for enum `{}` variant `{}`",
575                    enum_name, tag
576                ))
577            })?;
578            return Ok(Value::Number(d.into()));
579        }
580        return Ok(Value::String(tag.to_string()));
581    }
582
583    if args.is_empty() {
584        return Ok(Value::String(tag.to_string()));
585    }
586
587    let payload = encode_wrapped_variant(heap, &args, &arg_types, ts, opts)?;
588    let mut out = Map::new();
589    out.insert(tag.to_string(), payload);
590    Ok(Value::Object(out))
591}
592
593fn decode_direct_variant(
594    heap: &Heap,
595    json: &Value,
596    ctor: &Symbol,
597    arg_types: &[Type],
598    ts: &TypeSystem,
599    opts: &JsonOptions,
600) -> Result<Pointer, EngineError> {
601    match arg_types {
602        [] => match json {
603            Value::Null => heap.alloc_adt(ctor.clone(), vec![]),
604            Value::String(tag) if tag == ctor.as_ref() => heap.alloc_adt(ctor.clone(), vec![]),
605            _ => Err(error(format!(
606                "expected null or `{}` for unit constructor, got {}",
607                ctor, json
608            ))),
609        },
610        [t0] => {
611            let p = json_to_rex(heap, json, t0, ts, opts)?;
612            heap.alloc_adt(ctor.clone(), vec![p])
613        }
614        _ => match json {
615            Value::Array(items) if items.len() == arg_types.len() => {
616                let mut args = Vec::with_capacity(items.len());
617                for (item, t) in items.iter().zip(arg_types.iter()) {
618                    args.push(json_to_rex(heap, item, t, ts, opts)?);
619                }
620                heap.alloc_adt(ctor.clone(), args)
621            }
622            _ => Err(error(format!(
623                "expected array payload for constructor `{}`, got {}",
624                ctor, json
625            ))),
626        },
627    }
628}
629
630fn decode_wrapped_variant(
631    heap: &Heap,
632    payload: &Value,
633    ctor: &Symbol,
634    arg_types: &[Type],
635    ts: &TypeSystem,
636    opts: &JsonOptions,
637) -> Result<Pointer, EngineError> {
638    match arg_types {
639        [] => heap.alloc_adt(ctor.clone(), vec![]),
640        [t0] => {
641            let p = json_to_rex(heap, payload, t0, ts, opts)?;
642            heap.alloc_adt(ctor.clone(), vec![p])
643        }
644        _ => match payload {
645            Value::Array(items) if items.len() == arg_types.len() => {
646                let mut args = Vec::with_capacity(items.len());
647                for (item, t) in items.iter().zip(arg_types.iter()) {
648                    args.push(json_to_rex(heap, item, t, ts, opts)?);
649                }
650                heap.alloc_adt(ctor.clone(), args)
651            }
652            _ => Err(error(format!(
653                "expected array payload for constructor `{}`, got {}",
654                ctor, payload
655            ))),
656        },
657    }
658}
659
660fn encode_direct_variant(
661    heap: &Heap,
662    ctor: &Symbol,
663    args: &[Pointer],
664    arg_types: &[Type],
665    ts: &TypeSystem,
666    opts: &JsonOptions,
667) -> Result<Value, EngineError> {
668    match arg_types {
669        [] => Ok(Value::String(ctor.to_string())),
670        [t0] => rex_to_json(heap, &args[0], t0, ts, opts),
671        _ => {
672            let mut out = Vec::with_capacity(args.len());
673            for (arg, t) in args.iter().zip(arg_types.iter()) {
674                out.push(rex_to_json(heap, arg, t, ts, opts)?);
675            }
676            Ok(Value::Array(out))
677        }
678    }
679}
680
681fn encode_wrapped_variant(
682    heap: &Heap,
683    args: &[Pointer],
684    arg_types: &[Type],
685    ts: &TypeSystem,
686    opts: &JsonOptions,
687) -> Result<Value, EngineError> {
688    match arg_types {
689        [] => Ok(Value::Null),
690        [t0] => rex_to_json(heap, &args[0], t0, ts, opts),
691        _ => {
692            let mut out = Vec::with_capacity(args.len());
693            for (arg, t) in args.iter().zip(arg_types.iter()) {
694                out.push(rex_to_json(heap, arg, t, ts, opts)?);
695            }
696            Ok(Value::Array(out))
697        }
698    }
699}
700
701fn decompose_type_app(typ: &Type) -> (Type, Vec<Type>) {
702    let mut args = Vec::new();
703    let mut cur = typ.clone();
704    while let TypeKind::App(f, a) = cur.as_ref() {
705        args.push(a.clone());
706        cur = f.clone();
707    }
708    args.reverse();
709    (cur, args)
710}
711
712fn adt_subst(adt: &AdtDecl, type_args: &[Type]) -> Result<BTreeMap<usize, Type>, EngineError> {
713    if adt.params.len() != type_args.len() {
714        return Err(error(format!(
715            "ADT `{}` expects {} type args, got {}",
716            adt.name,
717            adt.params.len(),
718            type_args.len()
719        )));
720    }
721    let mut subst = BTreeMap::new();
722    for (param, arg) in adt.params.iter().zip(type_args.iter()) {
723        subst.insert(param.var.id, arg.clone());
724    }
725    Ok(subst)
726}
727
728fn instantiate_types(ts: &[Type], subst: &BTreeMap<usize, Type>) -> Vec<Type> {
729    ts.iter().map(|t| instantiate_type(t, subst)).collect()
730}
731
732fn instantiate_type(t: &Type, subst: &BTreeMap<usize, Type>) -> Type {
733    match t.as_ref() {
734        TypeKind::Var(tv) => subst.get(&tv.id).cloned().unwrap_or_else(|| t.clone()),
735        TypeKind::Con(_) => t.clone(),
736        TypeKind::App(f, a) => Type::app(instantiate_type(f, subst), instantiate_type(a, subst)),
737        TypeKind::Fun(a, b) => Type::fun(instantiate_type(a, subst), instantiate_type(b, subst)),
738        TypeKind::Tuple(xs) => Type::tuple(xs.iter().map(|x| instantiate_type(x, subst)).collect()),
739        TypeKind::Record(fields) => Type::record(
740            fields
741                .iter()
742                .map(|(k, v)| (k.clone(), instantiate_type(v, subst)))
743                .collect(),
744        ),
745    }
746}
747
748fn variant_discriminant(
749    enum_type_name: &str,
750    variant_name: &Symbol,
751    idx: usize,
752    opts: &JsonOptions,
753) -> Option<i64> {
754    let patches = opts.int_enums.get(enum_type_name)?;
755    for p in patches {
756        if p.enum_name == variant_name.as_ref() {
757            return Some(p.discriminant);
758        }
759    }
760    Some(idx as i64)
761}
762
763fn list_to_vec(heap: &Heap, pointer: &Pointer) -> Result<Vec<Pointer>, EngineError> {
764    let mut out = Vec::new();
765    let mut cur = *pointer;
766    loop {
767        let (tag, args) = heap.pointer_as_adt(&cur)?;
768        if tag.as_ref() == "Empty" && args.is_empty() {
769            return Ok(out);
770        }
771        if tag.as_ref() == "Cons" && args.len() == 2 {
772            out.push(args[0]);
773            cur = args[1];
774            continue;
775        }
776        return Err(error(format!(
777            "expected list ADT chain (Cons/Empty), found constructor `{}`",
778            tag
779        )));
780    }
781}
782
783fn json_u64(json: &Value) -> Result<u64, EngineError> {
784    match json {
785        Value::Number(n) => n
786            .as_u64()
787            .ok_or_else(|| error(format!("expected unsigned integer JSON, got {}", json))),
788        _ => Err(error(format!(
789            "expected unsigned integer JSON, got {}",
790            json
791        ))),
792    }
793}
794
795fn json_i64(json: &Value) -> Result<i64, EngineError> {
796    match json {
797        Value::Number(n) => n
798            .as_i64()
799            .ok_or_else(|| error(format!("expected signed integer JSON, got {}", json))),
800        _ => Err(error(format!("expected signed integer JSON, got {}", json))),
801    }
802}
803
804fn json_f64(json: &Value) -> Result<f64, EngineError> {
805    match json {
806        Value::Number(n) => n
807            .as_f64()
808            .ok_or_else(|| error(format!("expected floating-point JSON, got {}", json))),
809        _ => Err(error(format!("expected floating-point JSON, got {}", json))),
810    }
811}
812
813fn error(msg: String) -> EngineError {
814    EngineError::Custom(msg)
815}
816
817fn type_mismatch_json(json: &Value, want: &Type) -> EngineError {
818    error(format!(
819        "JSON value {} does not match Rex type {}",
820        json, want
821    ))
822}
823
824fn type_mismatch_pointer(heap: &Heap, pointer: &Pointer, want: &Type) -> EngineError {
825    match heap.type_name(pointer) {
826        Ok(got) => error(format!(
827            "Rex value of runtime kind `{}` does not match Rex type {}",
828            got, want
829        )),
830        Err(e) => e,
831    }
832}