• Stars
    star
    523
  • Rank 84,684 (Top 2 %)
  • Language
    Rust
  • License
    Apache License 2.0
  • Created almost 5 years ago
  • Updated about 2 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Easy way to write Node.js module using Rust

node-bindgen

Easy way to write native Node.js module using idiomatic Rust

Features

  • Easy: Just write idiomatic Rust code, node-bindgen take care of generating Node.js FFI wrapper codes.
  • Safe: Node.js arguments are checked automatically based on Rust types.
  • Async: Support Async Rust. Async codes are translated into Node.js promises.
  • Class: Rust struct can be accessed using Node.js classes.
  • Stream: Implement Node.js stream using Rust
  • N-API: Use Node.js N-API, which means you don't have to recompile your module.

Compatibility with Node.js version

This project uses the v7 of Node N-API. Please see following compatibility matrix.

Following OS are supported:

  • Linux
  • MacOs
  • Windows

Why node-bindgen?

Writing native node-js requires lots of boilerplate code. Node-bindgen generates external "C" glue code from rust code, including native module registration. node-bindgen make it writing node-js module easy and fun.

Getting started

CLI Installation

Install nj-cli command line, which will be used to generate the native library.

cargo install nj-cli

This is a one time step.

Configuring Cargo.toml

Add two dependencies to your projects' Cargo.toml.

Add node-bindgen as a regular dependency (as below):

[dependencies]
node-bindgen = { version = "5.1" }

Then add node-bindgen's procedure macro to your build-dependencies as below:

[build-dependencies]
node-bindgen = { version = "5.1", features = ["build"] }

Then update crate type to cdylib to generate node.js compatible native module:

[lib]
crate-type = ["cdylib"]

Finally, add build.rs at the top of the project with following content:

fn main() {
    node_bindgen::build::configure();
}

Example

Here is a function that adds two numbers. Note that you don't need to worry about JS conversion.

use node_bindgen::derive::node_bindgen;

/// add two integer
#[node_bindgen]
fn sum(first: i32, second: i32) -> i32 {
    first + second
}

Building native library

To build node.js library, using nj-cli to build:

nj-cli build

This will generate Node.js module in "./dist" folder.

To build a release version:

nj-cli build --release

Watching ./src for Changes

While developing your native module, you may want to watch for file changes and run a command when a change occurs, for example cargo check or cargo build.

For this, we can use nj-cli watch.

nj-cli watch installs [if it does not exist] and passes arguments to cargo watch. By default, nj-cli watch will run cargo check against your ./src files.

To see all available methods for nj-cli watch, run the following command:

nj-cli watch -- --help

Using in Node.js

Then in the Node.js, rust function can be invoked as normal node.js function:

$ node
Welcome to Node.js v14.0.0.
Type ".help" for more information.
> let addon = require('./dist');
undefined
> addon.sum(2,3)
5
>

Features

Function name or method can be renamed instead of default mapping

#[node_bindgen(name="multiply")]
fn mul(first: i32,second: i32) -> i32 {
    first * second
}

Rust function mul is re-mapped as multiply

Optional argument

Argument can be skipped if it is marked as optional

#[node_bindgen]
fn sum(first: i32, second: Option<i32>) -> i32 {
    first + second.unwrap_or(0)
}

Then sum can be invoked as sum(10) or sum(10,20)

Callback

JS callback are mapped as Rust closure.

#[node_bindgen]
fn hello<F: Fn(String)>(first: f64, second: F) {

    let msg = format!("argument is: {}", first);

    second(msg);
}

from node:

let addon = require('./dist');

addon.hello(2,function(msg){
  assert.equal(msg,"argument is: 2");
  console.log(msg);  // print out argument is 2
});

Callback are supported in Async rust as well.

Support for Async Rust

Async rust function is mapped to 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);
});

Struct serialization

Structs, including generic structs, can have have the to-JS conversion boilerplate autogenerated. Just apply the node_bindgen macro to your struct:

#[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
});

Note that the fields must implement node_bindgen::core::TryIntoJs themselves. Any references must also implement Clone. Field names will be converted to camelCase.

Enums

Enums will also have their JS representation autogenerated with the help of node_bindgen:

#[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")

Tuple variants will be converted into lists, struct variants converted to objects, and unit variants converted into strings matching the variant's name in PascalCase. Generics and references are supported, with the same caveats as for structs.

JavaScript class

JavaScript class is supported.

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);

There are more features in the examples folder.

Windows + Electron Support

When using node-bindgen with electron on Windows, nj-build must compile a C++ file, win_delay_load_hook.cc, and therefore it is required that the development environment has a valid C/C++ compiler.

If your machine does not have a valid C/C++ compiler, install Microsoft VSCode.

In the future, this file will be re-written in Rust, removing this dependency.

Just make sure that you are compiling the rust module using

$ npx electron-build-env nj-cli build --release

otherwise you will get dreaded A dynamic link library (DLL) initialization routine failed when importing the rust module in electron

Contributing

If you'd like to contribute to the project, please read our Contributing guide.

License

This project is licensed under the Apache license.

More Repositories

1

fluvio

Lean and mean distributed stream processing system written in rust and web assembly. Alternative to Kafka + Flink in one.
Rust
3,789
star
2

fluvio-jolt

Rust implementation of Java Jolt JSON to JSON transformation library
Rust
40
star
3

k8-api

Native Rust implementation of Kubernetes api
Rust
34
star
4

fluvio-connectors

The go to place for official fluvio connectors
Rust
23
star
5

fluvio-duck

Fluvio DuckDB Integration
Rust
19
star
6

future-aio

Advanced futures library
Rust
15
star
7

flv-tls-proxy

Simple TLS Proxy
Rust
14
star
8

fluvio-demo-apps-rust

Mysql Change Data Capture Demo App for Fluvio Streaming Platform
Rust
14
star
9

flv-kf-protocol

native Rust implementation of Kafka protocol and api
Rust
13
star
10

fluvio-client-python

The Fluvio Python Client!
Python
13
star
11

stateful-dataflows-examples

Stateful Dataflows tutorials and examples.
HTML
12
star
12

fluvio-client-wasm

Rust
11
star
13

labs-projects

InfinyOn Labs projects
11
star
14

fluvio-client-node

Node.js client for Fluvio
Rust
9
star
15

fluvio-helm

Helm Library
Rust
9
star
16

fluvio-demo-apps-node

Node Demo Apps for Fluvio Data Streaming Platform
TypeScript
8
star
17

http-sink-connector

Official Infinyon HTTP Sink Connector
Shell
8
star
18

fluvio-website

Website for Fluvio Project
SCSS
8
star
19

cargo-cvm

Rust Crate Version Manager (CVM) Cargo CLI Extended Utility
Rust
7
star
20

fluvio-client-java

A Java Client for Fluvio!
Rust
6
star
21

fluvio-smartmodule-examples

Example SmartModules
Rust
6
star
22

kafka_webinar_16_August_2022

Code for Fluvio to Kafka webinar
5
star
23

http-source-connector

Official Infinyon HTTP Source Connector
Shell
5
star
24

sql-connector

Official Infinyon SQL connector
Rust
5
star
25

fluvio-client-capacitor

Capacitor wrapper that enables mobile devices to produce real-time events to a Fluvio Cluster.
Swift
5
star
26

kafka-connector

Official Infinyon Kafka connector
Rust
4
star
27

mqtt-connector

Official Infinyon MQTT connector
Shell
4
star
28

connector-hello-source

Simple Fluvio connector reference repository
Rust
4
star
29

fluvio-smartmodule-template

A cargo-generate template for creating Fluvio SmartModule projects
Rust
3
star
30

fluvio-protocol

fluvio protocol and API
Rust
3
star
31

fluvio-demo-04-12-2022-finance-demo

Fluvio Webinar Finance Demo Code
Rust
3
star
32

streaming-hacker-news

Streaming hacker news rss feed using InfinyOn Cloud and Fluvio
3
star
33

labs-redis-sink-connector

Redis Sink connector
Rust
3
star
34

stateful-services-examples

Tutorials and Examples for SSDK
3
star
35

labs-regex-map-sm

Regex transformations on arbitrary data.
Rust
2
star
36

sendgrid-async

An async-std based client for sending emails with SendGrid
Rust
2
star
37

fluvio-socket

Socket abstraction to Fluvio cluster
Rust
2
star
38

labs-stars-forks-changes-sm

Experimental SmartModule to count stars
Rust
2
star
39

flv-client-rust

Rust client sample APIs for Fluvio streaming platform
Rust
2
star
40

labs-regex-map-json-sm

Regex transformations on JSON values
Rust
2
star
41

nats-connector

Connector that bridges NATS with Fluvio.
2
star
42

fluvio-docs

📚 Documentation for Fluvio & InfinyOn Cloud
MDX
2
star
43

fluvio-client-swift

Swift
1
star
44

graphite-sink-connector

Official InfinyOn Graphite Sink Connector
Shell
1
star
45

industrial_iot_webinar

1
star
46

sdf-http-guest

Http Guest library for SDF
Rust
1
star
47

duckdb-connector

Official InfinyOn connector to Duckdb
Shell
1
star
48

infinyon-support

1
star