use std::sync::{Arc, Mutex};
use mumu::parser::interpreter::{DynamicFn, DynamicFnInfo, Interpreter};
use mumu::parser::interpreter::apply::apply_n_ary_function_value;
use mumu::parser::types::{FunctionValue, Value, InkIteratorKind};
use serde_json::Value as JsonVal;
pub fn register_json_decode(interp: &mut Interpreter) {
let f: DynamicFn = Arc::new(Mutex::new(json_decode_bridge));
let info = DynamicFnInfo::new(f, false);
interp.register_dynamic_function_ex("json:decode", info);
interp.set_variable(
"json:decode",
Value::Function(Box::new(FunctionValue::Named("json:decode".to_string()))),
);
}
pub fn register_json_encode(interp: &mut Interpreter) {
let f: DynamicFn = Arc::new(Mutex::new(json_encode_bridge));
let info = DynamicFnInfo::new(f, false);
interp.register_dynamic_function_ex("json:encode", info);
interp.set_variable(
"json:encode",
Value::Function(Box::new(FunctionValue::Named("json:encode".to_string()))),
);
}
fn json_decode_bridge(intp: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
match args.len() {
0 => Ok(make_decode_partial(None)),
1 => {
if is_placeholder(&args[0]) {
Ok(make_decode_partial(None))
} else {
finalize_decode(intp, args[0].clone())
}
}
n => Err(format!("json:decode ⇒ expected ≤1 argument, got {n}")),
}
}
#[derive(Clone)]
struct DecodePartialState {
arg: Option<Value>,
}
fn make_decode_partial(arg: Option<Value>) -> Value {
use FunctionValue::RustClosure;
let shared = Arc::new(Mutex::new(DecodePartialState { arg }));
let closure: DynamicFn = Arc::new(Mutex::new(
move |intp: &mut Interpreter, new_args: Vec<Value>| -> Result<Value, String> {
let mut st = shared
.lock()
.map_err(|_| "json:decode partial lock error".to_string())?;
for v in new_args {
if st.arg.is_none() && !is_placeholder(&v) {
st.arg = Some(v);
} else {
return Err("json:decode partial ⇒ too many arguments".to_string());
}
}
match st.arg.clone() {
Some(v) => finalize_decode(intp, v),
None => Ok(make_decode_partial(None)),
}
},
));
Value::Function(Box::new(RustClosure(
"json:decode-partial".to_string(),
closure,
0,
)))
}
fn finalize_decode(_intp: &mut Interpreter, src: Value) -> Result<Value, String> {
match src {
Value::SingleString(s) => parse_json_string(&s),
Value::StrArray(sa) if sa.len() == 1 => parse_json_string(&sa[0]),
Value::InkIterator(handle) => {
let mut txt = String::new();
match &handle.kind {
InkIteratorKind::Core(state_arc) => {
loop {
let ch_opt = {
let mut g = state_arc.lock().map_err(|_| "ink lock")?;
if g.done || g.current >= g.end {
g.done = true;
None
} else {
let c = g.current;
g.current += 1;
if g.current >= g.end {
g.done = true;
}
Some(c)
}
};
match ch_opt {
Some(code) if (0..=127).contains(&code) => txt.push(code as u8 as char),
Some(code) => return Err(format!("json:decode ⇒ invalid char code {code}")),
None => break,
}
}
}
InkIteratorKind::Plugin(plugin_arc) => {
let mut plugin = plugin_arc.lock().map_err(|_| "ink plugin lock")?;
loop {
match plugin.next_value() {
Ok(Value::Int(code)) if (0..=127).contains(&code) => {
txt.push(code as u8 as char);
}
Ok(other) => return Err(format!("json:decode ⇒ plugin yielded non-int/invalid char: {:?}", other)),
Err(e) if e == "NO_MORE_DATA" => break,
Err(e) => return Err(e),
}
}
}
}
parse_json_string(&txt)
}
Value::InkTransform(fb) => {
let mut txt = String::new();
loop {
match apply_n_ary_function_value(_intp, fb.clone(), vec![]) {
Ok(chunk) => match chunk {
Value::SingleString(s) => txt.push_str(&s),
Value::Int(code) if (0..=127).contains(&code) => {
txt.push(code as u8 as char)
}
_ => {
return Err(
"json:decode transform ⇒ must yield strings or ASCII ints"
.to_string(),
)
}
},
Err(e) if e == "NO_MORE_DATA" => break,
Err(e) => return Err(e),
}
}
parse_json_string(&txt)
}
other => Err(format!(
"json:decode ⇒ argument must be string/ink, got {other:?}"
)),
}
}
fn parse_json_string(text: &str) -> Result<Value, String> {
serde_json::from_str::<JsonVal>(text)
.map(|v| crate::jsonval_to_mumu(&v))
.map_err(|e| format!("json:decode ⇒ parse error: {e}"))
}
fn json_encode_bridge(_intp: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
match args.len() {
1 => finalize_encode(&Value::Bool(false), &args[0]),
2 => finalize_encode(&args[0], &args[1]),
n => Err(format!("json:encode ⇒ expected 1-2 arguments, got {n}")),
}
}
fn finalize_encode(opt: &Value, data: &Value) -> Result<Value, String> {
let (pretty, indent) = parse_options(opt)?;
let mut out = if pretty {
serde_json::to_string_pretty(&crate::mumu_value_to_json(data)).map_err(|e| e.to_string())?
} else {
serde_json::to_string(&crate::mumu_value_to_json(data)).map_err(|e| e.to_string())?
};
if pretty {
if let Some(indent_str) = indent {
out = out.replace(" ", &indent_str);
}
}
Ok(Value::SingleString(out))
}
fn parse_options(v: &Value) -> Result<(bool, Option<String>), String> {
match v {
Value::Bool(false) | Value::Placeholder => Ok((false, None)),
Value::Bool(true) => Ok((true, None)),
Value::KeyedArray(map) => {
let pretty = match map.get("pretty") {
Some(Value::Bool(b)) => *b,
_ => false,
};
let indent = match map.get("indent") {
Some(Value::Int(i)) if *i > 0 => Some(" ".repeat(*i as usize)),
Some(Value::SingleString(s)) if !s.is_empty() => Some(s.clone()),
_ => None,
};
Ok((pretty, indent))
}
other => Err(format!(
"json:encode options must be bool or keyed array, got {other:?}"
)),
}
}
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,
}
}