From 8607a77a9b909a4ed42ab78a44aaa53b4ec60ffb Mon Sep 17 00:00:00 2001 From: David Herman Date: Sat, 12 Nov 2022 16:37:18 -0800 Subject: [PATCH 01/26] API docs for `JsValue` --- crates/neon/src/types_impl/mod.rs | 35 +++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/crates/neon/src/types_impl/mod.rs b/crates/neon/src/types_impl/mod.rs index 047d32722..7c8c0f966 100644 --- a/crates/neon/src/types_impl/mod.rs +++ b/crates/neon/src/types_impl/mod.rs @@ -93,6 +93,41 @@ pub trait Value: private::ValueInternal { } /// A JavaScript value of any type. +/// +/// The `JsValue` type is a catch-all type that sits at the top of the +/// [JavaScript type hierarchy](./index.html#the-javascript-type-hierarchy). +/// All JavaScript values can be safely and statically +/// [upcast](crate::handle::Handle::upcast) to `JsValue`; by contrast, a +/// [downcast](crate::handle::Handle::downcast) of a `JsValue` to another type +/// requires a runtime check. +/// (For TypeScript programmers, this can be thought of as similar to TypeScript's +/// [`unknown`](https://www.typescriptlang.org/docs/handbook/2/functions.html#unknown) +/// type.) +/// +/// The `JsValue` type can be useful for generic, dynamic, or otherwise +/// hard-to-express API signatures, such as overloaded types: +/// +/// ``` +/// # use neon::prelude::*; +/// // Takes a string and adds the specified padding to the left. +/// // If the padding is a string, it's added as-is. +/// // If the padding is a number, then that number of spaces is added. +/// fn pad_left(mut cx: FunctionContext) -> JsResult { +/// let string: Handle = cx.argument(0)?; +/// let padding: Handle = cx.argument(1)?; +/// +/// let padding: String = if let Ok(str) = padding.downcast::(&mut cx) { +/// str.value(&mut cx) +/// } else if let Ok(num) = padding.downcast::(&mut cx) { +/// " ".repeat(num.value(&mut cx) as usize) +/// } else { +/// return cx.throw_type_error("expected string or number"); +/// }; +/// +/// let new_value = padding + &string.value(&mut cx); +/// Ok(cx.string(&new_value)) +/// } +/// ``` #[derive(Debug)] #[repr(transparent)] pub struct JsValue(raw::Local); From ce33685153dc9af3b4948ada95be7d56f6a09026 Mon Sep 17 00:00:00 2001 From: David Herman Date: Sun, 13 Nov 2022 17:01:26 -0800 Subject: [PATCH 02/26] Align the wording style of doc summaries for all entires in `neon::types`. --- crates/neon/src/types_impl/boxed.rs | 9 +++-- crates/neon/src/types_impl/buffer/types.rs | 14 +++++--- crates/neon/src/types_impl/date.rs | 26 ++++++++++----- crates/neon/src/types_impl/error.rs | 4 ++- crates/neon/src/types_impl/mod.rs | 39 ++++++++++++++++------ crates/neon/src/types_impl/promise.rs | 10 ++++-- 6 files changed, 71 insertions(+), 31 deletions(-) diff --git a/crates/neon/src/types_impl/boxed.rs b/crates/neon/src/types_impl/boxed.rs index 78c6415fe..c8933b6f3 100644 --- a/crates/neon/src/types_impl/boxed.rs +++ b/crates/neon/src/types_impl/boxed.rs @@ -29,7 +29,7 @@ mod private { } } -/// A smart pointer for Rust data managed by the JavaScript engine. +/// A type of JavaScript smart pointer object that owns Rust data. /// /// The type `JsBox` provides shared ownership of a value of type `T`, /// allocated in the heap. The data is owned by the JavaScript engine and the @@ -281,8 +281,11 @@ impl<'a, T: Send + 'static> Deref for JsBox { } } -/// Finalize is executed on the main JavaScript thread and executed immediately -/// before garbage collection. +/// A trait for finalizing values owned by the main JavaScript thread. +/// +/// [`Finalize::finalize`] is executed on the main JavaScript thread +/// immediately before garbage collection. +/// /// Values contained by a `JsBox` must implement `Finalize`. /// /// ## Examples diff --git a/crates/neon/src/types_impl/buffer/types.rs b/crates/neon/src/types_impl/buffer/types.rs index edb7b0038..2f612da5a 100644 --- a/crates/neon/src/types_impl/buffer/types.rs +++ b/crates/neon/src/types_impl/buffer/types.rs @@ -25,7 +25,9 @@ macro_rules! doc_comment { {$comment:expr, $decl:item} => { $decl }; } -/// The Node [`Buffer`](https://nodejs.org/api/buffer.html) type. +/// The type of Node +/// [`Buffer`](https://nodejs.org/api/buffer.html) +/// objects. /// /// # Example /// @@ -206,7 +208,9 @@ impl TypedArray for JsBuffer { } } -/// The standard JS [`ArrayBuffer`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer) type. +/// The type of JavaScript +/// [`ArrayBuffer`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer) +/// objects. /// /// # Example /// @@ -420,7 +424,7 @@ pub trait Binary: private::Sealed + Copy { const TYPE_TAG: TypedArrayType; } -/// The family of JS [typed array][typed-arrays] types. +/// The family of JavaScript [typed array][typed-arrays] types. /// /// ## Typed Arrays /// @@ -814,11 +818,11 @@ macro_rules! impl_typed_array { doc_comment! { concat!( - "The standard JS [`", + "The type of JavaScript [`", stringify!($typ), "`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/", stringify!($typ), - ") type. + ") objects. # Example diff --git a/crates/neon/src/types_impl/date.rs b/crates/neon/src/types_impl/date.rs index 4095c8b54..2a38b015b 100644 --- a/crates/neon/src/types_impl/date.rs +++ b/crates/neon/src/types_impl/date.rs @@ -13,7 +13,9 @@ use crate::{ sys::{self, raw}, }; -/// A JavaScript Date object +/// The type of JavaScript +/// [`Date`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) +/// objects. #[cfg_attr(docsrs, doc(cfg(feature = "napi-5")))] #[derive(Debug)] #[repr(transparent)] @@ -39,7 +41,7 @@ impl Managed for JsDate { } } -/// The Error struct for a Date +/// An error produced when constructing a date with an invalid value. #[derive(Debug)] #[cfg_attr(docsrs, doc(cfg(feature = "napi-5")))] pub struct DateError(DateErrorKind); @@ -58,11 +60,15 @@ impl fmt::Display for DateError { impl Error for DateError {} -/// The error kinds corresponding to `DateError` +/// The error kinds of a [`DateError`](DateError). #[derive(Debug, Copy, Clone, PartialEq)] #[cfg_attr(docsrs, doc(cfg(feature = "napi-5")))] pub enum DateErrorKind { + /// Produced for an initialization value greater than + /// [`JsDate::MAX_VALUE`](JsDate::MAX_VALUE). Overflow, + /// Produced for an initialization value lesser than + /// [`JsDate::MIN_VALUE`](JsDate::MIN_VALUE). Underflow, } @@ -83,12 +89,14 @@ impl<'a, T: Value> ResultExt> for Result, DateError> } impl JsDate { - /// The smallest possible Date value, defined by ECMAScript. See + /// The smallest possible `Date` value, + /// [defined by ECMAScript](https://www.ecma-international.org/ecma-262/5.1/#sec-15.7.3.3). pub const MIN_VALUE: f64 = -8.64e15; - /// The largest possible Date value, defined by ECMAScript. See + /// The largest possible `Date` value, + /// [defined by ECMAScript](https://www.ecma-international.org/ecma-262/5.1/#sec-15.7.3.2). pub const MAX_VALUE: f64 = 8.64e15; - /// Creates a new Date. It errors when `value` is outside the range of valid JavaScript Date values. When `value` + /// Creates a new `Date`. It errors when `value` is outside the range of valid JavaScript Date values. When `value` /// is `NaN`, the operation will succeed but with an invalid Date pub fn new<'a, C: Context<'a>, T: Into>( cx: &mut C, @@ -108,7 +116,7 @@ impl JsDate { Ok(date) } - /// Creates a new Date with lossy conversion for out of bounds Date values. Out of bounds + /// Creates a new `Date` with lossy conversion for out of bounds Date values. Out of bounds /// values will be treated as NaN pub fn new_lossy<'a, C: Context<'a>, V: Into>(cx: &mut C, value: V) -> Handle<'a, JsDate> { let env = cx.env().to_raw(); @@ -116,13 +124,13 @@ impl JsDate { Handle::new_internal(JsDate(local)) } - /// Gets the Date's value. An invalid Date will return `std::f64::NaN` + /// Gets the `Date`'s value. An invalid Date will return `std::f64::NaN` pub fn value<'a, C: Context<'a>>(&self, cx: &mut C) -> f64 { let env = cx.env().to_raw(); unsafe { sys::date::value(env, self.to_raw()) } } - /// Checks if the Date's value is valid. A Date is valid if its value is between + /// Checks if the `Date`'s value is valid. A Date is valid if its value is between /// `JsDate::MIN_VALUE` and `JsDate::MAX_VALUE` or if it is `NaN` pub fn is_valid<'a, C: Context<'a>>(&self, cx: &mut C) -> bool { let value = self.value(cx); diff --git a/crates/neon/src/types_impl/error.rs b/crates/neon/src/types_impl/error.rs index d4e7a002d..30094eebe 100644 --- a/crates/neon/src/types_impl/error.rs +++ b/crates/neon/src/types_impl/error.rs @@ -11,7 +11,9 @@ use crate::{ types::{build, private::ValueInternal, utf8::Utf8, Value}, }; -/// A JS `Error` object. +/// The type of JavaScript +/// [`Error`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) +/// objects. #[repr(transparent)] #[derive(Debug)] pub struct JsError(raw::Local); diff --git a/crates/neon/src/types_impl/mod.rs b/crates/neon/src/types_impl/mod.rs index 7c8c0f966..005b69289 100644 --- a/crates/neon/src/types_impl/mod.rs +++ b/crates/neon/src/types_impl/mod.rs @@ -92,7 +92,7 @@ pub trait Value: private::ValueInternal { } } -/// A JavaScript value of any type. +/// The type of any JavaScript value, i.e., the root of all types. /// /// The `JsValue` type is a catch-all type that sits at the top of the /// [JavaScript type hierarchy](./index.html#the-javascript-type-hierarchy). @@ -168,7 +168,9 @@ impl JsValue { } } -/// The JavaScript `undefined` value. +/// The type of JavaScript +/// [`undefined`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#primitive_values) +/// primitives. #[derive(Debug)] #[repr(transparent)] pub struct JsUndefined(raw::Local); @@ -217,7 +219,9 @@ impl private::ValueInternal for JsUndefined { } } -/// The JavaScript `null` value. +/// The type of JavaScript +/// [`null`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#primitive_values) +/// primitives. #[derive(Debug)] #[repr(transparent)] pub struct JsNull(raw::Local); @@ -266,7 +270,9 @@ impl private::ValueInternal for JsNull { } } -/// A JavaScript boolean primitive value. +/// The type of JavaScript +/// [Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#primitive_values) +/// primitives. #[derive(Debug)] #[repr(transparent)] pub struct JsBoolean(raw::Local); @@ -320,12 +326,14 @@ impl private::ValueInternal for JsBoolean { } } -/// A JavaScript string primitive value. +/// The type of JavaScript +/// [string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#primitive_values) +/// primitives. #[derive(Debug)] #[repr(transparent)] pub struct JsString(raw::Local); -/// An error produced when constructing a string that exceeds the JS engine's maximum string size. +/// An error produced when constructing a string that exceeds the limits of the runtime. #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug)] pub struct StringOverflow(usize); @@ -427,7 +435,9 @@ impl JsString { } } -/// A JavaScript number value. +/// The type of JavaScript +/// [number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#primitive_values) +/// primitives. #[derive(Debug)] #[repr(transparent)] pub struct JsNumber(raw::Local); @@ -481,7 +491,9 @@ impl private::ValueInternal for JsNumber { } } -/// A JavaScript object. +/// The type of JavaScript +/// [objects](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#objects), +/// i.e., the root of all object types. #[derive(Debug)] #[repr(transparent)] pub struct JsObject(raw::Local); @@ -536,7 +548,12 @@ impl JsObject { } } -/// A JavaScript array object, i.e. a value for which `Array.isArray` +/// The type of JavaScript +/// [`Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) +/// objects. +/// +/// An array is any JavaScript value for which +/// [`Array.isArray`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray) /// would return `true`. #[derive(Debug)] #[repr(transparent)] @@ -615,7 +632,9 @@ impl private::ValueInternal for JsArray { impl Object for JsArray {} -/// A JavaScript function object. +/// The type of JavaScript +/// [`Function`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function) +/// objects. #[derive(Debug)] #[repr(transparent)] /// diff --git a/crates/neon/src/types_impl/promise.rs b/crates/neon/src/types_impl/promise.rs index 314c62bfb..bce1c9e4d 100644 --- a/crates/neon/src/types_impl/promise.rs +++ b/crates/neon/src/types_impl/promise.rs @@ -49,7 +49,9 @@ const BOUNDARY: FailureBoundary = FailureBoundary { feature = "promise-api", deprecated = "`promise-api` feature has no impact and may be removed" )] -/// The JavaScript [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) value. +/// The type of JavaScript +/// [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) +/// objects. /// /// [`JsPromise`] may be constructed with [`Context::promise`]. pub struct JsPromise(raw::Local); @@ -190,7 +192,7 @@ impl Value for JsPromise {} impl Object for JsPromise {} -/// [`Deferred`] is a handle that can be used to resolve or reject a [`JsPromise`] +/// A controller struct that can be used to resolve or reject a [`JsPromise`]. /// /// It is recommended to settle a [`Deferred`] with [`Deferred::settle_with`] to ensure /// exceptions are caught. @@ -348,7 +350,9 @@ impl Drop for Deferred { #[cfg(all(feature = "napi-5", feature = "futures"))] #[cfg_attr(docsrs, doc(cfg(all(feature = "napi-5", feature = "futures"))))] -/// A [`Future`](std::future::Future) created from a [`JsPromise`]. +/// A type of JavaScript +/// [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) +/// object that acts as a [`Future`](std::future::Future). /// /// Unlike typical `Future`, `JsFuture` are eagerly executed because they /// are backed by a `Promise`. From 977f1cdb147d77f11d9658b649521d184891ff92 Mon Sep 17 00:00:00 2001 From: David Herman Date: Sun, 13 Nov 2022 17:14:45 -0800 Subject: [PATCH 03/26] Remove redundant markdown. --- crates/neon/src/types_impl/date.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/neon/src/types_impl/date.rs b/crates/neon/src/types_impl/date.rs index 2a38b015b..3d6ae8abc 100644 --- a/crates/neon/src/types_impl/date.rs +++ b/crates/neon/src/types_impl/date.rs @@ -60,7 +60,7 @@ impl fmt::Display for DateError { impl Error for DateError {} -/// The error kinds of a [`DateError`](DateError). +/// The error kinds of a [`DateError`]. #[derive(Debug, Copy, Clone, PartialEq)] #[cfg_attr(docsrs, doc(cfg(feature = "napi-5")))] pub enum DateErrorKind { From fd50eeabca6463a17a922513b45d6cbcf839600a Mon Sep 17 00:00:00 2001 From: David Herman Date: Sat, 19 Nov 2022 07:38:59 -0800 Subject: [PATCH 04/26] `JsArray` example and method docs. --- crates/neon/src/types_impl/mod.rs | 37 +++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/crates/neon/src/types_impl/mod.rs b/crates/neon/src/types_impl/mod.rs index 005b69289..11d27cb02 100644 --- a/crates/neon/src/types_impl/mod.rs +++ b/crates/neon/src/types_impl/mod.rs @@ -555,11 +555,39 @@ impl JsObject { /// An array is any JavaScript value for which /// [`Array.isArray`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray) /// would return `true`. +/// +/// # Example +/// +/// ``` +/// # use neon::prelude::*; +/// # fn foo(mut cx: FunctionContext) -> JsResult { +/// // Create a new empty array: +/// let a = cx.empty_array(); +/// +/// // Create some new values to push onto the array: +/// let n = cx.number(17); +/// let s = cx.string("hello"); +/// +/// // Push the elements onto the array: +/// a.set(&mut cx, 0, n)?; +/// a.set(&mut cx, 1, s)?; +/// # Ok(a) +/// # } +/// ``` #[derive(Debug)] #[repr(transparent)] pub struct JsArray(raw::Local); impl JsArray { + /// Constructs a new empty array of length `len`, equivalent to the JavaScript + /// expression `new Array(len)`. + /// + /// Note that for non-zero `len`, this creates a + /// [sparse array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Indexed_collections#sparse_arrays), + /// which can sometimes have surprising behavior. To ensure that a new array + /// is and remains dense (i.e., not sparse), consider creating an empty array + /// with `JsArray::new(cx, 0)` or `cx.empty_array()` and only appending + /// elements to the end of the array. pub fn new<'a, C: Context<'a>>(cx: &mut C, len: u32) -> Handle<'a, JsArray> { JsArray::new_internal(cx.env(), len) } @@ -572,6 +600,11 @@ impl JsArray { } } + /// Copies the array contents into a new [`Vec`] by iterating through all indices + /// from 0 to `self.len()`. + /// + /// The length is dynamically checked on each iteration in case the array is modified + /// during the computation. pub fn to_vec<'a, C: Context<'a>>(&self, cx: &mut C) -> NeonResult>> { let mut result = Vec::with_capacity(self.len_inner(cx.env()) as usize); let mut i = 0; @@ -591,10 +624,14 @@ impl JsArray { } #[allow(clippy::len_without_is_empty)] + /// Returns the length of the array, equivalent to the JavaScript expression + /// [`this.length`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/length). pub fn len<'a, C: Context<'a>>(&self, cx: &mut C) -> u32 { self.len_inner(cx.env()) } + /// Indicates whether the array is empty, equivalent to + /// `self.len() == 0`. pub fn is_empty<'a, C: Context<'a>>(&self, cx: &mut C) -> bool { self.len(cx) == 0 } From 942dc887e6a4b2316358dc8e067778380e048e36 Mon Sep 17 00:00:00 2001 From: David Herman Date: Sat, 19 Nov 2022 08:11:25 -0800 Subject: [PATCH 05/26] Add explicit `JsArray` type annotation to code example --- crates/neon/src/types_impl/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/neon/src/types_impl/mod.rs b/crates/neon/src/types_impl/mod.rs index 11d27cb02..64ddf0529 100644 --- a/crates/neon/src/types_impl/mod.rs +++ b/crates/neon/src/types_impl/mod.rs @@ -562,7 +562,7 @@ impl JsObject { /// # use neon::prelude::*; /// # fn foo(mut cx: FunctionContext) -> JsResult { /// // Create a new empty array: -/// let a = cx.empty_array(); +/// let a: Handle = cx.empty_array(); /// /// // Create some new values to push onto the array: /// let n = cx.number(17); From 21df710415f0e82df867a500fac392c139a57dfd Mon Sep 17 00:00:00 2001 From: David Herman Date: Sat, 19 Nov 2022 10:35:36 -0800 Subject: [PATCH 06/26] `JsFunction` API docs --- crates/neon/src/types_impl/mod.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/crates/neon/src/types_impl/mod.rs b/crates/neon/src/types_impl/mod.rs index 64ddf0529..49251cf69 100644 --- a/crates/neon/src/types_impl/mod.rs +++ b/crates/neon/src/types_impl/mod.rs @@ -783,6 +783,7 @@ impl JsFunction { } #[cfg(feature = "napi-5")] + /// Returns a new `JsFunction` implemented by `f`. pub fn new<'a, C, F, V>(cx: &mut C, f: F) -> JsResult<'a, JsFunction> where C: Context<'a>, @@ -834,6 +835,9 @@ impl JsFunction { } impl JsFunction { + /// Calls this function. + /// + /// See also: [`JsFunction::call_with`]. pub fn call<'a, 'b, C: Context<'a>, T, AS>( &self, cx: &mut C, @@ -851,6 +855,7 @@ impl JsFunction { }) } + /// Calls this function for side effect, discarding its result. pub fn exec<'a, 'b, C: Context<'a>, T, AS>( &self, cx: &mut C, @@ -865,6 +870,9 @@ impl JsFunction { Ok(()) } + /// Calls this function as a constructor. + /// + /// See also: [`JsFunction::construct_with`]. pub fn construct<'a, 'b, C: Context<'a>, AS>(&self, cx: &mut C, args: AS) -> JsResult<'a, CL> where AS: AsRef<[Handle<'b, JsValue>]>, From 8aca9557a2e77674946e74816e1147fab3920bb0 Mon Sep 17 00:00:00 2001 From: David Herman Date: Mon, 21 Nov 2022 17:07:27 -0800 Subject: [PATCH 07/26] API docs for `JsUndefined` --- crates/neon/src/types_impl/mod.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/crates/neon/src/types_impl/mod.rs b/crates/neon/src/types_impl/mod.rs index 49251cf69..9b942cfaf 100644 --- a/crates/neon/src/types_impl/mod.rs +++ b/crates/neon/src/types_impl/mod.rs @@ -171,11 +171,33 @@ impl JsValue { /// The type of JavaScript /// [`undefined`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#primitive_values) /// primitives. +/// +/// # Example +/// +/// ``` +/// # use neon::prelude::*; +/// # fn test(mut cx: FunctionContext) -> JsResult { +/// // Extract the console.log function: +/// let console: Handle = cx.global().get(&mut cx, "console")?; +/// let log: Handle = console.get(&mut cx, "log")?; +/// +/// // The undefined value: +/// let undefined = cx.undefined(); +/// +/// // Call console.log(undefined): +/// log.call_with(&cx).arg(undefined).exec(&mut cx)?; +/// # Ok(undefined) +/// # } +/// ``` #[derive(Debug)] #[repr(transparent)] pub struct JsUndefined(raw::Local); impl JsUndefined { + /// Creates an `undefined` value. + /// + /// Although this method can be called many times, all `undefined` + /// values are indistinguishable. pub fn new<'a, C: Context<'a>>(cx: &mut C) -> Handle<'a, JsUndefined> { JsUndefined::new_internal(cx.env()) } From 68356d8b8020b2efe2c74bb28dfc522acf8468c8 Mon Sep 17 00:00:00 2001 From: David Herman Date: Mon, 21 Nov 2022 18:15:59 -0800 Subject: [PATCH 08/26] API docs for: - null - Boolean - number - object --- crates/neon/src/types_impl/buffer/types.rs | 4 + crates/neon/src/types_impl/error.rs | 6 ++ crates/neon/src/types_impl/mod.rs | 101 ++++++++++++++++++++- 3 files changed, 109 insertions(+), 2 deletions(-) diff --git a/crates/neon/src/types_impl/buffer/types.rs b/crates/neon/src/types_impl/buffer/types.rs index 2f612da5a..026731dad 100644 --- a/crates/neon/src/types_impl/buffer/types.rs +++ b/crates/neon/src/types_impl/buffer/types.rs @@ -52,6 +52,8 @@ pub struct JsBuffer(raw::Local); impl JsBuffer { /// Constructs a new `Buffer` object, safely zero-filled. + /// + /// **See also:** [`Context::buffer`] pub fn new<'a, C: Context<'a>>(cx: &mut C, len: usize) -> JsResult<'a, Self> { let result = unsafe { sys::buffer::new(cx.env().to_raw(), len) }; @@ -235,6 +237,8 @@ pub struct JsArrayBuffer(raw::Local); impl JsArrayBuffer { /// Constructs a new `JsArrayBuffer` object, safely zero-filled. + /// + /// **See also:** [`Context::array_buffer`] pub fn new<'a, C: Context<'a>>(cx: &mut C, len: usize) -> JsResult<'a, Self> { let result = unsafe { sys::arraybuffer::new(cx.env().to_raw(), len) }; diff --git a/crates/neon/src/types_impl/error.rs b/crates/neon/src/types_impl/error.rs index 30094eebe..f22bc9feb 100644 --- a/crates/neon/src/types_impl/error.rs +++ b/crates/neon/src/types_impl/error.rs @@ -52,6 +52,8 @@ impl Object for JsError {} impl JsError { /// Creates a direct instance of the [`Error`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error) class. + /// + /// **See also:** [`Context::error`] pub fn error<'a, C: Context<'a>, S: AsRef>( cx: &mut C, msg: S, @@ -64,6 +66,8 @@ impl JsError { } /// Creates an instance of the [`TypeError`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/TypeError) class. + /// + /// **See also:** [`Context::type_error`] pub fn type_error<'a, C: Context<'a>, S: AsRef>( cx: &mut C, msg: S, @@ -76,6 +80,8 @@ impl JsError { } /// Creates an instance of the [`RangeError`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RangeError) class. + /// + /// **See also:** [`Context::range_error`] pub fn range_error<'a, C: Context<'a>, S: AsRef>( cx: &mut C, msg: S, diff --git a/crates/neon/src/types_impl/mod.rs b/crates/neon/src/types_impl/mod.rs index 9b942cfaf..5d0955fe2 100644 --- a/crates/neon/src/types_impl/mod.rs +++ b/crates/neon/src/types_impl/mod.rs @@ -198,6 +198,8 @@ impl JsUndefined { /// /// Although this method can be called many times, all `undefined` /// values are indistinguishable. + /// + /// **See also:** [`Context::undefined`] pub fn new<'a, C: Context<'a>>(cx: &mut C) -> Handle<'a, JsUndefined> { JsUndefined::new_internal(cx.env()) } @@ -244,11 +246,35 @@ impl private::ValueInternal for JsUndefined { /// The type of JavaScript /// [`null`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#primitive_values) /// primitives. +/// +/// # Example +/// +/// ``` +/// # use neon::prelude::*; +/// # fn test(mut cx: FunctionContext) -> JsResult { +/// // Extract the console.log function: +/// let console: Handle = cx.global().get(&mut cx, "console")?; +/// let log: Handle = console.get(&mut cx, "log")?; +/// +/// // The null value: +/// let null = cx.null(); +/// +/// // Call console.log(undefined): +/// log.call_with(&cx).arg(null).exec(&mut cx)?; +/// # Ok(null) +/// # } +/// ``` #[derive(Debug)] #[repr(transparent)] pub struct JsNull(raw::Local); impl JsNull { + /// Creates a `null` value. + /// + /// Although this method can be called many times, all `null` + /// values are indistinguishable. + /// + /// **See also:** [`Context::null`] pub fn new<'a, C: Context<'a>>(cx: &mut C) -> Handle<'a, JsNull> { JsNull::new_internal(cx.env()) } @@ -295,11 +321,33 @@ impl private::ValueInternal for JsNull { /// The type of JavaScript /// [Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#primitive_values) /// primitives. +/// +/// # Example +/// +/// ``` +/// # use neon::prelude::*; +/// # fn test(mut cx: FunctionContext) -> JsResult { +/// // Extract the console.log function: +/// let console: Handle = cx.global().get(&mut cx, "console")?; +/// let log: Handle = console.get(&mut cx, "log")?; +/// +/// // The two Boolean values: +/// let t = cx.boolean(true); +/// let f = cx.boolean(false); +/// +/// // Call console.log(true, false): +/// log.call_with(&cx).arg(t).arg(f).exec(&mut cx)?; +/// # Ok(cx.undefined()) +/// # } +/// ``` #[derive(Debug)] #[repr(transparent)] pub struct JsBoolean(raw::Local); impl JsBoolean { + /// Creates a Boolean value with value `b`. + /// + /// **See also:** [`Context::boolean`] pub fn new<'a, C: Context<'a>>(cx: &mut C, b: bool) -> Handle<'a, JsBoolean> { JsBoolean::new_internal(cx.env(), b) } @@ -312,6 +360,7 @@ impl JsBoolean { } } + /// Returns the value of this Boolean as a Rust `bool`. pub fn value<'a, C: Context<'a>>(&self, cx: &mut C) -> bool { let env = cx.env().to_raw(); unsafe { sys::primitive::boolean_value(env, self.to_raw()) } @@ -460,11 +509,32 @@ impl JsString { /// The type of JavaScript /// [number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#primitive_values) /// primitives. +/// +/// # Example +/// +/// ``` +/// # use neon::prelude::*; +/// # fn test(mut cx: FunctionContext) -> JsResult { +/// // Extract the console.log function: +/// let console: Handle = cx.global().get(&mut cx, "console")?; +/// let log: Handle = console.get(&mut cx, "log")?; +/// +/// // Create a number: +/// let n = cx.number(17.0); +/// +/// // Call console.log(n): +/// log.call_with(&cx).arg(n).exec(&mut cx)?; +/// # Ok(cx.undefined()) +/// # } +/// ``` #[derive(Debug)] #[repr(transparent)] pub struct JsNumber(raw::Local); impl JsNumber { + /// Creates a new number with value `x`. + /// + /// **See also:** [`Context::number`] pub fn new<'a, C: Context<'a>, T: Into>(cx: &mut C, x: T) -> Handle<'a, JsNumber> { JsNumber::new_internal(cx.env(), x.into()) } @@ -477,6 +547,7 @@ impl JsNumber { } } + /// Returns the value of this number as a Rust `f64`. pub fn value<'a, C: Context<'a>>(&self, cx: &mut C) -> f64 { let env = cx.env().to_raw(); unsafe { sys::primitive::number_value(env, self.to_raw()) } @@ -516,6 +587,27 @@ impl private::ValueInternal for JsNumber { /// The type of JavaScript /// [objects](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#objects), /// i.e., the root of all object types. +/// +/// # Example +/// +/// ``` +/// # use neon::prelude::*; +/// # fn test(mut cx: FunctionContext) -> JsResult { +/// // Extract the console.log function: +/// let console: Handle = cx.global().get(&mut cx, "console")?; +/// let log: Handle = console.get(&mut cx, "log")?; +/// +/// // Create an object: +/// let obj = cx.empty_object(); +/// +/// obj.set(&mut cx, "name", cx.string("Neon"))?; +/// obj.set(&mut cx, "url", cx.string("https://neon-bindings.com"))?; +/// +/// // Call console.log(obj): +/// log.call_with(&cx).arg(obj).exec(&mut cx)?; +/// # Ok(cx.undefined()) +/// # } +/// ``` #[derive(Debug)] #[repr(transparent)] pub struct JsObject(raw::Local); @@ -553,6 +645,9 @@ impl private::ValueInternal for JsObject { impl Object for JsObject {} impl JsObject { + /// Creates a new empty object. + /// + /// **See also:** [`Context::empty_object`] pub fn new<'a, C: Context<'a>>(c: &mut C) -> Handle<'a, JsObject> { JsObject::new_internal(c.env()) } @@ -610,6 +705,8 @@ impl JsArray { /// is and remains dense (i.e., not sparse), consider creating an empty array /// with `JsArray::new(cx, 0)` or `cx.empty_array()` and only appending /// elements to the end of the array. + /// + /// **See also:** [`Context::empty_array`] pub fn new<'a, C: Context<'a>>(cx: &mut C, len: u32) -> Handle<'a, JsArray> { JsArray::new_internal(cx.env(), len) } @@ -859,7 +956,7 @@ impl JsFunction { impl JsFunction { /// Calls this function. /// - /// See also: [`JsFunction::call_with`]. + /// **See also:** [`JsFunction::call_with`]. pub fn call<'a, 'b, C: Context<'a>, T, AS>( &self, cx: &mut C, @@ -894,7 +991,7 @@ impl JsFunction { /// Calls this function as a constructor. /// - /// See also: [`JsFunction::construct_with`]. + /// **See also:** [`JsFunction::construct_with`]. pub fn construct<'a, 'b, C: Context<'a>, AS>(&self, cx: &mut C, args: AS) -> JsResult<'a, CL> where AS: AsRef<[Handle<'b, JsValue>]>, From f12525635fb2d528d982bd7609a75d2bed2270b3 Mon Sep 17 00:00:00 2001 From: David Herman Date: Mon, 21 Nov 2022 18:24:56 -0800 Subject: [PATCH 09/26] `JsError` example --- crates/neon/src/types_impl/error.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/crates/neon/src/types_impl/error.rs b/crates/neon/src/types_impl/error.rs index f22bc9feb..834ac3043 100644 --- a/crates/neon/src/types_impl/error.rs +++ b/crates/neon/src/types_impl/error.rs @@ -14,6 +14,24 @@ use crate::{ /// The type of JavaScript /// [`Error`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) /// objects. +/// +/// # Example +/// +/// ``` +/// # use neon::prelude::*; +/// # fn test(mut cx: FunctionContext) -> JsResult { +/// // Create a type error: +/// let err = cx.type_error("expected a number, found a string"); +/// +/// // Add some custom diagnostic properties to the error: +/// err.set(&mut cx, "expected", cx.string("number"))?; +/// err.set(&mut cx, "found", cx.string("string"))?; +/// +/// // Throw the error: +/// cx.throw(err)?; +/// # Ok(cx.undefined()) +/// # } +/// ``` #[repr(transparent)] #[derive(Debug)] pub struct JsError(raw::Local); From 13fe17806121647c4287c4d03cae29333c1c2104 Mon Sep 17 00:00:00 2001 From: David Herman Date: Wed, 23 Nov 2022 11:10:16 -0800 Subject: [PATCH 10/26] `JsDate` API docs --- crates/neon/src/types_impl/date.rs | 51 ++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 7 deletions(-) diff --git a/crates/neon/src/types_impl/date.rs b/crates/neon/src/types_impl/date.rs index 3d6ae8abc..5a9027737 100644 --- a/crates/neon/src/types_impl/date.rs +++ b/crates/neon/src/types_impl/date.rs @@ -16,6 +16,42 @@ use crate::{ /// The type of JavaScript /// [`Date`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) /// objects. +/// +/// # Example +/// +/// The following shows an example of converting Rust +/// [`SystemTime`](std::time::SystemTime) timestamps to JavaScript `Date` objects. +/// +/// ``` +/// # use neon::prelude::*; +/// use neon::types::JsDate; +/// use std::fs::File; +/// use std::time::SystemTime; +/// +/// /// Return the "modified" timestamp for the file at the given path. +/// /// (A more robust implementation would do more detailed error reporting +/// /// and handle edge cases for u128 -> f64 timestamp conversion.) +/// fn last_modified(path: &str) -> Option { +/// Some(File::open(&path).ok()? +/// .metadata().ok()? +/// .modified().ok()? +/// .duration_since(SystemTime::UNIX_EPOCH).ok()? +/// .as_millis() as f64) +/// } +/// +/// fn modified(mut cx: FunctionContext) -> JsResult { +/// let path: Handle = cx.argument(0)?; +/// +/// let modified = last_modified(&path.value(&mut cx)) +/// .and_then(|n| cx.date(n).ok()); +/// +/// if let Some(date) = modified { +/// Ok(date) +/// } else { +/// cx.throw_error("failed to get timestamp") +/// } +/// } +/// ``` #[cfg_attr(docsrs, doc(cfg(feature = "napi-5")))] #[derive(Debug)] #[repr(transparent)] @@ -96,8 +132,9 @@ impl JsDate { /// [defined by ECMAScript](https://www.ecma-international.org/ecma-262/5.1/#sec-15.7.3.2). pub const MAX_VALUE: f64 = 8.64e15; - /// Creates a new `Date`. It errors when `value` is outside the range of valid JavaScript Date values. When `value` - /// is `NaN`, the operation will succeed but with an invalid Date + /// Creates a new `Date`. It errors when `value` is outside the range of valid JavaScript + /// `Date` values. When `value` is `NaN`, the operation will succeed but with an + /// invalid `Date`. pub fn new<'a, C: Context<'a>, T: Into>( cx: &mut C, value: T, @@ -116,22 +153,22 @@ impl JsDate { Ok(date) } - /// Creates a new `Date` with lossy conversion for out of bounds Date values. Out of bounds - /// values will be treated as NaN + /// Creates a new `Date` with lossy conversion for out of bounds `Date` values. + /// Out of bounds values will be treated as `NaN`. pub fn new_lossy<'a, C: Context<'a>, V: Into>(cx: &mut C, value: V) -> Handle<'a, JsDate> { let env = cx.env().to_raw(); let local = unsafe { sys::date::new_date(env, value.into()) }; Handle::new_internal(JsDate(local)) } - /// Gets the `Date`'s value. An invalid Date will return `std::f64::NaN` + /// Gets the `Date`'s value. An invalid `Date` will return [`std::f64::NAN`]. pub fn value<'a, C: Context<'a>>(&self, cx: &mut C) -> f64 { let env = cx.env().to_raw(); unsafe { sys::date::value(env, self.to_raw()) } } - /// Checks if the `Date`'s value is valid. A Date is valid if its value is between - /// `JsDate::MIN_VALUE` and `JsDate::MAX_VALUE` or if it is `NaN` + /// Checks if the `Date`'s value is valid. A `Date` is valid if its value is + /// between [`JsDate::MIN_VALUE`] and [`JsDate::MAX_VALUE`] or if it is `NaN`. pub fn is_valid<'a, C: Context<'a>>(&self, cx: &mut C) -> bool { let value = self.value(cx); (JsDate::MIN_VALUE..=JsDate::MAX_VALUE).contains(&value) From f774d7619f33858c678ee487bd3bad4897fd1d8d Mon Sep 17 00:00:00 2001 From: David Herman Date: Wed, 23 Nov 2022 22:14:09 -0800 Subject: [PATCH 11/26] Use easy-cast library to clean up the numeric conversion logic in the Date example. --- Cargo.lock | 7 +++++++ crates/neon/Cargo.toml | 3 ++- crates/neon/src/types_impl/date.rs | 8 ++++---- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7b47f5275..b290e04ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -132,6 +132,12 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" +[[package]] +name = "easy-cast" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2953e4bb4103fd7db41b83431f681a583e2c18e529beb4ca6c679a0dad16c8d" + [[package]] name = "either" version = "1.8.0" @@ -258,6 +264,7 @@ dependencies = [ "anyhow", "aquamarine", "doc-comment", + "easy-cast", "getrandom", "libloading", "neon-macros", diff --git a/crates/neon/Cargo.toml b/crates/neon/Cargo.toml index cbee3ea1c..5c4761265 100644 --- a/crates/neon/Cargo.toml +++ b/crates/neon/Cargo.toml @@ -27,6 +27,7 @@ smallvec = "1.4.2" once_cell = "1.10.0" neon-macros = { version = "=1.0.0-alpha.1", path = "../neon-macros" } aquamarine = { version = "0.1.11", optional = true } +easy-cast = { version = "0.5.1", optional = true } doc-comment = { version = "0.3.3", optional = true } [dependencies.tokio] @@ -79,7 +80,7 @@ task-api = [] proc-macros = [] # Enables the optional dependencies that are only used for generating the API docs. -doc-dependencies = ["doc-comment", "aquamarine"] +doc-dependencies = ["doc-comment", "aquamarine", "easy-cast"] [package.metadata.docs.rs] rustdoc-args = ["--cfg", "docsrs"] diff --git a/crates/neon/src/types_impl/date.rs b/crates/neon/src/types_impl/date.rs index 5a9027737..98489c18b 100644 --- a/crates/neon/src/types_impl/date.rs +++ b/crates/neon/src/types_impl/date.rs @@ -24,19 +24,19 @@ use crate::{ /// /// ``` /// # use neon::prelude::*; +/// use easy_cast::Cast; // for safe numeric conversions /// use neon::types::JsDate; /// use std::fs::File; /// use std::time::SystemTime; /// /// /// Return the "modified" timestamp for the file at the given path. -/// /// (A more robust implementation would do more detailed error reporting -/// /// and handle edge cases for u128 -> f64 timestamp conversion.) /// fn last_modified(path: &str) -> Option { -/// Some(File::open(&path).ok()? +/// // A more robust implementation would do more detailed error reporting. +/// File::open(&path).ok()? /// .metadata().ok()? /// .modified().ok()? /// .duration_since(SystemTime::UNIX_EPOCH).ok()? -/// .as_millis() as f64) +/// .as_millis().try_cast().ok() /// } /// /// fn modified(mut cx: FunctionContext) -> JsResult { From 94ead0d89c3c1aacfcb2ca88d3ff6a98f583095a Mon Sep 17 00:00:00 2001 From: David Herman Date: Fri, 23 Dec 2022 13:04:27 -0800 Subject: [PATCH 12/26] Fix failing doc-tests: - add doc-dependencies to `neon-test` Cargo alias - fix a couple of borrowck errors --- .cargo/config.toml | 2 +- crates/neon/src/types_impl/error.rs | 9 ++++++--- crates/neon/src/types_impl/mod.rs | 7 +++++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 61b55184a..d5d222bd5 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -3,5 +3,5 @@ # The following aliases simplify linting the entire workspace neon-check = " check --all --all-targets --features napi-experimental,futures,external-buffers" neon-clippy = "clippy --all --all-targets --features napi-experimental,futures,external-buffers -- -A clippy::missing_safety_doc" -neon-test = " test --all --features=doc-comment,napi-experimental,futures,external-buffers" +neon-test = " test --all --features=doc-dependencies,doc-comment,napi-experimental,futures,external-buffers" neon-doc = " rustdoc -p neon --features=doc-dependencies,napi-experimental,futures,external-buffers -- --cfg docsrs" diff --git a/crates/neon/src/types_impl/error.rs b/crates/neon/src/types_impl/error.rs index 834ac3043..b192a30af 100644 --- a/crates/neon/src/types_impl/error.rs +++ b/crates/neon/src/types_impl/error.rs @@ -21,11 +21,14 @@ use crate::{ /// # use neon::prelude::*; /// # fn test(mut cx: FunctionContext) -> JsResult { /// // Create a type error: -/// let err = cx.type_error("expected a number, found a string"); +/// let err = cx.type_error("expected a number, found a string")?; /// /// // Add some custom diagnostic properties to the error: -/// err.set(&mut cx, "expected", cx.string("number"))?; -/// err.set(&mut cx, "found", cx.string("string"))?; +/// let expected = cx.string("number"); +/// err.set(&mut cx, "expected", expected)?; +/// +/// let found = cx.string("string"); +/// err.set(&mut cx, "found", found)?; /// /// // Throw the error: /// cx.throw(err)?; diff --git a/crates/neon/src/types_impl/mod.rs b/crates/neon/src/types_impl/mod.rs index 3dbebf3de..9255a5640 100644 --- a/crates/neon/src/types_impl/mod.rs +++ b/crates/neon/src/types_impl/mod.rs @@ -710,8 +710,11 @@ impl private::ValueInternal for JsNumber { /// // Create an object: /// let obj = cx.empty_object(); /// -/// obj.set(&mut cx, "name", cx.string("Neon"))?; -/// obj.set(&mut cx, "url", cx.string("https://neon-bindings.com"))?; +/// let name = cx.string("Neon"); +/// obj.set(&mut cx, "name", name)?; +/// +/// let url = cx.string("https://neon-bindings.com"); +/// obj.set(&mut cx, "url", url)?; /// /// // Call console.log(obj): /// log.call_with(&cx).arg(obj).exec(&mut cx)?; From c43c31987bfb719294824e8a2a7982adbe91eeb7 Mon Sep 17 00:00:00 2001 From: David Herman Date: Sat, 7 Jan 2023 12:48:19 -0800 Subject: [PATCH 13/26] Example for `JsFuture` --- crates/neon/src/types_impl/promise.rs | 74 +++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/crates/neon/src/types_impl/promise.rs b/crates/neon/src/types_impl/promise.rs index bce1c9e4d..43f706662 100644 --- a/crates/neon/src/types_impl/promise.rs +++ b/crates/neon/src/types_impl/promise.rs @@ -356,6 +356,80 @@ impl Drop for Deferred { /// /// Unlike typical `Future`, `JsFuture` are eagerly executed because they /// are backed by a `Promise`. +/// +/// # Example +/// +/// This example uses a `JsFuture` to take asynchronous binary data and perform +/// potentially expensive computations on that data in a Rust thread. +/// +/// The example uses a [Tokio](https://tokio.rs) thread pool (allocated and +/// stored on demand with a [`OnceCell`](https://crates.io/crates/once_cell)) +/// to run the computations. +/// +/// ``` +/// # use neon::prelude::*; +/// use neon::types::buffer::TypedArray; +/// use once_cell::sync::OnceCell; +/// use tokio::runtime::Runtime; +/// +/// // Lazily allocate a Tokio runtime to use as the thread pool. +/// fn runtime<'a, C: Context<'a>>(cx: &mut C) -> NeonResult<&'static Runtime> { +/// static RUNTIME: OnceCell = OnceCell::new(); +/// +/// RUNTIME +/// .get_or_try_init(Runtime::new) +/// .or_else(|err| cx.throw_error(&err.to_string())) +/// } +/// +/// // async_compute: Promise -> Promise +/// // +/// // Takes a promise that produces a typed array and returns a promise that: +/// // - awaits the typed array from the original promise; +/// // - computes a value from the contents of the array in a background thread; and +/// // - resolves once the computation is completed +/// pub fn async_compute(mut cx: FunctionContext) -> JsResult { +/// let nums: Handle = cx.argument(0)?; +/// +/// // Convert the JS Promise to a Rust Future for use in a compute thread. +/// let nums = nums.to_future(&mut cx, |mut cx, result| { +/// // Get the promise's result value (or throw if it was rejected). +/// let value = result.or_throw(&mut cx)?; +/// +/// // Downcast the result value to a Float64Array. +/// let array: Handle = value.downcast_or_throw(&mut cx)?; +/// +/// // Convert the typed array to a Rust vector. +/// let vec = array.as_slice(&cx).to_vec(); +/// Ok(vec) +/// })?; +/// +/// // Construct a result promise which will be fulfilled when the computation completes. +/// let (deferred, promise) = cx.promise(); +/// let channel = cx.channel(); +/// let runtime = runtime(&mut cx)?; +/// +/// // Perform the computation in a background thread using the Tokio thread pool. +/// runtime.spawn(async move { +/// // Await the JsFuture, which yields Result, JoinError>. +/// let result = match nums.await { +/// // Perform the computation. For this example, we just compute the sum +/// // of all values in the array; more involved examples might be running +/// // compression or decompression algorithms, encoding or decoding media +/// // codecs, image filters or other media transformations, etc. +/// Ok(nums) => Ok(nums.into_iter().sum::()), +/// Err(err) => Err(err) +/// }; +/// +/// // Resolve the result promise with the result of the computation. +/// deferred.settle_with(&channel, |mut cx| { +/// let result = result.or_throw(&mut cx)?; +/// Ok(cx.number(result)) +/// }); +/// }); +/// +/// Ok(promise) +/// } +/// ``` pub struct JsFuture { // `Err` is always `Throw`, but `Throw` cannot be sent across threads rx: oneshot::Receiver>, From 5190416e6a45861edb977306959e228aa53dfaf0 Mon Sep 17 00:00:00 2001 From: David Herman Date: Sun, 8 Jan 2023 00:55:48 -0800 Subject: [PATCH 14/26] Examples for `JsPromise` --- Cargo.lock | 10 ++++ crates/neon/Cargo.toml | 3 +- crates/neon/src/types_impl/promise.rs | 68 +++++++++++++++++++++++++-- 3 files changed, 77 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eb8a0d411..5805e3c11 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -233,6 +233,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "linkify" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96dd5884008358112bc66093362197c7248ece00d46624e2cf71e50029f8cff5" +dependencies = [ + "memchr", +] + [[package]] name = "log" version = "0.4.17" @@ -267,6 +276,7 @@ dependencies = [ "easy-cast", "getrandom", "libloading", + "linkify", "neon-macros", "nodejs-sys", "once_cell", diff --git a/crates/neon/Cargo.toml b/crates/neon/Cargo.toml index f8b391580..5852541c5 100644 --- a/crates/neon/Cargo.toml +++ b/crates/neon/Cargo.toml @@ -30,6 +30,7 @@ neon-macros = { version = "=1.0.0-alpha.2", path = "../neon-macros" } aquamarine = { version = "0.1.11", optional = true } easy-cast = { version = "0.5.1", optional = true } doc-comment = { version = "0.3.3", optional = true } +linkify = { version = "0.9.0", optional = true } [dependencies.tokio] version = "1.18.2" @@ -81,7 +82,7 @@ task-api = [] proc-macros = [] # Enables the optional dependencies that are only used for generating the API docs. -doc-dependencies = ["doc-comment", "aquamarine", "easy-cast"] +doc-dependencies = ["doc-comment", "aquamarine", "easy-cast", "linkify"] [package.metadata.docs.rs] rustdoc-args = ["--cfg", "docsrs"] diff --git a/crates/neon/src/types_impl/promise.rs b/crates/neon/src/types_impl/promise.rs index 43f706662..bd02ae535 100644 --- a/crates/neon/src/types_impl/promise.rs +++ b/crates/neon/src/types_impl/promise.rs @@ -54,6 +54,68 @@ const BOUNDARY: FailureBoundary = FailureBoundary { /// objects. /// /// [`JsPromise`] may be constructed with [`Context::promise`]. +/// +/// # Example +/// +/// ``` +/// # use neon::prelude::*; +/// fn resolve_promise(mut cx: FunctionContext) -> JsResult { +/// let (deferred, promise) = cx.promise(); +/// let msg = cx.string("Hello, World!"); +/// +/// deferred.resolve(&mut cx, msg); +/// +/// Ok(promise) +/// } +/// ``` +/// +/// # Example: Asynchronous task +/// +/// This example uses the [linkify](https://crates.io/crates/linkify) crate in an +/// asynchronous task, i.e. a +/// [Node worker pool](https://nodejs.org/en/docs/guides/dont-block-the-event-loop/) +/// thread, to find all the links in a text string. +/// +/// More robust implementations might use a custom Rust thread or thread pool to avoid +/// blocking the worker pool; for more information, see the [`JsFuture`] example. +/// +/// ``` +/// # use neon::prelude::*; +/// use linkify::{LinkFinder, LinkKind}; +/// +/// fn linkify(mut cx: FunctionContext) -> JsResult { +/// let text = cx.argument::(0)?.value(&mut cx); +/// +/// let promise = cx +/// .task(move || { +/// let (indices, kinds): (Vec<_>, Vec<_>) = LinkFinder::new() +/// .spans(&text) +/// .map(|span| { +/// let end: u32 = span.end() as u32; +/// +/// let kind: u8 = match span.kind() { +/// Some(LinkKind::Url) => 1, +/// Some(LinkKind::Email) => 2, +/// _ => 0, +/// }; +/// +/// (end, kind) +/// }) +/// .unzip(); +/// (indices, kinds) +/// }) +/// .promise(|mut cx, (indices, kinds)| { +/// let indices = JsUint32Array::from_slice(&mut cx, &indices)?; +/// let kinds = JsUint8Array::from_slice(&mut cx, &kinds)?; +/// let result = cx.empty_object(); +/// result.set(&mut cx, "indices", indices)?; +/// result.set(&mut cx, "kinds", kinds)?; +/// Ok(result) +/// }); +/// +/// Ok(promise) +/// } +/// ``` pub struct JsPromise(raw::Local); impl JsPromise { @@ -354,8 +416,8 @@ impl Drop for Deferred { /// [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) /// object that acts as a [`Future`](std::future::Future). /// -/// Unlike typical `Future`, `JsFuture` are eagerly executed because they -/// are backed by a `Promise`. +/// Unlike typical `Future` implementations, `JsFuture`s are eagerly executed +/// because they are backed by a `Promise`. /// /// # Example /// @@ -412,7 +474,7 @@ impl Drop for Deferred { /// runtime.spawn(async move { /// // Await the JsFuture, which yields Result, JoinError>. /// let result = match nums.await { -/// // Perform the computation. For this example, we just compute the sum +/// // Perform the computation. In this example, we just calculate the sum /// // of all values in the array; more involved examples might be running /// // compression or decompression algorithms, encoding or decoding media /// // codecs, image filters or other media transformations, etc. From e93a6ba4f65a77ea58b60d5103735a423cc44b42 Mon Sep 17 00:00:00 2001 From: David Herman Date: Sun, 8 Jan 2023 01:00:54 -0800 Subject: [PATCH 15/26] Delete trailing whitespace --- crates/neon/src/types_impl/promise.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/neon/src/types_impl/promise.rs b/crates/neon/src/types_impl/promise.rs index bd02ae535..ec8e52ad1 100644 --- a/crates/neon/src/types_impl/promise.rs +++ b/crates/neon/src/types_impl/promise.rs @@ -82,23 +82,23 @@ const BOUNDARY: FailureBoundary = FailureBoundary { /// ``` /// # use neon::prelude::*; /// use linkify::{LinkFinder, LinkKind}; -/// +/// /// fn linkify(mut cx: FunctionContext) -> JsResult { /// let text = cx.argument::(0)?.value(&mut cx); -/// +/// /// let promise = cx /// .task(move || { /// let (indices, kinds): (Vec<_>, Vec<_>) = LinkFinder::new() /// .spans(&text) /// .map(|span| { /// let end: u32 = span.end() as u32; -/// +/// /// let kind: u8 = match span.kind() { /// Some(LinkKind::Url) => 1, /// Some(LinkKind::Email) => 2, /// _ => 0, /// }; -/// +/// /// (end, kind) /// }) /// .unzip(); @@ -112,7 +112,7 @@ const BOUNDARY: FailureBoundary = FailureBoundary { /// result.set(&mut cx, "kinds", kinds)?; /// Ok(result) /// }); -/// +/// /// Ok(promise) /// } /// ``` From c063e50400bc39a6956221fdfea01d6a52decca1 Mon Sep 17 00:00:00 2001 From: David Herman Date: Sun, 8 Jan 2023 19:11:58 -0800 Subject: [PATCH 16/26] Examples for `JsString` --- crates/neon/Cargo.toml | 4 +- crates/neon/src/types_impl/mod.rs | 140 +++++++++++++++++++++--------- 2 files changed, 102 insertions(+), 42 deletions(-) diff --git a/crates/neon/Cargo.toml b/crates/neon/Cargo.toml index 5852541c5..be7678f5c 100644 --- a/crates/neon/Cargo.toml +++ b/crates/neon/Cargo.toml @@ -15,6 +15,7 @@ semver = "1" psd = "0.3.1" # used for a doc example anyhow = "1.0.58" # used for a doc example widestring = "1.0.2" # used for a doc example +linkify = "0.9.0" # used for a doc example [target.'cfg(not(target = "windows"))'.dev-dependencies] # Avoid `clang` as a dependency on windows @@ -30,7 +31,6 @@ neon-macros = { version = "=1.0.0-alpha.2", path = "../neon-macros" } aquamarine = { version = "0.1.11", optional = true } easy-cast = { version = "0.5.1", optional = true } doc-comment = { version = "0.3.3", optional = true } -linkify = { version = "0.9.0", optional = true } [dependencies.tokio] version = "1.18.2" @@ -82,7 +82,7 @@ task-api = [] proc-macros = [] # Enables the optional dependencies that are only used for generating the API docs. -doc-dependencies = ["doc-comment", "aquamarine", "easy-cast", "linkify"] +doc-dependencies = ["doc-comment", "aquamarine", "easy-cast"] [package.metadata.docs.rs] rustdoc-args = ["--cfg", "docsrs"] diff --git a/crates/neon/src/types_impl/mod.rs b/crates/neon/src/types_impl/mod.rs index 9255a5640..654ee53c7 100644 --- a/crates/neon/src/types_impl/mod.rs +++ b/crates/neon/src/types_impl/mod.rs @@ -400,6 +400,24 @@ impl private::ValueInternal for JsBoolean { /// The type of JavaScript /// [string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#primitive_values) /// primitives. +/// +/// # Example +/// +/// ``` +/// # use neon::prelude::*; +/// # fn test(mut cx: FunctionContext) -> JsResult { +/// // Extract the console.log function: +/// let console: Handle = cx.global().get(&mut cx, "console")?; +/// let log: Handle = console.get(&mut cx, "log")?; +/// +/// // Create a string: +/// let s = cx.string("hello 🥹"); +/// +/// // Call console.log(s): +/// log.call_with(&cx).arg(s).exec(&mut cx)?; +/// # Ok(cx.undefined()) +/// # } +/// ``` #[derive(Debug)] #[repr(transparent)] pub struct JsString(raw::Local); @@ -457,26 +475,25 @@ impl private::ValueInternal for JsString { } impl JsString { - /// Return the byte size of this string when converted to a Rust [`String`] with - /// [`JsString::value`]. + /// Returns the size of the UTF-8 representation of this string, + /// measured in 8-bit code units. + /// + /// Equivalent to `self.value(cx).len()` (but more efficient). /// /// # Example /// - /// A function that verifies the length of the passed JavaScript string. The string is assumed - /// to be `hello 🥹` here, which encodes as 10 bytes in UTF-8: + /// The string `"hello 🥹"` encodes as 10 bytes in UTF-8: /// - /// - 6 bytes for `hello ` (including the space). - /// - 4 bytes for the emoji `🥹`. + /// - 6 bytes for `"hello "` (including the space). + /// - 4 bytes for the emoji `"🥹"`. /// /// ```rust /// # use neon::prelude::*; - /// fn string_len(mut cx: FunctionContext) -> JsResult { - /// let len = cx.argument::(0)?.size(&mut cx); - /// // assuming the function is called with the JS string `hello 🥹`. - /// assert_eq!(10, len); - /// - /// Ok(cx.undefined()) - /// } + /// # fn string_len(mut cx: FunctionContext) -> JsResult { + /// let str = cx.string("hello 🥹"); + /// assert_eq!(10, str.size(&mut cx)); + /// # Ok(cx.undefined()) + /// # } /// ``` pub fn size<'a, C: Context<'a>>(&self, cx: &mut C) -> usize { let env = cx.env().to_raw(); @@ -484,25 +501,25 @@ impl JsString { unsafe { sys::string::utf8_len(env, self.to_raw()) } } - /// Return the size of this string encoded as UTF-16 with [`JsString::to_utf16`]. + /// Returns the size of the UTF-16 representation of this string, + /// measured in 16-bit code units. + /// + /// Equivalent to `self.to_utf16(cx).len()` (but more efficient). /// /// # Example /// - /// A function that verifies the length of the passed JavaScript string. The string is assumed - /// to be `hello 🥹` here, which encodes as 8 `u16`s in UTF-16: + /// The string `"hello 🥹"` encodes as 8 code units in UTF-16: /// - /// - 6 `u16`s for `hello ` (including the space). - /// - 2 `u16`s for the emoji `🥹`. + /// - 6 `u16`s for `"hello "` (including the space). + /// - 2 `u16`s for the emoji `"🥹"`. /// /// ```rust /// # use neon::prelude::*; - /// fn string_len_utf16(mut cx: FunctionContext) -> JsResult { - /// let len = cx.argument::(0)?.size_utf16(&mut cx); - /// // assuming the function is called with the JS string `hello 🥹`. - /// assert_eq!(8, len); - /// - /// Ok(cx.undefined()) - /// } + /// # fn string_len_utf16(mut cx: FunctionContext) -> JsResult { + /// let str = cx.string("hello 🥹"); + /// assert_eq!(8, str.size_utf16(&mut cx)); + /// # Ok(cx.undefined()) + /// # } /// ``` pub fn size_utf16<'a, C: Context<'a>>(&self, cx: &mut C) -> usize { let env = cx.env().to_raw(); @@ -510,17 +527,18 @@ impl JsString { unsafe { sys::string::utf16_len(env, self.to_raw()) } } - /// Convert the JavaScript string into a Rust [`String`]. + /// Convert this JavaScript string into a Rust [`String`]. /// /// # Example /// - /// A function that expects a single JavaScript string as argument and prints it out. + /// This example function expects a single JavaScript string as argument + /// and prints it out. /// /// ```rust /// # use neon::prelude::*; /// fn print_string(mut cx: FunctionContext) -> JsResult { /// let s = cx.argument::(0)?.value(&mut cx); - /// println!("JavaScript string content: {}", s); + /// println!("JavaScript string contents: {}", s); /// /// Ok(cx.undefined()) /// } @@ -537,16 +555,15 @@ impl JsString { } } - /// Convert the JavaScript String into a UTF-16 encoded [`Vec`]. + /// Convert this JavaScript string into a [`Vec`] encoded as UTF-16. /// - /// The returned vector is guaranteed to be valid UTF-16. Therefore, any external crate that - /// handles UTF-16 encoded strings, can assume the content to be valid and skip eventual - /// validation steps. + /// The returned vector is guaranteed to be valid UTF-16, so libraries that handle + /// UTF-16-encoded strings can assume the content to be valid. /// /// # Example /// - /// A function that expects a single JavaScript string as argument and prints it out as a raw - /// vector of `u16`s. + /// This example function expects a single JavaScript string as argument and prints it out + /// as a raw vector of `u16`s. /// /// ```rust /// # use neon::prelude::*; @@ -558,17 +575,21 @@ impl JsString { /// } /// ``` /// - /// Again a function that expects a single JavaScript string as argument, but utilizes the - /// [`widestring`](https://crates.io/crates/widestring) crate to handle the raw [`Vec`] as - /// a typical string. + /// This next example function also expects a single JavaScript string as argument and converts + /// to a [`Vec`], but utilizes the [`widestring`](https://crates.io/crates/widestring) + /// crate to handle the vector as a typical string. /// /// ```rust /// # use neon::prelude::*; + /// use widestring::Utf16String; + /// /// fn print_with_widestring(mut cx: FunctionContext) -> JsResult { /// let s = cx.argument::(0)?.to_utf16(&mut cx); - /// // The returned vector is guaranteed to be valid UTF-16. - /// // Therefore, we can skip the validation step. - /// let s = unsafe { widestring::Utf16String::from_vec_unchecked(s) }; + /// + /// // The returned vector is guaranteed to be valid UTF-16, so we can + /// // safely skip the validation step. + /// let s = unsafe { Utf16String::from_vec_unchecked(s) }; + /// /// println!("JavaScript string as UTF-16: {}", s); /// /// Ok(cx.undefined()) @@ -586,10 +607,49 @@ impl JsString { } } + /// Creates a new `JsString` value from a Rust string by copying its contents. + /// + /// This method panics if the string is longer than the maximum string size allowed + /// by the JavaScript engine. + /// + /// # Example + /// + /// ``` + /// # use neon::prelude::*; + /// # fn string_new(mut cx: FunctionContext) -> JsResult { + /// let str = JsString::new(&mut cx, "hello 🥹"); + /// assert_eq!(10, str.size(&mut cx)); + /// # Ok(cx.undefined()) + /// # } + /// ``` + /// + /// **See also:** [`Context::string`] pub fn new<'a, C: Context<'a>, S: AsRef>(cx: &mut C, val: S) -> Handle<'a, JsString> { JsString::try_new(cx, val).unwrap() } + /// Tries to create a new `JsString` value from a Rust string by copying its contents. + /// + /// Returns `Err(StringOverflow)` if the string is longer than the maximum string size + /// allowed by the JavaScript engine. + /// + /// # Example + /// + /// This example tries to construct a JavaScript string from a Rust string of + /// unknown length, and on overflow generates an alternate truncated string with + /// a suffix (`"[…]"`) to indicate the truncation. + /// + /// ``` + /// # use neon::prelude::*; + /// # fn string_try_new(mut cx: FunctionContext) -> JsResult { + /// # static str: &'static str = "hello 🥹"; + /// let s = match JsString::try_new(&mut cx, str) { + /// Ok(s) => s, + /// Err(_) => cx.string(format!("{}[…]", &str[0..32])), + /// }; + /// # Ok(s) + /// # } + /// ``` pub fn try_new<'a, C: Context<'a>, S: AsRef>(cx: &mut C, val: S) -> StringResult<'a> { let val = val.as_ref(); match JsString::new_internal(cx.env(), val) { From a53102181d9b142cab37b5c631d202be65d47089 Mon Sep 17 00:00:00 2001 From: David Herman Date: Mon, 9 Jan 2023 11:48:53 -0800 Subject: [PATCH 17/26] Use easy-cast for the Promise example --- crates/neon/src/types_impl/promise.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/neon/src/types_impl/promise.rs b/crates/neon/src/types_impl/promise.rs index ec8e52ad1..710491123 100644 --- a/crates/neon/src/types_impl/promise.rs +++ b/crates/neon/src/types_impl/promise.rs @@ -82,6 +82,7 @@ const BOUNDARY: FailureBoundary = FailureBoundary { /// ``` /// # use neon::prelude::*; /// use linkify::{LinkFinder, LinkKind}; +/// use easy_cast::Cast; // for safe numerical conversions /// /// fn linkify(mut cx: FunctionContext) -> JsResult { /// let text = cx.argument::(0)?.value(&mut cx); @@ -91,7 +92,7 @@ const BOUNDARY: FailureBoundary = FailureBoundary { /// let (indices, kinds): (Vec<_>, Vec<_>) = LinkFinder::new() /// .spans(&text) /// .map(|span| { -/// let end: u32 = span.end() as u32; +/// let end: u32 = span.end().cast(); /// /// let kind: u8 = match span.kind() { /// Some(LinkKind::Url) => 1, From 342d2bef4addf1582e1e2bbb02e7f1d587feb6b2 Mon Sep 17 00:00:00 2001 From: David Herman Date: Mon, 9 Jan 2023 15:42:20 -0800 Subject: [PATCH 18/26] - Link `Deferred` to examples in `JsPromise` and `JsFuture` - copy edits --- crates/neon/src/types_impl/promise.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/neon/src/types_impl/promise.rs b/crates/neon/src/types_impl/promise.rs index 710491123..eba860311 100644 --- a/crates/neon/src/types_impl/promise.rs +++ b/crates/neon/src/types_impl/promise.rs @@ -53,7 +53,7 @@ const BOUNDARY: FailureBoundary = FailureBoundary { /// [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) /// objects. /// -/// [`JsPromise`] may be constructed with [`Context::promise`]. +/// [`JsPromise`] instances may be constructed with [`Context::promise`]. /// /// # Example /// @@ -263,6 +263,10 @@ impl Object for JsPromise {} /// On Node-API versions less than 6, dropping a [`Deferred`] without settling will /// cause a panic. On Node-API 6+, the associated [`JsPromise`] will be automatically /// rejected. +/// +/// # Examples +/// +/// See [`JsPromise`], [`JsFuture`]. pub struct Deferred { internal: Option, #[cfg(feature = "napi-6")] From 66868225ddd2ad358bc5e2bcb1c63b384415e0ee Mon Sep 17 00:00:00 2001 From: Dave Herman Date: Mon, 23 Jan 2023 09:42:29 -0800 Subject: [PATCH 19/26] copy edit: "type of JS smart pointer" -> "JS smart pointer" Co-authored-by: K.J. Valencik --- crates/neon/src/types_impl/boxed.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/neon/src/types_impl/boxed.rs b/crates/neon/src/types_impl/boxed.rs index a3fd618f1..5abf15884 100644 --- a/crates/neon/src/types_impl/boxed.rs +++ b/crates/neon/src/types_impl/boxed.rs @@ -29,7 +29,7 @@ mod private { } } -/// A type of JavaScript smart pointer object that owns Rust data. +/// A JavaScript smart pointer object that owns Rust data. /// /// The type `JsBox` provides shared ownership of a value of type `T`, /// allocated in the heap. The data is owned by the JavaScript engine and the From 7904f1b2fb686b2e2aa9ddad2242a3be22740569 Mon Sep 17 00:00:00 2001 From: Dave Herman Date: Mon, 23 Jan 2023 09:43:49 -0800 Subject: [PATCH 20/26] Simplification for Date example code Co-authored-by: K.J. Valencik --- crates/neon/src/types_impl/date.rs | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/crates/neon/src/types_impl/date.rs b/crates/neon/src/types_impl/date.rs index 4bf32a4be..5a396c30f 100644 --- a/crates/neon/src/types_impl/date.rs +++ b/crates/neon/src/types_impl/date.rs @@ -26,30 +26,24 @@ use crate::{ /// # use neon::prelude::*; /// use easy_cast::Cast; // for safe numeric conversions /// use neon::types::JsDate; -/// use std::fs::File; -/// use std::time::SystemTime; +/// use std::{error::Error, fs::File, time::SystemTime}; /// /// /// Return the "modified" timestamp for the file at the given path. -/// fn last_modified(path: &str) -> Option { -/// // A more robust implementation would do more detailed error reporting. -/// File::open(&path).ok()? -/// .metadata().ok()? -/// .modified().ok()? -/// .duration_since(SystemTime::UNIX_EPOCH).ok()? -/// .as_millis().try_cast().ok() +/// fn last_modified(path: &str) -> Result> { +/// Ok(File::open(&path)? +/// .metadata()? +/// .modified()? +/// .duration_since(SystemTime::UNIX_EPOCH)? +/// .as_millis() +/// .try_cast()?) /// } /// /// fn modified(mut cx: FunctionContext) -> JsResult { /// let path: Handle = cx.argument(0)?; /// -/// let modified = last_modified(&path.value(&mut cx)) -/// .and_then(|n| cx.date(n).ok()); -/// -/// if let Some(date) = modified { -/// Ok(date) -/// } else { -/// cx.throw_error("failed to get timestamp") -/// } +/// last_modified(&path.value(&mut cx)) +/// .and_then(|n| Ok(cx.date(n)?)) +/// .or_else(|err| cx.throw_error(err.to_string())) /// } /// ``` #[cfg_attr(docsrs, doc(cfg(feature = "napi-5")))] From cdd8cc8d5def9bb9c128837a14f8ec51fd38e631 Mon Sep 17 00:00:00 2001 From: Dave Herman Date: Mon, 23 Jan 2023 09:49:08 -0800 Subject: [PATCH 21/26] Simplify example with `call_method_with` Co-authored-by: K.J. Valencik --- crates/neon/src/types_impl/mod.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/neon/src/types_impl/mod.rs b/crates/neon/src/types_impl/mod.rs index 654ee53c7..7f559e51c 100644 --- a/crates/neon/src/types_impl/mod.rs +++ b/crates/neon/src/types_impl/mod.rs @@ -177,15 +177,14 @@ impl JsValue { /// ``` /// # use neon::prelude::*; /// # fn test(mut cx: FunctionContext) -> JsResult { -/// // Extract the console.log function: +/// // Extract the console object: /// let console: Handle = cx.global().get(&mut cx, "console")?; -/// let log: Handle = console.get(&mut cx, "log")?; /// /// // The undefined value: /// let undefined = cx.undefined(); /// /// // Call console.log(undefined): -/// log.call_with(&cx).arg(undefined).exec(&mut cx)?; +/// console.call_method_with(&mut cx, "log")?.arg(undefined).exec(&mut cx)?; /// # Ok(undefined) /// # } /// ``` From 1f332d609327b261ff3b4758181c0116d665d3e9 Mon Sep 17 00:00:00 2001 From: Dave Herman Date: Mon, 23 Jan 2023 09:49:52 -0800 Subject: [PATCH 22/26] Simplify another example with `call_method_with` Co-authored-by: K.J. Valencik --- crates/neon/src/types_impl/mod.rs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/crates/neon/src/types_impl/mod.rs b/crates/neon/src/types_impl/mod.rs index 7f559e51c..57f4530f3 100644 --- a/crates/neon/src/types_impl/mod.rs +++ b/crates/neon/src/types_impl/mod.rs @@ -251,15 +251,11 @@ impl private::ValueInternal for JsUndefined { /// ``` /// # use neon::prelude::*; /// # fn test(mut cx: FunctionContext) -> JsResult { -/// // Extract the console.log function: -/// let console: Handle = cx.global().get(&mut cx, "console")?; -/// let log: Handle = console.get(&mut cx, "log")?; -/// -/// // The null value: -/// let null = cx.null(); -/// -/// // Call console.log(undefined): -/// log.call_with(&cx).arg(null).exec(&mut cx)?; +/// cx.global() +/// .get::(&mut cx, "console")? +/// .call_method_with(&mut cx, "log")? +/// .arg(cx.null()) +/// .exec(&mut cx)?; /// # Ok(null) /// # } /// ``` From 3945cba6fd6e98fbf703bb2fcf6cf29766a9714d Mon Sep 17 00:00:00 2001 From: Dave Herman Date: Mon, 23 Jan 2023 09:55:00 -0800 Subject: [PATCH 23/26] Add "see also" reference from `exec` to `call_with`. Co-authored-by: K.J. Valencik --- crates/neon/src/types_impl/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/neon/src/types_impl/mod.rs b/crates/neon/src/types_impl/mod.rs index 57f4530f3..535254077 100644 --- a/crates/neon/src/types_impl/mod.rs +++ b/crates/neon/src/types_impl/mod.rs @@ -1143,6 +1143,8 @@ impl JsFunction { } /// Calls this function for side effect, discarding its result. + /// + /// **See also:** [`JsFunction::call_with`]. pub fn exec<'a, 'b, C: Context<'a>, T, AS>( &self, cx: &mut C, From e0055bd97d04ff5f448cd6c2443acc284f750144 Mon Sep 17 00:00:00 2001 From: David Herman Date: Fri, 27 Jan 2023 09:27:17 -0800 Subject: [PATCH 24/26] Clarifying copy --- crates/neon/src/types_impl/promise.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/neon/src/types_impl/promise.rs b/crates/neon/src/types_impl/promise.rs index eba860311..7a873a93d 100644 --- a/crates/neon/src/types_impl/promise.rs +++ b/crates/neon/src/types_impl/promise.rs @@ -76,7 +76,7 @@ const BOUNDARY: FailureBoundary = FailureBoundary { /// [Node worker pool](https://nodejs.org/en/docs/guides/dont-block-the-event-loop/) /// thread, to find all the links in a text string. /// -/// More robust implementations might use a custom Rust thread or thread pool to avoid +/// Alternate implementations might use a custom Rust thread or thread pool to avoid /// blocking the worker pool; for more information, see the [`JsFuture`] example. /// /// ``` @@ -90,8 +90,13 @@ const BOUNDARY: FailureBoundary = FailureBoundary { /// let promise = cx /// .task(move || { /// let (indices, kinds): (Vec<_>, Vec<_>) = LinkFinder::new() +/// // The spans() method fully partitions the text +/// // into a sequence of contiguous spans, some of which +/// // are plain text and some of which are links. /// .spans(&text) /// .map(|span| { +/// // The first span starts at 0 and the rest start +/// // at their preceding span's end index. /// let end: u32 = span.end().cast(); /// /// let kind: u8 = match span.kind() { From 4e35241b6ad7c586bb3918982a0a4ca5fe8ce031 Mon Sep 17 00:00:00 2001 From: David Herman Date: Fri, 27 Jan 2023 09:41:35 -0800 Subject: [PATCH 25/26] Explain the role of Deferred in the Promise docs --- crates/neon/src/types_impl/promise.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/crates/neon/src/types_impl/promise.rs b/crates/neon/src/types_impl/promise.rs index 7a873a93d..90abb0ff6 100644 --- a/crates/neon/src/types_impl/promise.rs +++ b/crates/neon/src/types_impl/promise.rs @@ -53,7 +53,19 @@ const BOUNDARY: FailureBoundary = FailureBoundary { /// [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) /// objects. /// -/// [`JsPromise`] instances may be constructed with [`Context::promise`]. +/// [`JsPromise`] instances may be constructed with [`Context::promise`], which +/// produces both a promise and a [`Deferred`], which can be used to control +/// the behavior of the promise. A `Deferred` struct is similar to the `resolve` +/// and `reject` functions produced by JavaScript's standard +/// [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/Promise) +/// constructor: +/// +/// ```javascript +/// let deferred; +/// let promise = new Promise((resolve, reject) => { +/// deferred = { resolve, reject }; +/// }); +/// ``` /// /// # Example /// From 498606add5bbd8cb990124e2a57d1b7ee17d04ad Mon Sep 17 00:00:00 2001 From: David Herman Date: Fri, 27 Jan 2023 09:51:43 -0800 Subject: [PATCH 26/26] bugfix in doc comment code --- crates/neon/src/types_impl/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/neon/src/types_impl/mod.rs b/crates/neon/src/types_impl/mod.rs index 535254077..161eceba6 100644 --- a/crates/neon/src/types_impl/mod.rs +++ b/crates/neon/src/types_impl/mod.rs @@ -256,7 +256,7 @@ impl private::ValueInternal for JsUndefined { /// .call_method_with(&mut cx, "log")? /// .arg(cx.null()) /// .exec(&mut cx)?; -/// # Ok(null) +/// # Ok(cx.null()) /// # } /// ``` #[derive(Debug)]