neon-bindings / neon

Rust bindings for writing safe and fast native Node.js modules.
https://www.neon-bindings.com/
Apache License 2.0
8k stars 283 forks source link

feat(neon): Extractors #997

Closed kjvalencik closed 5 months ago

kjvalencik commented 1 year ago

Extract Rust data from JavaScript values and arguments.

fn add(mut cx: FunctionContext) -> JsResult<JsNumber> {
    let (a, b): (f64, f64) = cx.args()?;

    Ok(cx.number(a + b))
}
dherman commented 1 year ago

Since you have to return NeonResult for full generality, I do agree that nested Results for the overloading case would be awkward. But I wonder if we should consider a Result<Option> version of args called something like match_args or args_opt. That would allow you to do the overloading example like this:

fn combine(mut cx: FunctionContext) -> JsResult<JsValue> {
    if let Some((a, b)) = cx.args_opt()? {
        return Ok(add(cx, a, b).upcast());
    }

    let (a, b) = cx.args()?;

    Ok(concat(cx, a, b).upcast())
}

This is slightly more readable IMO, but it also means you're not accidentally suppressing actual JavaScript exceptions that might occur, so it's more correct.

kjvalencik commented 1 year ago

@dherman I'm having a difficult time picturing how that ladders up from the TryFromJs trait. Would we have a specialized error that means "wrong type" that we could use for this? Something like a combination of DowncastResult and Throw?

kjvalencik commented 6 months ago
pub trait TryFromJs<'cx>: Sized {
    type Error: Error + Send + Sync + 'static;

    fn try_from_js<C>(cx: &mut C, v: Handle<'cx, JsValue>) -> NeonResult<Result<Self, Self::Error>>
        where C: Context<'cx>;

    fn from_js<C>(cx: &mut C, v: Handle<'cx, JsValue>) -> NeonResult<Self>
        where C: Context<'cx>
    {
        Self::try_from_js(cx, v)?.or_throw(cx)
    }
}