diff --git a/crates/neon/src/types_impl/extract/mod.rs b/crates/neon/src/types_impl/extract/mod.rs index 043739ca7..997fd9c9c 100644 --- a/crates/neon/src/types_impl/extract/mod.rs +++ b/crates/neon/src/types_impl/extract/mod.rs @@ -108,7 +108,8 @@ use crate::{ types::{JsValue, Value}, }; -pub use self::error::Error; +pub use self::{error::Error, with::With}; + #[cfg(feature = "serde")] #[cfg_attr(docsrs, doc(cfg(feature = "serde")))] pub use self::json::Json; @@ -119,6 +120,7 @@ mod json; mod private; mod try_from_js; mod try_into_js; +mod with; /// Extract Rust data from a JavaScript value pub trait TryFromJs<'cx> diff --git a/crates/neon/src/types_impl/extract/with.rs b/crates/neon/src/types_impl/extract/with.rs new file mode 100644 index 000000000..01caba986 --- /dev/null +++ b/crates/neon/src/types_impl/extract/with.rs @@ -0,0 +1,58 @@ +use crate::{ + context::{Context, Cx}, + result::JsResult, + types::extract::TryIntoJs, +}; + +/// Wraps a closure that will be lazily evaluated when [`TryIntoJs::try_into_js`] is +/// called. +/// +/// Useful for executing arbitrary code on the main thread before returning from a +/// function exported with [`neon::export`](crate::export). +/// +/// ## Example +/// +/// ``` +/// # use neon::{prelude::*, types::extract::{TryIntoJs, With}}; +/// use std::time::Instant; +/// +/// #[neon::export(task)] +/// fn sum(nums: Vec) -> impl for<'cx> TryIntoJs<'cx> { +/// let start = Instant::now(); +/// let sum = nums.into_iter().sum::(); +/// let log = format!("sum took {} ms", start.elapsed().as_millis()); +/// +/// With(move |cx| -> NeonResult<_> { +/// cx.global::("console")? +/// .call_method_with(cx, "log")? +/// .arg(cx.string(log)) +/// .exec(cx)?; +/// +/// Ok(sum) +/// }) +/// } +/// ``` +pub struct With(pub F) +where + // N.B.: We include additional required bounds to allow the compiler to infer the + // correct closure argument when using `impl for<'cx> TryIntoJs<'cx>`. Without + // these bounds, it would be necessary to write a more verbose signature: + // `With FnOnce(&mut Cx<'cx>) -> SomeConcreteReturnType>`. + for<'cx> F: FnOnce(&mut Cx<'cx>) -> O; + +impl<'cx, F, O> TryIntoJs<'cx> for With +where + F: FnOnce(&mut Cx) -> O, + O: TryIntoJs<'cx>, +{ + type Value = O::Value; + + fn try_into_js(self, cx: &mut C) -> JsResult<'cx, Self::Value> + where + C: Context<'cx>, + { + (self.0)(cx.cx_mut()).try_into_js(cx) + } +} + +impl super::private::Sealed for With where for<'cx> F: FnOnce(&mut Cx<'cx>) -> O {}