use crate::engine::{EngineError, Handle, Heap};
use rex_ast::Symbol;
use rex_typesystem::{
types::{AdtDecl, BuiltinTypeId, Type, TypeKind},
typesystem::TypeSystem,
};
use serde_json::{Map, Number, Value};
use std::collections::BTreeMap;
fn local_name(name: &Symbol) -> &str {
name.as_ref().rsplit('.').next().unwrap_or(name.as_ref())
}
fn local_name_matches(name: &Symbol, expected: &str) -> bool {
local_name(name) == expected
}
fn runtime_ctor(name: &Symbol) -> Symbol {
Symbol::intern(local_name(name))
}
pub fn json_to_rex(
heap: &Heap,
json: &Value,
want: &Type,
ts: &TypeSystem,
) -> Result<Handle, EngineError> {
match want.as_ref() {
TypeKind::Var(tv) => Err(error(format!(
"cannot decode JSON into unresolved type variable t{}",
tv.id
))),
TypeKind::Con(con) => json_to_handle_for_con(heap, json, &con.name(), &[], ts),
TypeKind::App(_, _) => {
let (head, args) = decompose_type_app(want);
if let TypeKind::Con(con) = head.as_ref() {
json_to_handle_for_con(heap, json, &con.name(), &args, ts)
} else {
Err(error(format!("unsupported applied type {}", want)))
}
}
TypeKind::Fun(_, _) => Err(error("cannot decode JSON into function type".to_string())),
TypeKind::Tuple(items) => match json {
Value::Array(values) if values.len() == items.len() => {
let mut out = Vec::with_capacity(values.len());
for (value, item_ty) in values.iter().zip(items.iter()) {
out.push(json_to_rex(heap, value, item_ty, ts)?);
}
heap.alloc_tuple(out)
}
_ => Err(type_mismatch_json(json, want)),
},
TypeKind::Record(fields) => match json {
Value::Object(entries) => {
let mut out = BTreeMap::new();
for (k, t) in fields {
let j = entries.get(k.as_ref()).unwrap_or(&Value::Null);
out.insert(k.clone(), json_to_rex(heap, j, t, ts)?);
}
heap.alloc_dict(out)
}
_ => Err(type_mismatch_json(json, want)),
},
}
}
pub fn rex_to_json(handle: &Handle, want: &Type, ts: &TypeSystem) -> Result<Value, EngineError> {
match want.as_ref() {
TypeKind::Var(tv) => Err(error(format!(
"cannot encode unresolved type variable t{} to JSON",
tv.id
))),
TypeKind::Con(con) => handle_to_json_for_con(handle, &con.name(), &[], ts),
TypeKind::App(_, _) => {
let (head, args) = decompose_type_app(want);
if let TypeKind::Con(con) = head.as_ref() {
handle_to_json_for_con(handle, &con.name(), &args, ts)
} else {
Err(error(format!("unsupported applied type {}", want)))
}
}
TypeKind::Fun(_, _) => Err(error("cannot encode function value to JSON".to_string())),
TypeKind::Tuple(item_types) => {
let values = handle.as_tuple()?;
if values.len() != item_types.len() {
return Err(type_mismatch_handle(handle, want));
}
let mut out = Vec::with_capacity(values.len());
for (p, t) in values.iter().zip(item_types.iter()) {
out.push(rex_to_json(p, t, ts)?);
}
Ok(Value::Array(out))
}
TypeKind::Record(fields) => {
let entries = handle.as_dict()?;
if entries.len() != fields.len() {
return Err(type_mismatch_handle(handle, want));
}
let mut out = Map::new();
for (k, t) in fields {
let p = entries
.get(k)
.ok_or_else(|| type_mismatch_handle(handle, want))?;
out.insert(k.to_string(), rex_to_json(p, t, ts)?);
}
Ok(Value::Object(out))
}
}
}
fn json_to_handle_for_con(
heap: &Heap,
json: &Value,
con_name: &Symbol,
con_args: &[Type],
ts: &TypeSystem,
) -> Result<Handle, EngineError> {
match (con_name.as_ref(), con_args) {
("bool", []) => match json {
Value::Bool(v) => heap.alloc_bool(*v),
_ => Err(type_mismatch_json(
json,
&Type::builtin(BuiltinTypeId::Bool),
)),
},
("u8", []) => {
let v = json_u64(json)?;
u8::try_from(v)
.map_err(|_| error(format!("value {} out of range for u8", v)))
.and_then(|x| heap.alloc_u8(x))
}
("u16", []) => {
let v = json_u64(json)?;
u16::try_from(v)
.map_err(|_| error(format!("value {} out of range for u16", v)))
.and_then(|x| heap.alloc_u16(x))
}
("u32", []) => {
let v = json_u64(json)?;
u32::try_from(v)
.map_err(|_| error(format!("value {} out of range for u32", v)))
.and_then(|x| heap.alloc_u32(x))
}
("u64", []) => heap.alloc_u64(json_u64(json)?),
("i8", []) => {
let v = json_i64(json)?;
i8::try_from(v)
.map_err(|_| error(format!("value {} out of range for i8", v)))
.and_then(|x| heap.alloc_i8(x))
}
("i16", []) => {
let v = json_i64(json)?;
i16::try_from(v)
.map_err(|_| error(format!("value {} out of range for i16", v)))
.and_then(|x| heap.alloc_i16(x))
}
("i32", []) => {
let v = json_i64(json)?;
i32::try_from(v)
.map_err(|_| error(format!("value {} out of range for i32", v)))
.and_then(|x| heap.alloc_i32(x))
}
("i64", []) => heap.alloc_i64(json_i64(json)?),
("f32", []) => heap.alloc_f32(json_f64(json)? as f32),
("f64", []) => heap.alloc_f64(json_f64(json)?),
("string", []) => match json {
Value::String(s) => heap.alloc_string(s.clone()),
_ => Err(type_mismatch_json(
json,
&Type::builtin(BuiltinTypeId::String),
)),
},
("uuid", []) => {
let u = serde_json::from_value(json.clone())
.map_err(|e| error(format!("invalid uuid JSON: {e}")))?;
heap.alloc_uuid(u)
}
("datetime", []) => {
let dt = serde_json::from_value(json.clone())
.map_err(|e| error(format!("invalid datetime JSON: {e}")))?;
heap.alloc_datetime(dt)
}
("Option", [inner]) => match json {
Value::Null => heap.alloc_adt(Symbol::intern("None"), vec![]),
_ => {
let inner_handle = json_to_rex(heap, json, inner, ts)?;
heap.alloc_adt(Symbol::intern("Some"), vec![inner_handle])
}
},
("Promise", [_inner]) => {
let promise_id = json_to_rex(heap, json, &Type::builtin(BuiltinTypeId::Uuid), ts)?;
heap.alloc_adt(Symbol::intern("Promise"), vec![promise_id])
}
("Result", [err_t, ok_t]) => match json {
Value::Object(obj) if obj.len() == 1 => {
if let Some(v) = obj.get("Ok") {
let p = json_to_rex(heap, v, ok_t, ts)?;
heap.alloc_adt(Symbol::intern("Ok"), vec![p])
} else if let Some(v) = obj.get("Err") {
let p = json_to_rex(heap, v, err_t, ts)?;
heap.alloc_adt(Symbol::intern("Err"), vec![p])
} else {
Err(error(format!(
"expected {{Ok:..}} or {{Err:..}}, got {}",
json
)))
}
}
_ => Err(error(format!("expected result object JSON, got {}", json))),
},
("Array", [elem_t]) => match json {
Value::Array(items) => {
let mut out = Vec::with_capacity(items.len());
for item in items {
out.push(json_to_rex(heap, item, elem_t, ts)?);
}
heap.alloc_array(out)
}
_ => Err(error(format!(
"expected array JSON for Array, got {}",
json
))),
},
("List", [elem_t]) => match json {
Value::Array(items) => {
let mut out = Vec::with_capacity(items.len());
for item in items {
out.push(json_to_rex(heap, item, elem_t, ts)?);
}
let mut list = heap.alloc_adt(Symbol::intern("Empty"), vec![])?;
for p in out.into_iter().rev() {
list = heap.alloc_adt(Symbol::intern("Cons"), vec![p, list])?;
}
Ok(list)
}
_ => Err(error(format!("expected array JSON for List, got {}", json))),
},
("Dict", [elem_t]) => match json {
Value::Object(obj) => {
let mut out = BTreeMap::new();
for (k, v) in obj {
out.insert(Symbol::intern(k), json_to_rex(heap, v, elem_t, ts)?);
}
heap.alloc_dict(out)
}
_ => Err(error(format!(
"expected object JSON for Dict, got {}",
json
))),
},
_ => json_to_handle_for_adt(heap, json, con_name, con_args, ts),
}
}
fn handle_to_json_for_con(
handle: &Handle,
con_name: &Symbol,
con_args: &[Type],
ts: &TypeSystem,
) -> Result<Value, EngineError> {
match (con_name.as_ref(), con_args) {
("bool", []) => Ok(Value::Bool(handle.as_bool()?)),
("u8", []) => Ok(Value::Number(u64::from(handle.as_u8()?).into())),
("u16", []) => Ok(Value::Number(u64::from(handle.as_u16()?).into())),
("u32", []) => Ok(Value::Number(u64::from(handle.as_u32()?).into())),
("u64", []) => Ok(Value::Number(handle.as_u64()?.into())),
("i8", []) => Ok(Value::Number(i64::from(handle.as_i8()?).into())),
("i16", []) => Ok(Value::Number(i64::from(handle.as_i16()?).into())),
("i32", []) => Ok(Value::Number(i64::from(handle.as_i32()?).into())),
("i64", []) => Ok(Value::Number(handle.as_i64()?.into())),
("f32", []) => Number::from_f64(f64::from(handle.as_f32()?))
.map(Value::Number)
.ok_or_else(|| error("invalid f32 value for JSON".to_string())),
("f64", []) => Number::from_f64(handle.as_f64()?)
.map(Value::Number)
.ok_or_else(|| error("invalid f64 value for JSON".to_string())),
("string", []) => Ok(Value::String(handle.as_string()?)),
("uuid", []) => serde_json::to_value(handle.as_uuid()?)
.map_err(|e| error(format!("failed to serialize uuid: {e}"))),
("datetime", []) => serde_json::to_value(handle.as_datetime()?)
.map_err(|e| error(format!("failed to serialize datetime: {e}"))),
("Option", [inner_t]) => {
let (tag, args) = handle.as_adt()?;
match (tag.as_ref(), args.as_slice()) {
("None", []) => Ok(Value::Null),
("Some", [x]) => rex_to_json(x, inner_t, ts),
_ => Err(type_mismatch_handle(
handle,
&Type::app(Type::builtin(BuiltinTypeId::Option), inner_t.clone()),
)),
}
}
("Promise", [inner_t]) => {
let (tag, args) = handle.as_adt()?;
match (tag.as_ref(), args.as_slice()) {
("Promise", [promise_id]) => {
rex_to_json(promise_id, &Type::builtin(BuiltinTypeId::Uuid), ts)
}
_ => Err(type_mismatch_handle(
handle,
&Type::app(Type::builtin(BuiltinTypeId::Promise), inner_t.clone()),
)),
}
}
("Result", [err_t, ok_t]) => {
let (tag, args) = handle.as_adt()?;
match (tag.as_ref(), args.as_slice()) {
("Ok", [x]) => {
let mut out = Map::new();
out.insert("Ok".to_string(), rex_to_json(x, ok_t, ts)?);
Ok(Value::Object(out))
}
("Err", [x]) => {
let mut out = Map::new();
out.insert("Err".to_string(), rex_to_json(x, err_t, ts)?);
Ok(Value::Object(out))
}
_ => Err(type_mismatch_handle(
handle,
&Type::app(
Type::app(Type::builtin(BuiltinTypeId::Result), err_t.clone()),
ok_t.clone(),
),
)),
}
}
("Array", [elem_t]) => {
let items = handle.as_array()?;
let mut out = Vec::with_capacity(items.len());
for item in &items {
out.push(rex_to_json(item, elem_t, ts)?);
}
Ok(Value::Array(out))
}
("List", [elem_t]) => {
let items = list_to_vec(handle)?;
let mut out = Vec::with_capacity(items.len());
for item in &items {
out.push(rex_to_json(item, elem_t, ts)?);
}
Ok(Value::Array(out))
}
("Dict", [elem_t]) => {
let entries = handle.as_dict()?;
let mut out = Map::new();
for (k, v) in &entries {
out.insert(k.to_string(), rex_to_json(v, elem_t, ts)?);
}
Ok(Value::Object(out))
}
_ => handle_to_json_for_adt(handle, con_name, con_args, ts),
}
}
fn json_to_handle_for_adt(
heap: &Heap,
json: &Value,
adt_name: &Symbol,
type_args: &[Type],
ts: &TypeSystem,
) -> Result<Handle, EngineError> {
let adt = ts
.adts
.get(adt_name)
.ok_or_else(|| error(format!("unknown ADT `{}`", adt_name)))?;
let subst = adt_subst(adt, type_args)?;
if adt.variants.len() == 1 {
let v = &adt.variants[0];
let arg_types = instantiate_types(&v.args, &subst);
return decode_direct_variant(heap, json, &v.name, &arg_types, ts);
}
let enum_name = adt.name.to_string();
let enum_like = adt.variants.iter().all(|v| v.args.is_empty());
if enum_like {
if let Value::String(tag) = json {
if let Some(v) = adt
.variants
.iter()
.find(|v| local_name_matches(&v.name, tag))
{
return heap.alloc_adt(runtime_ctor(&v.name), vec![]);
}
return Err(error(format!(
"unknown enum tag `{}` for `{}`",
tag, enum_name
)));
}
return Err(error(format!(
"expected enum string JSON for `{}`, got {}",
enum_name, json
)));
}
if let Value::String(tag) = json
&& let Some(v) = adt
.variants
.iter()
.find(|v| v.args.is_empty() && local_name_matches(&v.name, tag))
{
return heap.alloc_adt(runtime_ctor(&v.name), vec![]);
}
if let Value::Object(obj) = json
&& obj.len() == 1
{
let Some((tag, payload)) = obj.iter().next() else {
return Err(error(format!(
"expected ADT JSON representation for `{}`; got {}",
adt_name, json
)));
};
if let Some(v) = adt
.variants
.iter()
.find(|v| local_name_matches(&v.name, tag))
{
let arg_types = instantiate_types(&v.args, &subst);
return decode_wrapped_variant(heap, payload, &v.name, &arg_types, ts);
}
}
Err(error(format!(
"expected ADT JSON representation for `{}`; got {}",
adt_name, json
)))
}
fn handle_to_json_for_adt(
handle: &Handle,
adt_name: &Symbol,
type_args: &[Type],
ts: &TypeSystem,
) -> Result<Value, EngineError> {
let adt = ts
.adts
.get(adt_name)
.ok_or_else(|| error(format!("unknown ADT `{}`", adt_name)))?;
let subst = adt_subst(adt, type_args)?;
let (tag, args) = handle.as_adt()?;
let v = adt
.variants
.iter()
.find(|v| local_name_matches(&v.name, local_name(&tag)))
.ok_or_else(|| {
error(format!(
"constructor `{}` is not in ADT `{}`",
tag, adt_name
))
})?;
let arg_types = instantiate_types(&v.args, &subst);
if args.len() != arg_types.len() {
return Err(error(format!(
"constructor `{}` expected {} args, got {}",
tag,
arg_types.len(),
args.len()
)));
}
if adt.variants.len() == 1 {
return encode_direct_variant(&tag, &args, &arg_types, ts);
}
let enum_like = adt.variants.iter().all(|v| v.args.is_empty());
if enum_like && args.is_empty() {
return Ok(Value::String(local_name(&tag).to_string()));
}
if args.is_empty() {
return Ok(Value::String(local_name(&tag).to_string()));
}
let payload = encode_wrapped_variant(&args, &arg_types, ts)?;
let mut out = Map::new();
out.insert(local_name(&tag).to_string(), payload);
Ok(Value::Object(out))
}
fn decode_direct_variant(
heap: &Heap,
json: &Value,
ctor: &Symbol,
arg_types: &[Type],
ts: &TypeSystem,
) -> Result<Handle, EngineError> {
match arg_types {
[] => match json {
Value::Null => heap.alloc_adt(runtime_ctor(ctor), vec![]),
Value::String(tag) if tag == local_name(ctor) => {
heap.alloc_adt(runtime_ctor(ctor), vec![])
}
_ => Err(error(format!(
"expected null or `{}` for unit constructor, got {}",
local_name(ctor),
json
))),
},
[t0] => {
let p = json_to_rex(heap, json, t0, ts)?;
heap.alloc_adt(runtime_ctor(ctor), vec![p])
}
_ => match json {
Value::Array(items) if items.len() == arg_types.len() => {
let mut args = Vec::with_capacity(items.len());
for (item, t) in items.iter().zip(arg_types.iter()) {
args.push(json_to_rex(heap, item, t, ts)?);
}
heap.alloc_adt(runtime_ctor(ctor), args)
}
_ => Err(error(format!(
"expected array payload for constructor `{}`, got {}",
local_name(ctor),
json
))),
},
}
}
fn decode_wrapped_variant(
heap: &Heap,
payload: &Value,
ctor: &Symbol,
arg_types: &[Type],
ts: &TypeSystem,
) -> Result<Handle, EngineError> {
match arg_types {
[] => heap.alloc_adt(runtime_ctor(ctor), vec![]),
[t0] => {
let p = json_to_rex(heap, payload, t0, ts)?;
heap.alloc_adt(runtime_ctor(ctor), vec![p])
}
_ => match payload {
Value::Array(items) if items.len() == arg_types.len() => {
let mut args = Vec::with_capacity(items.len());
for (item, t) in items.iter().zip(arg_types.iter()) {
args.push(json_to_rex(heap, item, t, ts)?);
}
heap.alloc_adt(runtime_ctor(ctor), args)
}
_ => Err(error(format!(
"expected array payload for constructor `{}`, got {}",
local_name(ctor),
payload
))),
},
}
}
fn encode_direct_variant(
ctor: &Symbol,
args: &[Handle],
arg_types: &[Type],
ts: &TypeSystem,
) -> Result<Value, EngineError> {
match arg_types {
[] => Ok(Value::String(local_name(ctor).to_string())),
[t0] => rex_to_json(&args[0], t0, ts),
_ => {
let mut out = Vec::with_capacity(args.len());
for (arg, t) in args.iter().zip(arg_types.iter()) {
out.push(rex_to_json(arg, t, ts)?);
}
Ok(Value::Array(out))
}
}
}
fn encode_wrapped_variant(
args: &[Handle],
arg_types: &[Type],
ts: &TypeSystem,
) -> Result<Value, EngineError> {
match arg_types {
[] => Ok(Value::Null),
[t0] => rex_to_json(&args[0], t0, ts),
_ => {
let mut out = Vec::with_capacity(args.len());
for (arg, t) in args.iter().zip(arg_types.iter()) {
out.push(rex_to_json(arg, t, ts)?);
}
Ok(Value::Array(out))
}
}
}
fn decompose_type_app(typ: &Type) -> (Type, Vec<Type>) {
let mut args = Vec::new();
let mut cur = typ.clone();
while let TypeKind::App(f, a) = cur.as_ref() {
args.push(a.clone());
cur = f.clone();
}
args.reverse();
(cur, args)
}
fn adt_subst(adt: &AdtDecl, type_args: &[Type]) -> Result<BTreeMap<usize, Type>, EngineError> {
if adt.params.len() != type_args.len() {
return Err(error(format!(
"ADT `{}` expects {} type args, got {}",
adt.name,
adt.params.len(),
type_args.len()
)));
}
let mut subst = BTreeMap::new();
for (param, arg) in adt.params.iter().zip(type_args.iter()) {
subst.insert(param.var.id, arg.clone());
}
Ok(subst)
}
fn instantiate_types(ts: &[Type], subst: &BTreeMap<usize, Type>) -> Vec<Type> {
ts.iter().map(|t| instantiate_type(t, subst)).collect()
}
fn instantiate_type(t: &Type, subst: &BTreeMap<usize, Type>) -> Type {
match t.as_ref() {
TypeKind::Var(tv) => subst.get(&tv.id).cloned().unwrap_or_else(|| t.clone()),
TypeKind::Con(_) => t.clone(),
TypeKind::App(f, a) => Type::app(instantiate_type(f, subst), instantiate_type(a, subst)),
TypeKind::Fun(a, b) => Type::fun(instantiate_type(a, subst), instantiate_type(b, subst)),
TypeKind::Tuple(xs) => Type::tuple(xs.iter().map(|x| instantiate_type(x, subst)).collect()),
TypeKind::Record(fields) => Type::record(
fields
.iter()
.map(|(k, v)| (k.clone(), instantiate_type(v, subst)))
.collect(),
),
}
}
fn list_to_vec(handle: &Handle) -> Result<Vec<Handle>, EngineError> {
let mut out = Vec::new();
let mut cur = handle.clone();
loop {
let (tag, args) = cur.as_adt()?;
if tag.as_ref() == "Empty" && args.is_empty() {
return Ok(out);
}
if tag.as_ref() == "Cons" && args.len() == 2 {
out.push(args[0].clone());
cur = args[1].clone();
continue;
}
return Err(error(format!(
"expected list ADT chain (Cons/Empty), found constructor `{}`",
tag
)));
}
}
fn json_u64(json: &Value) -> Result<u64, EngineError> {
match json {
Value::Number(n) => n
.as_u64()
.ok_or_else(|| error(format!("expected unsigned integer JSON, got {}", json))),
_ => Err(error(format!(
"expected unsigned integer JSON, got {}",
json
))),
}
}
fn json_i64(json: &Value) -> Result<i64, EngineError> {
match json {
Value::Number(n) => n
.as_i64()
.ok_or_else(|| error(format!("expected signed integer JSON, got {}", json))),
_ => Err(error(format!("expected signed integer JSON, got {}", json))),
}
}
fn json_f64(json: &Value) -> Result<f64, EngineError> {
match json {
Value::Number(n) => n
.as_f64()
.ok_or_else(|| error(format!("expected floating-point JSON, got {}", json))),
_ => Err(error(format!("expected floating-point JSON, got {}", json))),
}
}
fn error(msg: String) -> EngineError {
EngineError::Custom(msg)
}
fn type_mismatch_json(json: &Value, want: &Type) -> EngineError {
error(format!(
"JSON value {} does not match Rex type {}",
json, want
))
}
fn type_mismatch_handle(handle: &Handle, want: &Type) -> EngineError {
match handle.type_name() {
Ok(got) => error(format!(
"Rex value of runtime kind `{}` does not match Rex type {}",
got, want
)),
Err(e) => e,
}
}