docs: update documents

This commit is contained in:
LongYinan 2020-12-03 17:17:40 +08:00
parent 66088642ee
commit f90640d7aa
No known key found for this signature in database
GPG key ID: A3FFE134A3E20881
7 changed files with 86 additions and 63 deletions

View file

@ -32,11 +32,11 @@ A minimal library for building compiled `NodeJS` add-ons in `Rust`.
| ------ | ------- | ------ | ------ | | ------ | ------- | ------ | ------ |
| ✓ | ✓ | ✓ | ✓ | | ✓ | ✓ | ✓ | ✓ |
This library depends on N-API and requires `Node@8.9` or later. This library depends on N-API and requires `Node@10.0.0` or later.
We already have some packages written by `napi-rs`: [node-rs](https://github.com/napi-rs/node-rs) We already have some packages written by `napi-rs`: [node-rs](https://github.com/napi-rs/node-rs)
One nice feature is that this crate allows you to build add-ons purely with the `Rust` toolchain and without involving `node-gyp`. One nice feature is that this crate allows you to build add-ons purely with the `Rust/JavaScript` toolchain and without involving `node-gyp`.
## Taste ## Taste
@ -51,7 +51,7 @@ fn fibonacci(ctx: CallContext) -> Result<JsNumber> {
ctx.env.create_int64(fibonacci_native(n)) ctx.env.create_int64(fibonacci_native(n))
} }
#[inline] #[inline(always)]
fn fibonacci_native(n: i64) -> i64 { fn fibonacci_native(n: i64) -> i64 {
match n { match n {
1 | 2 => 1, 1 | 2 => 1,
@ -63,31 +63,46 @@ fn fibonacci_native(n: i64) -> i64 {
### Register module ### Register module
```rust ```rust
/// test_module is Module name use napi::{JsObject, Result};
register_module!(test_module, init);
/// Module is `module` object in NodeJS /// `exports` is `module.exports` object in NodeJS
fn init(module: &mut Module) -> Result<()> { #[module_exports]
module.create_named_method("fibonacci", fibonacci)?; fn init(mut exports: JsObject) -> Result<()> {
exports.create_named_method("fibonacci", fibonacci)?;
}
```
And you can also create `JavaScript` value while registering module:
```rust
use napi::{JsObject, Result, Env};
#[module_exports]
fn init(mut exports: JsObject, env: Env) -> Result<()> {
exports.create_named_method("fibonacci", fibonacci)?;
exports.set_named_property("DEFAULT_VALUE", env.create_int64(100)?)?;
} }
``` ```
## Building ## Building
This repository is a Cargo crate. Any napi-based add-on should contain `Cargo.toml` to make it a Cargo crate. This repository is a `Cargo` crate. Any napi-based add-on should contain `Cargo.toml` to make it a Cargo crate.
In your `Cargo.toml` you need to set the `crate-type` to `"cdylib"` so that cargo builds a C-style shared library that can be dynamically loaded by the Node executable. You'll also need to add this crate as a dependency. In your `Cargo.toml` you need to set the `crate-type` to `"cdylib"` so that cargo builds a C-style shared library that can be dynamically loaded by the Node executable. You'll also need to add this crate as a dependency.
```toml ```toml
[package]
name = "awesome"
[lib] [lib]
crate-type = ["cdylib"] crate-type = ["cdylib"]
[dependencies] [dependencies]
napi = "0.5" napi = "1.0"
napi-derive = "0.5" napi-derive = "1.0"
[build-dependencies] [build-dependencies]
napi-build = "0.2" napi-build = "1.0"
``` ```
And create `build.rs` in your own project: And create `build.rs` in your own project:
@ -101,21 +116,24 @@ fn main() {
} }
``` ```
So far, the `napi` build script has only been tested on `macOS` `Linux` and `Windows x64 MSVC`. So far, the `napi` build script has only been tested on `macOS` `Linux` `Windows x64 MSVC` and `FreeBSD`.
See the included [test_module](./test_module) for an example add-on. See the included [test_module](./test_module) for an example add-on.
Run `cargo build` to produce the `Dynamic lib` file. And install the `napi-rs` to help you copy `Dynamic lib` file to `.node` file in case you can `require` it in your program. Install the `@napi-rs/cli` to help you build your `Rust` codes and copy `Dynamic lib` file to `.node` file in case you can `require` it in your program.
```json ```json
{ {
"package": "your pkg", "package": "awesome-package",
"devDependencies": { "devDependencies": {
"@napi-rs/cli": "latest" "@napi-rs/cli": "^1.0.0"
},
"napi": {
"name": "jarvis" // <----------- Config the name of native addon, or the napi command will use the name of `Cargo.toml` for the binary file name.
}, },
"scripts": { "scripts": {
"build": "napi build", "build": "napi build --release",
"build-release": "napi build --release" "build:debug": "napi build"
} }
} }
``` ```
@ -123,22 +141,24 @@ Run `cargo build` to produce the `Dynamic lib` file. And install the `napi-rs` t
Then you can require your native binding: Then you can require your native binding:
```js ```js
require('./target/debug|release/[module_name].node') require('./jarvis.node')
``` ```
The `module_name` would be your `package` name in your `Cargo.toml`. The `module_name` would be your `package` name in your `Cargo.toml`.
`xxx => ./target/debug|release/xxx.node` `xxx => ./xxx.node`
`xxx-yyy => ./target/debug|release/xxx_yyy.node` `xxx-yyy => ./xxx_yyy.node`
You can also copy `Dynamic lib` file to an appointed location: You can also copy `Dynamic lib` file to an appointed location:
```bash ```bash
napi build [--release] . napi build [--release] ./dll
napi build [--release] ./mylib napi build [--release] ./artifacts
``` ```
There are [documents](./cli) which contains more details about the `@napi-rs/cli` usage.
## Testing ## Testing
Because libraries that depend on this crate must be loaded into a Node executable in order to resolve symbols, all tests are written in JavaScript in the `test_module` subdirectory. Because libraries that depend on this crate must be loaded into a Node executable in order to resolve symbols, all tests are written in JavaScript in the `test_module` subdirectory.

View file

@ -3,5 +3,7 @@ pub(crate) struct CleanupEnvHookData<T: 'static> {
pub(crate) hook: Box<dyn FnOnce(T)>, pub(crate) hook: Box<dyn FnOnce(T)>,
} }
/// Created by `Env::add_env_cleanup_hook`
/// And used by `Env::remove_env_cleanup_hook`
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct CleanupEnvHook<T: 'static>(pub(crate) *mut CleanupEnvHookData<T>); pub struct CleanupEnvHook<T: 'static>(pub(crate) *mut CleanupEnvHookData<T>);

View file

@ -35,6 +35,12 @@ use tokio::sync::mpsc::error::TrySendError;
pub type Callback = extern "C" fn(sys::napi_env, sys::napi_callback_info) -> sys::napi_value; pub type Callback = extern "C" fn(sys::napi_env, sys::napi_callback_info) -> sys::napi_value;
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
/// `Env` is used to represent a context that the underlying N-API implementation can use to persist VM-specific state.
/// This structure is passed to native functions when they're invoked, and it must be passed back when making N-API calls.
/// Specifically, the same `Env` that was passed in when the initial native function was called must be passed to any subsequent nested N-API calls.
/// Caching the `Env` for the purpose of general reuse, and passing the `Env` between instances of the same addon running on different Worker threads is not allowed.
/// The `Env` becomes invalid when an instance of a native addon is unloaded.
/// Notification of this event is delivered through the callbacks given to `Env::add_env_cleanup_hook` and `Env::set_instance_data`.
pub struct Env(pub(crate) sys::napi_env); pub struct Env(pub(crate) sys::napi_env);
impl Env { impl Env {

View file

@ -13,6 +13,9 @@ use crate::{sys, Status};
pub type Result<T> = std::result::Result<T, Error>; pub type Result<T> = std::result::Result<T, Error>;
/// Represent `JsError`.
/// Return this Error in `js_function`, **napi-rs** will throw it as `JsError` for you.
/// If you want throw it as `TypeError` or `RangeError`, you can use `JsTypeError/JsRangeError::from(Error).throw_into(env)`
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Error { pub struct Error {
pub status: Status, pub status: Status,

View file

@ -6,30 +6,13 @@
//! //!
//! ## Feature flags //! ## Feature flags
//! //!
//! ### libuv //! ### napi1 ~ napi6
//! With `libuv` feature, you can execute a rust future in `libuv` in NodeJS, and return a `promise` object.
//! ```
//! use std::thread;
//! use std::fs;
//! //!
//! use futures::prelude::*; //! Because `NodeJS` N-API has versions. So there are feature flags to choose what version of `N-API` you want to build for.
//! use futures::channel::oneshot; //! For example, if you want build a library which can be used by `node@10.17.0`, you should choose the `napi5` or lower.
//! use napi::{CallContext, Result, JsString, JsObject, Status, Error}; //!
//! The details of N-API versions and support matrix: [n_api_n_api_version_matrix](https://nodejs.org/api/n-api.html#n_api_n_api_version_matrix)
//! //!
//! #[js_function(1)]
//! pub fn uv_read_file(ctx: CallContext) -> Result<JsObject> {
//! let path = ctx.get::<JsString>(0)?;
//! let (sender, receiver) = oneshot::channel();
//! let p = path.as_str()?.to_owned();
//! thread::spawn(|| {
//! let res = fs::read(p).map_err(|e| Error::new(Status::Unknown, format!("{}", e)));
//! sender.send(res).expect("Send data failed");
//! });
//! ctx.env.execute(receiver.map_err(|e| Error::new(Status::Unknown, format!("{}", e))).map(|x| x.and_then(|x| x)), |&mut env, data| {
//! env.create_buffer_with_data(data)
//! })
//! }
//! ```
//! ### tokio_rt //! ### tokio_rt
//! With `tokio_rt` feature, `napi-rs` provides a ***tokio runtime*** in an additional thread. //! With `tokio_rt` feature, `napi-rs` provides a ***tokio runtime*** in an additional thread.
//! And you can easily run tokio `future` in it and return `promise`. //! And you can easily run tokio `future` in it and return `promise`.

View file

@ -41,7 +41,7 @@ impl Into<napi_threadsafe_function_call_mode> for ThreadsafeFunctionCallMode {
/// ## Example /// ## Example
/// An example of using `ThreadsafeFunction`: /// An example of using `ThreadsafeFunction`:
/// ///
/// ``` /// ```rust
/// #[macro_use] /// #[macro_use]
/// extern crate napi_derive; /// extern crate napi_derive;
/// ///
@ -53,28 +53,36 @@ impl Into<napi_threadsafe_function_call_mode> for ThreadsafeFunctionCallMode {
/// }, /// },
/// CallContext, Error, JsFunction, JsNumber, JsUndefined, Result, Status, /// CallContext, Error, JsFunction, JsNumber, JsUndefined, Result, Status,
/// }; /// };
///
/// #[js_function(1)] /// #[js_function(1)]
/// pub fn test_threadsafe_function(ctx: CallContext) -> Result<JsUndefined> { /// pub fn test_threadsafe_function(ctx: CallContext) -> Result<JsUndefined> {
/// let func = ctx.get::<JsFunction>(0)?; /// let func = ctx.get::<JsFunction>(0)?;
///
/// let tsfn = ctx.env /// let tsfn =
/// .create_threadsafe_function(func, 0, |ctx: ThreadSafeCallContext<Vec<u32>>| { /// ctx
/// .env
/// .create_threadsafe_function(&func, 0, |ctx: ThreadSafeCallContext<Vec<u32>>| {
/// ctx.value /// ctx.value
/// .iter() /// .iter()
/// .map(|v| ctx.env.create_uint32(*v))] /// .map(|v| ctx.env.create_uint32(*v))
/// .collect::<Result<Vec<JsNumber>>>() /// .collect::<Result<Vec<JsNumber>>>()
/// })?; /// })?;
///
/// thread::spawn(move || { /// let tsfn_cloned = tsfn.try_clone()?;
/// let output: Vec<u32> = vec![42, 1, 2, 3]; ///
/// // It's okay to call a threadsafe function multiple times. /// thread::spawn(move || {
/// tsfn.call(Ok(output.clone()), ThreadsafeFunctionCallMode::Blocking); /// let output: Vec<u32> = vec![0, 1, 2, 3];
/// tsfn.call(Ok(output.clone()), ThreadsafeFunctionCallMode::NonBlocking); /// // It's okay to call a threadsafe function multiple times.
/// tsfn.release(ThreadsafeFunctionReleaseMode::Release); /// tsfn.call(Ok(output.clone()), ThreadsafeFunctionCallMode::Blocking);
/// }); /// });
///
/// ctx.env.get_undefined() /// thread::spawn(move || {
/// let output: Vec<u32> = vec![3, 2, 1, 0];
/// // It's okay to call a threadsafe function multiple times.
/// tsfn_cloned.call(Ok(output.clone()), ThreadsafeFunctionCallMode::NonBlocking);
/// });
///
/// ctx.env.get_undefined()
/// } /// }
/// ``` /// ```
pub struct ThreadsafeFunction<T: 'static> { pub struct ThreadsafeFunction<T: 'static> {

View file

@ -43,6 +43,7 @@ pub(crate) fn get_tokio_sender() -> &'static mpsc::Sender<Message> {
}) })
} }
#[doc(hidden)]
pub unsafe extern "C" fn shutdown(_data: *mut c_void) { pub unsafe extern "C" fn shutdown(_data: *mut c_void) {
let sender = get_tokio_sender().clone(); let sender = get_tokio_sender().clone();
sender sender