From 3367d20aa0acfb070743ee905b87cc58e6a0f462 Mon Sep 17 00:00:00 2001 From: "K.J. Valencik" Date: Fri, 27 Sep 2024 13:05:31 -0400 Subject: [PATCH] feat(neon): Add neon::types::extract::Boxed for JsBox conversions --- crates/neon/src/types_impl/extract/boxed.rs | 79 +++++++++++++++++++++ crates/neon/src/types_impl/extract/mod.rs | 3 +- 2 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 crates/neon/src/types_impl/extract/boxed.rs diff --git a/crates/neon/src/types_impl/extract/boxed.rs b/crates/neon/src/types_impl/extract/boxed.rs new file mode 100644 index 000000000..187b2a6bb --- /dev/null +++ b/crates/neon/src/types_impl/extract/boxed.rs @@ -0,0 +1,79 @@ +use crate::{ + context::{Context, Cx}, + handle::Handle, + result::{JsResult, NeonResult, ResultExt}, + types::{ + extract::{private, TryFromJs, TryIntoJs, TypeExpected}, + Finalize, JsBox, JsValue, + }, +}; + +/// Wrapper to extract `T` from a [`JsBox`](JsBox) or create a [`JsBox`] +/// from a `T`. +/// +/// [`Boxed`] is especially useful for exporting async functions and tasks. +/// +/// ``` +/// # use std::sync::Arc; +/// # use neon::{prelude::*, types::extract::Boxed}; +/// struct Greeter { +/// greeting: String, +/// } +/// +/// impl Finalize for Greeter {} +/// +/// impl Greeter { +/// fn new(greeting: String) -> Self { +/// Self { greeting } +/// } +/// +/// fn greet(&self, name: &str) -> String { +/// format!("{}, {name}!", self.greeting) +/// } +/// } +/// +/// #[neon::export] +/// fn create_greeter(greeting: String) -> Boxed> { +/// Boxed(Arc::new(Greeter::new(greeting))) +/// } +/// +/// #[neon::export(task)] +/// fn greet(Boxed(greeter): Boxed>, name: String) -> String { +/// greeter.greet(&name) +/// } +/// ``` +pub struct Boxed(pub T); + +impl<'cx, T> TryFromJs<'cx> for Boxed +where + T: Clone + 'static, +{ + type Error = TypeExpected>; + + fn try_from_js( + cx: &mut Cx<'cx>, + v: Handle<'cx, JsValue>, + ) -> NeonResult> { + match v.downcast::, _>(cx) { + Ok(v) => Ok(Ok(Self(T::clone(&v)))), + Err(_) => Ok(Err(TypeExpected::new())), + } + } + + fn from_js(cx: &mut Cx<'cx>, v: Handle<'cx, JsValue>) -> NeonResult { + Self::try_from_js(cx, v)?.or_throw(cx) + } +} + +impl<'cx, T> TryIntoJs<'cx> for Boxed +where + T: Finalize + 'static, +{ + type Value = JsBox; + + fn try_into_js(self, cx: &mut Cx<'cx>) -> JsResult<'cx, Self::Value> { + Ok(cx.boxed(self.0)) + } +} + +impl private::Sealed for Boxed {} diff --git a/crates/neon/src/types_impl/extract/mod.rs b/crates/neon/src/types_impl/extract/mod.rs index 3e2815ce9..83b6ae58e 100644 --- a/crates/neon/src/types_impl/extract/mod.rs +++ b/crates/neon/src/types_impl/extract/mod.rs @@ -108,12 +108,13 @@ use crate::{ types::{JsValue, Value}, }; -pub use self::{error::Error, with::With}; +pub use self::{boxed::Boxed, error::Error, with::With}; #[cfg(feature = "serde")] #[cfg_attr(docsrs, doc(cfg(feature = "serde")))] pub use self::json::Json; +mod boxed; mod error; #[cfg(feature = "serde")] mod json;