#[repr(u32)]pub enum NapiThreadsafeFunctionReleaseMode {
Release = 0,
Abort = 1,
}
Expand description
§Nodex - Nodejs eXtension 🥳
Yet another crate to create native nodejs addons :)
This crate aims to make creating native nodejs addons very easy and comfortable.
click here: uuhan/nodex@dev to see the most recent developments.
§Platform Support
- linux
- macos
- windows (>=0.2.1)
§Changelog
§Usage
[lib]
crate-type = ["cdylib"]
[dependencies.nodex-api]
version = "0.2.3"
features = ["v8"]
The default napi version is set to v1, you can use other version with your need.
We have v1,v2,v3,…v8 versions.
Currently, nodex just reexports nodex-api:
[lib]
crate-type = ["cdylib"]
[dependencies.nodex]
version = "0.2.3"
features = ["v8"]
§Napi Level
§v1
- NapiValueT::wrap::<T, Finalizer>() - Wraps a native instance, call finalizer when value is garbage-collected.
- NapiValueT::remove_wrap::<T>() - Remove the wrapped native instance. The finalizer will not be called if the wrapped instance is removed.
- NapiValueT::unwrap::<T>() - Access the wrapped instance.
- NapiValueT::gc::<Finalizer>() - Hook fired when value is gabage-collected.
§v3
- NapiEnv::add_cleanup_hook() - Do the cleanup when nodejs environment exits.
§v4
- NapiThreadsafeFunction::<Data, const N: usize> - Thread safe function.
§v5
- NapiValueT::finalizer() - Adds a napi_finalize callback which will be called when the JavaScript object is ready for gc.
§v6
- NapiEnv::set_instance_data::<Data, Finalizer> - Set data to current agent.
- NapiENv::get_instance_data::<Data> - Get Option<&mut Data> from current agent.
§v8
- NapiEnv::add_async_cleanup_hook() - Do the cleanup when nodejs environment exits, asynchronous.
§Examples
§Init Module
simply define your module by:
use nodex::prelude::*;
nodex::napi_module!(init);
fn init(env: NapiEnv, exports: JsObject) -> NapiResult<()> {
Ok(())
}
§Version Guard
make sure the node api version is large or equal than your compiled addon’s.
use nodex::prelude::*;
fn env(env: NapiEnv) -> NapiResult<()> {
nodex::napi_guard!(env.napi_version()?);
Ok(())
}
§Nodejs Version & Napi Version
get the runtime version:
use nodex::prelude::*;
fn env(env: NapiEnv) -> NapiResult<()> {
let node_version = env.node_version()?;
let napi_version = env.napi_version()?;
Ok(())
}
§Define Js Variable
use nodex::prelude::*;
fn env(env: NapiEnv) -> NapiResult<()> {
// String & Symbol
let label: JsSymbol = env.symbol()?;
let name: JsString = env.string("")?;
// Object
let mut obj: JsObject = env.object()?;
obj.set_property(name, env.null()?)?;
// Function
let func: JsFunction = env.func(move |this, (a1, a2, a3): (JsValue, JsValue, JsValue)| {
let env = this.env();
a1.as_function()?.call(this, ())?;
a1.as_function()?.call(this, env.string("I am from rust world.")?)
})?;
let func: JsFunction = env.func(move |this, a1: JsFunction| {
let env = this.env();
a1.call(this, env.string("I am from rust world.")?)
})?;
let class: JsClass = env.class("myclass", |mut this, a1: JsNumber| {
this.set_named_property("a1", a1)?;
Ok(this)
}, &[])?;
// Error
let error: JsError = JsError::error(env, "error", Some("code"))?;
Ok(())
}
§Napi handle scope
use nodex::prelude::*;
fn env(env: NapiEnv) -> NapiResult<()> {
// napi handle scope
let _scope: NapiHandleScope = env.handle_scope()?;
let _escapable_scope: NapiEscapableHandleScope = env.escapable_handle_scope()?;
Ok(())
}
§Napi cleanup hook
§sync
use nodex::prelude::*;
fn env(env: NapiEnv) -> NapiResult<()> {
env.add_cleanup_hook(|| {
println!("clean hook fired");
Ok(())
})?;
let hook_to_remove = env.add_cleanup_hook(|| {
println!("clean hook fired");
Ok(())
})?;
hook_to_remove.remove()?;
Ok(())
}
§aync
use nodex::prelude::*;
fn env(env: NapiEnv) -> NapiResult<()> {
match env.add_async_cleanup_hook(|hook| {
// DO SOME CLEANUP
// NB: should call remove after done
hook.remove()
})? {
Some(hook) => {
// NB: also the hook can be removed before it is fired.
hook.remove()?;
}
None => {}
}
Ok(())
}
§Set Property Descriptor
use nodex::prelude::*;
fn env(env: NapiEnv) -> NapiResult<()> {
let mut obj: JsObject = env.object()?;
obj.define_properties(&[DescriptorValueBuilder::new()
.with_utf8name("myvalue")
.with_value(env.string("myvalue")?)
.build()?])?;
obj.define_properties(&[DescriptorMethodBuilder::new()
.with_utf8name("mymethod")
.with_method(move |this, ()| this.env().double(200.))
.build()?])?;
obj.define_properties(&[DescriptorAccessorBuilder::new()
.with_utf8name("myaccessor")
.with_getter(|this| this.env().double(100.))
.with_setter(|_this: JsObject, n: JsNumber| {
println!("setter: {}", n.get_value_int32()?);
Ok(())
})
.build()?])?;
Ok(())
}
§Create An Async Work
use nodex::prelude::*;
fn env(env: NapiEnv) -> NapiResult<()> {
// without shared state
env.async_work(
"my-test-async-task",
(),
move |_| {
// you can do the hard work in the thread-pool context.
// NB: js work is not allowed here.
println!("execute async task");
},
move |_, status, _| {
// you can do some js work in this context
println!("[{}] complete async task", status);
Ok(())
},
)?
.queue()?;
Ok(())
}
§gabage-collected hook
for napi less than 5, implement by napi_wrap, otherwise by napi_add_finalizer.
use nodex::prelude::*;
fn env(env: NapiEnv) -> NapiResult<()> {
let mut obj = env.object()?;
obj.gc(move |_| {
println!("obj garbage-collected");
Ok(())
});
Ok(())
}
§Wrap native instance
use nodex::prelude::*;
fn env(env: NapiEnv) -> NapiResult<()> {
let mut obj = env.object()?;
obj.wrap([1usize; 2], move |_, wrapped| {
Ok(())
})?;
obj.unwrap::<[usize; 2]>()?; // access the wrapped instance
obj.remove_wrap::<[usize; 2]>()?; // the finalizer will not be called
Ok(())
}
§Thread safe function
require: napi >= 4
use nodex::prelude::*;
fn env(env: NapiEnv) -> NapiResult<()> {
let tsfn = NapiThreadsafeFunction::<_, 0>::new(
env,
"tsfn-task",
env.func(|this, a1: JsString| {
println!("callback result: {}", a1.get()?);
this.env().undefined()
})?,
// finalizer
move |_| Ok(()),
// js-callback
move |f, data: String| {
f.call(env.object()?, env.string(&data)?)?;
Ok(())
},
)?;
std::thread::spawn(move || {
tsfn.non_blocking("hello, world - 1".into()).unwrap();
tsfn.non_blocking("hello, world - 2".into()).unwrap();
tsfn.release().unwrap();
});
Ok(())
}
§Promise for some heavy work
use nodex::prelude::*;
fn test(env: NapiEnv) -> NapiResult<()> {
let promise: JsPromise<JsString, JsError> = env.promise(
move |result| {
for i in 1..=3 {
std::thread::sleep(std::time::Duration::from_secs(1));
println!("[{}] Doing...", i);
}
*result = true;
},
move |promise, _, result| {
let env = promise.env();
if result {
promise.resolve(env.string("the promise is resolved.")?)?;
} else {
promise.reject(env.error("the promise is rejected.")?)?;
}
Ok(())
},
)?;
Ok(())
}
// the `promise.value()` can return to js world as a Promise
§Run script
use nodex::prelude::*;
fn script(env: NapiEnv) -> NapiResult<()> {
let func: Function<JsUndefined> = env.run_script(
r#"
function hello() {
console.log(this);
}
hello
"#,
)?;
func.call(env.global()?.object(), ())?;
Ok(())
}
§More
Run:
bash demo.sh
§How to participate in
§Code of conduct
cat >> .git/hooks/pre-push << EOF
#!/bin/sh
cargo fmt || exit
cargo clippy -- -D warnings || exit
EOF
chmod +x .git/hooks/pre-push
§TODO
- ergonomical api design.
-
export the codebase from crates world, make it easy to call rust function from js world.
- sweet syntax, like: #[nodex::function] fn foo()
-
import the huge codebase from npm world, make it easy to call js function from rust side.
- sweet syntax, like: let lodash = nodex::import!(lodash);
-
nodejs async runtime to drive rust async code
- async runtime for async rust
-
macros like: #[nodex::rt] async fn main(), so you can use nodejs to run any rust async-code.
- node –require=main.node
- rust code introspection with nodejs repl
-
cargo-nodex cargo subcommand to make ease of create nodejs addons, e.g. auto generate ts typings.
- cargo nodex build
- cargo nodex typings
- cargo nodex package
§License
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option. napi_threadsafe_function_release_mode
Variants§
Trait Implementations§
Source§impl Clone for NapiThreadsafeFunctionReleaseMode
impl Clone for NapiThreadsafeFunctionReleaseMode
Source§fn clone(&self) -> NapiThreadsafeFunctionReleaseMode
fn clone(&self) -> NapiThreadsafeFunctionReleaseMode
Returns a copy of the value. Read more
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
Performs copy-assignment from
source
. Read moreSource§impl PartialEq for NapiThreadsafeFunctionReleaseMode
impl PartialEq for NapiThreadsafeFunctionReleaseMode
Source§fn eq(&self, other: &NapiThreadsafeFunctionReleaseMode) -> bool
fn eq(&self, other: &NapiThreadsafeFunctionReleaseMode) -> bool
Tests for
self
and other
values to be equal, and is used by ==
.impl Copy for NapiThreadsafeFunctionReleaseMode
impl Eq for NapiThreadsafeFunctionReleaseMode
impl StructuralPartialEq for NapiThreadsafeFunctionReleaseMode
Auto Trait Implementations§
impl Freeze for NapiThreadsafeFunctionReleaseMode
impl RefUnwindSafe for NapiThreadsafeFunctionReleaseMode
impl Send for NapiThreadsafeFunctionReleaseMode
impl Sync for NapiThreadsafeFunctionReleaseMode
impl Unpin for NapiThreadsafeFunctionReleaseMode
impl UnwindSafe for NapiThreadsafeFunctionReleaseMode
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more