use crate::error::AnyError;
use crate::serialize_op_result;
use crate::Op;
use crate::OpFn;
use crate::OpState;
use serde::de::DeserializeOwned;
use serde::Serialize;
use std::cell::RefCell;
use std::future::Future;
use std::rc::Rc;
pub fn op_sync<F, A, B, R>(op_fn: F) -> Box<OpFn>
where
F: Fn(&mut OpState, A, B) -> Result<R, AnyError> + 'static,
A: DeserializeOwned,
B: DeserializeOwned,
R: Serialize + 'static,
{
Box::new(move |state, payload| -> Op {
let result = payload
.deserialize()
.and_then(|(a, b)| op_fn(&mut state.borrow_mut(), a, b));
Op::Sync(serialize_op_result(result, state))
})
}
pub fn op_async<F, A, B, R, RV>(op_fn: F) -> Box<OpFn>
where
F: Fn(Rc<RefCell<OpState>>, A, B) -> R + 'static,
A: DeserializeOwned,
B: DeserializeOwned,
R: Future<Output = Result<RV, AnyError>> + 'static,
RV: Serialize + 'static,
{
Box::new(move |state, payload| -> Op {
let pid = payload.promise_id;
let args = match payload.deserialize() {
Ok(args) => args,
Err(err) => {
return Op::Sync(serialize_op_result(Err::<(), AnyError>(err), state))
}
};
let (a, b) = args;
use crate::futures::FutureExt;
let fut = op_fn(state.clone(), a, b)
.map(move |result| (pid, serialize_op_result(result, state)));
Op::Async(Box::pin(fut))
})
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn op_async_stack_trace() {
let mut runtime = crate::JsRuntime::new(Default::default());
async fn op_throw(
_state: Rc<RefCell<OpState>>,
msg: Option<String>,
_: (),
) -> Result<(), AnyError> {
assert_eq!(msg.unwrap(), "hello");
Err(crate::error::generic_error("foo"))
}
runtime.register_op("op_throw", op_async(op_throw));
runtime.sync_ops_cache();
runtime
.execute(
"<init>",
r#"
async function f1() {
await Deno.core.opAsync('op_throw', 'hello');
}
async function f2() {
await f1();
}
f2();
"#,
)
.unwrap();
let e = runtime.run_event_loop(false).await.unwrap_err().to_string();
println!("{}", e);
assert!(e.contains("Error: foo"));
assert!(e.contains("at async f1 (<init>:"));
assert!(e.contains("at async f2 (<init>:"));
}
}