mod bridge;
use std::collections::HashMap;
use crate::{CompiledExpr, EvalHandle, EvalInput, EvalIter, NumericResult};
pub fn compile_from_json(ast_json: &str, constants_json: &str) -> Result<CompiledExpr, String> {
let ast: mathlex::Expression =
serde_json::from_str(ast_json).map_err(|e| format!("invalid AST JSON: {e}"))?;
let constants_raw: HashMap<String, NumericResult> =
serde_json::from_str(constants_json).map_err(|e| format!("invalid constants JSON: {e}"))?;
let constants: HashMap<&str, NumericResult> = constants_raw
.iter()
.map(|(k, v)| (k.as_str(), *v))
.collect();
crate::compile(&ast, &constants).map_err(|e| e.to_string())
}
pub fn eval_with_json(expr: &CompiledExpr, args_json: &str) -> Result<EvalHandle, String> {
let args_raw: HashMap<String, serde_json::Value> =
serde_json::from_str(args_json).map_err(|e| format!("invalid args JSON: {e}"))?;
let mut args: HashMap<&str, EvalInput> = HashMap::new();
for (name, value) in &args_raw {
let input = json_to_eval_input(value)?;
args.insert(name.as_str(), input);
}
crate::eval(expr, args).map_err(|e| e.to_string())
}
pub fn eval_with_iters(
expr: &CompiledExpr,
args_json: &str,
iter_arg_names: Vec<String>,
mut next_values: Vec<Box<dyn FnMut() -> Option<f64>>>,
) -> Result<EvalHandle, String> {
let args_raw: HashMap<String, serde_json::Value> =
serde_json::from_str(args_json).map_err(|e| format!("invalid args JSON: {e}"))?;
let mut args: HashMap<&str, EvalInput> = HashMap::new();
for (name, value) in &args_raw {
let input = json_to_eval_input(value)?;
args.insert(name.as_str(), input);
}
for name in &iter_arg_names {
let callback = next_values.remove(0);
let adapter = SwiftIterAdapter { callback };
args.insert(
unsafe { &*(name.as_str() as *const str) },
EvalInput::Iter(Box::new(adapter)),
);
}
crate::eval(expr, args).map_err(|e| e.to_string())
}
pub fn handle_shape(handle: &EvalHandle) -> Vec<i64> {
handle.shape().iter().map(|&s| s as i64).collect()
}
pub fn handle_len(handle: &EvalHandle) -> i64 {
handle.len() as i64
}
pub fn handle_to_array_json(handle: EvalHandle) -> Result<String, String> {
let array = handle.to_array().map_err(|e| e.to_string())?;
let results: Vec<NumericResult> = array.iter().copied().collect();
serde_json::to_string(&results).map_err(|e| e.to_string())
}
pub fn handle_scalar_json(handle: EvalHandle) -> Result<String, String> {
let result = handle.scalar().map_err(|e| e.to_string())?;
serde_json::to_string(&result).map_err(|e| e.to_string())
}
pub fn handle_into_iter(handle: EvalHandle) -> EvalIter {
handle.iter()
}
pub fn iter_next_result(iter: &mut EvalIter) -> Option<String> {
iter.next().map(|r| match r {
Ok(v) => serde_json::to_string(&v).unwrap_or_else(|e| format!("{{\"error\":\"{e}\"}}",)),
Err(e) => format!("{{\"error\":\"{e}\"}}"),
})
}
pub fn expr_argument_names(expr: &CompiledExpr) -> Vec<String> {
expr.argument_names().to_vec()
}
pub fn expr_is_complex(expr: &CompiledExpr) -> bool {
expr.is_complex()
}
fn json_to_eval_input(value: &serde_json::Value) -> Result<EvalInput, String> {
match value {
serde_json::Value::Number(n) => {
let f = n
.as_f64()
.ok_or_else(|| "invalid number in args".to_string())?;
Ok(EvalInput::Scalar(f))
}
serde_json::Value::Array(arr) => {
let values: Result<Vec<f64>, String> = arr
.iter()
.map(|v| {
v.as_f64()
.ok_or_else(|| "invalid number in array arg".to_string())
})
.collect();
Ok(EvalInput::from(values?))
}
_ => Err("unsupported arg type: expected number or array".to_string()),
}
}
struct SwiftIterAdapter {
callback: Box<dyn FnMut() -> Option<f64>>,
}
impl Iterator for SwiftIterAdapter {
type Item = f64;
fn next(&mut self) -> Option<f64> {
(self.callback)()
}
}