wasm-rquickjs 0.4.4

Tool for wrapping JavaScript modules as WebAssembly components using the QuickJS engine
Documentation
use futures::future::{AbortHandle, Abortable};
use rquickjs::function::This;
use rquickjs::{Ctx, Persistent, Value};

pub(crate) async fn with_abort_signal<'js, F, T>(
    ctx: &Ctx<'js>,
    signal: Option<Value<'js>>,
    future: F,
) -> rquickjs::Result<T>
where
    F: Future<Output = rquickjs::Result<T>>,
{
    let signal = match signal {
        Some(signal) if !signal.is_undefined() && !signal.is_null() => signal,
        _ => return future.await,
    };
    let signal = rquickjs::Object::from_value(signal)?;
    if signal.get::<_, bool>("aborted")? {
        return Err(ctx.throw(signal.get::<_, Value<'js>>("reason")?));
    }

    let add: rquickjs::Function<'js> = signal.get("addEventListener")?;
    let remove: rquickjs::Function<'js> = signal.get("removeEventListener")?;
    let (handle, registration) = AbortHandle::new_pair();
    let callback = rquickjs::Function::new(ctx.clone(), move || handle.abort())?;
    let options = rquickjs::Object::new(ctx.clone())?;
    options.set("once", true)?;
    add.call::<_, ()>((This(signal.clone()), "abort", callback.clone(), options))?;

    if signal.get::<_, bool>("aborted")? {
        let _ = remove.call::<_, ()>((This(signal.clone()), "abort", callback));
        return Err(ctx.throw(signal.get::<_, Value<'js>>("reason")?));
    }

    let signal = Persistent::save(ctx, signal);
    let callback = Persistent::save(ctx, callback);
    let remove = Persistent::save(ctx, remove);
    let result = Abortable::new(future, registration).await;
    let signal = signal.restore(ctx)?;
    let callback = callback.restore(ctx)?;
    let remove = remove.restore(ctx)?;
    let _ = remove.call::<_, ()>((This(signal.clone()), "abort", callback));

    match result {
        Ok(result) => result,
        Err(_) => Err(ctx.throw(signal.get::<_, Value<'js>>("reason")?)),
    }
}