10 个版本
0.4.4 | 2024年6月20日 |
---|---|
0.4.3 | 2023年9月27日 |
0.4.2 | 2022年3月8日 |
0.4.1 | 2021年8月31日 |
0.1.2 | 2020年2月7日 |
在 #class 中排名 6
每月下载 651 次
14KB
350 行
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.js
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")
元组变体会被转换为列表,结构体变体会被转换为对象,而单元变体会被转换为与变体名称匹配的 PascalCase 字符串。泛型和引用被支持,与结构体的要求相同。
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
否则,当在 electron 中导入 rust 模块时,您将得到可怕的 A dynamic link library (DLL) initialization routine failed
错误
准备 npm 软件包
使用 node-bindgen
生成的节点模块可以直接在任何 Node.js 项目中使用,只需将 index.node
复制到其中。但是,如果直接访问模块,IDE 将不会突出显示可用的函数、类等。通常,这并不方便,并且一旦节点模块的公共 API 发生更改,就会增加潜在错误的风险。
要创建一个带有 TypeScript 类型定义和所有必要的 JavaScript 包装器的完整 npm 包,可以使用 tslink crate。
tslink
crate 生成包含 npm 模块描述的文件 *.d.ts
、*.js
和 package.json
。这样的包可以以最小的努力集成到最终项目中。
此外,由于 tslink
生成 TypeScript 类型定义,任何对本地节点模块(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
默认将方法名称转换为蛇形命名。您应该使用 #[tslink(snake_case_naming)]
来考虑这一点(更多内容请参见 crate 页面)。
tslink
需要在项目根目录的 Cargo.toml
中进行配置([tslink]
部分)。配置应包括到本地节点模块的有效路径。默认情况下,node-bindgen
在您的 root
的 ./dist
文件夹中创建 index.node
。
文件:./Cargo.toml
(项目根目录中)
[project]
...
[lib]
...
[tslink]
node = "./dist/index.node"
使用 tslink
和 node-bindgen
的完整示例在此 处。
有关 tslink
的更多 API 文档,请参见 crate 页面。
注意。node-bindgen 的开发者不对 tslink crate 的工作的正确性负责。与 tslink 相关的所有可能的问题和功能请求都应提交给 tslink 的开发者。
贡献
如果您想为项目做出贡献,请阅读我们的 贡献指南。
许可证
本项目采用Apache许可证。
依赖项
约2.4–3.5MB
约60K SLoC