use core_mumu::{
parser::interpreter::apply::apply_n_ary_function_value,
parser::types::{Value, FunctionValue},
Interpreter,
};
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
#[derive(Clone)]
pub struct TimeoutTask {
pub deadline: Instant,
pub callback: Value, }
pub fn event_timeout_bridge(
interp: &mut Interpreter,
args: Vec<Value>
) -> Result<Value, String> {
let verbose = interp.is_verbose();
if verbose {
eprintln!("[event:timeout] => event_timeout_bridge => got {} arg(s): {:?}", args.len(), args);
}
match args.len() {
0 => {
if verbose {
eprintln!("[event:timeout] => zero args => returning partial => (ms=?, cb=?)");
}
Ok(make_partial(None, None, interp.is_verbose()))
}
1 => {
let val = &args[0];
if verbose {
eprintln!("[event:timeout] => single arg => val={:?}", val);
}
match val {
Value::Placeholder => {
if verbose {
eprintln!("[event:timeout] => single arg is Placeholder => partial => (ms=?, cb=?)");
}
Ok(make_partial(None, None, verbose))
}
Value::Int(_) | Value::IntArray(_) => {
if verbose {
eprintln!("[event:timeout] => single arg is int => partial => ms known, cb=?");
}
Ok(make_partial(Some(val.clone()), None, verbose))
}
Value::Function(_) => {
if verbose {
eprintln!("[event:timeout] => single arg is Function => partial => ms=?, cb known");
}
Ok(make_partial(None, Some(val.clone()), verbose))
}
other => {
let msg = format!(
"event:timeout => single argument must be placeholder, int, or function => got: {:?}",
other
);
if verbose {
eprintln!("[event:timeout] => ERROR => {}", msg);
}
Err(msg)
}
}
}
2 => {
let ms_val = &args[0];
let cb_val = &args[1];
let ms_is_pl = matches!(ms_val, Value::Placeholder);
let cb_is_pl = matches!(cb_val, Value::Placeholder);
if verbose {
eprintln!(
"[event:timeout] => 2 args => ms={:?} (placeholder={}), cb={:?} (placeholder={})",
ms_val, ms_is_pl, cb_val, cb_is_pl
);
}
if !ms_is_pl && !cb_is_pl {
if verbose {
eprintln!("[event:timeout] => both real => scheduling now");
}
schedule_timeout(interp, ms_val.clone(), cb_val.clone())
} else {
if verbose {
eprintln!("[event:timeout] => partial usage => returning partial");
}
let maybe_ms = if ms_is_pl { None } else { Some(ms_val.clone()) };
let maybe_cb = if cb_is_pl { None } else { Some(cb_val.clone()) };
Ok(make_partial(maybe_ms, maybe_cb, verbose))
}
}
n => {
let msg = format!(
"event:timeout => expected up to 2 arguments, got {}",
n
);
if verbose {
eprintln!("[event:timeout] => ERROR => {}", msg);
}
Err(msg)
}
}
}
fn make_partial(
ms_opt: Option<Value>,
cb_opt: Option<Value>,
verbose: bool
) -> Value {
Value::Function(Box::new(FunctionValue::RustClosure(
"event:timeout-partial".to_string(),
Arc::new(Mutex::new(move |interp: &mut Interpreter, new_args: Vec<Value>| {
let mut ms_current = ms_opt.clone();
let mut cb_current = cb_opt.clone();
let local_verbose = interp.is_verbose() || verbose;
if local_verbose {
eprintln!(
"[event:timeout-partial] => invoked => got {} new_args => {:?}",
new_args.len(),
new_args
);
eprintln!(
"[event:timeout-partial] => so far => ms_current={:?}, cb_current={:?}",
ms_current, cb_current
);
}
for arg in new_args {
if ms_current.is_none() {
if matches!(arg, Value::Placeholder) {
if local_verbose {
eprintln!("[event:timeout-partial] => new arg is placeholder => ms remains None");
}
} else {
if local_verbose {
eprintln!(
"[event:timeout-partial] => filling ms_current with {:?}",
arg
);
}
ms_current = Some(arg);
}
continue;
}
if cb_current.is_none() {
if matches!(arg, Value::Placeholder) {
if local_verbose {
eprintln!(
"[event:timeout-partial] => new arg is placeholder => cb remains None"
);
}
} else {
if local_verbose {
eprintln!(
"[event:timeout-partial] => filling cb_current with {:?}",
arg
);
}
cb_current = Some(arg);
}
continue;
}
let msg = "event:timeout => partial => too many arguments".to_string();
if local_verbose {
eprintln!("[event:timeout-partial] => ERROR => {}", msg);
}
return Err(msg);
}
if ms_current.is_some() && cb_current.is_some() {
if local_verbose {
eprintln!("[event:timeout-partial] => both ms and cb known => scheduling now");
}
schedule_timeout(
interp,
ms_current.as_ref().unwrap().clone(),
cb_current.as_ref().unwrap().clone()
)
} else {
if local_verbose {
eprintln!("[event:timeout-partial] => still partial => returning closure");
}
Ok(make_partial(ms_current, cb_current, local_verbose))
}
})),
0,
)))
}
fn schedule_timeout(
interp: &mut Interpreter,
ms_val: Value,
cb_val: Value
) -> Result<Value, String> {
let verbose = interp.is_verbose();
if verbose {
eprintln!(
"[event:timeout] => schedule_timeout => ms_val={:?}, cb_val={:?}",
ms_val, cb_val
);
}
let ms_i32 = match ms_val {
Value::Int(i) => i,
Value::IntArray(ref arr) if arr.len() == 1 => arr[0],
other => {
let msg = format!("event:timeout => 'ms' must be an integer, got {:?}", other);
if verbose {
eprintln!("[event:timeout] => schedule_timeout => ERROR => {}", msg);
}
return Err(msg);
}
};
if ms_i32 < 0 {
let msg = "event:timeout => 'ms' must be >= 0".to_string();
if verbose {
eprintln!("[event:timeout] => schedule_timeout => ERROR => {}", msg);
}
return Err(msg);
}
let cb_func = match cb_val {
Value::Function(_) => cb_val,
other => {
let msg = format!(
"event:timeout => second argument must be a Function(...), got {:?}",
other
);
if verbose {
eprintln!("[event:timeout] => schedule_timeout => ERROR => {}", msg);
}
return Err(msg);
}
};
let now = Instant::now();
let deadline = now + Duration::from_millis(ms_i32 as u64);
if verbose {
eprintln!(
"[event:timeout] => schedule_timeout => now={:?}, deadline={:?}, ms_i32={}",
now, deadline, ms_i32
);
}
let task = TimeoutTask {
deadline,
callback: cb_func,
};
crate::push_timeout_task(task, interp);
if verbose {
eprintln!(
"[event:timeout] => schedule_timeout => done => returning Bool(true)"
);
}
Ok(Value::Bool(true))
}
pub fn execute_callback(interp: &mut Interpreter, cb_val: &Value) -> Result<(), String> {
let verbose = interp.is_verbose();
if verbose {
eprintln!(
"[event:timeout] => execute_callback => about to call function with 0 args => val={:?}",
cb_val
);
}
if let Value::Function(cb_func) = cb_val {
let result = apply_n_ary_function_value(interp, cb_func.clone(), vec![]);
if let Err(e) = &result {
if verbose {
eprintln!(
"[event:timeout] => execute_callback => ERROR when calling callback => {}",
e
);
}
}
result.map(|_ignored| ())
} else {
let msg = "execute_callback => value is not a function".to_string();
if verbose {
eprintln!("[event:timeout] => execute_callback => ERROR => {}", msg);
}
Err(msg)
}
}