16 个稳定版本 (6 个主要版本)
6.0.2 | 2024 年 6 月 20 日 |
---|---|
6.0.1 | 2023 年 9 月 27 日 |
5.1.0 | 2023 年 2 月 1 日 |
5.0.0 | 2021 年 7 月 15 日 |
0.1.2 | 2020 年 3 月 9 日 |
#29 in FFI
每月下载量 178
用于 request-window-attention
57KB
1K SLoC
node-bindgen
特性
- 简单:只需编写惯用的 Rust 代码,node-bindgen 会处理生成 Node.js FFI 包装代码。
- 安全:根据 Rust 类型自动检查 Node.js 参数。
- 异步:支持异步 Rust。异步代码被转换为 Node.js promises。
- 类:可以使用 Node.js 类访问 Rust 结构体。
- 流:使用 Rust 实现 Node.js 流。
- N-API:使用 Node.js N-API,这意味着您不需要重新编译您的模块。
与 Node.js 版本的兼容性
此项目使用 Node N-API 的 v8。请参阅以下 兼容性矩阵。
以下操作系统受支持
- Linux
- MacOs
- Windows
为什么选择 node-bindgen?
编写原生 node-js 需要大量的模板代码。node-bindgen 从 rust 代码生成外部的 "C" 粘合代码,包括原生模块注册。node-bindgen 使编写 node-js 模块变得简单而有趣。
入门
CLI 安装
安装 nj-cli 命令行,它将用于生成原生库。
cargo install nj-cli
这是一个一次性步骤。
配置 Cargo.toml
将两个依赖项添加到项目的 Cargo.toml
。
然后添加 node-bindgen
作为常规依赖项(如下所示)
[dependencies]
node-bindgen = { version = "6.0" }
然后添加 node-bindgen
的过程宏到您的构建依赖项中(如下所示)
[build-dependencies]
node-bindgen = { version = "6.0", default-features = false, features = ["build"] }
然后将crate类型更新为cdylib
以生成兼容node.js的本地模块
[lib]
crate-type = ["cdylib"]
最后,在项目顶部添加build.rs
,内容如下
fn main() {
node_bindgen::build::configure();
}
示例
这是一个添加两个数字的函数。请注意,您不需要担心JS转换。
use node_bindgen::derive::node_bindgen;
/// add two integer
#[node_bindgen]
fn sum(first: i32, second: i32) -> i32 {
first + second
}
构建本地库
要构建node.js库,使用nj-cli
进行构建
nj-cli build
这将生成位于"./dist"文件夹中的Node.js模块。
构建发布版本
nj-cli build --release
监视./src
中的更改
在开发您的本地模块时,您可能希望监视文件更改并在更改发生时运行命令,例如 cargo check
或 cargo build
。
为此,我们可以使用nj-cli watch
。
nj-cli watch
(如果不存在)安装并传递参数给cargo watch
。默认情况下,nj-cli watch
将运行cargo check
以检查您的./src
文件。
要查看nj-cli watch
的所有可用方法,请运行以下命令
nj-cli watch-- --help
在Node.js中使用
然后在Node.js中,rust函数可以像正常的node.js函数一样被调用
$ node
Welcome to Node.js v18.18.0.
Type ".help" for more information.
> let addon = require('./dist');
undefined
> addon.sum(2,3)
5
>
特性
函数名或方法可以重命名为默认映射
#[node_bindgen(name="multiply")]
fn mul(first: i32,second: i32) -> i32 {
first * second
}
Rust函数mul重命名为multiply
可选参数
如果参数被标记为可选,则可以跳过参数
#[node_bindgen]
fn sum(first: i32, second: Option<i32>) -> i32 {
first + second.unwrap_or(0)
}
然后sum可以调用为sum(10)
或sum(10,20)
回调
JS回调映射为Rust闭包。
#[node_bindgen]
fn hello<F: Fn(String)>(first: f64, second: F) {
let msg = format!("argument is: {}", first);
second(msg);
}
从node
let addon = require('./dist');
addon.hello(2,function(msg){
assert.equal(msg,"argument is: 2");
console.log(msg); // print out argument is 2
});
异步Rust也支持回调。
异步Rust支持
异步Rust函数映射为Node.js promise。
use std::time::Duration;
use flv_future_aio::time::sleep;
use node_bindgen::derive::node_bindgen;
#[node_bindgen]
async fn hello(arg: f64) -> f64 {
println!("sleeping");
sleep(Duration::from_secs(1)).await;
println!("woke and adding 10.0");
arg + 10.0
}
let addon = require('./dist');
addon.hello(5).then((val) => {
console.log("future value is %s",val);
});
结构体序列化
结构体,包括泛型结构体,可以自动生成到-JS转换的模板。只需将node_bindgen
宏应用于您的结构体
#[node_bindgen]
struct MyJson {
some_name: String,
a_number: i64
}
#[node_bindgen]
fn my_json() -> MyJson {
MyJson {
some_name: "John".to_owned(),
a_number: 1337
}
}
let addon = require('./dist');
assert.deepStrictEqual(addon.my_json(), {
someName: "John",
aNumber: 1337
});
请注意,字段必须自行实现node_bindgen::core::TryIntoJs
。任何引用也必须实现Clone
。字段名称将转换为驼峰命名。
枚举
枚举也将使用node_bindgen
的帮助自动生成其JS表示。
#[node_bindgen]
enum ErrorType {
WithMessage(String, usize),
WithFields {
val: usize
},
UnitErrorType
}
#[node_bindgen]
fn with_message() -> ErrorType {
ErrorType::WithMessage("test".to_owned(), 321)
}
#[node_bindgen]
fn with_fields() -> ErrorType {
ErrorType::WithFields {
val: 123
}
}
#[node_bindgen]
fn with_unit() -> ErrorType {
ErrorType::UnitErrorType
}
assert.deepStrictEqual(addon.withMessage(), {
withMessage: ["test", 321n]
});
assert.deepStrictEqual(addon.withFields(), {
withFields: {
val: 123n
}
});
assert.deepStrictEqual(addon.withUnit(), "UnitErrorType")
元组变体将被转换为列表,结构体变体将转换为对象,单元变体将转换为与变体名称匹配的大驼峰字符串。支持泛型和引用,与结构体的注意事项相同。
JavaScript类
支持JavaScript类。
struct MyClass {
val: f64,
}
#[node_bindgen]
impl MyClass {
#[node_bindgen(constructor)]
fn new(val: f64) -> Self {
Self { val }
}
#[node_bindgen]
fn plus_one(&self) -> f64 {
self.val + 1.0
}
#[node_bindgen(getter)]
fn value(&self) -> f64 {
self.val
}
}
let addon = require('./dist');
const assert = require('assert');
let obj = new addon.MyObject(10);
assert.equal(obj.value,10,"verify value works");
assert.equal(obj.plusOne(),11);
示例文件夹中有更多功能。
Windows + Electron支持
在Windows上使用node-bindgen与electron时,nj-build
必须编译一个C++文件,win_delay_load_hook.cc
,因此开发环境必须有一个有效的C/C++编译器。
如果你的机器没有有效的C/C++编译器,请安装Microsoft VSCode。
将来,这个文件将用Rust重新编写,移除这个依赖。
只需确保你正在使用以下方式编译rust模块:
$ npx electron-build-env nj-cli build --release
否则,在将rust模块导入electron时,你会得到令人讨厌的错误:动态链接库(DLL)初始化例程失败
准备npm包
使用node-bindgen
生成的Node模块可以直接在任何Node JS项目中使用,只需将index.node
复制进去即可。但如果是直接访问模块,IDE将不会突出显示可用的函数、类等。通常,这并不方便,并且当Node模块的公共API更改时,潜在的错误风险会更高。
要创建一个具有TypeScript类型定义和所有必要JavaScript包装的完整npm包,可以使用tslink crate。
tslink
crate生成描述npm模块的文件*.d.ts
、*.js
和package.json
。这样的包可以以最小的努力集成到最终项目中。
此外,由于tslink
生成了TypeScript类型定义,任何对本地Node模块(index.node
)的更改都将由TypeScript
编译器突出显示,从而大大降低与更改的API或公共数据类型相关的错误风险。
例如,
#[macro_use] extern crate tslink;
use tslink::tslink;
use node_bindgen::derive::node_bindgen;
struct MyScruct {
inc: i32,
}
#[tslink(class)]
#[node_bindgen]
impl MyScruct {
#[tslink(constructor)]
#[node_bindgen(constructor)]
pub fn new(inc: i32) -> Self {
Self { inc }
}
#[tslink(snake_case_naming)]
#[node_bindgen]
fn inc_my_number(&self, a: i32) -> i32 {
a + self.inc
}
}
将表示为(*.d.ts
)为
export declare class MyStruct {
constructor(inc: number);
incMyNumber(a: number): number;
}
请注意,调用#[tslink]
应始终在调用#[node_bindgen]
之前。
此外,请注意,node-bindgen
默认将方法名称转换为snake case。您应该使用#[tslink)
来考虑这一点(更多信息请参阅crate页面)。
tslink
需要在项目根目录的Cargo.toml
([tslink]
部分)中进行配置。配置应包括到本地Node模块的有效路径。默认情况下,node-bindgen
在您的root
的./dist
文件夹中创建index.node
。
文件:./Cargo.toml
(在项目的root
中)
[project]
...
[lib]
...
[tslink]
node = "./dist/index.node"
tslink
和node-bindgen
的完整使用示例在这里:此处。
查看更多关于 tslink
的API文档,请访问crate页面。
注意。node-bindgen的开发者不对tslink crate的工作正确性负责。所有与tslink相关的可能问题和功能请求应联系tslink的开发者。
贡献
如果您想为该项目做出贡献,请阅读我们的贡献指南。
许可
本项目采用Apache许可证授权。
依赖
~0–12MB
~146K SLoC