use std::sync::{Arc, Mutex};
use mumu::parser::interpreter::{DynamicFn, DynamicFnInfo, Interpreter};
use mumu::parser::types::{FunctionValue, Value};
pub fn register_json_report(interp: &mut Interpreter) {
let f: DynamicFn = Arc::new(Mutex::new(json_report_bridge));
let info = DynamicFnInfo::new(f, true); interp.register_dynamic_function_ex("json:report", info);
interp.set_variable(
"json:report",
Value::Function(Box::new(FunctionValue::Named("json:report".to_string()))),
);
}
fn json_report_bridge(_intp: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
match args.len() {
0 => Ok(make_partial(None)),
1 => {
if is_placeholder(&args[0]) {
Ok(make_partial(None))
} else {
Ok(report_result(&args[0]))
}
}
n => Err(format!("json:report ⇒ expected ≤1 argument, got {n}")),
}
}
#[derive(Clone)]
struct PartialState {
arg: Option<Value>,
}
fn make_partial(arg: Option<Value>) -> Value {
use FunctionValue::RustClosure;
let shared = Arc::new(Mutex::new(PartialState { arg }));
let closure: DynamicFn = Arc::new(Mutex::new(
move |_intp: &mut Interpreter, new: Vec<Value>| -> Result<Value, String> {
let mut st = shared
.lock()
.map_err(|_| "json:report partial lock error".to_string())?;
for v in new {
if st.arg.is_none() && !is_placeholder(&v) {
st.arg = Some(v);
} else {
return Err("json:report partial ⇒ too many arguments".to_string());
}
}
match st.arg.clone() {
Some(v) if !is_placeholder(&v) => Ok(report_result(&v)),
_ => Ok(make_partial(st.arg.clone())),
}
},
));
Value::Function(Box::new(RustClosure(
"json:report-partial".to_string(),
closure,
0,
)))
}
fn report_result(v: &Value) -> Value {
match v {
Value::SingleString(s) => parse_and_report(s),
Value::StrArray(sa) if sa.len() == 1 => parse_and_report(&sa[0]),
_ => Value::Bool(true),
}
}
fn parse_and_report(text: &str) -> Value {
match serde_json::from_str::<serde_json::Value>(text) {
Ok(_) => Value::Bool(true),
Err(e) => Value::StrArray(vec![e.to_string()]),
}
}
fn is_placeholder(v: &Value) -> bool {
match v {
Value::Placeholder => true,
Value::SingleString(s) if s == "_" => true,
Value::StrArray(sa) if sa.len() == 1 && sa[0] == "_" => true,
_ => false,
}
}