use std::sync::{Arc, Mutex};
use mumu::parser::interpreter::{DynamicFn, DynamicFnInfo, Interpreter};
use mumu::parser::types::{FunctionValue, Value};
pub fn register_json_validate(interp: &mut Interpreter) {
let f: DynamicFn = Arc::new(Mutex::new(json_validate_bridge));
let info = DynamicFnInfo::new(f, true); interp.register_dynamic_function_ex("json:validate", info);
interp.set_variable(
"json:validate",
Value::Function(Box::new(FunctionValue::Named("json:validate".to_string()))),
);
}
fn json_validate_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(Value::Bool(is_valid_json_value(&args[0])))
}
}
n => Err(format!("json:validate ⇒ 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:validate 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:validate partial ⇒ too many arguments".to_string());
}
}
match st.arg.clone() {
Some(v) if !is_placeholder(&v) => Ok(Value::Bool(is_valid_json_value(&v))),
_ => Ok(make_partial(st.arg.clone())),
}
},
));
Value::Function(Box::new(RustClosure(
"json:validate-partial".to_string(),
closure,
0,
)))
}
fn is_valid_json_value(v: &Value) -> bool {
match v {
Value::SingleString(s) => serde_json::from_str::<serde_json::Value>(s).is_ok(),
Value::StrArray(sa) if sa.len() == 1 => {
serde_json::from_str::<serde_json::Value>(&sa[0]).is_ok()
}
_ => true, }
}
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,
}
}