napi-rs/napi/src/lib.rs

192 lines
5.6 KiB
Rust
Raw Normal View History

2020-12-01 15:55:19 +09:00
#![deny(clippy::all)]
2020-07-14 23:57:11 +09:00
//! High level NodeJS [N-API](https://nodejs.org/api/n-api.html) binding
//!
//! **napi-rs** provides minimal overhead to write N-API modules in `Rust`.
2020-09-03 21:38:28 +09:00
//!
2020-07-14 23:57:11 +09:00
//! ## Feature flags
2020-09-03 21:38:28 +09:00
//!
2020-12-10 15:26:53 +09:00
//! ### napi1 ~ napi7
2020-07-14 23:57:11 +09:00
//!
2020-12-03 18:17:40 +09:00
//! Because `NodeJS` N-API has versions. So there are feature flags to choose what version of `N-API` you want to build for.
//! For example, if you want build a library which can be used by `node@10.17.0`, you should choose the `napi5` or lower.
//!
2020-12-22 22:32:50 +09:00
//! The details of N-API versions and support matrix: [n_api_version_matrix](https://nodejs.org/api/n-api.html#n_api_n_api_version_matrix)
2020-07-14 23:57:11 +09:00
//!
//! ### tokio_rt
//! 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`.
//!
//! ```
//! use futures::prelude::*;
//! use napi::{CallContext, Error, JsObject, JsString, Result, Status};
//! use tokio;
//!
//! #[js_function(1)]
//! pub fn tokio_readfile(ctx: CallContext) -> Result<JsObject> {
2020-10-14 12:29:51 +09:00
//! let js_filepath = ctx.get::<JsString>(0)?;
//! let path_str = js_filepath.as_str()?;
//! ctx.env.execute_tokio_future(
//! tokio::fs::read(path_str.to_owned())
//! .map(|v| v.map_err(|e| Error::new(Status::Unknown, format!("failed to read file, {}", e)))),
//! |&mut env, data| env.create_buffer_with_data(data),
//! )
2020-07-14 23:57:11 +09:00
//! }
//! ```
//!
//! ***Tokio channel in `napi-rs` buffer size is default `100`.***
//!
//! ***You can adjust it via `NAPI_RS_TOKIO_CHANNEL_BUFFER_SIZE` environment variable***
//!
//! ```
//! NAPI_RS_TOKIO_CHANNEL_BUFFER_SIZE=1000 node ./app.js
//! ```
//!
2020-09-03 21:38:28 +09:00
//! ### latin1
//!
//! Decode latin1 string from JavaScript using [encoding_rs](https://docs.rs/encoding_rs).
//!
//! With this feature, you can use `JsString.as_latin1_string` function
//!
//! ### serde-json
//!
//! Enable Serialize/Deserialize data cross `JavaScript Object` and `Rust struct`.
//!
//! ```
//! #[derive(Serialize, Debug, Deserialize)]
//! struct AnObject {
2020-10-14 12:29:51 +09:00
//! a: u32,
//! b: Vec<f64>,
//! c: String,
2020-09-03 21:38:28 +09:00
//! }
//!
//! #[js_function(1)]
//! fn deserialize_from_js(ctx: CallContext) -> Result<JsUndefined> {
2020-10-14 12:29:51 +09:00
//! let arg0 = ctx.get::<JsUnknown>(0)?;
//! let de_serialized: AnObject = ctx.env.from_js_value(arg0)?;
//! ...
2020-09-03 21:38:28 +09:00
//! }
//!
//! #[js_function]
//! fn serialize(ctx: CallContext) -> Result<JsUnknown> {
2020-10-14 12:29:51 +09:00
//! let value = AnyObject { a: 1, b: vec![0.1, 2.22], c: "hello" };
//! ctx.env.to_js_value(&value)
2020-09-03 21:38:28 +09:00
//! }
//! ```
//!
2020-07-14 23:57:11 +09:00
mod async_work;
mod call_context;
2020-11-10 12:09:25 +09:00
#[cfg(feature = "napi3")]
2020-10-04 17:02:04 +09:00
mod cleanup_env;
mod env;
mod error;
mod js_values;
mod module;
2020-11-10 12:09:25 +09:00
#[cfg(all(feature = "tokio_rt", feature = "napi4"))]
mod promise;
mod status;
mod task;
2020-11-10 12:09:25 +09:00
#[cfg(feature = "napi3")]
2020-10-04 17:02:04 +09:00
pub use cleanup_env::CleanupEnvHook;
2020-11-10 12:09:25 +09:00
#[cfg(feature = "napi4")]
pub mod threadsafe_function;
2020-11-10 12:09:25 +09:00
#[cfg(all(feature = "tokio_rt", feature = "napi4"))]
2020-07-08 01:59:09 +09:00
mod tokio_rt;
mod version;
2020-10-31 10:24:19 +09:00
#[cfg(target_os = "windows")]
mod win_delay_load_hook;
2018-04-28 17:26:38 +09:00
pub use napi_sys as sys;
2020-12-22 22:32:50 +09:00
pub use async_work::AsyncWorkPromise;
2020-04-21 01:20:35 +09:00
pub use call_context::CallContext;
pub use env::*;
2020-12-02 15:10:48 +09:00
pub use error::{Error, ExtendedErrorInfo, Result};
pub use js_values::*;
pub use module::Module;
pub use status::Status;
pub use task::Task;
pub use version::NodeVersion;
2018-04-28 17:26:38 +09:00
2020-11-10 12:09:25 +09:00
#[cfg(all(feature = "tokio_rt", feature = "napi4"))]
2020-07-08 01:59:09 +09:00
pub use tokio_rt::shutdown as shutdown_tokio_rt;
2020-08-26 01:07:27 +09:00
#[cfg(feature = "serde-json")]
#[macro_use]
extern crate serde;
pub type ContextlessResult<T> = Result<Option<T>>;
/// Deprecated
2020-07-14 23:57:11 +09:00
/// register nodejs module
///
/// ## Example
/// ```
/// register_module!(test_module, init);
///
/// fn init(module: &mut Module) -> Result<()> {
2020-10-14 12:29:51 +09:00
/// module.create_named_method("nativeFunction", native_function)?;
2020-07-14 23:57:11 +09:00
/// }
/// ```
2018-04-28 17:26:38 +09:00
#[macro_export]
#[deprecated(since = "1.0.0", note = "[module_exports] macro instead")]
2018-04-28 17:26:38 +09:00
macro_rules! register_module {
($module_name:ident, $init:ident) => {
2020-07-08 01:59:09 +09:00
#[inline]
2020-11-10 12:09:25 +09:00
#[cfg(all(feature = "tokio_rt", feature = "napi4"))]
2020-07-08 01:59:09 +09:00
fn check_status(code: $crate::sys::napi_status) -> Result<()> {
use $crate::{Error, Status};
2020-07-08 01:59:09 +09:00
let status = Status::from(code);
match status {
Status::Ok => Ok(()),
_ => Err(Error::from_status(status)),
}
}
2020-10-14 12:29:51 +09:00
#[no_mangle]
unsafe extern "C" fn napi_register_module_v1(
2020-11-12 12:41:41 +09:00
raw_env: $crate::sys::napi_env,
raw_exports: $crate::sys::napi_value,
) -> $crate::sys::napi_value {
use std::ffi::CString;
use std::io::Write;
use std::os::raw::c_char;
use std::ptr;
use $crate::{Env, JsObject, NapiValue};
#[cfg(all(feature = "tokio_rt", feature = "napi4"))]
use $crate::shutdown_tokio_rt;
if cfg!(debug_assertions) {
println!("`register_module` macro will deprecate soon, please migrate to [module_exports]");
}
let env = Env::from_raw(raw_env);
let mut exports: JsObject = JsObject::from_raw_unchecked(raw_env, raw_exports);
let mut cjs_module = Module { env, exports };
let result = $init(&mut cjs_module);
2020-11-10 12:09:25 +09:00
#[cfg(all(feature = "tokio_rt", feature = "napi4"))]
let hook_result = check_status(unsafe {
2020-11-12 12:41:41 +09:00
$crate::sys::napi_add_env_cleanup_hook(raw_env, Some(shutdown_tokio_rt), ptr::null_mut())
});
2020-11-10 12:09:25 +09:00
#[cfg(not(all(feature = "tokio_rt", feature = "napi4")))]
let hook_result = Ok(());
match hook_result.and_then(move |_| result) {
Ok(_) => cjs_module.exports.raw(),
Err(e) => {
unsafe {
2020-11-12 12:41:41 +09:00
$crate::sys::napi_throw_error(
raw_env,
ptr::null(),
CString::from_vec_unchecked(format!("Error initializing module: {}", e).into())
2020-11-20 10:13:25 +09:00
.as_ptr(),
)
};
ptr::null_mut()
2018-04-28 17:26:38 +09:00
}
}
2020-10-14 12:29:51 +09:00
}
2018-04-28 17:26:38 +09:00
};
}