├── .github ├── dependabot.yaml └── workflows │ ├── ci.yml │ └── release.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE-APACHE ├── Makefile ├── README.md ├── bors.toml ├── examples ├── Cargo.toml ├── Makefile ├── array │ ├── .gitignore │ ├── Cargo.toml │ ├── Makefile │ ├── README.md │ ├── build.rs │ ├── src │ │ └── lib.rs │ └── test.js ├── async-cb │ ├── .gitignore │ ├── Cargo.toml │ ├── Makefile │ ├── README.md │ ├── build.rs │ ├── src │ │ └── lib.rs │ └── test_cb.js ├── bigint │ ├── .gitignore │ ├── Cargo.toml │ ├── Makefile │ ├── README.md │ ├── build.rs │ ├── src │ │ └── lib.rs │ └── test.js ├── buffer │ ├── .gitignore │ ├── Cargo.toml │ ├── Makefile │ ├── README.md │ ├── build.rs │ ├── src │ │ └── lib.rs │ └── test.js ├── cb │ ├── .gitignore │ ├── Cargo.toml │ ├── Makefile │ ├── README.md │ ├── build.rs │ ├── src │ │ └── lib.rs │ └── test.js ├── class-async │ ├── .gitignore │ ├── Cargo.toml │ ├── Makefile │ ├── README.md │ ├── build.rs │ ├── src │ │ └── lib.rs │ └── test.js ├── class-simple │ ├── .gitignore │ ├── Cargo.toml │ ├── Makefile │ ├── README.md │ ├── build.rs │ ├── src │ │ └── lib.rs │ └── test.js ├── class-wrapper │ ├── .gitignore │ ├── Cargo.toml │ ├── Makefile │ ├── README.md │ ├── build.rs │ ├── src │ │ └── lib.rs │ └── test.js ├── cleanup │ ├── .gitignore │ ├── Cargo.toml │ ├── Makefile │ ├── build.rs │ ├── src │ │ └── lib.rs │ └── test.js ├── electron │ ├── Cargo.toml │ ├── Makefile │ ├── build.rs │ ├── package-lock.json │ ├── package.json │ ├── src │ │ └── lib.rs │ └── test.js ├── function │ ├── .gitignore │ ├── Cargo.toml │ ├── Makefile │ ├── README.md │ ├── build.rs │ ├── src │ │ └── lib.rs │ └── test.js ├── js-env │ ├── .gitignore │ ├── Cargo.toml │ ├── Makefile │ ├── README.md │ ├── build.rs │ ├── src │ │ └── lib.rs │ └── test.js ├── json │ ├── .gitignore │ ├── Cargo.toml │ ├── Makefile │ ├── README.md │ ├── build.rs │ ├── src │ │ └── lib.rs │ └── test.js ├── logging │ ├── .gitignore │ ├── Cargo.toml │ ├── Makefile │ ├── README.md │ ├── build.rs │ ├── src │ │ └── lib.rs │ └── test.js ├── option │ ├── .gitignore │ ├── Cargo.toml │ ├── Makefile │ ├── README.md │ ├── build.rs │ ├── src │ │ └── lib.rs │ └── test.js ├── package-lock.json ├── package.json ├── param │ ├── .gitignore │ ├── Cargo.toml │ ├── Makefile │ ├── README.md │ ├── build.rs │ ├── src │ │ └── lib.rs │ └── test.js ├── promise │ ├── .gitignore │ ├── Cargo.toml │ ├── Makefile │ ├── README.md │ ├── build.rs │ ├── src │ │ └── lib.rs │ └── test.js ├── stream │ ├── Cargo.toml │ ├── Makefile │ ├── build.rs │ ├── src │ │ └── lib.rs │ └── test.js ├── tuples │ ├── .gitignore │ ├── Cargo.toml │ ├── Makefile │ ├── README.md │ ├── build.rs │ ├── package-lock.json │ ├── package.json │ ├── src │ │ └── lib.rs │ └── test.ts └── uuid │ ├── .gitignore │ ├── Cargo.toml │ ├── Makefile │ ├── README.md │ ├── build.rs │ ├── package-lock.json │ ├── package.json │ ├── src │ └── lib.rs │ └── test.ts ├── nj-cli ├── Cargo.lock ├── Cargo.toml ├── Makefile └── src │ ├── init │ ├── mod.rs │ └── project_files.rs │ ├── main.rs │ └── watch.rs ├── nj-core ├── Cargo.toml ├── LICENSE-APACHE ├── README.md └── src │ ├── basic.rs │ ├── bigint.rs │ ├── buffer.rs │ ├── class.rs │ ├── convert.rs │ ├── error.rs │ ├── lib.rs │ ├── module.rs │ ├── property.rs │ ├── stream.rs │ ├── thread_fn.rs │ └── worker.rs ├── nj-derive ├── .gitinore ├── Cargo.toml ├── LICENSE-APACHE ├── README.md ├── src │ ├── ast │ │ ├── arg.rs │ │ ├── attribute.rs │ │ ├── class.rs │ │ ├── mod.rs │ │ ├── node.rs │ │ ├── types.rs │ │ └── util.rs │ ├── generator │ │ ├── class │ │ │ ├── arg.rs │ │ │ ├── constructor.rs │ │ │ └── mod.rs │ │ ├── context.rs │ │ ├── derive.rs │ │ ├── function.rs │ │ ├── mod.rs │ │ ├── napi.rs │ │ └── property.rs │ ├── lib.rs │ └── util.rs ├── tests │ └── parse.rs └── ui-tests │ ├── fail_attribute_name.rs │ ├── fail_attribute_number.rs │ ├── fail_attriubte_gibberish.rs │ ├── fail_class_attr_name.rs │ ├── fail_class_attr_number.rs │ ├── fail_class_gibberish.rs │ ├── fail_non_method.rs │ ├── fail_union.rs │ ├── fail_unit_struct.rs │ ├── pass_async.rs │ ├── pass_callback.rs │ ├── pass_class_async.rs │ ├── pass_class_lifetimes.rs │ ├── pass_class_simple.rs │ ├── pass_enum.rs │ ├── pass_function.rs │ └── pass_struct.rs ├── notes.md ├── rustfmt.toml └── src └── lib.rs /.github/dependabot.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | # Check for updates to GitHub Actions every weekday 7 | interval: "daily" 8 | commit-message: 9 | prefix: ci 10 | include: scope 11 | 12 | # Maintain dependencies for Cargo 13 | - package-ecosystem: "cargo" 14 | directory: "/" 15 | schedule: 16 | interval: "weekly" 17 | commit-message: 18 | prefix: build 19 | include: scope 20 | ignore: 21 | - dependency-name: "*" 22 | update-types: [ 23 | "version-update:semver-patch", 24 | ] 25 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | concurrency: 4 | group: ci-${{ github.ref }} 5 | cancel-in-progress: true 6 | 7 | on: 8 | merge_group: 9 | pull_request: 10 | branches: [ master ] 11 | workflow_dispatch: 12 | inputs: 13 | verbose: 14 | description: "Set --verbose to get verbose build output" 15 | required: false 16 | default: "" 17 | 18 | jobs: 19 | rustfmt: 20 | name: Rustfmt (${{ matrix.os }}) 21 | runs-on: ${{ matrix.os }} 22 | strategy: 23 | matrix: 24 | os: [ubuntu-latest] 25 | rust: [stable] 26 | steps: 27 | - uses: actions/checkout@v4 28 | - name: Install ${{ matrix.rust }} 29 | uses: actions-rs/toolchain@v1 30 | with: 31 | toolchain: ${{ matrix.rust }} 32 | override: true 33 | - name: check fmt 34 | run: make check-fmt RUSTV=${{ matrix.rust }} 35 | 36 | clippy: 37 | name: Clippy (${{ matrix.os }}) 38 | runs-on: ${{ matrix.os }} 39 | strategy: 40 | matrix: 41 | os: [ubuntu-latest] 42 | rust: [stable] 43 | steps: 44 | - uses: actions/checkout@v4 45 | - name: Install ${{ matrix.rust }} 46 | uses: actions-rs/toolchain@v1 47 | with: 48 | toolchain: ${{ matrix.rust }} 49 | profile: minimal 50 | - name: check clippy 51 | run: make check-clippy 52 | 53 | test: 54 | name: Smoke test (${{ matrix.os }}, ${{ matrix.node-version }}) 55 | runs-on: ${{ matrix.os }} 56 | strategy: 57 | fail-fast: false 58 | matrix: 59 | # os: [ubuntu-latest, windows-latest, macOS-latest] 60 | os: [ubuntu-latest, macOS-13] 61 | rust: [stable] 62 | node-version: [ '16', '18', '20' ] 63 | steps: 64 | - uses: actions/checkout@v4 65 | - name: Install ${{ matrix.rust }} 66 | uses: actions-rs/toolchain@v1 67 | with: 68 | toolchain: ${{ matrix.rust }} 69 | override: true 70 | - uses: Swatinem/rust-cache@v2 71 | with: 72 | key: ${{ matrix.os }} 73 | - name: Install nj-cli build 74 | run: cargo install --path nj-cli 75 | - name: Use Node.js ${{ matrix.node-version }} 76 | uses: actions/setup-node@v4 77 | with: 78 | node-version: ${{ matrix.node-version }} 79 | - name: All Tests 80 | run: make test-all 81 | 82 | done: 83 | name: Done 84 | needs: [rustfmt, clippy, test] 85 | runs-on: ubuntu-latest 86 | steps: 87 | - run: echo "Done!" 88 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Tag, Build, Release and Publish 2 | on: 3 | push: 4 | tags: 5 | - 'v*' 6 | jobs: 7 | release_cli: 8 | name: Build nj-cli 9 | runs-on: ${{ matrix.os }} 10 | permissions: 11 | contents: write 12 | strategy: 13 | matrix: 14 | include: 15 | - os: ubuntu-latest 16 | artifact_name: nj-cli 17 | asset_name: nj-cli-linux-amd64 18 | - os: macos-latest 19 | artifact_name: nj-cli 20 | asset_name: nj-cli-macos-amd64 21 | - os: windows-latest 22 | artifact_name: nj-cli.exe 23 | asset_name: nj-cli.exe 24 | steps: 25 | - name: Checkout code 26 | uses: actions/checkout@v4 27 | - name: Build nj-cli 28 | run: cargo build --release --manifest-path nj-cli/Cargo.toml 29 | - name: Upload binary to release 30 | uses: svenstaro/upload-release-action@v2 31 | with: 32 | repo_token: ${{ secrets.GITHUB_TOKEN }} 33 | file: target/release/${{ matrix.artifact_name }} 34 | asset_name: ${{ matrix.asset_name }} 35 | tag: ${{ github.ref }} 36 | overwrite: true 37 | publish_crates: 38 | name: Publish to Crates.io 39 | runs-on: ubuntu-latest 40 | steps: 41 | - uses: actions/checkout@v4 42 | - uses: actions-rs/toolchain@v1 43 | with: 44 | toolchain: stable 45 | - uses: actions-rs/install@v0.1 46 | with: 47 | crate: cargo-workspaces 48 | version: latest 49 | - name: Publish 50 | run: cargo workspaces publish --token ${{ secrets.CRATES_TOKEN }} --from-git --yes 51 | 52 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | **/*.rs.bk 3 | .DS_Store 4 | .docker-cargo 5 | target-docker 6 | .vscode/tasks.json 7 | dist 8 | dylib 9 | .idea/ 10 | node_modules -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ohos-node-bindgen" 3 | version = "6.0.3" 4 | authors = ["Stuart Zhang "] 5 | edition = "2021" 6 | description = "easy way to write nodejs module using rust" 7 | repository = "https://github.com/stuartZhang/node-bindgen" 8 | readme = "README.md" 9 | license = "Apache-2.0" 10 | 11 | [features] 12 | default = ["node"] 13 | node = ["nj-sys", "nj-core", "nj-derive"] 14 | serde-json = ["nj-core/serde-json"] 15 | uuid = ["nj-core/convert-uuid"] 16 | 17 | [dependencies] 18 | nj-sys = { version = "0.1.1", package = "oh-napi-sys", optional = true } 19 | nj-core = { path = "nj-core", package = "ohos-nj-core", version = "6.0.1", optional = true } 20 | nj-derive = { path = "nj-derive", package = "ohos-nj-derive", version = "3.2.0", optional = true } 21 | 22 | [workspace] 23 | resolver = "2" 24 | members = ["nj-cli", "nj-core", "nj-derive"] 25 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | RUSTV=stable 2 | 3 | install_windows_on_mac: 4 | rustup target add x86_64-pc-windows-gnu 5 | brew install mingw-w64 6 | 7 | 8 | build-windows: 9 | cargo build --target=x86_64-pc-windows-gnu 10 | 11 | 12 | test-all: test-derive test-examples 13 | 14 | test-unit: 15 | cargo test --lib --all-features 16 | 17 | test-examples: 18 | make -C examples test 19 | 20 | test-derive: 21 | cd nj-derive; RUST_LOG=debug cargo test -- --nocapture 22 | 23 | 24 | # 25 | # Various Lint tools 26 | # 27 | 28 | install-fmt: 29 | rustup component add rustfmt --toolchain $(RUSTV) 30 | 31 | check-fmt: 32 | cargo fmt -- --check 33 | 34 | install-clippy: 35 | rustup component add clippy 36 | 37 | check-clippy: install-clippy check-clippy-examples 38 | cargo clippy --all --all-features -- \ 39 | -D warnings \ 40 | -A clippy::upper_case_acronyms \ 41 | -A clippy::needless-question-mark 42 | 43 | check-clippy-examples: install-clippy 44 | make -C examples check-clippy 45 | -------------------------------------------------------------------------------- /bors.toml: -------------------------------------------------------------------------------- 1 | status = [ 2 | "Done", 3 | ] 4 | use_squash_merge = true 5 | delete_merged_branches = true 6 | timeout_sec = 1800 # 30 mins 7 | -------------------------------------------------------------------------------- /examples/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = [ 4 | "function", 5 | "cb", 6 | "promise", 7 | "async-cb", 8 | "json", 9 | "param", 10 | "js-env", 11 | "class-simple", 12 | "class-wrapper", 13 | "class-async", 14 | "stream", 15 | "buffer", 16 | "array", 17 | "electron", 18 | "cleanup", 19 | "bigint", 20 | "tuples", 21 | "uuid", 22 | "logging", 23 | "option", 24 | ] 25 | 26 | 27 | 28 | [workspace.dependencies] 29 | serde = { version = "1.0.110", features = ["derive"] } 30 | serde_json = "1.0.53" 31 | futures-lite = "1.7.0" 32 | uuid = "1.1.0" 33 | tracing = "0.1.37" 34 | 35 | ohos-node-bindgen = { path = "..", features = ["default"]} 36 | fluvio-future = { version = "0.6.0", features = ["timer"] } -------------------------------------------------------------------------------- /examples/Makefile: -------------------------------------------------------------------------------- 1 | clean: 2 | cargo clean 3 | make -C function clean 4 | make -C async-cb clean 5 | make -C cb clean 6 | make -C promise clean 7 | make -C stream clean 8 | make -C json clean 9 | make -C class-simple clean 10 | make -C class-wrapper clean 11 | make -C class-async clean 12 | make -C param clean 13 | make -C electron clean 14 | make -C bigint clean 15 | make -C tuple clean 16 | make -C cleanup clean 17 | make -C logging clean 18 | make -C option clean 19 | 20 | install: 21 | npm install 22 | 23 | test: install test-function test-cb test-async-cb test-promise test-json test-class-simple \ 24 | test-class-wrapper test-class-async test-stream test-buffer test-array test-bigint test-logging\ 25 | test-cleanup test-jsenv test-option 26 | 27 | test-function: 28 | make -C function test 29 | 30 | test-async-cb: 31 | make -C async-cb test 32 | 33 | test-cb: 34 | make -C cb test 35 | 36 | test-promise: 37 | make -C promise test 38 | 39 | test-json: 40 | make -C json test 41 | 42 | test-stream: 43 | make -C stream test 44 | 45 | test-class-simple: 46 | make -C class-simple test 47 | 48 | test-class-wrapper: 49 | make -C class-wrapper test 50 | 51 | test-class-async: 52 | make -C class-async test 53 | 54 | test-param: 55 | make -C param test 56 | 57 | test-buffer: 58 | make -C buffer test 59 | 60 | test-array: 61 | make -C array test 62 | 63 | test-electron: 64 | make -C electron test 65 | 66 | test-bigint: 67 | make -C bigint test 68 | 69 | test-tuple: 70 | make -C tuple test 71 | 72 | test-logging: 73 | make -C logging test 74 | 75 | test-cleanup: 76 | make -C cleanup test 77 | 78 | test-jsenv: 79 | make -C js-env test 80 | 81 | test-option: 82 | make -C option test 83 | 84 | check-clippy: 85 | cargo clippy --all --all-features -- \ 86 | -D warnings \ 87 | -A clippy::upper_case_acronyms \ 88 | -A clippy::needless-question-mark 89 | -------------------------------------------------------------------------------- /examples/array/.gitignore: -------------------------------------------------------------------------------- 1 | dist -------------------------------------------------------------------------------- /examples/array/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nj-example-array" 3 | version = "0.0.0" 4 | authors = ["Stuart Zhang "] 5 | edition = "2021" 6 | publish = false 7 | 8 | [lib] 9 | crate-type = ["cdylib"] 10 | 11 | 12 | [dependencies] 13 | ohos-node-bindgen = { workspace = true} 14 | 15 | 16 | [build-dependencies] 17 | ohos-node-bindgen = { path = "../../", default-features = false, features = ["build"] } 18 | -------------------------------------------------------------------------------- /examples/array/Makefile: -------------------------------------------------------------------------------- 1 | all: build 2 | 3 | build: 4 | nj-cli build 5 | 6 | test: build 7 | node test.js 8 | 9 | 10 | clean: 11 | rm -rf dist 12 | 13 | -------------------------------------------------------------------------------- /examples/array/README.md: -------------------------------------------------------------------------------- 1 | return json object -------------------------------------------------------------------------------- /examples/array/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | ohos_node_bindgen::build::configure(); 3 | } 4 | -------------------------------------------------------------------------------- /examples/array/src/lib.rs: -------------------------------------------------------------------------------- 1 | use ohos_node_bindgen::derive::node_bindgen; 2 | 3 | /// create array and fill with increase value 4 | #[ohos_node_bindgen] 5 | fn make_array(count: i32) -> Vec { 6 | let mut array = vec![]; 7 | for i in 0..count { 8 | array.push(i); 9 | } 10 | array 11 | } 12 | 13 | /// sum array of values 14 | #[ohos_node_bindgen] 15 | fn sum_array(array: Vec) -> i32 { 16 | array.iter().sum() 17 | } 18 | -------------------------------------------------------------------------------- /examples/array/test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | let addon = require('./dist'); 4 | const { format } = require('path'); 5 | 6 | let array = addon.makeArray(10); 7 | 8 | let count = 0; 9 | array.forEach( (val, index) => { 10 | assert.equal(val,index,"index value is same as value"); 11 | count = count + 1; 12 | }); 13 | assert.equal(count,10); 14 | 15 | assert.equal(addon.sumArray([1,2,3]),6,"sum should be 6"); 16 | 17 | assert.throws( () => addon.sumArray({ x: 2}),{ 18 | message: 'Provided value was not an array as expected' 19 | }); 20 | -------------------------------------------------------------------------------- /examples/async-cb/.gitignore: -------------------------------------------------------------------------------- 1 | dylib -------------------------------------------------------------------------------- /examples/async-cb/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nj-example-async-cb" 3 | version = "0.0.0" 4 | authors = ["Stuart Zhang "] 5 | edition = "2021" 6 | publish = false 7 | 8 | [lib] 9 | crate-type = ["cdylib"] 10 | 11 | [dependencies] 12 | tracing = { workspace = true } 13 | ohos-node-bindgen = { workspace = true} 14 | fluvio-future = { workspace = true} 15 | 16 | [build-dependencies] 17 | ohos-node-bindgen = { path = "../../", default-features = false, features = ["build"] } -------------------------------------------------------------------------------- /examples/async-cb/Makefile: -------------------------------------------------------------------------------- 1 | all: build 2 | 3 | build: 4 | nj-cli build 5 | 6 | test: build 7 | node test_cb.js 8 | 9 | 10 | clean: 11 | rm -rf dist 12 | 13 | -------------------------------------------------------------------------------- /examples/async-cb/README.md: -------------------------------------------------------------------------------- 1 | callback inside async fn -------------------------------------------------------------------------------- /examples/async-cb/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | ohos_node_bindgen::build::configure(); 3 | } 4 | -------------------------------------------------------------------------------- /examples/async-cb/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | 3 | use fluvio_future::timer::sleep; 4 | use ohos_node_bindgen::derive::node_bindgen; 5 | 6 | #[ohos_node_bindgen] 7 | async fn basic(seconds: i32, cb: F) { 8 | sleep(Duration::from_secs(1)).await; 9 | cb(seconds as f64, (seconds * 2) as f64); 10 | } 11 | 12 | #[ohos_node_bindgen] 13 | async fn hello(seconds: i32, cb: F) { 14 | // println!("sleeping"); 15 | sleep(Duration::from_secs(seconds as u64)).await; 16 | // println!("woke from time"); 17 | 18 | cb(10.0, "hello world".to_string()); 19 | } 20 | -------------------------------------------------------------------------------- /examples/async-cb/test_cb.js: -------------------------------------------------------------------------------- 1 | let addon = require('./dist'); 2 | const assert = require('assert'); 3 | 4 | 5 | addon.hello(1,function(val,msg){ 6 | assert.strictEqual(val,10); 7 | assert.strictEqual(msg,"hello world"); 8 | console.log("callback test succeed"); 9 | }); 10 | 11 | 12 | addon.basic(10,function(val,val2){ 13 | assert.strictEqual(val,10); 14 | assert.strictEqual(val2,20); 15 | console.log("callback test succeed"); 16 | }); 17 | 18 | -------------------------------------------------------------------------------- /examples/bigint/.gitignore: -------------------------------------------------------------------------------- 1 | dist -------------------------------------------------------------------------------- /examples/bigint/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nj-example-bigint" 3 | version = "0.0.0" 4 | authors = ["Stuart Zhang "] 5 | edition = "2021" 6 | publish = false 7 | 8 | [lib] 9 | crate-type = ["cdylib"] 10 | 11 | 12 | [dependencies] 13 | ohos-node-bindgen = { workspace = true} 14 | 15 | 16 | [build-dependencies] 17 | ohos-node-bindgen = { path = "../../", default-features = false, features = ["build"] } -------------------------------------------------------------------------------- /examples/bigint/Makefile: -------------------------------------------------------------------------------- 1 | all: build 2 | 3 | build: 4 | nj-cli build 5 | 6 | test: build 7 | node test.js 8 | 9 | 10 | clean: 11 | rm -rf dist 12 | 13 | -------------------------------------------------------------------------------- /examples/bigint/README.md: -------------------------------------------------------------------------------- 1 | manual JS callback -------------------------------------------------------------------------------- /examples/bigint/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | ohos_node_bindgen::build::configure(); 3 | } 4 | -------------------------------------------------------------------------------- /examples/bigint/src/lib.rs: -------------------------------------------------------------------------------- 1 | use ohos_node_bindgen::derive::node_bindgen; 2 | use ohos_node_bindgen::core::bigint::BigInt; 3 | 4 | // example where we multiply a big_int. 5 | #[ohos_node_bindgen] 6 | fn multiply_big_int(arg: BigInt) -> BigInt { 7 | println!("bigint arg: {arg}"); 8 | arg * 2 9 | } 10 | 11 | // Test that we can go across the FFI without screwing up the bits. 12 | #[ohos_node_bindgen] 13 | fn do_nothing(arg: BigInt) -> BigInt { 14 | println!("bigint arg: {arg}"); 15 | arg 16 | } 17 | 18 | // Test out the signage conversion 19 | #[ohos_node_bindgen] 20 | fn go_negative(arg: BigInt) -> BigInt { 21 | println!("bigint arg: {arg}"); 22 | -1 * arg 23 | } 24 | 25 | // Test out that we can return a u64 which is automatically converted to a bigint. 26 | #[ohos_node_bindgen] 27 | fn return_u64(arg: u32) -> u64 { 28 | println!("bigint arg: {arg}"); 29 | arg as u64 30 | } 31 | -------------------------------------------------------------------------------- /examples/bigint/test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | let addon = require('./dist'); 4 | 5 | const hugeBin = BigInt("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF") 6 | 7 | // Test that we can go back and forth through the FFI 8 | assert.equal(addon.doNothing(hugeBin), hugeBin); 9 | 10 | // Test out some signage 11 | assert.equal(addon.goNegative(hugeBin), hugeBin*BigInt(-1)); 12 | 13 | // How about multiplication? 14 | assert.equal(addon.multiplyBigInt(hugeBin), hugeBin*(BigInt(2))); 15 | 16 | // Basic tests from js-env 17 | assert.equal(addon.multiplyBigInt(BigInt(-5)), BigInt(-10)); 18 | assert.equal(addon.multiplyBigInt(BigInt(5)), BigInt(10)); 19 | assert.equal(addon.multiplyBigInt(BigInt(5)), BigInt(10)); 20 | 21 | // Rust's u64s turn into JS BigInts. 22 | assert.equal(addon.returnU64(5), BigInt(5)); 23 | -------------------------------------------------------------------------------- /examples/buffer/.gitignore: -------------------------------------------------------------------------------- 1 | dist -------------------------------------------------------------------------------- /examples/buffer/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nj-example-buffer" 3 | version = "0.0.0" 4 | authors = ["Stuart Zhang "] 5 | edition = "2021" 6 | publish = false 7 | 8 | 9 | [lib] 10 | crate-type = ["cdylib"] 11 | 12 | 13 | [dependencies] 14 | serde = { workspace = true } 15 | serde_json = { workspace = true } 16 | ohos-node-bindgen = { workspace = true} 17 | 18 | 19 | [build-dependencies] 20 | ohos-node-bindgen = { path = "../../", default-features = false, features = ["build"] } 21 | -------------------------------------------------------------------------------- /examples/buffer/Makefile: -------------------------------------------------------------------------------- 1 | all: build 2 | 3 | build: 4 | nj-cli build -- -p nj-example-buffer 5 | 6 | test: build 7 | node test.js 8 | 9 | 10 | clean: 11 | rm -rf dist 12 | 13 | -------------------------------------------------------------------------------- /examples/buffer/README.md: -------------------------------------------------------------------------------- 1 | return json object -------------------------------------------------------------------------------- /examples/buffer/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | ohos_node_bindgen::build::configure(); 3 | } 4 | -------------------------------------------------------------------------------- /examples/buffer/src/lib.rs: -------------------------------------------------------------------------------- 1 | use serde::Serialize; 2 | 3 | use ohos_node_bindgen::derive::node_bindgen; 4 | use ohos_node_bindgen::core::buffer::{ArrayBuffer, JSArrayBuffer}; 5 | use ohos_node_bindgen::core::NjError; 6 | 7 | #[derive(Serialize)] 8 | struct MyStruct { 9 | a: String, 10 | b: i32, 11 | } 12 | 13 | /// byte array buffer from json bytes 14 | #[ohos_node_bindgen] 15 | fn test(b: i32) -> Result { 16 | let my_struct = MyStruct { 17 | a: "b".to_string(), 18 | b, 19 | }; 20 | 21 | let json_string = serde_json::to_vec(&my_struct) 22 | .map_err(|err| NjError::Other(format!("serialization error: {err}")))?; 23 | 24 | Ok(ArrayBuffer::new(json_string)) 25 | } 26 | 27 | use ohos_node_bindgen::core::val::JsEnv; 28 | use ohos_node_bindgen::core::TryIntoJs; 29 | use ohos_node_bindgen::core::val::JsObject; 30 | use ohos_node_bindgen::sys::napi_value; 31 | 32 | struct Record { 33 | buffer: ArrayBuffer, 34 | comment: String, 35 | } 36 | 37 | impl TryIntoJs for Record { 38 | /// serialize into json object 39 | fn try_to_js(self, js_env: &JsEnv) -> Result { 40 | // create JSON 41 | let mut json = JsObject::create(js_env)?; 42 | 43 | json.set_property("buffer", self.buffer.try_to_js(js_env)?)?; 44 | json.set_property("comment", js_env.create_string_utf8(&self.comment)?)?; 45 | 46 | json.try_to_js(js_env) 47 | } 48 | } 49 | 50 | /// create byte array and wrap in side another json obj 51 | #[ohos_node_bindgen] 52 | fn test2(b: i32) -> Result { 53 | let my_struct = MyStruct { 54 | a: "b".to_string(), 55 | b, 56 | }; 57 | 58 | let json_string = serde_json::to_vec(&my_struct) 59 | .map_err(|err| NjError::Other(format!("serialization error: {err}")))?; 60 | 61 | Ok(Record { 62 | buffer: ArrayBuffer::new(json_string), 63 | comment: "array buffer is cool!".to_owned(), 64 | }) 65 | } 66 | 67 | #[ohos_node_bindgen] 68 | fn test3(data: JSArrayBuffer) -> Result { 69 | let message = String::from_utf8(data.to_vec())?; 70 | Ok(format!("reply {message}")) 71 | } 72 | 73 | #[ohos_node_bindgen] 74 | fn test4(first: JSArrayBuffer, second: JSArrayBuffer) -> Result { 75 | let message1 = String::from_utf8(first.to_vec())?; 76 | let message2 = String::from_utf8(second.to_vec())?; 77 | 78 | Ok(format!("{message1} {message2}")) 79 | } 80 | -------------------------------------------------------------------------------- /examples/buffer/test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | let addon = require('./dist'); 4 | const { format } = require('path'); 5 | 6 | 7 | let bytes = addon.test(5); 8 | console.log("received bytes: ", bytes.byteLength); 9 | 10 | // create buffer view from byte array 11 | let buffer = Buffer.from(bytes); 12 | assert.deepStrictEqual(JSON.parse(buffer), { a: 'b', b: 5 }); 13 | 14 | 15 | let record = addon.test2(10); 16 | assert.strictEqual(record.comment, "array buffer is cool!"); 17 | assert.deepStrictEqual(JSON.parse(Buffer.from(record.buffer)), { a: 'b', b: 10 }); 18 | 19 | 20 | assert.strictEqual(addon.test3(Buffer.from("hello")),"reply hello"); 21 | assert.strictEqual(addon.test4(Buffer.from("hello"),Buffer.from("world")),"hello world"); -------------------------------------------------------------------------------- /examples/cb/.gitignore: -------------------------------------------------------------------------------- 1 | dylib -------------------------------------------------------------------------------- /examples/cb/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nj-example-cb" 3 | version = "0.1.0" 4 | authors = ["Stuart Zhang "] 5 | edition = "2021" 6 | 7 | 8 | [lib] 9 | crate-type = ["cdylib"] 10 | 11 | 12 | [dependencies] 13 | ohos-node-bindgen = { workspace = true} 14 | 15 | [build-dependencies] 16 | ohos-node-bindgen = { path = "../../", default-features = false, features = ["build"] } -------------------------------------------------------------------------------- /examples/cb/Makefile: -------------------------------------------------------------------------------- 1 | all: build 2 | 3 | build: 4 | nj-cli build 5 | 6 | 7 | test: build 8 | node test.js 9 | 10 | 11 | clean: 12 | rm -rf dist 13 | 14 | -------------------------------------------------------------------------------- /examples/cb/README.md: -------------------------------------------------------------------------------- 1 | simple hello world callback -------------------------------------------------------------------------------- /examples/cb/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | ohos_node_bindgen::build::configure(); 3 | } 4 | -------------------------------------------------------------------------------- /examples/cb/src/lib.rs: -------------------------------------------------------------------------------- 1 | use ohos_node_bindgen::derive::node_bindgen; 2 | 3 | #[ohos_node_bindgen] 4 | fn hello(first: f64, second: F) { 5 | let msg = format!("argument is: {first}"); 6 | 7 | second(msg); 8 | } 9 | 10 | #[ohos_node_bindgen] 11 | fn example(cb: F, second: i32) { 12 | cb(second * 2) 13 | } 14 | 15 | /* 16 | #[ohos_node_bindgen] 17 | fn sum String>(cb: F,second: i32) -> String { 18 | let message = cb(second*2); 19 | format!("my message: {}",message) 20 | } 21 | */ 22 | -------------------------------------------------------------------------------- /examples/cb/test.js: -------------------------------------------------------------------------------- 1 | let addon = require('./dist'); 2 | const assert = require('assert'); 3 | 4 | addon.hello(2,function(msg){ 5 | assert.equal(msg,"argument is: 2"); 6 | console.log("callback test succeed"); 7 | }); 8 | 9 | assert.throws( () => addon.hello(2),{ 10 | message: 'trying to get arg at: 1 but only 1 args passed' 11 | }); 12 | 13 | addon.example(function(val){ 14 | assert.equal(val,20); 15 | },10); -------------------------------------------------------------------------------- /examples/class-async/.gitignore: -------------------------------------------------------------------------------- 1 | dylib -------------------------------------------------------------------------------- /examples/class-async/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nj-example-class-async" 3 | version = "0.0.0" 4 | authors = ["Stuart Zhang "] 5 | edition = "2021" 6 | publish = false 7 | 8 | 9 | [lib] 10 | crate-type = ["cdylib"] 11 | 12 | 13 | [dependencies] 14 | ohos-node-bindgen = { workspace = true} 15 | fluvio-future = { workspace = true} 16 | 17 | [build-dependencies] 18 | ohos-node-bindgen = { path = "../../", default-features = false, features = ["build"] } 19 | -------------------------------------------------------------------------------- /examples/class-async/Makefile: -------------------------------------------------------------------------------- 1 | all: build 2 | 3 | build: 4 | nj-cli build 5 | 6 | test: build 7 | node test.js 8 | 9 | 10 | clean: 11 | rm -rf dist 12 | 13 | -------------------------------------------------------------------------------- /examples/class-async/README.md: -------------------------------------------------------------------------------- 1 | simple hello world -------------------------------------------------------------------------------- /examples/class-async/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | ohos_node_bindgen::build::configure(); 3 | } 4 | -------------------------------------------------------------------------------- /examples/class-async/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | 3 | use fluvio_future::timer::sleep; 4 | 5 | use ohos_node_bindgen::sys::napi_value; 6 | use ohos_node_bindgen::core::NjError; 7 | use ohos_node_bindgen::core::val::JsObject; 8 | use ohos_node_bindgen::core::val::JsEnv; 9 | use ohos_node_bindgen::core::TryIntoJs; 10 | use ohos_node_bindgen::derive::node_bindgen; 11 | 12 | struct MyJson { 13 | val: f64, 14 | } 15 | 16 | impl TryIntoJs for MyJson { 17 | fn try_to_js(self, js_env: &JsEnv) -> Result { 18 | // create JSON 19 | let mut json = JsObject::new(*js_env, js_env.create_object()?); 20 | 21 | let js_val = js_env.create_double(self.val)?; 22 | json.set_property("val", js_val)?; 23 | 24 | json.try_to_js(js_env) 25 | } 26 | } 27 | 28 | struct MyObject { 29 | val: f64, 30 | } 31 | 32 | #[ohos_node_bindgen] 33 | impl MyObject { 34 | #[node_bindgen(constructor)] 35 | fn new(val: f64) -> Self { 36 | Self { val } 37 | } 38 | 39 | /// promise which result in primitive type 40 | #[ohos_node_bindgen] 41 | async fn plus_two(&self, arg: f64) -> f64 { 42 | println!("sleeping"); 43 | sleep(Duration::from_secs(1)).await; 44 | println!("woke and adding {arg}"); 45 | 46 | self.val + arg 47 | } 48 | 49 | /// promise where result is arbitrary struct. 50 | /// returning struct must implement TryIntoJs 51 | /// which can create new JS instance 52 | #[ohos_node_bindgen] 53 | async fn multiply2(&self, arg: f64) -> MyObjectConstructor { 54 | println!("sleeping"); 55 | sleep(Duration::from_secs(1)).await; 56 | println!("woke and adding {arg}"); 57 | 58 | MyObjectConstructor::new(self.val * arg) 59 | } 60 | 61 | /// loop and emit event 62 | #[ohos_node_bindgen] 63 | async fn sleep(&self, cb: F) { 64 | println!("sleeping"); 65 | sleep(Duration::from_secs(1)).await; 66 | let msg = "hello world".to_string(); 67 | cb(msg); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /examples/class-async/test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | let addon = require('./dist'); 4 | 5 | let obj = new addon.MyObject(10); 6 | 7 | 8 | obj.plusTwo(10).then((val) => { 9 | console.log("plus two is ", val); 10 | }); 11 | 12 | obj.multiply2(-1).then((obj3) => { 13 | console.log("multiply two ", obj3.value); 14 | }); 15 | 16 | obj.sleep((msg) => { 17 | assert.equal(msg, "hello world");; 18 | }); 19 | -------------------------------------------------------------------------------- /examples/class-simple/.gitignore: -------------------------------------------------------------------------------- 1 | dist -------------------------------------------------------------------------------- /examples/class-simple/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nj-example-class-simple" 3 | version = "0.0.0" 4 | authors = ["Stuart Zhang "] 5 | edition = "2021" 6 | publish = false 7 | 8 | 9 | [lib] 10 | crate-type = ["cdylib"] 11 | 12 | 13 | [dependencies] 14 | ohos-node-bindgen = { workspace = true} 15 | 16 | 17 | [build-dependencies] 18 | ohos-node-bindgen = { path = "../../", default-features = false, features = ["build"] } 19 | -------------------------------------------------------------------------------- /examples/class-simple/Makefile: -------------------------------------------------------------------------------- 1 | all: build 2 | 3 | build: 4 | nj-cli build 5 | 6 | test: build 7 | node test.js 8 | 9 | 10 | clean: 11 | rm -rf dist 12 | 13 | -------------------------------------------------------------------------------- /examples/class-simple/README.md: -------------------------------------------------------------------------------- 1 | simple hello world -------------------------------------------------------------------------------- /examples/class-simple/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | ohos_node_bindgen::build::configure(); 3 | } 4 | -------------------------------------------------------------------------------- /examples/class-simple/src/lib.rs: -------------------------------------------------------------------------------- 1 | use ohos_node_bindgen::derive::node_bindgen; 2 | 3 | struct MyObject { 4 | val: f64, 5 | val2: i64, 6 | } 7 | 8 | #[ohos_node_bindgen] 9 | impl MyObject { 10 | #[node_bindgen(constructor)] 11 | fn new(val: f64, val2: i64) -> Self { 12 | Self { val, val2 } 13 | } 14 | 15 | /// simple method which return f64 16 | /// rust values are automatically converted into equivalent JS value 17 | /// method name are generated from rust method name 18 | /// Js: let y = obj.plusOne(); 19 | #[ohos_node_bindgen] 20 | fn plus_one(&self) -> f64 { 21 | self.val + 1.0 22 | } 23 | 24 | /// JS getter 25 | /// Js: let y = obj.value; 26 | #[node_bindgen(getter)] 27 | fn value(&self) -> f64 { 28 | self.val 29 | } 30 | 31 | /// JS getter 32 | /// Js: let y = obj.value2; 33 | #[node_bindgen(getter)] 34 | fn value2(&self) -> i64 { 35 | self.val2 36 | } 37 | 38 | /// JS Setter 39 | /// Js: obj.value3 = 10; 40 | #[node_bindgen(setter)] 41 | fn value3(&mut self, val: f64) { 42 | self.val = val; 43 | } 44 | 45 | /// method with custom name instead of generated name 46 | /// Js: obj.updateValue(10); 47 | #[node_bindgen(name = "updateValue")] 48 | fn set_value(&mut self, val: f64) { 49 | self.val = val; 50 | } 51 | 52 | #[node_bindgen(setter, name = "value4")] 53 | fn set_value4(&mut self, val: f64) { 54 | self.val = val; 55 | } 56 | 57 | #[ohos_node_bindgen] 58 | fn change_value(&mut self, val: f64) { 59 | self.val = val; 60 | } 61 | 62 | #[node_bindgen(getter)] 63 | fn is_positive(&self) -> bool { 64 | self.val > 0.0 65 | } 66 | 67 | #[node_bindgen(setter)] 68 | fn clear(&mut self, val: bool) { 69 | if val { 70 | self.val = 0.0; 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /examples/class-simple/test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | let addon = require('./dist'); 4 | 5 | 6 | let obj = new addon.MyObject(10,2); 7 | assert.equal(obj.value,10,"verify value works"); 8 | assert.equal(obj.plusOne(),11); 9 | assert.equal(obj.value2,2); 10 | //console.print("first"); 11 | 12 | obj.changeValue(100); 13 | assert.equal(obj.value,100); 14 | 15 | obj.updateValue(50); 16 | assert.equal(obj.value,50); 17 | 18 | obj.value3 = 10; 19 | assert.equal(obj.value,10,"test setter"); 20 | 21 | obj.value4 = 60; 22 | assert.equal(obj.value,60,"test test setter with custom property"); 23 | 24 | obj.value4 = -10; 25 | assert.equal(obj.isPositive,false); 26 | 27 | obj.value4 = 10; 28 | assert.equal(obj.isPositive,true); 29 | 30 | obj.clear = false; 31 | assert.equal(obj.value,10); 32 | 33 | obj.clear = true; 34 | assert.equal(obj.value,0); 35 | 36 | console.log("class simple test succeed"); -------------------------------------------------------------------------------- /examples/class-wrapper/.gitignore: -------------------------------------------------------------------------------- 1 | dist -------------------------------------------------------------------------------- /examples/class-wrapper/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nj-example-class-wrapper" 3 | version = "0.0.0" 4 | authors = ["Stuart Zhang "] 5 | edition = "2021" 6 | publish = false 7 | 8 | 9 | [lib] 10 | crate-type = ["cdylib"] 11 | 12 | 13 | [dependencies] 14 | ohos-node-bindgen = { workspace = true} 15 | 16 | 17 | [build-dependencies] 18 | ohos-node-bindgen = { path = "../../", default-features = false, features = ["build"] } 19 | -------------------------------------------------------------------------------- /examples/class-wrapper/Makefile: -------------------------------------------------------------------------------- 1 | all: build 2 | 3 | build: 4 | nj-cli build -- -p nj-example-class-wrapper 5 | 6 | test: build 7 | node test.js 8 | 9 | 10 | clean: 11 | rm -rf dist 12 | 13 | -------------------------------------------------------------------------------- /examples/class-wrapper/README.md: -------------------------------------------------------------------------------- 1 | simple hello world -------------------------------------------------------------------------------- /examples/class-wrapper/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | ohos_node_bindgen::build::configure(); 3 | } 4 | -------------------------------------------------------------------------------- /examples/class-wrapper/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::io::Error as IoError; 2 | 3 | use ohos_node_bindgen::sys::napi_value; 4 | use ohos_node_bindgen::core::JSClass; 5 | use ohos_node_bindgen::core::NjError; 6 | use ohos_node_bindgen::core::val::JsEnv; 7 | use ohos_node_bindgen::core::TryIntoJs; 8 | use ohos_node_bindgen::derive::node_bindgen; 9 | 10 | /// simple wrapper 11 | #[ohos_node_bindgen] 12 | fn simple(val: f64) -> Result { 13 | Ok(TestObject { val: Some(val) }) 14 | } 15 | 16 | impl TryIntoJs for TestObject { 17 | fn try_to_js(self, js_env: &JsEnv) -> Result { 18 | let instance = Self::new_instance(js_env, vec![])?; 19 | let test_object = Self::unwrap_mut(js_env, instance)?; 20 | test_object.set_value(self.val.unwrap()); 21 | Ok(instance) 22 | } 23 | } 24 | 25 | /// indirect wrapper 26 | #[ohos_node_bindgen] 27 | async fn create(val: f64) -> Result { 28 | Ok(MyObjectWrapper { val }) 29 | } 30 | 31 | struct MyObjectWrapper { 32 | val: f64, 33 | } 34 | 35 | impl TryIntoJs for MyObjectWrapper { 36 | fn try_to_js(self, js_env: &JsEnv) -> Result { 37 | let instance = TestObject::new_instance(js_env, vec![])?; 38 | let test_object = TestObject::unwrap_mut(js_env, instance)?; 39 | test_object.set_value(self.val); 40 | Ok(instance) 41 | } 42 | } 43 | 44 | struct TestObject { 45 | val: Option, 46 | } 47 | 48 | #[ohos_node_bindgen] 49 | impl TestObject { 50 | #[node_bindgen(constructor)] 51 | fn new() -> Self { 52 | Self { val: None } 53 | } 54 | 55 | #[node_bindgen(setter, name = "value")] 56 | fn set_value(&mut self, val: f64) { 57 | self.val.replace(val); 58 | } 59 | 60 | #[node_bindgen(getter)] 61 | fn value2(&self) -> f64 { 62 | self.val.unwrap_or(0.0) 63 | } 64 | 65 | #[ohos_node_bindgen] 66 | fn test(&self) -> f64 { 67 | 0.0 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /examples/class-wrapper/test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | let addon = require('./dist'); 4 | 5 | let t = new addon.TestObject(); 6 | t.value = 20; 7 | assert.equal(t.value2, 20); 8 | 9 | 10 | assert.equal(addon.simple(5).value2, 5); 11 | 12 | addon.create(10).then((test_object) => { 13 | console.log("test value is %s", test_object.value2); 14 | }); -------------------------------------------------------------------------------- /examples/cleanup/.gitignore: -------------------------------------------------------------------------------- 1 | dist -------------------------------------------------------------------------------- /examples/cleanup/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nj-example-cleaup" 3 | version = "0.0.0" 4 | authors = ["Stuart Zhang "] 5 | edition = "2021" 6 | publish = false 7 | 8 | 9 | [lib] 10 | crate-type = ["cdylib"] 11 | 12 | 13 | [dependencies] 14 | ohos-node-bindgen = { workspace = true} 15 | 16 | 17 | [build-dependencies] 18 | ohos-node-bindgen = { path = "../../", default-features = false, features = ["build"] } 19 | -------------------------------------------------------------------------------- /examples/cleanup/Makefile: -------------------------------------------------------------------------------- 1 | all: build 2 | 3 | build: 4 | nj-cli build 5 | 6 | test: build 7 | node test.js 8 | 9 | 10 | clean: 11 | rm -rf dist 12 | 13 | -------------------------------------------------------------------------------- /examples/cleanup/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | ohos_node_bindgen::build::configure(); 3 | } 4 | -------------------------------------------------------------------------------- /examples/cleanup/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | 3 | use ohos_node_bindgen::derive::node_bindgen; 4 | use ohos_node_bindgen::core::val::JsEnv; 5 | use ohos_node_bindgen::core::NjError; 6 | 7 | /// initialize env hook up 8 | #[ohos_node_bindgen] 9 | fn init(env: JsEnv) -> Result<(), NjError> { 10 | unsafe { env.add_env_clean_up_hook(Some(my_cleanup), ptr::null_mut())? }; 11 | println!("init"); 12 | Ok(()) 13 | } 14 | 15 | unsafe extern "C" fn my_cleanup(_arg: *mut ::std::os::raw::c_void) { 16 | println!("I'm called from node to do cleanup"); 17 | } 18 | -------------------------------------------------------------------------------- /examples/cleanup/test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | let addon = require('./dist'); 4 | const { format } = require('path'); 5 | 6 | addon.init(); -------------------------------------------------------------------------------- /examples/electron/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test-electron" 3 | version = "0.0.0" 4 | authors = ["Stuart Zhang "] 5 | edition = "2021" 6 | publish = false 7 | 8 | [lib] 9 | crate-type = ["cdylib"] 10 | 11 | 12 | [dependencies] 13 | ohos-node-bindgen = { workspace = true} 14 | 15 | 16 | [build-dependencies] 17 | ohos-node-bindgen = { path = "../../", default-features = false, features = ["build"] } 18 | -------------------------------------------------------------------------------- /examples/electron/Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | npm run build 3 | 4 | test: 5 | npm run test 6 | 7 | clean: 8 | rm -rf dist 9 | 10 | -------------------------------------------------------------------------------- /examples/electron/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | ohos_node_bindgen::build::configure(); 3 | } 4 | -------------------------------------------------------------------------------- /examples/electron/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "electron-test", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "test.js", 6 | "scripts": { 7 | "test": "npm run build && npx electron ./test.js", 8 | "build": "npm install && npx electron-build-env nj-cli build -- -p test-electron", 9 | "start": "npm run test" 10 | }, 11 | "keywords": [ 12 | "ohos-node-bindgen", 13 | "electron" 14 | ], 15 | "author": "fluvio.io", 16 | "license": "MIT", 17 | "dependencies": { 18 | "assert": "^2.0.0", 19 | "electron": "^26.1.0" 20 | }, 21 | "devDependencies": { 22 | "electron-build-env": "^0.2.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /examples/electron/src/lib.rs: -------------------------------------------------------------------------------- 1 | use ohos_node_bindgen::derive::node_bindgen; 2 | 3 | struct MyObject { 4 | val: f64, 5 | val2: i64, 6 | } 7 | 8 | #[ohos_node_bindgen] 9 | impl MyObject { 10 | #[node_bindgen(constructor)] 11 | fn new(val: f64, val2: i64) -> Self { 12 | Self { val, val2 } 13 | } 14 | 15 | /// simple method which return f64 16 | /// rust values are automatically converted into equivalent JS value 17 | /// method name are generated from rust method name 18 | /// Js: let y = obj.plusOne(); 19 | #[ohos_node_bindgen] 20 | fn plus_one(&self) -> f64 { 21 | self.val + 1.0 22 | } 23 | 24 | /// JS getter 25 | /// Js: let y = obj.value; 26 | #[node_bindgen(getter)] 27 | fn value(&self) -> f64 { 28 | self.val 29 | } 30 | 31 | /// JS getter 32 | /// Js: let y = obj.value2; 33 | #[node_bindgen(getter)] 34 | fn value2(&self) -> i64 { 35 | self.val2 36 | } 37 | 38 | /// JS Setter 39 | /// Js: obj.value3 = 10; 40 | #[node_bindgen(setter)] 41 | fn value3(&mut self, val: f64) { 42 | self.val = val; 43 | } 44 | 45 | /// method with custom name instead of generated name 46 | /// Js: obj.updateValue(10); 47 | #[node_bindgen(name = "updateValue")] 48 | fn set_value(&mut self, val: f64) { 49 | self.val = val; 50 | } 51 | 52 | #[node_bindgen(setter, name = "value4")] 53 | fn set_value4(&mut self, val: f64) { 54 | self.val = val; 55 | } 56 | 57 | #[ohos_node_bindgen] 58 | fn change_value(&mut self, val: f64) { 59 | self.val = val; 60 | } 61 | 62 | #[node_bindgen(getter)] 63 | fn is_positive(&self) -> bool { 64 | self.val > 0.0 65 | } 66 | 67 | #[node_bindgen(setter)] 68 | fn clear(&mut self, val: bool) { 69 | if val { 70 | self.val = 0.0; 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /examples/electron/test.js: -------------------------------------------------------------------------------- 1 | const { app } = require("electron"); 2 | const assert = require("assert"); 3 | 4 | // NOTE: Tests are run without a BrowserWindow, in headless mode; 5 | // This is a minimum test harness for checking modules in an electron run-time; 6 | 7 | app.whenReady().then(() => { 8 | // Load native modules; 9 | let addon = require('./dist'); 10 | 11 | let obj = new addon.MyObject(10,2); 12 | 13 | assert.equal(obj.value,10,"verify value works"); 14 | assert.equal(obj.plusOne(),11); 15 | assert.equal(obj.value2,2); 16 | 17 | obj.changeValue(100); 18 | assert.equal(obj.value,100); 19 | 20 | obj.updateValue(50); 21 | assert.equal(obj.value,50); 22 | 23 | obj.value3 = 10; 24 | assert.equal(obj.value,10,"test setter"); 25 | 26 | obj.value4 = 60; 27 | assert.equal(obj.value,60,"test test setter with custom property"); 28 | 29 | obj.value4 = -10; 30 | assert.equal(obj.isPositive,false); 31 | 32 | obj.value4 = 10; 33 | assert.equal(obj.isPositive,true); 34 | 35 | obj.clear = false; 36 | assert.equal(obj.value,10); 37 | 38 | obj.clear = true; 39 | assert.equal(obj.value,0); 40 | 41 | console.log("class simple test succeed"); 42 | 43 | process.exit(0) 44 | }) -------------------------------------------------------------------------------- /examples/function/.gitignore: -------------------------------------------------------------------------------- 1 | dylib -------------------------------------------------------------------------------- /examples/function/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nj-example-function" 3 | version = "0.0.0" 4 | authors = ["Stuart Zhang "] 5 | edition = "2021" 6 | publish = false 7 | 8 | 9 | [lib] 10 | crate-type = ["cdylib"] 11 | 12 | 13 | [dependencies] 14 | ohos-node-bindgen = { workspace = true} 15 | 16 | [build-dependencies] 17 | ohos-node-bindgen = { path = "../../", default-features = false, features = ["build"] } -------------------------------------------------------------------------------- /examples/function/Makefile: -------------------------------------------------------------------------------- 1 | all: build 2 | 3 | build: 4 | nj-cli build 5 | 6 | test: build 7 | node test.js 8 | 9 | clean: 10 | rm -rf dist -------------------------------------------------------------------------------- /examples/function/README.md: -------------------------------------------------------------------------------- 1 | simple hello world -------------------------------------------------------------------------------- /examples/function/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | ohos_node_bindgen::build::configure(); 3 | } 4 | -------------------------------------------------------------------------------- /examples/function/src/lib.rs: -------------------------------------------------------------------------------- 1 | use ohos_node_bindgen::derive::node_bindgen; 2 | use ohos_node_bindgen::core::NjError; 3 | 4 | #[node_bindgen()] 5 | fn hello(count: i32) -> String { 6 | format!("hello world {count}") 7 | } 8 | 9 | #[ohos_node_bindgen] 10 | fn sum(first: i32, second: i32) -> i32 { 11 | first + second 12 | } 13 | 14 | // throw error if first > second, otherwise return sum 15 | #[ohos_node_bindgen] 16 | fn min_max(first: i32, second: i32) -> Result { 17 | if first > second { 18 | Err(NjError::Other("first arg is greater".to_owned())) 19 | } else { 20 | Ok(first + second) 21 | } 22 | } 23 | 24 | #[node_bindgen(name = "multiply")] 25 | fn mul(first: i32, second: i32) -> i32 { 26 | first * second 27 | } 28 | 29 | /// add second if supplied 30 | #[node_bindgen()] 31 | fn sum2(first: i32, second_arg: Option) -> i32 { 32 | if let Some(second) = second_arg { 33 | first + second 34 | } else { 35 | first 36 | } 37 | } 38 | 39 | #[node_bindgen()] 40 | fn string(first: String, second_arg: Option) -> String { 41 | if let Some(second) = second_arg { 42 | format!("{first} {second}") 43 | } else { 44 | first 45 | } 46 | } 47 | 48 | #[ohos_node_bindgen] 49 | fn give_null(null: bool) -> Option { 50 | if null { 51 | None 52 | } else { 53 | Some(null) 54 | } 55 | } 56 | 57 | #[ohos_node_bindgen] 58 | fn give_str(s: &str) -> String { 59 | s.to_string() 60 | } 61 | -------------------------------------------------------------------------------- /examples/function/test.js: -------------------------------------------------------------------------------- 1 | const crypto = require('crypto'); 2 | 3 | let addon =require('./dist'); 4 | const assert = require('assert'); 5 | 6 | assert.strictEqual(addon.hello(2),"hello world 2"); 7 | 8 | assert.throws( () => addon.hello("hello"),{ 9 | message: 'invalid type, expected: number, actual: string' 10 | }); 11 | 12 | assert.throws(() => addon.hello(),{ 13 | message: 'trying to get arg at: 0 but only 0 args passed' 14 | }); 15 | 16 | assert.strictEqual(addon.sum(1,2),3); 17 | 18 | assert.throws( () => addon.minMax(10,0),{ 19 | message: 'first arg is greater', 20 | }); 21 | 22 | assert.strictEqual(addon.minMax(1,2),3); 23 | 24 | assert.strictEqual(addon.multiply(2,5),10); 25 | 26 | assert.strictEqual(addon.sum2(10),10); 27 | assert.strictEqual(addon.sum2(5,100),105); 28 | 29 | assert.strictEqual(addon.giveNull(true), null); 30 | assert.strictEqual(addon.giveNull(false), false); 31 | 32 | const stringShort = _generateForCustomCharacters(5); 33 | const stringMedium = _generateForCustomCharacters(100); 34 | const stringLong = _generateForCustomCharacters(2000); 35 | const strings = new Set([stringShort, stringMedium, stringLong]); 36 | 37 | assert.strictEqual(addon.string(stringShort), stringShort); 38 | assert.strictEqual(addon.string(stringMedium), stringMedium); 39 | assert.strictEqual(addon.string(stringLong), stringLong); 40 | 41 | for(const string1 in strings) { 42 | for(const string2 in strings) { 43 | assert.strictEqual(addon.string(string1), string2); 44 | } 45 | } 46 | 47 | // str test 48 | assert.strictEqual(addon.giveStr(Buffer.from("THEQUICKBROWNFOXJUMPSOVERTHELAZYDOG")), "THEQUICKBROWNFOXJUMPSOVERTHELAZYDOG"); 49 | assert.strictEqual(addon.giveStr(Buffer.from("this is a tést 中 of utf-8")), "this is a tést 中 of utf-8"); 50 | 51 | console.log("function tests succeed"); 52 | 53 | /* 54 | * attribution: https://github.com/sindresorhus/crypto-random-string 55 | * MIT License 56 | * Copyright (c) Sindre Sorhus (sindresorhus.com) 57 | */ 58 | function _generateForCustomCharacters(length, characters) { 59 | characters = characters || '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~'.split(''); 60 | // Generating entropy is faster than complex math operations, so we use the simplest way 61 | const characterCount = characters.length; 62 | const maxValidSelector = (Math.floor(0x10000 / characterCount) * characterCount) - 1; // Using values above this will ruin distribution when using modular division 63 | const entropyLength = 2 * Math.ceil(1.1 * length); // Generating a bit more than required so chances we need more than one pass will be really low 64 | let string = ''; 65 | let stringLength = 0; 66 | 67 | while (stringLength < length) { // In case we had many bad values, which may happen for character sets of size above 0x8000 but close to it 68 | const entropy = crypto.randomBytes(entropyLength); 69 | let entropyPosition = 0; 70 | 71 | while (entropyPosition < entropyLength && stringLength < length) { 72 | const entropyValue = entropy.readUInt16LE(entropyPosition); 73 | entropyPosition += 2; 74 | if (entropyValue > maxValidSelector) { // Skip values which will ruin distribution when using modular division 75 | continue; 76 | } 77 | 78 | string += characters[entropyValue % characterCount]; 79 | stringLength++; 80 | } 81 | } 82 | 83 | return string; 84 | } 85 | -------------------------------------------------------------------------------- /examples/js-env/.gitignore: -------------------------------------------------------------------------------- 1 | dist -------------------------------------------------------------------------------- /examples/js-env/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nj-example-jsenv" 3 | version = "0.0.0" 4 | authors = ["Stuart Zhang "] 5 | edition = "2021" 6 | publish = false 7 | 8 | 9 | [lib] 10 | crate-type = ["cdylib"] 11 | 12 | 13 | [dependencies] 14 | ohos-node-bindgen = { workspace = true} 15 | 16 | 17 | [build-dependencies] 18 | ohos-node-bindgen = { path = "../../", default-features = false, features = ["build"] } 19 | -------------------------------------------------------------------------------- /examples/js-env/Makefile: -------------------------------------------------------------------------------- 1 | all: build 2 | 3 | build: 4 | nj-cli build 5 | 6 | test: build 7 | node test.js 8 | 9 | 10 | clean: 11 | rm -rf dist 12 | 13 | -------------------------------------------------------------------------------- /examples/js-env/README.md: -------------------------------------------------------------------------------- 1 | manual JS callback -------------------------------------------------------------------------------- /examples/js-env/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | ohos_node_bindgen::build::configure(); 3 | } 4 | -------------------------------------------------------------------------------- /examples/js-env/src/lib.rs: -------------------------------------------------------------------------------- 1 | use ohos_node_bindgen::derive::node_bindgen; 2 | use ohos_node_bindgen::sys::napi_value; 3 | use ohos_node_bindgen::core::NjError; 4 | use ohos_node_bindgen::core::val::JsEnv; 5 | 6 | /// example where we receive napi callback manually 7 | /// in order to do that, we use TryIntoJs trait 8 | #[ohos_node_bindgen] 9 | fn double(arg: f64) -> Result { 10 | println!("arg: {arg}"); 11 | Ok(EnvInterceptor(arg)) 12 | } 13 | 14 | struct EnvInterceptor(f64); 15 | 16 | use ohos_node_bindgen::core::TryIntoJs; 17 | 18 | impl TryIntoJs for EnvInterceptor { 19 | fn try_to_js(self, js_env: &JsEnv) -> Result { 20 | println!("intercepting env"); 21 | js_env.create_double(self.0 * 2.0) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /examples/js-env/test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | let addon = require('./dist'); 4 | 5 | assert.equal(addon.double(5), 10); 6 | -------------------------------------------------------------------------------- /examples/json/.gitignore: -------------------------------------------------------------------------------- 1 | dist -------------------------------------------------------------------------------- /examples/json/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nj-example-json" 3 | version = "0.0.0" 4 | authors = ["Stuart Zhang "] 5 | edition = "2021" 6 | publish = false 7 | 8 | 9 | [lib] 10 | crate-type = ["cdylib"] 11 | 12 | 13 | [dependencies] 14 | ohos-node-bindgen = { workspace = true, features = ["serde-json"]} 15 | serde_json = { workspace = true } 16 | 17 | 18 | [build-dependencies] 19 | ohos-node-bindgen = { path = "../../", default-features = false, features = ["build"] } 20 | -------------------------------------------------------------------------------- /examples/json/Makefile: -------------------------------------------------------------------------------- 1 | all: build 2 | 3 | build: 4 | nj-cli build 5 | 6 | test: build 7 | node test.js 8 | 9 | 10 | clean: 11 | rm -rf dist 12 | 13 | -------------------------------------------------------------------------------- /examples/json/README.md: -------------------------------------------------------------------------------- 1 | return json object -------------------------------------------------------------------------------- /examples/json/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | ohos_node_bindgen::build::configure(); 3 | } 4 | -------------------------------------------------------------------------------- /examples/json/src/lib.rs: -------------------------------------------------------------------------------- 1 | use ohos_node_bindgen::derive::node_bindgen; 2 | use ohos_node_bindgen::sys::napi_value; 3 | use ohos_node_bindgen::core::NjError; 4 | use ohos_node_bindgen::core::val::JsEnv; 5 | use ohos_node_bindgen::core::TryIntoJs; 6 | use ohos_node_bindgen::core::val::JsObject; 7 | 8 | use serde_json::value::Value; 9 | use serde_json::map::Map; 10 | 11 | // The recommended way of transforming to json 12 | #[ohos_node_bindgen] 13 | struct StandardJson { 14 | some_name: String, 15 | a_number: i64, 16 | } 17 | 18 | #[ohos_node_bindgen] 19 | struct Outer { 20 | val: Inner, 21 | } 22 | 23 | #[ohos_node_bindgen] 24 | struct Inner(String); 25 | 26 | #[ohos_node_bindgen] 27 | struct UnitStruct; 28 | 29 | #[ohos_node_bindgen] 30 | enum ErrorType { 31 | WithMessage(String, usize), 32 | WithFields { val: usize }, 33 | UnitError, 34 | } 35 | 36 | #[ohos_node_bindgen] 37 | struct WithSerdeJson { 38 | val: Value, 39 | } 40 | 41 | struct CustomJson { 42 | val: f64, 43 | } 44 | 45 | impl TryIntoJs for CustomJson { 46 | /// serialize into json object, with custom field names 47 | fn try_to_js(self, js_env: &JsEnv) -> Result { 48 | // create JSON 49 | let mut json = JsObject::new(*js_env, js_env.create_object()?); 50 | 51 | let js_val = js_env.create_double(self.val)?; 52 | json.set_property("customFieldName", js_val)?; 53 | 54 | json.try_to_js(js_env) 55 | } 56 | } 57 | 58 | /// return json object 59 | #[ohos_node_bindgen] 60 | fn custom_json() -> CustomJson { 61 | CustomJson { val: 10.0 } 62 | } 63 | 64 | #[ohos_node_bindgen] 65 | fn standard_json() -> StandardJson { 66 | StandardJson { 67 | some_name: "John".to_owned(), 68 | a_number: 1337, 69 | } 70 | } 71 | 72 | #[ohos_node_bindgen] 73 | fn multilevel_json() -> Outer { 74 | Outer { 75 | val: Inner("hello".to_owned()), 76 | } 77 | } 78 | 79 | #[ohos_node_bindgen] 80 | fn unit_struct() -> UnitStruct { 81 | UnitStruct 82 | } 83 | 84 | #[ohos_node_bindgen] 85 | fn with_message() -> ErrorType { 86 | ErrorType::WithMessage("test".to_owned(), 321) 87 | } 88 | 89 | #[ohos_node_bindgen] 90 | fn with_fields() -> ErrorType { 91 | ErrorType::WithFields { val: 123 } 92 | } 93 | 94 | #[ohos_node_bindgen] 95 | fn with_unit() -> ErrorType { 96 | ErrorType::UnitError 97 | } 98 | 99 | #[ohos_node_bindgen] 100 | fn failed_result_with_fields() -> Result<(), ErrorType> { 101 | Err(ErrorType::WithFields { val: 987 }) 102 | } 103 | 104 | #[ohos_node_bindgen] 105 | async fn async_result_failed_unit() -> Result<(), ErrorType> { 106 | Err(ErrorType::UnitError) 107 | } 108 | 109 | #[ohos_node_bindgen] 110 | fn with_serde_json() -> WithSerdeJson { 111 | let mut map = Map::new(); 112 | map.insert("first".to_owned(), Value::Bool(true)); 113 | map.insert("second".to_owned(), Value::String("hello".to_owned())); 114 | 115 | WithSerdeJson { 116 | val: Value::Object(map), 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /examples/json/test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | let addon = require('./dist'); 4 | 5 | assert.deepStrictEqual(addon.customJson(), { 6 | customFieldName: 10 7 | }, "verify custom json"); 8 | assert.deepStrictEqual(addon.standardJson(), { 9 | someName: "John", 10 | aNumber: 1337 11 | }, "verify standard json"); 12 | assert.deepStrictEqual(addon.multilevelJson(), { 13 | val: ["hello"] 14 | }, "verify multilevel json"); 15 | 16 | assert.strictEqual(addon.unitStruct(), null); 17 | 18 | assert.deepStrictEqual(addon.withMessage(), { 19 | withMessage: ["test", 321n] 20 | }, "simple unnamed enum variant"); 21 | assert.deepStrictEqual(addon.withFields(), { 22 | withFields: { 23 | val: 123n 24 | } 25 | }, "named enum variant"); 26 | assert.deepStrictEqual(addon.withUnit(), 27 | "UnitError", 28 | "unit enum variant") 29 | 30 | assert.throws(() => addon.failedResultWithFields(), { 31 | "withFields": { 32 | val: 987n 33 | } 34 | }, "sync exception"); 35 | 36 | assert.rejects(() => addon.asyncResultFailedUnit(), 37 | (err) => { 38 | assert.strictEqual(err, "UnitError"); 39 | return true; 40 | }, 41 | "async exception"); 42 | 43 | assert.deepStrictEqual(addon.withSerdeJson(), { 44 | val: { 45 | first: true, 46 | second: "hello" 47 | } 48 | }, "serde_json serialization") 49 | -------------------------------------------------------------------------------- /examples/logging/.gitignore: -------------------------------------------------------------------------------- 1 | dylib -------------------------------------------------------------------------------- /examples/logging/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nj-example-logging" 3 | version = "0.0.0" 4 | authors = ["Stuart Zhang "] 5 | edition = "2021" 6 | publish = false 7 | 8 | 9 | [lib] 10 | crate-type = ["cdylib"] 11 | 12 | 13 | [dependencies] 14 | ohos-node-bindgen = { workspace = true} 15 | tracing = { workspace = true } 16 | 17 | [build-dependencies] 18 | ohos-node-bindgen = { path = "../../", default-features = false, features = ["build"] } 19 | -------------------------------------------------------------------------------- /examples/logging/Makefile: -------------------------------------------------------------------------------- 1 | all: build 2 | 3 | build: 4 | nj-cli build 5 | 6 | test: build 7 | RUST_LOG=info node test.js 8 | 9 | clean: 10 | rm -rf dist 11 | -------------------------------------------------------------------------------- /examples/logging/README.md: -------------------------------------------------------------------------------- 1 | simple hello world -------------------------------------------------------------------------------- /examples/logging/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | ohos_node_bindgen::build::configure(); 3 | } 4 | -------------------------------------------------------------------------------- /examples/logging/src/lib.rs: -------------------------------------------------------------------------------- 1 | use tracing::{info, warn}; 2 | 3 | use ohos_node_bindgen::derive::node_bindgen; 4 | use ohos_node_bindgen::init::node_bindgen_init_once; 5 | 6 | 7 | #[node_bindgen_init_once] 8 | fn init_logging() { 9 | // initialize logging framework 10 | // logging is initialized already 11 | info!("logging initialized"); 12 | } 13 | 14 | #[node_bindgen()] 15 | fn hello(count: i32) -> String { 16 | warn!("calling hello"); 17 | format!("hello world {count}") 18 | } 19 | -------------------------------------------------------------------------------- /examples/logging/test.js: -------------------------------------------------------------------------------- 1 | let addon = require('./dist'); 2 | const assert = require('assert'); 3 | 4 | assert.strictEqual(addon.hello(2), "hello world 2"); 5 | 6 | 7 | console.log("logging tests succeed"); 8 | -------------------------------------------------------------------------------- /examples/option/.gitignore: -------------------------------------------------------------------------------- 1 | dylib -------------------------------------------------------------------------------- /examples/option/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nj-example-option" 3 | version = "0.0.0" 4 | authors = ["Stuart Zhang "] 5 | edition = "2021" 6 | publish = false 7 | 8 | 9 | [lib] 10 | crate-type = ["cdylib"] 11 | 12 | 13 | [dependencies] 14 | ohos-node-bindgen = { workspace = true} 15 | fluvio-future = { workspace = true} 16 | 17 | [build-dependencies] 18 | ohos-node-bindgen = { path = "../../", default-features = false, features = ["build"] } 19 | -------------------------------------------------------------------------------- /examples/option/Makefile: -------------------------------------------------------------------------------- 1 | all: build 2 | 3 | build: 4 | nj-cli build 5 | 6 | test: build 7 | node test.js 8 | 9 | 10 | clean: 11 | rm -rf dist 12 | 13 | -------------------------------------------------------------------------------- /examples/option/README.md: -------------------------------------------------------------------------------- 1 | simple hello world -------------------------------------------------------------------------------- /examples/option/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | ohos_node_bindgen::build::configure(); 3 | } 4 | -------------------------------------------------------------------------------- /examples/option/src/lib.rs: -------------------------------------------------------------------------------- 1 | use ohos_node_bindgen::derive::node_bindgen; 2 | 3 | #[ohos_node_bindgen] 4 | fn test(a: Option, b: Option) -> i32 { 5 | if let (Some(a), Some(b)) = (a, b) { 6 | a + b 7 | } else { 8 | a.unwrap_or(b.unwrap_or(1)) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/option/test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | let addon = require('./dist'); 4 | 5 | assert.equal(addon.test(100, 200), 300); 6 | 7 | assert.equal(addon.test(100), 100); 8 | 9 | assert.equal(addon.test(undefined, 200), 200); 10 | 11 | assert.equal(addon.test(null, 200), 200); 12 | 13 | assert.equal(addon.test(100, undefined), 100); 14 | 15 | assert.equal(addon.test(100, null), 100); 16 | 17 | assert.equal(addon.test(undefined, undefined), 1); 18 | 19 | assert.equal(addon.test(null, null), 1); 20 | -------------------------------------------------------------------------------- /examples/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ohos-node-bindgen-examples", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "ohos-node-bindgen-examples", 9 | "version": "1.0.0", 10 | "license": "ISC" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /examples/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ohos-node-bindgen-examples", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": {} 12 | } -------------------------------------------------------------------------------- /examples/param/.gitignore: -------------------------------------------------------------------------------- 1 | dist -------------------------------------------------------------------------------- /examples/param/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nj-example-param" 3 | version = "0.0.0" 4 | authors = ["Stuart Zhang "] 5 | edition = "2021" 6 | publish = false 7 | 8 | 9 | [lib] 10 | crate-type = ["cdylib"] 11 | 12 | 13 | [dependencies] 14 | ohos-node-bindgen = { workspace = true} 15 | 16 | 17 | [build-dependencies] 18 | ohos-node-bindgen = { path = "../../", default-features = false, features = ["build"] } 19 | -------------------------------------------------------------------------------- /examples/param/Makefile: -------------------------------------------------------------------------------- 1 | all: build 2 | 3 | build: 4 | nj-cli build 5 | 6 | test: build 7 | node test.js 8 | 9 | 10 | clean: 11 | rm -rf dist 12 | 13 | -------------------------------------------------------------------------------- /examples/param/README.md: -------------------------------------------------------------------------------- 1 | return json object -------------------------------------------------------------------------------- /examples/param/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | ohos_node_bindgen::build::configure(); 3 | } 4 | -------------------------------------------------------------------------------- /examples/param/src/lib.rs: -------------------------------------------------------------------------------- 1 | use ohos_node_bindgen::derive::node_bindgen; 2 | use ohos_node_bindgen::core::val::JsEnv; 3 | use ohos_node_bindgen::core::val::JsObject; 4 | use ohos_node_bindgen::core::JSValue; 5 | use ohos_node_bindgen::sys::napi_value; 6 | use ohos_node_bindgen::core::NjError; 7 | 8 | #[derive(Default)] 9 | struct Json { 10 | val: i32, 11 | name: Option, 12 | } 13 | 14 | /// accept integer 15 | /// or json 16 | enum MyParam { 17 | Val(i32), 18 | Json(Json), 19 | } 20 | 21 | impl JSValue<'_> for MyParam { 22 | fn convert_to_rust(env: &JsEnv, n_value: napi_value) -> Result { 23 | // check if it is integer 24 | if let Ok(int_value) = env.convert_to_rust::(n_value) { 25 | Ok(Self::Val(int_value)) 26 | } else if let Ok(js_obj) = env.convert_to_rust::(n_value) { 27 | let mut json = Json::default(); 28 | if let Some(val_property) = js_obj.get_property("val")? { 29 | json.val = val_property.as_value::()?; 30 | if let Some(name_property) = js_obj.get_property("name")? { 31 | json.name = Some(name_property.as_value::()?); 32 | } 33 | Ok(Self::Json(json)) 34 | } else { 35 | Err(NjError::Other("val is not found".to_owned())) 36 | } 37 | } else { 38 | Err(NjError::Other("not valid format".to_owned())) 39 | } 40 | } 41 | } 42 | 43 | /// accept argument either int or json 44 | #[ohos_node_bindgen] 45 | fn add(arg_opt: Option) -> i32 { 46 | if let Some(arg) = arg_opt { 47 | match arg { 48 | MyParam::Val(val) => val * 10, 49 | MyParam::Json(json) => json.val * 10, 50 | } 51 | } else { 52 | 0 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /examples/param/test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | let addon = require('./dist'); 4 | 5 | assert.equal(addon.add(1),10,"verify integer"); 6 | assert.equal(addon.add({ val: 1}),10,"verify json"); 7 | assert.equal(addon.add(),0,"verify no argument"); -------------------------------------------------------------------------------- /examples/promise/.gitignore: -------------------------------------------------------------------------------- 1 | dylib -------------------------------------------------------------------------------- /examples/promise/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nj-example-promise" 3 | version = "0.0.0" 4 | authors = ["Stuart Zhang "] 5 | edition = "2021" 6 | publish = false 7 | 8 | 9 | [lib] 10 | crate-type = ["cdylib"] 11 | 12 | 13 | [dependencies] 14 | ohos-node-bindgen = { workspace = true} 15 | fluvio-future = { workspace = true} 16 | 17 | [build-dependencies] 18 | ohos-node-bindgen = { path = "../../", default-features = false, features = ["build"] } -------------------------------------------------------------------------------- /examples/promise/Makefile: -------------------------------------------------------------------------------- 1 | all: build 2 | 3 | 4 | build: 5 | nj-cli build 6 | 7 | 8 | test: build 9 | node test.js 10 | 11 | 12 | clean: 13 | rm -rf dist 14 | 15 | -------------------------------------------------------------------------------- /examples/promise/README.md: -------------------------------------------------------------------------------- 1 | regular async fn return as promise -------------------------------------------------------------------------------- /examples/promise/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | ohos_node_bindgen::build::configure(); 3 | } 4 | -------------------------------------------------------------------------------- /examples/promise/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | 3 | use fluvio_future::timer::sleep; 4 | use ohos_node_bindgen::derive::node_bindgen; 5 | use ohos_node_bindgen::core::NjError; 6 | 7 | #[ohos_node_bindgen] 8 | async fn hello(arg: f64) -> f64 { 9 | println!("sleeping"); 10 | sleep(Duration::from_secs(1)).await; 11 | println!("woke and adding 10.0"); 12 | arg + 10.0 13 | } 14 | 15 | #[ohos_node_bindgen] 16 | async fn hello2(arg: f64) -> Result { 17 | println!("sleeping"); 18 | sleep(Duration::from_secs(1)).await; 19 | if arg < 0.0 { 20 | eprintln!("throwing error"); 21 | Err(NjError::Other("arg is negative".to_owned())) 22 | } else { 23 | println!("woke and adding 10.0"); 24 | Ok(arg + 10.0) 25 | } 26 | } 27 | 28 | /// just sleep 29 | #[ohos_node_bindgen] 30 | async fn just_sleep(seconds: i32) { 31 | println!("sleeping"); 32 | sleep(Duration::from_secs(seconds as u64)).await; 33 | println!("finished sleeping"); 34 | } 35 | 36 | #[derive(Debug)] 37 | struct NativeStore { 38 | val: String, 39 | } 40 | 41 | #[ohos_node_bindgen] 42 | impl NativeStore { 43 | #[node_bindgen(constructor)] 44 | fn new() -> Self { 45 | Self { 46 | val: String::from("unknown"), 47 | } 48 | } 49 | 50 | #[ohos_node_bindgen] 51 | async fn get(&self) -> String { 52 | sleep(std::time::Duration::from_micros(1)).await; 53 | self.val.clone() 54 | } 55 | 56 | #[ohos_node_bindgen] 57 | async fn put(&mut self, value: String) { 58 | sleep(std::time::Duration::from_millis(500)).await; 59 | self.val = value; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /examples/promise/test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | let addon = require('./dist'); 3 | 4 | addon.hello(5).then((val) => { 5 | assert.equal(val, 15); 6 | console.log("promise test succeed: %s", val); 7 | }); 8 | 9 | ( 10 | async () => { 11 | let val = await addon.hello(5); 12 | assert.equal(val, 15); 13 | })(); 14 | 15 | 16 | (async () => { 17 | await assert.rejects( 18 | async () => { 19 | let val = await addon.hello2(-5); 20 | }, 21 | { 22 | message: 'arg is negative' 23 | } 24 | ); 25 | })(); 26 | 27 | (async () => { 28 | await addon.justSleep(3); 29 | })(); 30 | 31 | (async () => { 32 | const store = new addon.NativeStore(); 33 | await store.put("hello world"); 34 | const val = await store.get(); 35 | assert.strictEqual(val, "hello world") 36 | })(); 37 | -------------------------------------------------------------------------------- /examples/stream/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nj-example-stream" 3 | version = "0.0.0" 4 | authors = ["Stuart Zhang "] 5 | edition = "2021" 6 | publish = false 7 | 8 | 9 | [lib] 10 | crate-type = ["cdylib"] 11 | 12 | 13 | [dependencies] 14 | futures-lite = { workspace = true } 15 | ohos-node-bindgen = { workspace = true} 16 | fluvio-future = { workspace = true} 17 | 18 | 19 | [build-dependencies] 20 | ohos-node-bindgen = { path = "../../", default-features = false, features = ["build"] } -------------------------------------------------------------------------------- /examples/stream/Makefile: -------------------------------------------------------------------------------- 1 | all: build 2 | 3 | 4 | build: 5 | nj-cli build 6 | 7 | 8 | test: build 9 | node test.js 10 | 11 | 12 | clean: 13 | rm -rf dist 14 | 15 | -------------------------------------------------------------------------------- /examples/stream/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | ohos_node_bindgen::build::configure(); 3 | } 4 | -------------------------------------------------------------------------------- /examples/stream/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | 3 | use futures_lite::Stream; 4 | use futures_lite::stream; 5 | use futures_lite::stream::StreamExt; 6 | 7 | use fluvio_future::timer::sleep; 8 | use ohos_node_bindgen::core::NjError; 9 | use ohos_node_bindgen::derive::node_bindgen; 10 | use ohos_node_bindgen::core::stream::NjStream; 11 | use ohos_node_bindgen::core::stream::JsThen; 12 | 13 | struct StreamFactory {} 14 | 15 | #[ohos_node_bindgen] 16 | impl StreamFactory { 17 | #[node_bindgen(constructor)] 18 | pub fn new() -> Self { 19 | Self {} 20 | } 21 | 22 | /// send back to nodejs using data as event 23 | #[node_bindgen(mt)] 24 | fn stream( 25 | &self, 26 | count: i32, 27 | cb: F, 28 | ) -> Result, impl FnMut(i32)>, NjError> { 29 | // only allow count to be less than 10 30 | if count > 10 { 31 | return Err(NjError::Other(format!( 32 | "count: {count} should be less than or equal to 10" 33 | ))); 34 | } 35 | let stream = test_stream(count); 36 | let event = "data".to_owned(); 37 | // println!("got stream with len: {}",count); 38 | Ok(stream.js_then(move |msg| { 39 | println!("sending to js callback"); 40 | cb(event.clone(), msg); 41 | })) 42 | } 43 | } 44 | 45 | // stream that generates count from 0..count with 100 milliseconds duration 46 | fn test_stream(count: i32) -> impl Stream { 47 | stream::iter(0..count).then(|index| async move { 48 | sleep(Duration::from_millis(100)).await; 49 | index 50 | }) 51 | } 52 | -------------------------------------------------------------------------------- /examples/stream/test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | let addon = require('./dist'); 3 | 4 | const EventEmitter = require('events').EventEmitter; 5 | const emitter = new EventEmitter(); 6 | 7 | let sum = 0; 8 | 9 | emitter.on('data', (evt) => { 10 | console.log("received event", evt); 11 | sum = sum + evt; 12 | }) 13 | 14 | let factory = new addon.StreamFactory(); 15 | 16 | // test for error 17 | assert.throws(() => factory.stream(20, emitter.emit.bind(emitter)), { 18 | message: 'count: 20 should be less than or equal to 10' 19 | }); 20 | 21 | 22 | factory.stream(10, emitter.emit.bind(emitter)); 23 | 24 | console.log("stream started"); 25 | 26 | // wait for stream to finish, since stream produce event at every 100ms we should wait at least 1 second 27 | setTimeout(() => { 28 | console.log("timer finished"); 29 | assert.equal(sum, 45); 30 | }, 3000); // Made a bit larger so it reliably works on Travis 31 | -------------------------------------------------------------------------------- /examples/tuples/.gitignore: -------------------------------------------------------------------------------- 1 | dist -------------------------------------------------------------------------------- /examples/tuples/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nj-example-tuple" 3 | version = "0.0.0" 4 | authors = ["Stuart Zhang "] 5 | edition = "2021" 6 | publish = false 7 | 8 | [lib] 9 | crate-type = ["cdylib"] 10 | 11 | [dependencies] 12 | ohos-node-bindgen = { workspace = true} 13 | 14 | [build-dependencies] 15 | ohos-node-bindgen = { path = "../../", default-features = false, features = ["build"] } 16 | -------------------------------------------------------------------------------- /examples/tuples/Makefile: -------------------------------------------------------------------------------- 1 | all: build 2 | 3 | install: 4 | npm install 5 | 6 | build: install 7 | nj-cli build 8 | 9 | test: build 10 | npx ts-node test.ts 11 | 12 | clean: 13 | rm -rf dist 14 | 15 | -------------------------------------------------------------------------------- /examples/tuples/README.md: -------------------------------------------------------------------------------- 1 | return json object -------------------------------------------------------------------------------- /examples/tuples/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | ohos_node_bindgen::build::configure(); 3 | } 4 | -------------------------------------------------------------------------------- /examples/tuples/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tuples", 3 | "lockfileVersion": 2, 4 | "requires": true, 5 | "packages": { 6 | "": { 7 | "dependencies": { 8 | "@types/node": "^14.14.37" 9 | } 10 | }, 11 | "node_modules/@types/node": { 12 | "version": "14.14.37", 13 | "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.37.tgz", 14 | "integrity": "sha512-XYmBiy+ohOR4Lh5jE379fV2IU+6Jn4g5qASinhitfyO71b/sCo6MKsMLF5tc7Zf2CE8hViVQyYSobJNke8OvUw==" 15 | } 16 | }, 17 | "dependencies": { 18 | "@types/node": { 19 | "version": "14.14.37", 20 | "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.37.tgz", 21 | "integrity": "sha512-XYmBiy+ohOR4Lh5jE379fV2IU+6Jn4g5qASinhitfyO71b/sCo6MKsMLF5tc7Zf2CE8hViVQyYSobJNke8OvUw==" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /examples/tuples/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "@types/node": "^14.14.37" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /examples/tuples/src/lib.rs: -------------------------------------------------------------------------------- 1 | use ohos_node_bindgen::derive::node_bindgen; 2 | 3 | #[ohos_node_bindgen] 4 | fn capitalize_and_square(value: (String, Vec)) -> (String, Vec) { 5 | let (x, y) = value; 6 | let x: String = x.to_ascii_uppercase(); 7 | let y: Vec<_> = y.into_iter().map(|it| it * it).collect(); 8 | (x, y) 9 | } 10 | -------------------------------------------------------------------------------- /examples/tuples/test.ts: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | type MyTuple = [string, number[]]; 4 | interface TupleTester { 5 | capitalizeAndSquare(value: MyTuple): MyTuple 6 | } 7 | 8 | 9 | // Test that a well-formed tuple works as expected 10 | const addon: TupleTester = require('./dist'); 11 | const item = addon.capitalizeAndSquare(["hello", [3, 5, 7]]); 12 | const [capitalized, squared]: MyTuple = item; 13 | assert.equal(capitalized, "HELLO"); 14 | assert.equal(squared[0], 9); 15 | assert.equal(squared[1], 25); 16 | assert.equal(squared[2], 49); 17 | 18 | 19 | // Test that giving a tuple of the wrong size will fail 20 | const addonUntyped: any = require('./dist'); 21 | assert.throws( 22 | () => { addonUntyped.capitalizeAndSquare(["hello"]) }, 23 | { message: "2Tuple must have exactly length 2" }, 24 | ); 25 | 26 | 27 | // Test that giving a tuple with the wrong types will fail 28 | assert.throws( 29 | () => { 30 | // This fails because the first item of the tuple should be a string 31 | addonUntyped.capitalizeAndSquare([ 5, [1, 2, 3] ]); 32 | }, 33 | { message: "invalid type, expected: string, actual: number" }, 34 | ); 35 | -------------------------------------------------------------------------------- /examples/uuid/.gitignore: -------------------------------------------------------------------------------- 1 | dist -------------------------------------------------------------------------------- /examples/uuid/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nj-example-uuid" 3 | version = "0.0.0" 4 | authors = ["Stuart Zhang "] 5 | edition = "2021" 6 | publish = false 7 | 8 | [lib] 9 | crate-type = ["cdylib"] 10 | 11 | [dependencies] 12 | uuid = { workspace = true } 13 | ohos-node-bindgen = { workspace = true, features = ["uuid"]} 14 | 15 | [build-dependencies] 16 | ohos-node-bindgen = { path = "../../", default-features = false, features = ["build"] } 17 | -------------------------------------------------------------------------------- /examples/uuid/Makefile: -------------------------------------------------------------------------------- 1 | all: build 2 | 3 | build: 4 | nj-cli build 5 | 6 | test: build 7 | npx -y ts-node test.ts 8 | 9 | clean: 10 | rm -rf dist 11 | 12 | -------------------------------------------------------------------------------- /examples/uuid/README.md: -------------------------------------------------------------------------------- 1 | Pass `uuid::Uuid` objects to and from Node -------------------------------------------------------------------------------- /examples/uuid/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | ohos_node_bindgen::build::configure(); 3 | } 4 | -------------------------------------------------------------------------------- /examples/uuid/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "uuid", 3 | "lockfileVersion": 2, 4 | "requires": true, 5 | "packages": { 6 | "": { 7 | "devDependencies": { 8 | "@types/node": "^15.12.0" 9 | } 10 | }, 11 | "node_modules/@types/node": { 12 | "version": "15.12.0", 13 | "resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.0.tgz", 14 | "integrity": "sha512-+aHJvoCsVhO2ZCuT4o5JtcPrCPyDE3+1nvbDprYes+pPkEsbjH7AGUCNtjMOXS0fqH14t+B7yLzaqSz92FPWyw==", 15 | "dev": true 16 | } 17 | }, 18 | "dependencies": { 19 | "@types/node": { 20 | "version": "15.12.0", 21 | "resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.0.tgz", 22 | "integrity": "sha512-+aHJvoCsVhO2ZCuT4o5JtcPrCPyDE3+1nvbDprYes+pPkEsbjH7AGUCNtjMOXS0fqH14t+B7yLzaqSz92FPWyw==", 23 | "dev": true 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/uuid/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "@types/node": "^15.12.0" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /examples/uuid/src/lib.rs: -------------------------------------------------------------------------------- 1 | use ohos_node_bindgen::derive::node_bindgen; 2 | 3 | /// Create a new random Uuid to return 4 | #[ohos_node_bindgen] 5 | fn make_uuid() -> uuid::Uuid { 6 | uuid::Uuid::parse_str("f7509856-9ae5-4c07-976d-a5b3f983e4af").unwrap() 7 | } 8 | 9 | #[ohos_node_bindgen] 10 | fn take_uuid(uuid: uuid::Uuid) { 11 | let string = uuid.to_string(); 12 | assert_eq!(string, "f7509856-9ae5-4c07-976d-a5b3f983e4af"); 13 | } 14 | -------------------------------------------------------------------------------- /examples/uuid/test.ts: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | interface UuidExample { 4 | makeUuid(): string; 5 | takeUuid(uuid: string); 6 | } 7 | let addon: UuidExample = require('./dist'); 8 | 9 | const new_uuid: string = addon.makeUuid(); 10 | assert.equal(new_uuid, "f7509856-9ae5-4c07-976d-a5b3f983e4af"); 11 | 12 | addon.takeUuid(new_uuid); 13 | -------------------------------------------------------------------------------- /nj-cli/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "bitflags" 5 | version = "1.2.1" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 8 | 9 | [[package]] 10 | name = "cargo_metadata" 11 | version = "0.9.1" 12 | source = "registry+https://github.com/rust-lang/crates.io-index" 13 | checksum = "46e3374c604fb39d1a2f35ed5e4a4e30e60d01fab49446e08f1b3e9a90aef202" 14 | dependencies = [ 15 | "semver", 16 | "serde", 17 | "serde_derive", 18 | "serde_json", 19 | ] 20 | 21 | [[package]] 22 | name = "cfg-if" 23 | version = "0.1.10" 24 | source = "registry+https://github.com/rust-lang/crates.io-index" 25 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 26 | 27 | [[package]] 28 | name = "clap" 29 | version = "2.33.1" 30 | source = "registry+https://github.com/rust-lang/crates.io-index" 31 | checksum = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129" 32 | dependencies = [ 33 | "bitflags", 34 | "textwrap", 35 | "unicode-width", 36 | ] 37 | 38 | [[package]] 39 | name = "heck" 40 | version = "0.3.1" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" 43 | dependencies = [ 44 | "unicode-segmentation", 45 | ] 46 | 47 | [[package]] 48 | name = "itoa" 49 | version = "0.4.6" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" 52 | 53 | [[package]] 54 | name = "log" 55 | version = "0.4.11" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" 58 | dependencies = [ 59 | "cfg-if", 60 | ] 61 | 62 | [[package]] 63 | name = "nj-cli" 64 | version = "0.3.0" 65 | dependencies = [ 66 | "cargo_metadata", 67 | "log", 68 | "serde", 69 | "structopt", 70 | "toml", 71 | ] 72 | 73 | [[package]] 74 | name = "proc-macro2" 75 | version = "0.4.30" 76 | source = "registry+https://github.com/rust-lang/crates.io-index" 77 | checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" 78 | dependencies = [ 79 | "unicode-xid 0.1.0", 80 | ] 81 | 82 | [[package]] 83 | name = "proc-macro2" 84 | version = "1.0.19" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | checksum = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12" 87 | dependencies = [ 88 | "unicode-xid 0.2.1", 89 | ] 90 | 91 | [[package]] 92 | name = "quote" 93 | version = "0.6.13" 94 | source = "registry+https://github.com/rust-lang/crates.io-index" 95 | checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" 96 | dependencies = [ 97 | "proc-macro2 0.4.30", 98 | ] 99 | 100 | [[package]] 101 | name = "quote" 102 | version = "1.0.7" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" 105 | dependencies = [ 106 | "proc-macro2 1.0.19", 107 | ] 108 | 109 | [[package]] 110 | name = "ryu" 111 | version = "1.0.5" 112 | source = "registry+https://github.com/rust-lang/crates.io-index" 113 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 114 | 115 | [[package]] 116 | name = "semver" 117 | version = "0.9.0" 118 | source = "registry+https://github.com/rust-lang/crates.io-index" 119 | checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 120 | dependencies = [ 121 | "semver-parser", 122 | "serde", 123 | ] 124 | 125 | [[package]] 126 | name = "semver-parser" 127 | version = "0.7.0" 128 | source = "registry+https://github.com/rust-lang/crates.io-index" 129 | checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 130 | 131 | [[package]] 132 | name = "serde" 133 | version = "1.0.114" 134 | source = "registry+https://github.com/rust-lang/crates.io-index" 135 | checksum = "5317f7588f0a5078ee60ef675ef96735a1442132dc645eb1d12c018620ed8cd3" 136 | dependencies = [ 137 | "serde_derive", 138 | ] 139 | 140 | [[package]] 141 | name = "serde_derive" 142 | version = "1.0.114" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | checksum = "2a0be94b04690fbaed37cddffc5c134bf537c8e3329d53e982fe04c374978f8e" 145 | dependencies = [ 146 | "proc-macro2 1.0.19", 147 | "quote 1.0.7", 148 | "syn 1.0.35", 149 | ] 150 | 151 | [[package]] 152 | name = "serde_json" 153 | version = "1.0.56" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | checksum = "3433e879a558dde8b5e8feb2a04899cf34fdde1fafb894687e52105fc1162ac3" 156 | dependencies = [ 157 | "itoa", 158 | "ryu", 159 | "serde", 160 | ] 161 | 162 | [[package]] 163 | name = "structopt" 164 | version = "0.2.18" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | checksum = "16c2cdbf9cc375f15d1b4141bc48aeef444806655cd0e904207edc8d68d86ed7" 167 | dependencies = [ 168 | "clap", 169 | "structopt-derive", 170 | ] 171 | 172 | [[package]] 173 | name = "structopt-derive" 174 | version = "0.2.18" 175 | source = "registry+https://github.com/rust-lang/crates.io-index" 176 | checksum = "53010261a84b37689f9ed7d395165029f9cc7abb9f56bbfe86bee2597ed25107" 177 | dependencies = [ 178 | "heck", 179 | "proc-macro2 0.4.30", 180 | "quote 0.6.13", 181 | "syn 0.15.44", 182 | ] 183 | 184 | [[package]] 185 | name = "syn" 186 | version = "0.15.44" 187 | source = "registry+https://github.com/rust-lang/crates.io-index" 188 | checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" 189 | dependencies = [ 190 | "proc-macro2 0.4.30", 191 | "quote 0.6.13", 192 | "unicode-xid 0.1.0", 193 | ] 194 | 195 | [[package]] 196 | name = "syn" 197 | version = "1.0.35" 198 | source = "registry+https://github.com/rust-lang/crates.io-index" 199 | checksum = "fb7f4c519df8c117855e19dd8cc851e89eb746fe7a73f0157e0d95fdec5369b0" 200 | dependencies = [ 201 | "proc-macro2 1.0.19", 202 | "quote 1.0.7", 203 | "unicode-xid 0.2.1", 204 | ] 205 | 206 | [[package]] 207 | name = "textwrap" 208 | version = "0.11.0" 209 | source = "registry+https://github.com/rust-lang/crates.io-index" 210 | checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 211 | dependencies = [ 212 | "unicode-width", 213 | ] 214 | 215 | [[package]] 216 | name = "toml" 217 | version = "0.5.6" 218 | source = "registry+https://github.com/rust-lang/crates.io-index" 219 | checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" 220 | dependencies = [ 221 | "serde", 222 | ] 223 | 224 | [[package]] 225 | name = "unicode-segmentation" 226 | version = "1.6.0" 227 | source = "registry+https://github.com/rust-lang/crates.io-index" 228 | checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" 229 | 230 | [[package]] 231 | name = "unicode-width" 232 | version = "0.1.8" 233 | source = "registry+https://github.com/rust-lang/crates.io-index" 234 | checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" 235 | 236 | [[package]] 237 | name = "unicode-xid" 238 | version = "0.1.0" 239 | source = "registry+https://github.com/rust-lang/crates.io-index" 240 | checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" 241 | 242 | [[package]] 243 | name = "unicode-xid" 244 | version = "0.2.1" 245 | source = "registry+https://github.com/rust-lang/crates.io-index" 246 | checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" 247 | -------------------------------------------------------------------------------- /nj-cli/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nj-cli" 3 | version = "0.4.3" 4 | authors = ["Stuart Zhang "] 5 | edition = "2021" 6 | description = "build tool for ohos-node-bindgen" 7 | repository = "https://github.com/stuartZhang/node-bindgen" 8 | license = "Apache-2.0" 9 | 10 | [dependencies] 11 | log = "0.4.8" 12 | structopt = { version = "0.3.18", default-features = false } 13 | cargo_metadata = "0.18.0" 14 | toml = "0.8.0" 15 | serde = { version = "1.0.114", features = ["derive"] } 16 | -------------------------------------------------------------------------------- /nj-cli/Makefile: -------------------------------------------------------------------------------- 1 | MPATH = $(dir $(realpath $(firstword $(MAKEFILE_LIST)))) 2 | 3 | install: build 4 | cargo install --path $(MPATH) 5 | 6 | build: 7 | cargo build 8 | 9 | -------------------------------------------------------------------------------- /nj-cli/src/init/mod.rs: -------------------------------------------------------------------------------- 1 | mod project_files; 2 | 3 | pub use project_files::{ProjectFiles}; 4 | -------------------------------------------------------------------------------- /nj-cli/src/init/project_files.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | use std::fs::{File, remove_file, read_to_string}; 3 | use std::io::Write; 4 | use serde::{Deserialize, Serialize}; 5 | #[derive(Debug, Clone, Deserialize, Serialize)] 6 | pub struct Config { 7 | package: ConfigPackage, 8 | lib: Option, 9 | dependencies: Option, 10 | build_dependencies: Option, 11 | } 12 | 13 | #[derive(Debug, Clone, Deserialize, Serialize)] 14 | pub struct ConfigPackage { 15 | name: String, 16 | version: String, 17 | authors: Vec, 18 | edition: String, 19 | } 20 | 21 | #[derive(Debug, Clone, Deserialize, Serialize)] 22 | pub struct ConfigLib { 23 | #[serde(rename = "crate-type")] 24 | crate_type: Vec, 25 | } 26 | 27 | #[derive(Debug, Clone, Deserialize, Serialize)] 28 | pub struct Dependencies { 29 | #[serde(rename = "ohos-node-bindgen")] 30 | ohos_node_bindgen: Option, 31 | } 32 | 33 | #[derive(Debug, Clone, Deserialize, Serialize)] 34 | pub struct Dependency { 35 | version: String, 36 | features: Vec, 37 | } 38 | 39 | #[derive(Debug, Clone)] 40 | pub struct ProjectFiles { 41 | dir: PathBuf, 42 | } 43 | 44 | impl ProjectFiles { 45 | pub fn new(dir: PathBuf) -> Result> { 46 | let project_files = Self { dir } 47 | .add_build_rs()? 48 | .add_lib_rs()? 49 | .add_cargo_toml()?; 50 | 51 | Ok(project_files) 52 | } 53 | 54 | fn add_build_rs(self) -> Result> { 55 | let mut build_rs = self.dir.clone(); 56 | build_rs.push("build.rs"); 57 | 58 | let mut file = File::create(&build_rs)?; 59 | file.write_all( 60 | b"fn main() { 61 | ohos_node_bindgen::build::configure(); 62 | }", 63 | )?; 64 | 65 | Ok(self) 66 | } 67 | 68 | fn add_lib_rs(self) -> Result> { 69 | let mut lib_rs = self.dir.clone(); 70 | lib_rs.push("src/lib.rs"); 71 | 72 | // remove existing file, if exists; 73 | if lib_rs.exists() { 74 | remove_file(&lib_rs)?; 75 | } 76 | 77 | const LIB_RS: &str = r#" 78 | use ohos_node_bindgen::derive::node_bindgen; 79 | 80 | struct MyObject {} 81 | 82 | #[ohos_node_bindgen] 83 | impl MyObject { 84 | 85 | #[node_bindgen(constructor)] 86 | fn new() -> Self { 87 | Self {} 88 | } 89 | 90 | #[node_bindgen(name = "hello")] 91 | fn hello(&self) -> String { 92 | "world".to_string() 93 | } 94 | } 95 | "#; 96 | 97 | let mut file = File::create(&lib_rs)?; 98 | file.write_all(LIB_RS.as_bytes())?; 99 | 100 | Ok(self) 101 | } 102 | 103 | fn add_cargo_toml(self) -> Result> { 104 | let mut cargo_toml = self.dir.clone(); 105 | cargo_toml.push("Cargo.toml"); 106 | 107 | let mut config: Config = toml::from_str(&read_to_string(&cargo_toml)?)?; 108 | 109 | // NOTE: Attempt to get the workspace version instead of this hard coded value; 110 | let version = "2.1.1".to_string(); 111 | 112 | config.lib = Some(ConfigLib { 113 | crate_type: vec!["cdylib".to_string()], 114 | }); 115 | 116 | config.dependencies = Some(Dependencies { 117 | ohos_node_bindgen: Some(Dependency { 118 | version: version.clone(), 119 | features: vec![], 120 | }), 121 | }); 122 | 123 | config.build_dependencies = Some(Dependencies { 124 | ohos_node_bindgen: Some(Dependency { 125 | version, 126 | features: vec!["build".to_string()], 127 | }), 128 | }); 129 | 130 | // Remove the old cargo toml file; 131 | remove_file(&cargo_toml)?; 132 | 133 | let mut file = File::create(cargo_toml)?; 134 | file.write_all(toml::to_string(&config)?.as_bytes())?; 135 | 136 | Ok(self) 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /nj-cli/src/main.rs: -------------------------------------------------------------------------------- 1 | mod init; 2 | mod watch; 3 | 4 | use cargo_metadata::camino::Utf8PathBuf; 5 | use watch::{WatchOpt}; 6 | use structopt::StructOpt; 7 | 8 | use std::process::Command; 9 | use std::process::Stdio; 10 | use std::path::Path; 11 | use std::path::PathBuf; 12 | use std::io::Result; 13 | 14 | use cargo_metadata::{MetadataCommand, CargoOpt}; 15 | use cargo_metadata::Package; 16 | use cargo_metadata::Metadata; 17 | use cargo_metadata::Target; 18 | 19 | #[derive(Debug, StructOpt)] 20 | #[structopt( 21 | about = "Nj Command Line Interface", 22 | author = "Fluvio", 23 | name = "ohos-node-bindgen cli" 24 | )] 25 | enum Opt { 26 | #[structopt(name = "build")] 27 | Build(BuildOpt), 28 | #[structopt(name = "init")] 29 | Init(InitOpt), 30 | #[structopt(name = "watch")] 31 | Watch(WatchOpt), 32 | } 33 | 34 | #[derive(Debug, StructOpt)] 35 | struct BuildOpt { 36 | #[structopt(short = "o", long = "out", default_value = "dist")] 37 | output: String, 38 | 39 | #[structopt(long)] 40 | release: bool, 41 | 42 | #[structopt(long)] 43 | target: Option, 44 | 45 | extras: Vec, 46 | } 47 | 48 | #[derive(Debug, StructOpt)] 49 | struct InitOpt { 50 | extras: Vec, 51 | } 52 | 53 | fn main() { 54 | let opt = Opt::from_args(); 55 | 56 | match opt { 57 | Opt::Build(opt) => build(opt), 58 | Opt::Init(opt) => init(opt), 59 | Opt::Watch(opt) => watch::run(opt), 60 | } 61 | } 62 | 63 | // Initialize a new project 64 | fn init(opt: InitOpt) { 65 | let mut args = vec!["init".to_string(), "--lib".to_string()]; 66 | args.extend(opt.extras); 67 | 68 | if args.len() <= 2 { 69 | panic!("please enter a path for this project, e.g.: ./my-project"); 70 | } 71 | 72 | let path = &args[2]; 73 | 74 | let mut build_command = Command::new("cargo") 75 | .args(&args) 76 | .stdout(Stdio::inherit()) 77 | .spawn() 78 | .expect("Failed to execute command"); 79 | 80 | build_command.wait().expect("failed to wait on child"); 81 | 82 | if let Ok(mut dir) = std::env::current_dir() { 83 | dir.push(path); 84 | if let Err(e) = init::ProjectFiles::new(dir.clone()) { 85 | panic!("Failed to create project: {:#?}", e); 86 | } else { 87 | let manifest_path = format!("--manifest-path={}/Cargo.toml", dir.display()); 88 | let mut fmt = Command::new("cargo") 89 | .stdout(Stdio::inherit()) 90 | .args(["fmt", &manifest_path]) 91 | .spawn() 92 | .expect("Failed to execute command"); 93 | 94 | fmt.wait().expect("Failed to execute command"); 95 | }; 96 | } 97 | } 98 | 99 | // kick off build 100 | fn build(opt: BuildOpt) { 101 | let mut args = vec!["build".to_string()]; 102 | if opt.release { 103 | args.push("--release".to_string()); 104 | } 105 | if let Some(ref target) = opt.target { 106 | args.push(format!("--target={target}")); 107 | } 108 | args.extend(opt.extras); 109 | 110 | let mut build_command = Command::new("cargo") 111 | .args(&args) 112 | .stdout(Stdio::inherit()) 113 | .spawn() 114 | .expect("Failed to execute command"); 115 | 116 | let status = build_command.wait().expect("failed to wait on child"); 117 | match status.code() { 118 | Some(code) if code != 0 => { 119 | std::process::exit(code); 120 | } 121 | None => { 122 | //https://doc.rust-lang.org/std/process/struct.ExitStatus.html#method.code 123 | #[cfg(unix)] 124 | { 125 | use std::os::unix::process::ExitStatusExt; 126 | if let Some(signal) = status.signal() { 127 | std::process::exit(signal); 128 | } else { 129 | std::process::exit(1); 130 | } 131 | } 132 | #[cfg(not(unix))] 133 | { 134 | std::process::exit(1); 135 | } 136 | } 137 | Some(_) => {} 138 | } 139 | 140 | let target_mode = if opt.release { "release" } else { "debug" }; 141 | 142 | copy_lib(opt.output, target_mode, opt.target); 143 | } 144 | 145 | /// copy library to target directory 146 | fn copy_lib(out: String, target_mode: &str, target_tripple: Option) { 147 | let manifest_path = manifest_path(); 148 | let metadata = load_metadata(&manifest_path); 149 | if let Some(package) = find_current_package(&metadata, &manifest_path) { 150 | if let Some(target) = find_cdylib(package) { 151 | let lib_path = lib_path( 152 | &metadata.target_directory, 153 | target_mode, 154 | &target.name, 155 | target_tripple, 156 | ); 157 | let error_msg = format!("copy failed of {lib_path:?}"); 158 | copy_cdylib(&lib_path, &out).expect(&error_msg); 159 | } else { 160 | eprintln!("no cdylib target was founded"); 161 | } 162 | } else { 163 | eprintln!("no valid Cargo.toml was founded"); 164 | } 165 | } 166 | 167 | fn find_cdylib(package: &Package) -> Option<&Target> { 168 | package 169 | .targets 170 | .iter() 171 | .find(|&target| target.name == package.name) 172 | } 173 | 174 | fn find_current_package<'a>(metadata: &'a Metadata, manifest_path: &Path) -> Option<&'a Package> { 175 | metadata 176 | .packages 177 | .iter() 178 | .find(|&package| package.manifest_path == manifest_path) 179 | } 180 | 181 | fn load_metadata(manifest_path: &Path) -> Metadata { 182 | MetadataCommand::new() 183 | .manifest_path(manifest_path) 184 | .features(CargoOpt::AllFeatures) 185 | .exec() 186 | .expect("cargo metadata") 187 | } 188 | 189 | fn manifest_path() -> PathBuf { 190 | let current_path = std::env::current_dir().expect("can't get current directory"); 191 | current_path.join("Cargo.toml") 192 | } 193 | 194 | fn lib_path( 195 | target: &Utf8PathBuf, 196 | build_type: &str, 197 | target_name: &str, 198 | target_tripple: Option, 199 | ) -> Utf8PathBuf { 200 | let file_name = if cfg!(target_os = "windows") { 201 | format!("{target_name}.dll") 202 | } else if cfg!(target_os = "macos") { 203 | format!("lib{target_name}.dylib") 204 | } else if cfg!(target_os = "linux") { 205 | format!("lib{target_name}.so") 206 | } else { 207 | panic!("Unsupported operating system."); 208 | } 209 | .replace('-', "_"); 210 | if let Some(target_tripple) = target_tripple { 211 | target 212 | .join(target) 213 | .join(target_tripple) 214 | .join(build_type) 215 | .join(file_name) 216 | } else { 217 | target.join(target).join(build_type).join(file_name) 218 | } 219 | } 220 | 221 | // where we are outputting 222 | fn output_dir(output: &str) -> Result { 223 | let current_path = std::env::current_dir().expect("can't get current directory"); 224 | let output_dir = current_path.join(output); 225 | // ensure we have directory 226 | std::fs::create_dir_all(&output_dir)?; 227 | 228 | Ok(output_dir) 229 | } 230 | 231 | fn copy_cdylib(lib_path: &Utf8PathBuf, out: &str) -> Result<()> { 232 | let dir = output_dir(out)?; 233 | let output_path = dir.join("index.node"); 234 | std::fs::copy(lib_path, output_path)?; 235 | Ok(()) 236 | } 237 | -------------------------------------------------------------------------------- /nj-cli/src/watch.rs: -------------------------------------------------------------------------------- 1 | use std::process::Command; 2 | use structopt::StructOpt; 3 | use std::process::Stdio; 4 | 5 | #[derive(Debug, StructOpt)] 6 | pub struct WatchOpt { 7 | extras: Vec, 8 | } 9 | 10 | pub fn check_cargo_watch() -> Result<(), Box> { 11 | let output = Command::new("cargo").args(["watch", "--help"]).output()?; 12 | 13 | if output.status.success() { 14 | println!("cargo watch is installed"); 15 | Ok(()) 16 | } else { 17 | println!("installing cargo watch... this might take a minute."); 18 | // Cargo watch is not installed, attempt to install; 19 | Command::new("cargo") 20 | .args(["install", "cargo-watch"]) 21 | .output()?; 22 | // Re-run check 23 | println!("checking cargo watch installation..."); 24 | Ok(check_cargo_watch()?) 25 | } 26 | } 27 | 28 | pub fn run(opt: WatchOpt) { 29 | if check_cargo_watch().is_ok() { 30 | // Use cargo watch to monintor files 31 | let mut args = vec!["watch".to_string()]; 32 | 33 | // Pass in extra 34 | args.extend(opt.extras); 35 | 36 | // Start watching files; 37 | let mut watch = Command::new("cargo") 38 | .args(&args) 39 | .stdout(Stdio::inherit()) 40 | .spawn() 41 | .expect("Failed to execute command"); 42 | 43 | // Wait on the child process; 44 | watch.wait().expect("failed to wait on child"); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /nj-core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ohos-nj-core" 3 | version = "6.0.2" 4 | authors = ["Stuart Zhang "] 5 | edition = "2021" 6 | description = "high level wrapper for Node N-API" 7 | repository = "https://github.com/stuartZhang/node-bindgen" 8 | readme = "README.md" 9 | license = "Apache-2.0" 10 | 11 | [lib] 12 | test = false 13 | 14 | [features] 15 | serde-json = ["serde_json"] 16 | convert-uuid = ["uuid"] 17 | 18 | [dependencies] 19 | tracing = "0.1.37" 20 | ctor = "0.2.4" 21 | libc = "0.2.66" 22 | inventory = "0.1.5" 23 | async-trait = "0.1.22" 24 | futures-lite = "1.7.0" 25 | nj-sys = { version = "0.1.1", package = "oh-napi-sys"} 26 | fluvio-future = { version = "0.6.0", default-features=false, features=["task","subscriber"]} 27 | pin-utils = "0.1.0" 28 | num-bigint = "0.4.0" 29 | serde_json = { version = "1.0", optional = true } 30 | uuid = { version = "1.4.1", optional = true } 31 | rustix = "=0.38.14" -------------------------------------------------------------------------------- /nj-core/README.md: -------------------------------------------------------------------------------- 1 | # High level bindings to Nodejs N-API. 2 | 3 | -------------------------------------------------------------------------------- /nj-core/src/bigint.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | 3 | use tracing::trace; 4 | 5 | use crate::TryIntoJs; 6 | use crate::JSValue; 7 | use crate::sys::{napi_value, size_t}; 8 | use crate::val::JsEnv; 9 | use crate::NjError; 10 | 11 | pub use num_bigint::*; 12 | 13 | impl<'a> JSValue<'a> for BigInt { 14 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 15 | fn convert_to_rust(env: &'a JsEnv, js_value: napi_value) -> Result { 16 | trace!("Converting JS BigInt to Rust!"); 17 | 18 | env.assert_type(js_value, crate::sys::napi_valuetype_napi_bigint)?; 19 | let word_count = 0_usize; 20 | 21 | // https://nodejs.org/api/n-api.html#n_api_napi_get_value_bigint_words 22 | // Frist call is to figure out how long of a vec to make. 23 | crate::napi_call_result!(crate::sys::napi_get_value_bigint_words( 24 | env.inner(), 25 | js_value, 26 | ptr::null_mut(), 27 | &mut (word_count as size_t), 28 | ptr::null_mut(), 29 | ))?; 30 | 31 | // Now we actually get the sign and the vector. 32 | let mut napi_buffer: Vec = vec![0; word_count]; 33 | let mut sign = 0; 34 | 35 | crate::napi_call_result!(crate::sys::napi_get_value_bigint_words( 36 | env.inner(), 37 | js_value, 38 | &mut sign, 39 | &mut (word_count as size_t), 40 | napi_buffer.as_mut_ptr(), 41 | ))?; 42 | 43 | // BigInt is initialized via a little endian &[u8] so we need to build the u8s from the 44 | // u64s 45 | let mut bytes: Vec = Vec::new(); 46 | for i in &napi_buffer { 47 | bytes.extend_from_slice(&i.to_le_bytes()); 48 | } 49 | 50 | // The N-API documentation on the signs is lacking. 51 | let sign = match sign { 52 | 0 => Sign::Plus, 53 | 1 => Sign::Minus, 54 | _ => unreachable!(), 55 | }; 56 | let res = BigInt::from_bytes_le(sign, &bytes); 57 | trace!( 58 | "Converted JS BigInt to Rust! words: {:#X?}, bytes: {:#?}, len: {:?}, bigint: {:#?}", 59 | napi_buffer, 60 | bytes, 61 | bytes.len(), 62 | res 63 | ); 64 | Ok(res) 65 | } 66 | } 67 | 68 | impl TryIntoJs for BigInt { 69 | fn try_to_js(self, env: &JsEnv) -> Result { 70 | let (sign, bytes) = self.to_bytes_le(); 71 | let mut words: Vec = Vec::new(); 72 | use std::cmp::min; 73 | 74 | // bytes can be non-multiples of 8. 75 | for i in 0..(bytes.len() / 8 + 1) { 76 | let mut slice: [u8; 8] = [0; 8]; 77 | 78 | // https://stackoverflow.com/a/29784723 seems to be the least bad way to convert a Vec 79 | // slice into an array :/ 80 | for (place, element) in slice 81 | .iter_mut() 82 | .zip(bytes[i * 8..min((i + 1) * 8, bytes.len())].iter()) 83 | { 84 | *place = *element; 85 | } 86 | words.push(u64::from_le_bytes(slice)); 87 | } 88 | let sign = match sign { 89 | Sign::Minus => 1, 90 | Sign::Plus | Sign::NoSign => 0, 91 | }; 92 | let word_count = words.len(); 93 | 94 | trace!( 95 | "Converted Rust BigInt to JS Bigint: {:#?}!, bytes: {:#?}, len: {:?}, words: {:#?}, word_count {:#?}, sign: {:#?}", 96 | self, 97 | bytes, 98 | bytes.len(), 99 | words, 100 | word_count, 101 | sign, 102 | ); 103 | 104 | let mut napi_buffer = ptr::null_mut(); 105 | 106 | // https://nodejs.org/api/n-api.html#n_api_napi_create_bigint_words 107 | crate::napi_call_result!(crate::sys::napi_create_bigint_words( 108 | env.inner(), 109 | sign, 110 | word_count as size_t, 111 | words.as_ptr(), 112 | &mut napi_buffer 113 | ))?; 114 | Ok(napi_buffer) 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /nj-core/src/buffer.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | use std::ops::Deref; 3 | 4 | use tracing::trace; 5 | 6 | use crate::TryIntoJs; 7 | use crate::JSValue; 8 | use crate::sys::{napi_value, napi_ref, napi_env, size_t}; 9 | use crate::val::JsEnv; 10 | use crate::NjError; 11 | 12 | /// pass rust byte arry as Node.js ArrayBuffer 13 | pub struct ArrayBuffer { 14 | data: Vec, 15 | } 16 | 17 | use std::fmt; 18 | use std::fmt::Debug; 19 | 20 | impl Debug for ArrayBuffer { 21 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 22 | f.write_fmt(format_args!("ArrayBuffer len: {}", self.data.len())) 23 | } 24 | } 25 | 26 | impl ArrayBuffer { 27 | pub fn new(data: Vec) -> Self { 28 | Self { data } 29 | } 30 | 31 | extern "C" fn finalize_buffer( 32 | _env: napi_env, 33 | _finalize_data: *mut ::std::os::raw::c_void, 34 | finalize_hint: *mut ::std::os::raw::c_void, 35 | ) { 36 | trace!("finalize array buffer"); 37 | unsafe { 38 | // use hint to reconstruct box instead of finalize data 39 | let ptr: *mut Vec = finalize_hint as *mut Vec; 40 | let _rust = Box::from_raw(ptr); 41 | } 42 | } 43 | } 44 | 45 | impl TryIntoJs for ArrayBuffer { 46 | fn try_to_js(self, js_env: &JsEnv) -> Result { 47 | let len = self.data.len(); 48 | 49 | let box_data = Box::new(self.data); 50 | 51 | let mut napi_buffer = ptr::null_mut(); 52 | 53 | // get pointer to vec's buffer 54 | let data_buffer = box_data.as_ptr(); 55 | 56 | // get raw pointer to box, this will be used to reconstruct box 57 | let data_box_ptr = Box::into_raw(box_data) as *mut core::ffi::c_void; 58 | 59 | crate::napi_call_result!(crate::sys::napi_create_external_arraybuffer( 60 | js_env.inner(), 61 | data_buffer as *mut core::ffi::c_void, 62 | len as size_t, 63 | Some(Self::finalize_buffer), 64 | data_box_ptr, 65 | &mut napi_buffer 66 | ))?; 67 | 68 | Ok(napi_buffer) 69 | } 70 | } 71 | 72 | impl<'a> JSValue<'a> for &'a [u8] { 73 | fn convert_to_rust(env: &'a JsEnv, js_value: napi_value) -> Result { 74 | // check if this is really buffer 75 | if !env.is_buffer(js_value)? { 76 | return Err(NjError::InvalidType( 77 | "Buffer".to_owned(), 78 | env.value_type_string(js_value)?.to_owned(), 79 | )); 80 | } 81 | 82 | let buffer = env.get_buffer_info(js_value)?; 83 | 84 | Ok(buffer) 85 | } 86 | } 87 | 88 | /// Rust representation of Nodejs [ArrayBuffer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer) 89 | /// This is safe to pass around rest of Rust code since this manages Node.js GC lifecycle. 90 | /// JSArrayBuffer is deference as `&[u8]` 91 | /// 92 | /// # Examples 93 | /// 94 | /// In this example, JS String is passed as array buffer. Rust code convert to String and concate with prefix message. 95 | /// 96 | /// ```no_run 97 | /// use ohos_node_bindgen::derive::node_bindgen; 98 | /// use ohos_node_bindgen::core::buffer::JSArrayBuffer; 99 | /// 100 | /// #[ohos_node_bindgen] 101 | /// fn hello(data: JSArrayBuffer) -> Result { 102 | /// let message = String::from_utf8(data.to_vec())?; 103 | /// Ok(format!("reply {}", message)) 104 | /// } 105 | /// ``` 106 | /// 107 | /// This can be invoked from Node.js 108 | /// ```text 109 | /// let addon = require('./your_module'); 110 | /// console.log(Buffer.from("hello")); 111 | /// ``` 112 | pub struct JSArrayBuffer { 113 | env: JsEnv, 114 | napi_ref: napi_ref, 115 | buffer: &'static [u8], 116 | } 117 | 118 | unsafe impl Send for JSArrayBuffer {} 119 | 120 | impl JSArrayBuffer { 121 | pub fn as_bytes(&self) -> &[u8] { 122 | self.buffer 123 | } 124 | } 125 | 126 | impl JSValue<'_> for JSArrayBuffer { 127 | fn convert_to_rust(env: &JsEnv, napi_value: napi_value) -> Result { 128 | use std::mem::transmute; 129 | 130 | let napi_ref = env.create_reference(napi_value, 1)?; 131 | 132 | // it is oky to transmute as static byte slice since we are managing slice 133 | let buffer: &'static [u8] = 134 | unsafe { transmute::<&[u8], &'static [u8]>(env.convert_to_rust(napi_value)?) }; 135 | Ok(Self { 136 | env: *env, 137 | napi_ref, 138 | buffer, 139 | }) 140 | } 141 | } 142 | 143 | impl Drop for JSArrayBuffer { 144 | fn drop(&mut self) { 145 | self.env 146 | .delete_reference(self.napi_ref) 147 | .expect("reference can't be deleted to array buf"); 148 | } 149 | } 150 | 151 | impl Deref for JSArrayBuffer { 152 | type Target = [u8]; 153 | 154 | fn deref(&self) -> &Self::Target { 155 | self.buffer 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /nj-core/src/class.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | 3 | use tracing::debug; 4 | use tracing::instrument; 5 | 6 | use crate::sys::napi_value; 7 | use crate::sys::napi_env; 8 | use crate::sys::napi_callback_info; 9 | use crate::sys::napi_ref; 10 | use crate::val::JsEnv; 11 | use crate::val::JsExports; 12 | use crate::val::JsCallback; 13 | use crate::NjError; 14 | use crate::IntoJs; 15 | use crate::PropertiesBuilder; 16 | 17 | pub struct JSObjectWrapper { 18 | wrapper: napi_ref, 19 | inner: T, 20 | } 21 | 22 | impl JSObjectWrapper { 23 | pub fn mut_inner(&mut self) -> &mut T { 24 | &mut self.inner 25 | } 26 | 27 | pub fn inner(&self) -> &T { 28 | &self.inner 29 | } 30 | } 31 | 32 | impl JSObjectWrapper 33 | where 34 | T: JSClass, 35 | { 36 | /// wrap myself in the JS instance 37 | /// and saved the reference 38 | #[instrument(skip(self))] 39 | fn wrap(self, js_env: &JsEnv, js_cb: JsCallback) -> Result { 40 | let boxed_self = Box::new(self); 41 | let raw_ptr = Box::into_raw(boxed_self); // rust no longer manages this struct 42 | debug!(?raw_ptr, "box into raw"); 43 | let wrap = js_env.wrap(js_cb.this(), raw_ptr as *mut u8, T::js_finalize)?; 44 | 45 | unsafe { 46 | // save the wrap reference in wrapper container 47 | let rust_ref: &mut Self = &mut *raw_ptr; 48 | rust_ref.wrapper = wrap; 49 | } 50 | 51 | Ok(js_cb.this_owned()) 52 | } 53 | } 54 | 55 | pub trait JSClass: Sized { 56 | const CLASS_NAME: &'static str; 57 | 58 | // create rust object from argument 59 | fn create_from_js( 60 | js_env: &JsEnv, 61 | cb: napi_callback_info, 62 | ) -> Result<(Self, JsCallback), NjError>; 63 | 64 | fn set_constructor(constructor: napi_ref); 65 | 66 | fn get_constructor() -> napi_ref; 67 | 68 | /// new instance 69 | #[instrument] 70 | fn new_instance(js_env: &JsEnv, js_args: Vec) -> Result { 71 | debug!("new instance"); 72 | let constructor = js_env.get_reference_value(Self::get_constructor())?; 73 | js_env.new_instance(constructor, js_args) 74 | } 75 | 76 | /// given instance, return my object 77 | #[instrument] 78 | fn unwrap_mut(js_env: &JsEnv, instance: napi_value) -> Result<&'static mut Self, NjError> { 79 | Ok(js_env 80 | .unwrap_mut::>(instance)? 81 | .mut_inner()) 82 | } 83 | 84 | fn unwrap(js_env: &JsEnv, instance: napi_value) -> Result<&'static Self, NjError> { 85 | Ok(js_env.unwrap::>(instance)?.inner()) 86 | } 87 | 88 | fn properties() -> PropertiesBuilder { 89 | vec![].into() 90 | } 91 | 92 | /// define class and properties under exports 93 | #[instrument] 94 | fn js_init(js_exports: &mut JsExports) -> Result<(), NjError> { 95 | let js_constructor = 96 | js_exports 97 | .env() 98 | .define_class(Self::CLASS_NAME, Self::js_new, Self::properties())?; 99 | 100 | debug!(?js_constructor, "class defined with constructor"); 101 | 102 | // save the constructor reference, we need this later in order to instantiate 103 | let js_ref = js_exports.env().create_reference(js_constructor, 1)?; 104 | debug!(?js_constructor, "created reference to constructor"); 105 | Self::set_constructor(js_ref); 106 | 107 | js_exports.set_name_property(Self::CLASS_NAME, js_constructor)?; 108 | Ok(()) 109 | } 110 | 111 | /// call when Javascript class constructor is called 112 | /// For example: new Car(...) 113 | #[instrument] 114 | extern "C" fn js_new(env: napi_env, info: napi_callback_info) -> napi_value { 115 | let js_env = JsEnv::new(env); 116 | 117 | let result: Result = (|| { 118 | debug!(clas = std::any::type_name::(), "getting new target"); 119 | 120 | let target = js_env.get_new_target(info)?; 121 | 122 | if target.is_null() { 123 | debug!("no target"); 124 | Err(NjError::NoPlainConstructor) 125 | } else { 126 | debug!(?target, "invoked as constructor"); 127 | 128 | let (rust_obj, js_cb) = Self::create_from_js(&js_env, info)?; 129 | debug!(?js_cb, "created rust object"); 130 | let my_obj = JSObjectWrapper { 131 | inner: rust_obj, 132 | wrapper: ptr::null_mut(), 133 | }; 134 | 135 | my_obj.wrap(&js_env, js_cb) 136 | } 137 | })(); 138 | 139 | result.into_js(&js_env) 140 | } 141 | 142 | extern "C" fn js_finalize( 143 | _env: napi_env, 144 | finalize_data: *mut ::std::os::raw::c_void, 145 | _finalize_hint: *mut ::std::os::raw::c_void, 146 | ) { 147 | debug!("my object finalize"); 148 | unsafe { 149 | let ptr: *mut JSObjectWrapper = finalize_data as *mut JSObjectWrapper; 150 | let _ = Box::from_raw(ptr); 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /nj-core/src/error.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::string::FromUtf8Error; 3 | use std::str::Utf8Error; 4 | use std::ptr; 5 | 6 | use crate::sys::napi_status; 7 | use crate::sys::napi_value; 8 | use crate::val::JsEnv; 9 | use crate::IntoJs; 10 | 11 | #[derive(Debug)] 12 | pub enum NjError { 13 | NapiCall(NapiStatus), 14 | InvalidArgCount(usize, usize), 15 | InvalidArgIndex(usize, usize), 16 | InvalidType(String, String), 17 | NoPlainConstructor, 18 | Utf8Error(FromUtf8Error), 19 | Utf8ErrorSlice(Utf8Error), 20 | Native(napi_value), 21 | Other(String), 22 | } 23 | 24 | // errors are thrown 25 | impl IntoJs for NjError { 26 | fn into_js(self, js_env: &JsEnv) -> napi_value { 27 | match self { 28 | NjError::Native(err) => { 29 | js_env.throw(err); 30 | ptr::null_mut() 31 | } 32 | _ => { 33 | let msg = self.to_string(); 34 | js_env.throw_type_error(&msg); 35 | ptr::null_mut() 36 | } 37 | } 38 | } 39 | } 40 | 41 | impl NjError { 42 | /// convert to napi value 43 | pub fn as_js(&self, js_env: &JsEnv) -> napi_value { 44 | match self { 45 | NjError::Native(err) => *err, 46 | _ => { 47 | let msg = self.to_string(); 48 | js_env.create_error(&msg).expect("error cannot be created") 49 | } 50 | } 51 | } 52 | } 53 | 54 | impl IntoJs for Result { 55 | fn into_js(self, js_env: &JsEnv) -> napi_value { 56 | match self { 57 | Ok(napi_val) => napi_val, 58 | Err(err) => err.into_js(js_env), 59 | } 60 | } 61 | } 62 | 63 | impl From for NjError { 64 | fn from(error: FromUtf8Error) -> Self { 65 | Self::Utf8Error(error) 66 | } 67 | } 68 | 69 | impl From for NjError { 70 | fn from(error: Utf8Error) -> Self { 71 | Self::Utf8ErrorSlice(error) 72 | } 73 | } 74 | 75 | impl From for NjError { 76 | fn from(status: NapiStatus) -> Self { 77 | Self::NapiCall(status) 78 | } 79 | } 80 | 81 | impl fmt::Display for NjError { 82 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 83 | match self { 84 | Self::NapiCall(status) => write!(f, "napi call failed {status:#?}"), 85 | Self::InvalidType(expected, actual) => { 86 | write!(f, "invalid type, expected: {expected}, actual: {actual}") 87 | } 88 | Self::Utf8Error(err) => write!(f, "ut8 error: {err}"), 89 | Self::Utf8ErrorSlice(err) => write!(f, "ut8 error: {err}"), 90 | Self::InvalidArgIndex(index, len) => { 91 | write!(f, "attempt to access arg: {index} out of len: {len}") 92 | } 93 | Self::InvalidArgCount(actual_count, expected_count) => write!( 94 | f, 95 | "{expected_count} args expected but {actual_count} is present" 96 | ), 97 | Self::NoPlainConstructor => write!(f, "Plain constructor not supported yet"), 98 | Self::Native(_val) => write!(f, "Native error payload"), 99 | Self::Other(msg) => write!(f, "{msg}"), 100 | } 101 | } 102 | } 103 | 104 | #[derive(Debug, PartialEq, Eq)] 105 | pub enum NapiStatus { 106 | Ok = crate::sys::napi_status_napi_ok as isize, 107 | InvalidArg = crate::sys::napi_status_napi_invalid_arg as isize, 108 | ObjectExpected = crate::sys::napi_status_napi_object_expected as isize, 109 | StringExpected = crate::sys::napi_status_napi_string_expected as isize, 110 | NameExpected = crate::sys::napi_status_napi_name_expected as isize, 111 | FunctionExpected = crate::sys::napi_status_napi_function_expected as isize, 112 | NumberExpected = crate::sys::napi_status_napi_number_expected as isize, 113 | BooleanExpected = crate::sys::napi_status_napi_boolean_expected as isize, 114 | ArrayExpected = crate::sys::napi_status_napi_array_expected as isize, 115 | GenericFailure = crate::sys::napi_status_napi_generic_failure as isize, 116 | PendingException = crate::sys::napi_status_napi_pending_exception as isize, 117 | Cancelled = crate::sys::napi_status_napi_cancelled as isize, 118 | EscapeCalledTwice = crate::sys::napi_status_napi_escape_called_twice as isize, 119 | HandleScopeMismatch = crate::sys::napi_status_napi_handle_scope_mismatch as isize, 120 | CallbackScopeMismatch = crate::sys::napi_status_napi_callback_scope_mismatch as isize, 121 | QueueFull = crate::sys::napi_status_napi_queue_full as isize, 122 | Closing = crate::sys::napi_status_napi_closing as isize, 123 | BigintExpected = crate::sys::napi_status_napi_bigint_expected as isize, 124 | DateExpected = crate::sys::napi_status_napi_date_expected as isize, 125 | ArraybufferExpected = crate::sys::napi_status_napi_arraybuffer_expected as isize, 126 | DetachableArrayBufferExpected = 127 | crate::sys::napi_status_napi_detachable_arraybuffer_expected as isize, 128 | } 129 | 130 | impl From for NapiStatus { 131 | fn from(status: napi_status) -> Self { 132 | match status { 133 | crate::sys::napi_status_napi_ok => Self::Ok, 134 | crate::sys::napi_status_napi_invalid_arg => Self::InvalidArg, 135 | crate::sys::napi_status_napi_object_expected => Self::ObjectExpected, 136 | crate::sys::napi_status_napi_string_expected => Self::StringExpected, 137 | crate::sys::napi_status_napi_name_expected => Self::NameExpected, 138 | crate::sys::napi_status_napi_function_expected => Self::FunctionExpected, 139 | crate::sys::napi_status_napi_number_expected => Self::NumberExpected, 140 | crate::sys::napi_status_napi_boolean_expected => Self::BooleanExpected, 141 | crate::sys::napi_status_napi_array_expected => Self::ArrayExpected, 142 | crate::sys::napi_status_napi_generic_failure => Self::GenericFailure, 143 | crate::sys::napi_status_napi_pending_exception => Self::PendingException, 144 | crate::sys::napi_status_napi_cancelled => Self::Cancelled, 145 | crate::sys::napi_status_napi_escape_called_twice => Self::EscapeCalledTwice, 146 | crate::sys::napi_status_napi_handle_scope_mismatch => Self::HandleScopeMismatch, 147 | crate::sys::napi_status_napi_callback_scope_mismatch => Self::CallbackScopeMismatch, 148 | crate::sys::napi_status_napi_queue_full => Self::QueueFull, 149 | crate::sys::napi_status_napi_closing => Self::Closing, 150 | crate::sys::napi_status_napi_bigint_expected => Self::BigintExpected, 151 | crate::sys::napi_status_napi_date_expected => Self::DateExpected, 152 | crate::sys::napi_status_napi_arraybuffer_expected => Self::ArraybufferExpected, 153 | crate::sys::napi_status_napi_detachable_arraybuffer_expected => { 154 | Self::DetachableArrayBufferExpected 155 | } 156 | _ => panic!("cannot convert: {}", status), 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /nj-core/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod basic; 2 | mod error; 3 | mod thread_fn; 4 | mod property; 5 | mod class; 6 | mod worker; 7 | mod convert; 8 | mod module; 9 | pub mod buffer; 10 | pub mod bigint; 11 | pub mod stream; 12 | 13 | pub use thread_fn::ThreadSafeFunction; 14 | pub use error::*; 15 | pub use property::Property; 16 | pub use property::PropertiesBuilder; 17 | pub use class::JSClass; 18 | pub use worker::create_promise; 19 | pub use worker::JsPromiseFuture; 20 | pub use worker::NjFutureExt; 21 | pub use convert::*; 22 | pub use ctor::ctor; 23 | pub use module::submit_property; 24 | pub use module::submit_register_callback; 25 | 26 | use class::JSObjectWrapper; 27 | 28 | pub mod sys { 29 | pub use nj_sys::*; 30 | #[allow(non_camel_case_types)] 31 | pub type raw_pt = *mut ::std::os::raw::c_void; 32 | #[allow(non_camel_case_types)] 33 | pub type napi_callback_raw = unsafe extern "C" fn( 34 | env: napi_env, 35 | info: napi_callback_info, 36 | ) -> napi_value; 37 | #[allow(non_camel_case_types)] 38 | pub type napi_finalize_raw = unsafe extern "C" fn(env: napi_env, finalize_data: raw_pt, finalize_hint: raw_pt); 39 | #[cfg(any( 40 | target_os = "windows", 41 | all( 42 | target_os = "linux", 43 | target_env="ohos", 44 | target_arch = "arm" 45 | ) 46 | ))] 47 | #[allow(non_camel_case_types)] 48 | pub type size_t = u32; 49 | #[cfg(all( 50 | target_os = "linux", 51 | target_env="ohos", 52 | any( 53 | target_arch = "x86_64", 54 | target_arch = "aarch64" 55 | ) 56 | ))] 57 | #[allow(non_camel_case_types)] 58 | pub type size_t = u64; 59 | } 60 | pub mod init { 61 | pub use ctor::ctor as node_bindgen_init_once; 62 | } 63 | 64 | pub mod future { 65 | pub use fluvio_future::task::spawn; 66 | } 67 | 68 | pub mod val { 69 | pub use crate::basic::*; 70 | } 71 | 72 | pub mod log { 73 | pub use tracing::*; 74 | } 75 | 76 | /// call napi and assert 77 | /// used only in this crate 78 | #[macro_export] 79 | macro_rules! napi_call_assert { 80 | ($napi_expr:expr) => {{ 81 | let status = unsafe { $napi_expr }; 82 | if status != $crate::sys::napi_status_napi_ok { 83 | let nj_status: $crate::NapiStatus = status.into(); 84 | tracing::error!("error executing napi call {:#?}", nj_status); 85 | } 86 | }}; 87 | } 88 | 89 | /// call napi and wrap into result 90 | /// used only in this crate 91 | #[macro_export] 92 | macro_rules! napi_call_result { 93 | ($napi_expr:expr) => {{ 94 | let status = unsafe { $napi_expr }; 95 | if status == $crate::sys::napi_status_napi_ok { 96 | Ok(()) 97 | } else { 98 | let nj_status: $crate::NapiStatus = status.into(); 99 | tracing::error!("ohos-node-bindgen error {:#?}", nj_status); 100 | Err(NjError::NapiCall(nj_status)) 101 | } 102 | }}; 103 | } 104 | 105 | /// convert result into napi value if ok otherwise convert to error 106 | #[macro_export] 107 | macro_rules! result_to_napi { 108 | ($result:expr) => { 109 | match $result { 110 | Ok(val) => val, 111 | Err(err) => return err.into(), 112 | } 113 | }; 114 | } 115 | 116 | #[macro_export] 117 | macro_rules! callback_to_napi { 118 | ($result:expr,$js_env:expr) => { 119 | match $result { 120 | Ok(val) => val, 121 | Err(err) => { 122 | return; 123 | } 124 | } 125 | }; 126 | } 127 | 128 | /// assert the napi call 129 | #[macro_export] 130 | macro_rules! assert_napi { 131 | ($result:expr) => { 132 | match $result { 133 | Ok(val) => val, 134 | Err(err) => { 135 | panic!("napi call failed: {}", err) 136 | } 137 | } 138 | }; 139 | } 140 | 141 | #[macro_export] 142 | macro_rules! c_str { 143 | ($string:literal) => {{ 144 | const _C_STRING: &'static str = concat!($string, "\0"); 145 | _C_STRING 146 | }}; 147 | } 148 | 149 | #[macro_export] 150 | macro_rules! method { 151 | ($name:literal,$rs_method:expr) => {{ 152 | nj::core::Property::new($name).method($rs_method) 153 | }}; 154 | } 155 | -------------------------------------------------------------------------------- /nj-core/src/module.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | 3 | use inventory::Collect; 4 | use inventory::submit; 5 | use inventory::iter; 6 | use inventory::Registry; 7 | 8 | use crate::Property; 9 | use crate::val::JsExports; 10 | use crate::sys::napi_value; 11 | use crate::sys::napi_env; 12 | use crate::NjError; 13 | 14 | type ClassCallback = fn(&mut JsExports) -> Result<(), NjError>; 15 | 16 | enum NapiRegister { 17 | Property(Property), 18 | Callback(ClassCallback), 19 | } 20 | 21 | impl Collect for NapiRegister { 22 | fn registry() -> &'static Registry { 23 | static REGISTRY: Registry = Registry::new(); 24 | ®ISTRY 25 | } 26 | } 27 | 28 | /// submit property for including in global registry 29 | pub fn submit_property(value: Property) { 30 | submit::(NapiRegister::Property(value)) 31 | } 32 | 33 | pub fn submit_register_callback(callback: ClassCallback) { 34 | submit::(NapiRegister::Callback(callback)); 35 | } 36 | 37 | #[no_mangle] 38 | pub extern "C" fn init_modules(env: napi_env, exports: napi_value) -> napi_value { 39 | fluvio_future::subscriber::init_tracer(None); 40 | 41 | let mut js_exports = JsExports::new(env, exports); 42 | let mut prop_builder = js_exports.prop_builder(); 43 | 44 | for register in iter:: { 45 | match register { 46 | NapiRegister::Property(property) => { 47 | prop_builder.mut_append(property.to_owned()); 48 | } 49 | NapiRegister::Callback(callback) => { 50 | if let Err(err) = callback(&mut js_exports) { 51 | panic!("error invoking JS callback: {}", err); 52 | } 53 | } 54 | } 55 | } 56 | 57 | js_exports 58 | .define_property(prop_builder) 59 | .expect("property should not fail"); 60 | 61 | exports 62 | } 63 | 64 | #[crate::ctor] 65 | fn init_module() { 66 | use crate::c_str; 67 | use crate::sys::NAPI_VERSION; 68 | use crate::sys::napi_module; 69 | use crate::sys::napi_module_register; 70 | 71 | static mut _MODULE: napi_module = napi_module { 72 | nm_version: NAPI_VERSION as i32, 73 | nm_flags: 0, 74 | nm_filename: c_str!("lib.rs").as_ptr() as *const ::std::os::raw::c_char, 75 | nm_register_func: Some(init_modules), 76 | nm_modname: c_str!("rust_module").as_ptr() as *const ::std::os::raw::c_char, 77 | nm_priv: ptr::null_mut(), 78 | reserved: [ 79 | ptr::null_mut(), 80 | ptr::null_mut(), 81 | ptr::null_mut(), 82 | ptr::null_mut(), 83 | ], 84 | }; 85 | 86 | unsafe { 87 | napi_module_register(&mut _MODULE); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /nj-core/src/property.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | use std::ffi::CString; 3 | 4 | use crate::sys::napi_property_descriptor; 5 | use crate::sys::napi_property_attributes_napi_default; 6 | use crate::sys::napi_callback_raw; 7 | use crate::sys::napi_callback; 8 | 9 | #[derive(Debug, Clone)] 10 | pub struct Property { 11 | name: CString, 12 | method: napi_callback, 13 | getter: napi_callback, 14 | setter: napi_callback, 15 | } 16 | 17 | impl Property { 18 | pub fn new(name: &str) -> Self { 19 | Self { 20 | name: CString::new(name).expect("c-string should not fail"), 21 | method: None, 22 | getter: None, 23 | setter: None, 24 | } 25 | } 26 | 27 | pub fn method(mut self, method: napi_callback_raw) -> Self { 28 | self.method = Some(method); 29 | self 30 | } 31 | 32 | pub fn getter(mut self, getter: napi_callback_raw) -> Self { 33 | self.getter = Some(getter); 34 | self 35 | } 36 | 37 | pub fn setter(mut self, setter: napi_callback_raw) -> Self { 38 | self.setter = Some(setter); 39 | self 40 | } 41 | 42 | pub fn as_raw_property(&self) -> napi_property_descriptor { 43 | napi_property_descriptor { 44 | utf8name: self.name.as_ptr(), 45 | name: ptr::null_mut(), 46 | method: self.method, 47 | getter: self.getter, 48 | setter: self.setter, 49 | value: ptr::null_mut(), 50 | attributes: napi_property_attributes_napi_default, 51 | data: ptr::null_mut(), 52 | } 53 | } 54 | } 55 | 56 | #[derive(Default, Debug)] 57 | pub struct PropertiesBuilder(Vec); 58 | 59 | impl PropertiesBuilder { 60 | pub fn new() -> Self { 61 | Self::default() 62 | } 63 | 64 | pub fn append(mut self, property: Property) -> Self { 65 | self.0.push(property); 66 | self 67 | } 68 | 69 | pub fn mut_append(&mut self, property: Property) { 70 | self.0.push(property); 71 | } 72 | 73 | // define into env 74 | pub fn as_raw_properties(&self) -> Vec { 75 | self.0.iter().map(|p| p.as_raw_property()).collect() 76 | } 77 | } 78 | 79 | impl From> for PropertiesBuilder { 80 | fn from(properties: Vec) -> Self { 81 | Self(properties) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /nj-core/src/stream.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | use std::fmt::Debug; 3 | 4 | use tracing::debug; 5 | 6 | use futures_lite::Stream; 7 | use futures_lite::stream::StreamExt; 8 | use pin_utils::unsafe_pinned; 9 | use pin_utils::unsafe_unpinned; 10 | 11 | use fluvio_future::task::spawn; 12 | 13 | use crate::sys::napi_value; 14 | use crate::val::JsEnv; 15 | use crate::NjError; 16 | use crate::TryIntoJs; 17 | 18 | pub trait NjStream: Stream { 19 | fn js_then(self, fut: F) -> JsThen 20 | where 21 | F: FnMut(Self::Item), 22 | Self: Sized, 23 | { 24 | JsThen::new(self, fut) 25 | } 26 | } 27 | 28 | impl NjStream for T where T: Stream {} 29 | 30 | pub struct JsThen { 31 | stream: St, 32 | f: F, 33 | } 34 | 35 | impl Unpin for JsThen {} 36 | 37 | impl JsThen 38 | where 39 | St: Stream, 40 | F: FnMut(St::Item), 41 | { 42 | unsafe_pinned!(stream: St); 43 | unsafe_unpinned!(f: F); 44 | 45 | pub fn new(stream: St, f: F) -> JsThen { 46 | Self { stream, f } 47 | } 48 | } 49 | 50 | impl TryIntoJs for JsThen 51 | where 52 | St: Stream + Send + 'static, 53 | F: FnMut(St::Item) + Send + 'static, 54 | St::Item: Debug, 55 | { 56 | fn try_to_js(self, _js_env: &JsEnv) -> Result { 57 | let mut stream = Box::pin(self.stream); 58 | let mut cb = self.f; 59 | 60 | spawn(async move { 61 | while let Some(item) = stream.next().await { 62 | debug!("got item: {:#?}, invoking Js callback", item); 63 | cb(item); 64 | } 65 | }); 66 | 67 | Ok(ptr::null_mut()) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /nj-core/src/thread_fn.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | 3 | use tracing::debug; 4 | 5 | use crate::sys::napi_threadsafe_function; 6 | use crate::NjError; 7 | use crate::val::JsEnv; 8 | use crate::sys::napi_env; 9 | 10 | /// Wrapper for thread safe function that are safe to send and sync across thread 11 | pub struct ThreadSafeFunction { 12 | env: JsEnv, 13 | tf: napi_threadsafe_function, 14 | } 15 | 16 | unsafe impl Sync for ThreadSafeFunction {} 17 | unsafe impl Send for ThreadSafeFunction {} 18 | 19 | impl ThreadSafeFunction { 20 | pub fn new(env: E, tf: napi_threadsafe_function) -> Self 21 | where 22 | E: Into, 23 | { 24 | Self { 25 | env: env.into(), 26 | tf, 27 | } 28 | } 29 | 30 | pub fn inner(self) -> napi_threadsafe_function { 31 | self.tf 32 | } 33 | 34 | pub fn env(&self) -> napi_env { 35 | self.env.inner() 36 | } 37 | 38 | pub fn call(&self, data: Option<*mut ::std::os::raw::c_void>) -> Result<(), NjError> { 39 | let data_ptr = match data { 40 | Some(ptr) => ptr, 41 | None => ptr::null_mut(), 42 | }; 43 | debug!("calling thread safe"); 44 | crate::napi_call_result!(crate::sys::napi_call_threadsafe_function( 45 | self.tf, 46 | data_ptr, 47 | crate::sys::napi_threadsafe_function_call_mode_napi_tsfn_blocking 48 | )) 49 | } 50 | } 51 | 52 | impl Drop for ThreadSafeFunction { 53 | fn drop(&mut self) { 54 | crate::napi_call_assert!(crate::sys::napi_release_threadsafe_function( 55 | self.tf, 56 | crate::sys::napi_threadsafe_function_release_mode_napi_tsfn_release 57 | )); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /nj-core/src/worker.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | 3 | use tracing::error; 4 | use tracing::trace; 5 | use tracing::debug; 6 | use async_trait::async_trait; 7 | use futures_lite::Future; 8 | 9 | use fluvio_future::task::spawn; 10 | 11 | use crate::sys::napi_deferred; 12 | use crate::sys::napi_value; 13 | use crate::val::JsEnv; 14 | use crate::NjError; 15 | use crate::sys::napi_env; 16 | use crate::sys::napi_callback_info; 17 | use crate::TryIntoJs; 18 | use crate::IntoJs; 19 | use crate::assert_napi; 20 | use crate::ThreadSafeFunction; 21 | 22 | pub struct JsPromiseFuture { 23 | future: F, 24 | name: String, 25 | } 26 | 27 | impl JsPromiseFuture 28 | where 29 | F: Future, 30 | F::Output: TryIntoJs, 31 | { 32 | pub fn new(future: F, name: S) -> Self 33 | where 34 | S: Into, 35 | { 36 | Self { 37 | future, 38 | name: name.into(), 39 | } 40 | } 41 | } 42 | 43 | impl TryIntoJs for JsPromiseFuture 44 | where 45 | F: Future + 'static + Send, 46 | F::Output: TryIntoJs, 47 | { 48 | fn try_to_js(self, js_env: &JsEnv) -> Result { 49 | create_promise(js_env, &self.name, self.future) 50 | } 51 | } 52 | 53 | struct JsDeferred(napi_deferred); 54 | unsafe impl Send for JsDeferred {} 55 | 56 | pub struct WorkerResult { 57 | deferred: JsDeferred, 58 | result: O, 59 | } 60 | 61 | /// create promise and schedule work 62 | /// when this is finished it will return result in the main thread 63 | pub fn create_promise(js_env: &JsEnv, name: &str, future: F) -> Result 64 | where 65 | F: Future + 'static + Send, 66 | O: TryIntoJs, 67 | { 68 | let (promise, deferred) = js_env.create_promise()?; 69 | let function_name = format!("async_worker_th_{name}"); 70 | let ts_fn = 71 | js_env.create_thread_safe_function(&function_name, None, Some(promise_complete::))?; 72 | let js_deferred = JsDeferred(deferred); 73 | 74 | spawn(async move { 75 | let result = future.await; 76 | finish_worker(ts_fn, result, js_deferred); 77 | }); 78 | 79 | Ok(promise) 80 | } 81 | 82 | extern "C" fn promise_complete( 83 | env: napi_env, 84 | _js_cb: napi_value, 85 | _context: *mut ::std::os::raw::c_void, 86 | data: *mut ::std::os::raw::c_void, 87 | ) where 88 | O: TryIntoJs, 89 | { 90 | if !env.is_null() { 91 | trace!("promise complete"); 92 | let js_env = JsEnv::new(env); 93 | 94 | let worker_result: Box> = 95 | unsafe { Box::from_raw(data as *mut WorkerResult) }; 96 | 97 | let result: Result<(), NjError> = match worker_result.result.try_to_js(&js_env) { 98 | Ok(val) => { 99 | trace!("trying to resolve to deferred"); 100 | js_env.resolve_deferred(worker_result.deferred.0, val) 101 | } 102 | Err(js_err) => { 103 | trace!("trying to resolve to deferred"); 104 | js_env.reject_deferred(worker_result.deferred.0, js_err.as_js(&js_env)) 105 | } 106 | }; 107 | assert_napi!(result) 108 | } 109 | } 110 | 111 | fn finish_worker(ts_fn: ThreadSafeFunction, result: O, deferred: JsDeferred) 112 | where 113 | O: TryIntoJs, 114 | { 115 | let boxed_worker = Box::new(WorkerResult { deferred, result }); 116 | let ptr = Box::into_raw(boxed_worker); 117 | if let Err(err) = ts_fn.call(Some(ptr as *mut core::ffi::c_void)) { 118 | error!("error finishing worker: {}", err); 119 | } 120 | } 121 | 122 | #[async_trait] 123 | pub trait JSWorker: Sized + Send + 'static { 124 | type Output: TryIntoJs; 125 | 126 | /// create new worker based on argument based in the callback 127 | /// only need if it is called as method 128 | fn create_worker(_env: &JsEnv, _info: napi_callback_info) -> Result { 129 | Err(NjError::InvalidType( 130 | "worker".to_owned(), 131 | "worker".to_owned(), 132 | )) 133 | } 134 | 135 | /// call by Node to create promise 136 | extern "C" fn start_promise(env: napi_env, info: napi_callback_info) -> napi_value { 137 | let js_env = JsEnv::new(env); 138 | 139 | let result: Result = (|| { 140 | let worker = Self::create_worker(&js_env, info)?; 141 | worker.create_promise(&js_env) 142 | })(); 143 | 144 | result.into_js(&js_env) 145 | } 146 | 147 | /// create promise and schedule work 148 | /// when this is finished it will return result in the main thread 149 | fn create_promise(self, js_env: &JsEnv) -> Result { 150 | let (promise, deferred) = js_env.create_promise()?; 151 | let function_name = format!("async_worker_th_{}", std::any::type_name::()); 152 | let ts_fn = js_env.create_thread_safe_function( 153 | &function_name, 154 | None, 155 | Some(promise_complete::), 156 | )?; 157 | let js_deferred = JsDeferred(deferred); 158 | 159 | spawn(async move { 160 | let result = self.execute().await; 161 | finish_worker(ts_fn, result, js_deferred); 162 | }); 163 | 164 | Ok(promise) 165 | } 166 | 167 | /// execute this in async worker thread 168 | async fn execute(mut self) -> Self::Output; 169 | } 170 | 171 | pub trait NjFutureExt: Future { 172 | fn try_to_js(self, js_env: &JsEnv) -> Result 173 | where 174 | Self: Sized + Send + 'static, 175 | Self::Output: TryIntoJs, 176 | { 177 | extern "C" fn promise_complete2( 178 | env: napi_env, 179 | _js_cb: napi_value, 180 | _context: *mut ::std::os::raw::c_void, 181 | data: *mut ::std::os::raw::c_void, 182 | ) { 183 | if !env.is_null() { 184 | trace!("promise complete"); 185 | let _ = JsEnv::new(env); 186 | 187 | let _: Box = unsafe { Box::from_raw(data as *mut O) }; 188 | } 189 | } 190 | 191 | let function_name = "stream_example_1".to_string(); 192 | let _ = js_env.create_thread_safe_function( 193 | &function_name, 194 | None, 195 | Some(promise_complete2::), 196 | )?; 197 | 198 | debug!("spawning task"); 199 | spawn(async move { 200 | let _ = self.await; 201 | debug!("task completed"); 202 | }); 203 | 204 | Ok(ptr::null_mut()) 205 | } 206 | } 207 | 208 | impl NjFutureExt for T where T: Future {} 209 | -------------------------------------------------------------------------------- /nj-derive/.gitinore: -------------------------------------------------------------------------------- 1 | Cargo.lock -------------------------------------------------------------------------------- /nj-derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ohos-nj-derive" 3 | version = "3.4.2" 4 | authors = ["Stuart Zhang "] 5 | edition = "2018" 6 | description = "procedure macro for ohos-node-bindgen" 7 | repository = "https://github.com/stuartZhang/node-bindgen" 8 | readme = "README.md" 9 | license = "Apache-2.0" 10 | 11 | [lib] 12 | proc-macro = true 13 | 14 | 15 | [dependencies] 16 | proc-macro2 = "1.0" 17 | quote = "1.0" 18 | syn = { version = "1.0", features = ["full", "parsing", "visit-mut","derive","extra-traits"] } 19 | Inflector = "0.11.4" 20 | 21 | [dev-dependencies] 22 | trybuild = { git = "https://github.com/infinyon/trybuild", branch = "check_option" } 23 | ohos-node-bindgen = { path = ".." } 24 | fluvio-future = { version = "0.6.0", features = ["timer"] } 25 | -------------------------------------------------------------------------------- /nj-derive/README.md: -------------------------------------------------------------------------------- 1 | # Procedure macro for ohos-node-bindgen 2 | 3 | -------------------------------------------------------------------------------- /nj-derive/src/ast/arg.rs: -------------------------------------------------------------------------------- 1 | use syn::FnArg; 2 | use syn::Ident; 3 | use syn::Type; 4 | use syn::Pat; 5 | use syn::Error; 6 | use syn::Generics; 7 | use syn::TypeParam; 8 | use syn::Signature; 9 | use syn::TypeParamBound; 10 | use syn::PathArguments; 11 | use syn::Result; 12 | use syn::spanned::Spanned; 13 | use syn::ParenthesizedGenericArguments; 14 | use syn::Receiver; 15 | 16 | use super::MyTypePath; 17 | use super::MyReferenceType; 18 | use super::MyTupleType; 19 | 20 | /// Information about Function Arguments 21 | #[derive(Debug, Default)] 22 | pub struct FunctionArgs<'a> { 23 | pub args: Vec>, 24 | pub is_method: bool, 25 | _receiver: Option<&'a Receiver>, 26 | } 27 | 28 | impl<'a> FunctionArgs<'a> { 29 | pub fn from_ast(sig: &'a Signature) -> Result { 30 | //println!("fn: {:#?}",input_fn); 31 | let generics = &sig.generics; 32 | 33 | let mut args: Vec = vec![]; 34 | 35 | let is_method = has_receiver(sig); 36 | 37 | // extract arguments, 38 | let i = 0; 39 | for ref arg in &sig.inputs { 40 | // println!("arg: {:#?}",arg); 41 | match arg { 42 | FnArg::Receiver(_) => {} 43 | FnArg::Typed(arg_type) => match &*arg_type.pat { 44 | Pat::Ident(identity) => { 45 | let arg = FunctionArg::new(i, &identity.ident, &arg_type.ty, generics)?; 46 | args.push(arg); 47 | } 48 | _ => return Err(Error::new(arg_type.span(), "not supported type")), 49 | }, 50 | } 51 | } 52 | 53 | Ok(Self { 54 | args, 55 | is_method, 56 | ..Default::default() 57 | }) 58 | } 59 | 60 | pub fn inner(&self) -> &Vec { 61 | &self.args 62 | } 63 | 64 | pub fn len(&self) -> usize { 65 | self.args.len() 66 | } 67 | } 68 | 69 | /// find receiver if any, this will be used to indicate if this is method 70 | fn has_receiver(sig: &Signature) -> bool { 71 | sig.inputs 72 | .iter() 73 | .any(|input| matches!(input, FnArg::Receiver(_rec))) 74 | } 75 | 76 | #[derive(Debug)] 77 | pub struct FunctionArg<'a> { 78 | pub arg_index: u32, 79 | pub typ: FunctionArgType<'a>, 80 | } 81 | 82 | impl<'a> FunctionArg<'a> { 83 | /// given this, convert into normalized type signature 84 | fn new(arg_index: u32, ident: &'a Ident, ty: &'a Type, generics: &'a Generics) -> Result { 85 | match ty { 86 | Type::Path(path_type) => { 87 | let my_type = MyTypePath::from(path_type)?; 88 | 89 | // check whether type references in the generic indicates this is closure 90 | if let Some(param) = find_generic(generics, my_type.ident()) { 91 | let closure = ClosureType::from(ident, param)?; 92 | Ok(Self { 93 | arg_index, 94 | typ: FunctionArgType::Closure(closure), 95 | }) 96 | } else { 97 | Ok(Self { 98 | arg_index, 99 | typ: FunctionArgType::Path(my_type), 100 | }) 101 | } 102 | } 103 | Type::Reference(ref_type) => { 104 | let my_type = MyReferenceType::from(ref_type)?; 105 | Ok(Self { 106 | arg_index, 107 | typ: FunctionArgType::Ref(my_type), 108 | }) 109 | } 110 | Type::Tuple(tuple) => { 111 | let types: Vec<_> = tuple.elems.iter().collect(); 112 | let tuple = MyTupleType::from(types); 113 | Ok(Self { 114 | arg_index, 115 | typ: FunctionArgType::Tuple(tuple), 116 | }) 117 | } 118 | _ => Err(Error::new(ty.span(), "not supported type")), 119 | } 120 | } 121 | 122 | /* 123 | pub fn is_js_env(&self) -> bool { 124 | match self.typ { 125 | FunctionArgType::JsEnv(_) => true, 126 | _ => false, 127 | } 128 | } 129 | */ 130 | } 131 | 132 | /// Categorize function argument 133 | #[derive(Debug)] 134 | pub enum FunctionArgType<'a> { 135 | Path(MyTypePath<'a>), // normal type 136 | Ref(MyReferenceType<'a>), // reference type 137 | Tuple(MyTupleType<'a>), 138 | Closure(ClosureType<'a>), // closure callback 139 | } 140 | 141 | /// find generic with match ident 142 | fn find_generic<'a>(generics: &'a Generics, ident: Option<&Ident>) -> Option<&'a TypeParam> { 143 | if let Some(ident) = ident { 144 | generics 145 | .type_params() 146 | .find(|ty| *ty.ident.to_string() == *ident.to_string()) 147 | } else { 148 | None 149 | } 150 | } 151 | 152 | #[derive(Debug)] 153 | pub struct ClosureType<'a> { 154 | //pub ty: &'a ParenthesizedGenericArguments, 155 | pub inputs: Vec>, 156 | pub ident: &'a Ident, 157 | } 158 | 159 | impl<'a> ClosureType<'a> { 160 | // try to see if we can find closure, otherwise return none 161 | pub fn from(ident: &'a Ident, param: &'a TypeParam) -> Result { 162 | for ref bound in ¶m.bounds { 163 | match bound { 164 | TypeParamBound::Trait(tt) => { 165 | for segment in &tt.path.segments { 166 | match segment.arguments { 167 | PathArguments::Parenthesized(ref path) => { 168 | return Ok(Self { 169 | ident, 170 | inputs: find_inputs(path)?, 171 | }) 172 | } 173 | _ => { 174 | return Err(Error::new(param.span(), "not supported closure type")) 175 | } 176 | } 177 | } 178 | return Err(Error::new(param.span(), "not supported closure type")); 179 | } 180 | TypeParamBound::Lifetime(_) => { 181 | return Err(Error::new(param.span(), "not supported closure type")) 182 | } 183 | } 184 | } 185 | Err(Error::new(param.span(), "not supported closure type")) 186 | } 187 | 188 | // name of function is used by thread safe function to complete closure 189 | pub fn async_js_callback_identifier(&self) -> Ident { 190 | use proc_macro2::Span; 191 | 192 | Ident::new( 193 | &format!("thread_safe_{}_complete", self.ident), 194 | Span::call_site(), 195 | ) 196 | } 197 | } 198 | 199 | fn find_inputs(ty: &ParenthesizedGenericArguments) -> Result> { 200 | let mut types: Vec = vec![]; 201 | 202 | for path in &ty.inputs { 203 | let my_type = match path { 204 | Type::Path(ref path_type) => match MyTypePath::from(path_type) { 205 | Ok(m_type) => m_type, 206 | Err(err) => return Err(err), 207 | }, 208 | _ => return Err(Error::new(ty.span(), "not supported closure type")), 209 | }; 210 | types.push(my_type); 211 | } 212 | 213 | Ok(types) 214 | } 215 | -------------------------------------------------------------------------------- /nj-derive/src/ast/attribute.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::Span; 2 | use syn::AttributeArgs; 3 | use syn::Attribute; 4 | use syn::Result; 5 | use syn::spanned::Spanned; 6 | use syn::Error; 7 | use syn::NestedMeta; 8 | use syn::Meta; 9 | use syn::MetaNameValue; 10 | use syn::Lit; 11 | use syn::LitStr; 12 | use syn::Ident; 13 | use syn::Path; 14 | 15 | /// Represents bindgen attributes 16 | /// Attribute can be attached to free standing function or impl block 17 | /// name="my_function" 18 | /// constructor 19 | /// setter 20 | /// mt 21 | #[derive(Debug)] 22 | pub enum FunctionAttribute { 23 | Getter(Ident), 24 | Setter(Ident), 25 | Constructor(Ident), 26 | Name(LitStr), 27 | Mt(Ident), 28 | } 29 | 30 | impl FunctionAttribute { 31 | fn from_ast(meta: Meta) -> Result { 32 | match meta { 33 | Meta::NameValue(name_value) => { 34 | if has_attribute(&name_value, "name") { 35 | // check make sure name is str literal 36 | match name_value.lit { 37 | Lit::Str(str) => Ok(Self::Name(str)), 38 | _ => Err(Error::new( 39 | name_value.span(), 40 | "name value is not string literal", 41 | )), 42 | } 43 | } else { 44 | Err(Error::new(name_value.span(), "unsupported attribute:")) 45 | } 46 | } 47 | Meta::Path(p) => Self::from_ident(find_any_identifier(p)?), 48 | 49 | Meta::List(lit) => Err(Error::new( 50 | lit.span(), 51 | "nested attributes are not supported", 52 | )), 53 | } 54 | } 55 | 56 | fn from_ident(ident: Ident) -> Result { 57 | if ident == "constructor" { 58 | Ok(Self::Constructor(ident)) 59 | } else if ident == "getter" { 60 | Ok(Self::Getter(ident)) 61 | } else if ident == "setter" { 62 | Ok(Self::Setter(ident)) 63 | } else if ident == "mt" { 64 | Ok(Self::Mt(ident)) 65 | } else { 66 | Err(Error::new(ident.span(), "unrecognized attribute name")) 67 | } 68 | } 69 | 70 | fn is_constructor(&self) -> bool { 71 | matches!(self, Self::Constructor(_)) 72 | } 73 | 74 | fn is_multi_threaded(&self) -> bool { 75 | matches!(self, Self::Mt(_)) 76 | } 77 | 78 | /// get function name, if this is not name, return none 79 | fn fn_name(&self) -> Option<&LitStr> { 80 | match self { 81 | Self::Name(ref name) => Some(name), 82 | _ => None, 83 | } 84 | } 85 | 86 | fn is_getter(&self) -> bool { 87 | matches!(self, Self::Getter(_)) 88 | } 89 | 90 | fn is_setter(&self) -> bool { 91 | matches!(self, Self::Setter(_)) 92 | } 93 | } 94 | 95 | fn has_attribute(name_value: &MetaNameValue, attr_name: &str) -> bool { 96 | name_value 97 | .path 98 | .segments 99 | .iter() 100 | .any(|seg| seg.ident == attr_name) 101 | } 102 | 103 | fn find_any_identifier(path: Path) -> Result { 104 | if path.segments.is_empty() { 105 | Err(Error::new(path.span(), "invalid attribute")) 106 | } else { 107 | Ok(path 108 | .segments 109 | .into_iter() 110 | .find(|_| true) 111 | .map(|segment| segment.ident) 112 | .unwrap()) 113 | } 114 | } 115 | 116 | /// ast information related to attributes 117 | #[derive(Debug, Default)] 118 | pub struct FunctionAttributes { 119 | pub constructor: Option, 120 | pub multi_threaded: Option, 121 | pub getter: Option, 122 | pub setter: Option, 123 | name: Option, 124 | } 125 | 126 | impl FunctionAttributes { 127 | pub fn from_ast(args: AttributeArgs) -> Result { 128 | //println!("attrs: {:#?}",args); 129 | let mut attrs: Vec = vec![]; 130 | 131 | for attr in args { 132 | match attr { 133 | NestedMeta::Meta(meta) => { 134 | attrs.push(FunctionAttribute::from_ast(meta)?); 135 | } 136 | _ => return Err(Error::new(attr.span(), "invalid syntax")), 137 | } 138 | } 139 | 140 | Ok(Self::from(attrs)) 141 | } 142 | 143 | /// validate and parse attributes for individual features 144 | /// if check_method is true, check class specific attributes 145 | /// note that there are attribute parsing phases for class 146 | /// first phase is as part of Impl structure, this is where class level attribute validation can be done 147 | /// second phase is individual functions where we don't know if function is method or not 148 | fn from(attrs: Vec) -> Self { 149 | let mut constructor = None; 150 | let mut multi_threaded = None; 151 | let mut getter = None; 152 | let mut setter = None; 153 | let mut name = None; 154 | 155 | for attr in attrs { 156 | if attr.is_constructor() { 157 | constructor = Some(attr); 158 | } else if attr.is_multi_threaded() { 159 | multi_threaded = Some(attr); 160 | } else if attr.is_getter() { 161 | getter = Some(attr); 162 | } else if attr.is_setter() { 163 | setter = Some(attr); 164 | } else if let Some(name_lit) = attr.fn_name() { 165 | name = Some(name_lit.value()); 166 | } 167 | } 168 | 169 | Self { 170 | constructor, 171 | multi_threaded, 172 | getter, 173 | setter, 174 | name, 175 | } 176 | } 177 | 178 | pub fn from_method_attribute(attribute: &Attribute) -> Result { 179 | //println!("token tree: {:#?}",attribute); 180 | 181 | match attribute.parse_meta()? { 182 | Meta::Path(_) => { 183 | // ignore ohos_node_bindgen which already know exists 184 | Ok(FunctionAttributes::default()) 185 | } 186 | Meta::NameValue(n) => Err(Error::new(n.span(), "invalid attribute syntax")), 187 | Meta::List(list) => { 188 | let mut attrs = vec![]; 189 | for nested_meta in list.nested.into_iter() { 190 | match nested_meta { 191 | NestedMeta::Meta(meta) => { 192 | attrs.push(FunctionAttribute::from_ast(meta)?); 193 | } 194 | NestedMeta::Lit(lit) => { 195 | return Err(Error::new(lit.span(), "unrecognized syntax")) 196 | } 197 | } 198 | } 199 | 200 | Ok(Self::from(attrs)) 201 | } 202 | } 203 | } 204 | 205 | pub fn name(&self) -> Option<&String> { 206 | self.name.as_ref() 207 | } 208 | 209 | pub fn is_multi_threaded(&self) -> bool { 210 | self.multi_threaded.is_some() 211 | } 212 | 213 | pub fn is_constructor(&self) -> bool { 214 | self.constructor.is_some() 215 | } 216 | 217 | pub fn is_getter(&self) -> bool { 218 | self.getter.is_some() 219 | } 220 | 221 | pub fn is_setter(&self) -> bool { 222 | self.setter.is_some() 223 | } 224 | 225 | /// check if we method specific attribute if we not method 226 | pub fn valid_as_non_method(&self) -> Result<()> { 227 | /* 228 | if self.constructor.is_some() { 229 | return Err(Error::new(Span::call_site(), "constructor is only allowed in method")); 230 | } 231 | */ 232 | 233 | if self.setter.is_some() { 234 | return Err(Error::new( 235 | Span::call_site(), 236 | "setter is only allowed in method", 237 | )); 238 | } 239 | 240 | if self.getter.is_some() { 241 | return Err(Error::new( 242 | Span::call_site(), 243 | "getter is only allowed in method", 244 | )); 245 | } 246 | 247 | Ok(()) 248 | } 249 | } 250 | -------------------------------------------------------------------------------- /nj-derive/src/ast/class.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::Span; 2 | use syn::ItemImpl; 3 | use syn::ImplItem; 4 | use syn::Result; 5 | use syn::Ident; 6 | use syn::LitStr; 7 | use syn::Error; 8 | use syn::ImplItemMethod; 9 | use syn::spanned::Spanned; 10 | 11 | use crate::ast::MethodUtil; 12 | use crate::util::default_function_property_name; 13 | use super::FunctionAttributes; 14 | use super::FunctionArgs; 15 | use super::MyTypePath; 16 | 17 | pub struct Class<'a> { 18 | pub self_ty: MyTypePath<'a>, 19 | pub methods: Vec>, 20 | } 21 | 22 | impl<'a> Class<'a> { 23 | /// convert 24 | pub fn from_ast(item: &'a ItemImpl) -> Result { 25 | use syn::Type; 26 | 27 | let mut methods = vec![]; 28 | for item in &item.items { 29 | if let ImplItem::Method(method) = item { 30 | if let Some(method) = Method::from_ast(method)? { 31 | methods.push(method); 32 | } 33 | } 34 | } 35 | 36 | // find type path 37 | let self_ty = match &*item.self_ty { 38 | Type::Path(path_type) => MyTypePath::from(path_type)?, 39 | _ => return Err(Error::new(item.span(), "not supported receiver type")), 40 | }; 41 | 42 | Ok(Self { self_ty, methods }) 43 | } 44 | 45 | pub fn constructor(&'a self) -> Option<&'a Method> { 46 | self.methods 47 | .iter() 48 | .find(|method| method.attributes.is_constructor()) 49 | } 50 | 51 | pub fn my_type(&'a self) -> &MyTypePath<'a> { 52 | &self.self_ty 53 | } 54 | } 55 | 56 | pub struct Method<'a> { 57 | pub method: &'a ImplItemMethod, 58 | pub attributes: FunctionAttributes, 59 | pub args: FunctionArgs<'a>, 60 | } 61 | 62 | impl<'a> Method<'a> { 63 | /// extract js method, if it can't find marker attribute, return some 64 | pub fn from_ast(method: &'a ImplItemMethod) -> Result> { 65 | if let Some(attr) = method.find_attr() { 66 | let args = FunctionArgs::from_ast(&method.sig)?; 67 | Ok(Some(Self { 68 | method, 69 | args, 70 | attributes: FunctionAttributes::from_method_attribute(attr)?, 71 | })) 72 | } else { 73 | Ok(None) 74 | } 75 | } 76 | 77 | pub fn method_name(&self) -> &Ident { 78 | &self.method.sig.ident 79 | } 80 | 81 | /// used for registering in the Napi 82 | pub fn property_name(&self) -> LitStr { 83 | if let Some(name) = self.attributes.name() { 84 | LitStr::new(name, Span::call_site()) 85 | } else { 86 | LitStr::new( 87 | &default_function_property_name(&self.method_name().to_string()), 88 | Span::call_site(), 89 | ) 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /nj-derive/src/ast/mod.rs: -------------------------------------------------------------------------------- 1 | mod attribute; 2 | mod arg; 3 | mod node; 4 | mod util; 5 | mod class; 6 | mod types; 7 | 8 | pub use attribute::*; 9 | pub use arg::*; 10 | pub use node::*; 11 | pub use util::*; 12 | pub use class::*; 13 | pub use types::*; 14 | -------------------------------------------------------------------------------- /nj-derive/src/ast/node.rs: -------------------------------------------------------------------------------- 1 | use syn::{Result, Token}; 2 | use syn::parse::{Parse, ParseStream}; 3 | use syn::ItemImpl; 4 | use syn::ItemFn; 5 | use syn::DeriveInput; 6 | 7 | #[derive(Debug)] 8 | pub enum NodeItem { 9 | Function(ItemFn), 10 | Impl(ItemImpl), 11 | Derive(DeriveInput), 12 | } 13 | 14 | impl Parse for NodeItem { 15 | fn parse(input: ParseStream) -> Result { 16 | let lookahead = input.lookahead1(); 17 | 18 | if lookahead.peek(Token!(impl)) { 19 | input.parse().map(NodeItem::Impl) 20 | } else if input.fork().parse::().is_ok() { 21 | input.parse().map(NodeItem::Derive) 22 | } else { 23 | input.parse().map(NodeItem::Function) 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /nj-derive/src/ast/types.rs: -------------------------------------------------------------------------------- 1 | use syn::Ident; 2 | use syn::TypePath; 3 | use syn::Type; 4 | use syn::TypeReference; 5 | use syn::Error; 6 | use syn::Result; 7 | use syn::spanned::Spanned; 8 | use syn::DeriveInput; 9 | use syn::Data; 10 | use syn::DataEnum; 11 | use syn::DataStruct; 12 | use syn::Variant; 13 | use syn::Fields; 14 | use syn::GenericParam; 15 | use syn::WhereClause; 16 | use quote::quote; 17 | use proc_macro2::TokenStream; 18 | 19 | use crate::ast::TypePathUtil; 20 | 21 | #[derive(Debug)] 22 | pub struct MyTypePath<'a>(&'a TypePath); 23 | 24 | impl<'a> MyTypePath<'a> { 25 | pub fn from(ty: &'a TypePath) -> Result { 26 | Ok(Self(ty)) 27 | } 28 | 29 | pub fn ident(&self) -> Option<&Ident> { 30 | self.0.name_identifier() 31 | } 32 | pub fn lifetime(&self) -> Option { 33 | let ty = self.0.lifetime()?; 34 | Some(quote! { 35 | #ty 36 | }) 37 | } 38 | 39 | pub fn expansion(&self) -> TokenStream { 40 | let ty = self.0; 41 | quote! { 42 | #ty 43 | } 44 | } 45 | } 46 | 47 | #[derive(Debug)] 48 | pub struct MyReferenceType<'a> { 49 | ident: &'a Ident, 50 | inner: &'a TypeReference, 51 | } 52 | 53 | impl<'a> MyReferenceType<'a> { 54 | pub fn from(ty: &'a TypeReference) -> Result { 55 | //println!("tye: {:#?}", ty); 56 | Ok(Self { 57 | ident: get_type_name(ty.elem.as_ref())?, 58 | inner: ty, 59 | }) 60 | } 61 | 62 | /// return any first one 63 | #[allow(unused)] 64 | pub fn type_name(&self) -> &Ident { 65 | self.ident 66 | } 67 | 68 | pub fn expansion(&self) -> TokenStream { 69 | let ty = self.inner; 70 | quote! { 71 | #ty 72 | } 73 | } 74 | } 75 | 76 | fn get_type_name(ty: &Type) -> Result<&Ident> { 77 | match ty { 78 | Type::Path(path) => { 79 | if let Some(name_id) = path.name_identifier() { 80 | Ok(name_id) 81 | } else { 82 | Err(Error::new( 83 | ty.span(), 84 | "no named type identifier found for type path", 85 | )) 86 | } 87 | } 88 | Type::Slice(slice) => get_type_name(&slice.elem), 89 | _ => Err(Error::new(ty.span(), "no other type reference found")), 90 | } 91 | } 92 | 93 | #[derive(Debug)] 94 | pub struct MyTupleType<'a> { 95 | types: Vec<&'a Type>, 96 | } 97 | 98 | impl<'a> From> for MyTupleType<'a> { 99 | fn from(types: Vec<&'a Type>) -> Self { 100 | Self { types } 101 | } 102 | } 103 | 104 | impl MyTupleType<'_> { 105 | pub fn expansion(&self) -> TokenStream { 106 | let types = &self.types; 107 | quote! { 108 | ( #( #types ),* ) 109 | } 110 | } 111 | } 112 | 113 | #[derive(Debug)] 114 | pub struct MyDeriveInput<'a> { 115 | pub name: &'a Ident, 116 | pub generics: MyGenerics<'a>, 117 | pub payload: MyDerivePayload<'a>, 118 | } 119 | 120 | #[derive(Debug)] 121 | pub enum MyDerivePayload<'a> { 122 | Struct(MyStruct<'a>), 123 | Enum(MyEnum<'a>), 124 | } 125 | 126 | impl<'a> MyDeriveInput<'a> { 127 | pub fn from_ast(input: &'a DeriveInput) -> Result { 128 | let name = &input.ident; 129 | let generic_params = input.generics.params.clone().into_iter().collect(); 130 | let generics = MyGenerics { 131 | params: generic_params, 132 | where_clause: &input.generics.where_clause, 133 | }; 134 | 135 | match &input.data { 136 | Data::Struct(inner_struct) => { 137 | let parsed_struct = MyStruct::from_ast(inner_struct)?; 138 | Ok(MyDeriveInput { 139 | name, 140 | generics, 141 | payload: MyDerivePayload::Struct(parsed_struct), 142 | }) 143 | } 144 | Data::Enum(inner_enum) => { 145 | let parsed_enum = MyEnum::from_ast(inner_enum)?; 146 | Ok(MyDeriveInput { 147 | name, 148 | generics, 149 | payload: MyDerivePayload::Enum(parsed_enum), 150 | }) 151 | } 152 | Data::Union(_) => Err(Error::new( 153 | input.span(), 154 | "Unions are not supported \ 155 | for automatic conversion to JavaScript representation", 156 | )), 157 | } 158 | } 159 | } 160 | 161 | #[derive(Debug)] 162 | pub enum MyFields<'a> { 163 | Named(Vec>), 164 | Unnamed(Vec>), 165 | Unit, 166 | } 167 | 168 | #[derive(Debug)] 169 | pub struct MyNamedField<'a> { 170 | pub name: &'a Ident, 171 | pub ty: MyFieldType<'a>, 172 | } 173 | 174 | #[derive(Debug)] 175 | pub struct MyUnnamedField<'a> { 176 | pub ty: MyFieldType<'a>, 177 | } 178 | 179 | #[derive(Debug)] 180 | pub enum MyFieldType<'a> { 181 | Path(MyTypePath<'a>), 182 | Ref(MyReferenceType<'a>), 183 | } 184 | 185 | impl<'a> MyFields<'a> { 186 | pub fn from_ast(input: &'a Fields) -> Result { 187 | match &input { 188 | Fields::Named(named_fields) => { 189 | let fields = named_fields 190 | .named 191 | .iter() 192 | .filter_map(|field| field.ident.as_ref().map(|ident| (ident, &field.ty))) 193 | .map(|(ident, ty)| { 194 | MyFieldType::from(ty).map(|ty| MyNamedField { name: ident, ty }) 195 | }) 196 | .collect::>>>()?; 197 | 198 | Ok(MyFields::Named(fields)) 199 | } 200 | Fields::Unnamed(unnamed_fields) => { 201 | let fields = unnamed_fields 202 | .unnamed 203 | .iter() 204 | .map(|field| MyFieldType::from(&field.ty)) 205 | .collect::>>>()? 206 | .into_iter() 207 | .map(|ty| MyUnnamedField { ty }) 208 | .collect::>>(); 209 | 210 | Ok(MyFields::Unnamed(fields)) 211 | } 212 | Fields::Unit => Ok(MyFields::Unit), 213 | } 214 | } 215 | } 216 | 217 | impl<'a> MyFieldType<'a> { 218 | pub fn from(ty: &'a Type) -> Result { 219 | match ty { 220 | Type::Path(type_path) => Ok(MyFieldType::Path(MyTypePath::from(type_path)?)), 221 | Type::Reference(reference) => Ok(MyFieldType::Ref(MyReferenceType::from(reference)?)), 222 | _ => Err(Error::new( 223 | ty.span(), 224 | "Only type paths and references \ 225 | are supported as field types", 226 | )), 227 | } 228 | } 229 | } 230 | 231 | #[derive(Debug)] 232 | pub struct MyEnum<'a> { 233 | pub variants: Vec>, 234 | } 235 | 236 | impl<'a> MyEnum<'a> { 237 | pub fn from_ast(enum_data: &'a DataEnum) -> Result { 238 | let variants = enum_data 239 | .variants 240 | .iter() 241 | .map(MyVariant::from_ast) 242 | .collect::>>()?; 243 | 244 | Ok(MyEnum { variants }) 245 | } 246 | } 247 | 248 | #[derive(Debug)] 249 | pub struct MyVariant<'a> { 250 | pub name: &'a Ident, 251 | pub fields: MyFields<'a>, 252 | } 253 | 254 | impl<'a> MyVariant<'a> { 255 | pub fn from_ast(variant_data: &'a Variant) -> Result { 256 | let fields = MyFields::from_ast(&variant_data.fields)?; 257 | 258 | Ok(MyVariant { 259 | name: &variant_data.ident, 260 | fields, 261 | }) 262 | } 263 | } 264 | 265 | #[derive(Debug)] 266 | pub struct MyStruct<'a> { 267 | pub fields: MyFields<'a>, 268 | } 269 | 270 | impl<'a> MyStruct<'a> { 271 | pub fn from_ast(struct_data: &'a DataStruct) -> Result { 272 | let fields = MyFields::from_ast(&struct_data.fields)?; 273 | 274 | Ok(MyStruct { fields }) 275 | } 276 | } 277 | 278 | #[derive(Debug)] 279 | pub struct MyGenerics<'a> { 280 | pub params: Vec, 281 | pub where_clause: &'a Option, 282 | } 283 | -------------------------------------------------------------------------------- /nj-derive/src/ast/util.rs: -------------------------------------------------------------------------------- 1 | use syn::ItemFn; 2 | use syn::Ident; 3 | use syn::TypePath; 4 | use syn::ImplItemMethod; 5 | use syn::Attribute; 6 | use syn::PathArguments; 7 | use syn::Lifetime; 8 | use syn::GenericArgument; 9 | 10 | /// traits for function item 11 | pub trait FunctionItem { 12 | fn name(&self) -> &Ident; 13 | } 14 | 15 | impl FunctionItem for ItemFn { 16 | fn name(&self) -> &Ident { 17 | &self.sig.ident 18 | } 19 | } 20 | 21 | pub trait TypePathUtil { 22 | fn name_identifier(&self) -> Option<&Ident>; 23 | fn lifetime(&self) -> Option<&Lifetime>; 24 | } 25 | 26 | impl TypePathUtil for TypePath { 27 | /// find name identifier 28 | fn name_identifier(&self) -> Option<&Ident> { 29 | self.path 30 | .segments 31 | .iter() 32 | .find(|_| true) 33 | .map(|segment| &segment.ident) 34 | } 35 | /// find lifetime name. 36 | fn lifetime(&self) -> Option<&Lifetime> { 37 | let first = self.path.segments.first()?; 38 | let lifetime_arg = if let PathArguments::AngleBracketed(arguments) = &first.arguments { 39 | arguments.args.first()? 40 | } else { 41 | return None; 42 | }; 43 | let lifetime = if let GenericArgument::Lifetime(lifetime) = lifetime_arg { 44 | lifetime 45 | } else { 46 | return None; 47 | }; 48 | Some(lifetime) 49 | } 50 | } 51 | 52 | pub trait MethodUtil { 53 | fn find_attr(&self) -> Option<&Attribute>; 54 | } 55 | 56 | impl MethodUtil for ImplItemMethod { 57 | /// find attr that contains node bindgen 58 | fn find_attr(&self) -> Option<&Attribute> { 59 | self.attrs.iter().find(|attr| { 60 | attr.path 61 | .segments 62 | .iter() 63 | .any(|seg| seg.ident == "ohos_node_bindgen") 64 | }) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /nj-derive/src/generator/class/arg.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use quote::quote; 3 | 4 | use crate::ast::FunctionArgType; 5 | use crate::ast::Method; 6 | use crate::ast::Class; 7 | use crate::ast::FunctionArgs; 8 | use crate::util::arg_ident; 9 | use crate::util::ident; 10 | 11 | pub fn generate_class_arg(method: Option<&Method>, class: &Class) -> TokenStream { 12 | if let Some(method) = method { 13 | let class_name = class.my_type().ident().unwrap(); // class should have identifier 14 | let args = generate_args(&method.args); 15 | let struct_args = generate_structure_args(&method.args); 16 | 17 | let constr_conversion = as_constructor_try_to_js(&method.args); 18 | let invocation = as_constructor_invocation(&method.args); 19 | let construct_name = ident(&format!("{class_name}Constructor")); 20 | quote! { 21 | 22 | pub struct #construct_name { 23 | #args 24 | } 25 | 26 | impl #construct_name { 27 | pub fn new(#args) -> Self { 28 | Self { 29 | #struct_args 30 | } 31 | } 32 | } 33 | 34 | impl ohos_node_bindgen::core::TryIntoJs for #construct_name { 35 | 36 | fn try_to_js(self, js_env: &ohos_node_bindgen::core::val::JsEnv) -> Result { 37 | 38 | #constr_conversion 39 | let new_instance = #class_name::new_instance(js_env,vec![#invocation])?; 40 | Ok(new_instance) 41 | } 42 | } 43 | } 44 | } else { 45 | quote! {} 46 | } 47 | } 48 | 49 | /// generate arg with type, for used in defining structures 50 | fn generate_args(args: &FunctionArgs) -> TokenStream { 51 | let args = args 52 | .args 53 | .iter() 54 | // .filter(|arg| !arg.is_callback()) 55 | .enumerate() 56 | .map(|(i, arg)| { 57 | let name = arg_ident(i); 58 | match &arg.typ { 59 | FunctionArgType::Path(ty) => { 60 | let inner = ty.expansion(); 61 | quote! { 62 | #name: #inner 63 | } 64 | } 65 | FunctionArgType::Tuple(ty) => { 66 | let inner = ty.expansion(); 67 | quote! { 68 | #name: #inner 69 | } 70 | } 71 | FunctionArgType::Closure(_) => { 72 | quote! { compile_error!("closure can't be used in constructor ")} 73 | } 74 | // FunctionArgType::JSCallback(_) => quote! { compile_error!("JsCallback can't be used in constructor")}, 75 | FunctionArgType::Ref(_) => { 76 | quote! { compile_error!("ref can't be used in constructor")} 77 | } 78 | } 79 | }); 80 | 81 | quote! { 82 | #(#args),* 83 | } 84 | } 85 | 86 | fn generate_structure_args(args: &FunctionArgs) -> TokenStream { 87 | let args = args 88 | .args 89 | .iter() 90 | // .filter(|arg| !arg.is_callback()) 91 | .enumerate() 92 | .map(|(i, _arg)| { 93 | let name = arg_ident(i); 94 | quote! { 95 | #name 96 | } 97 | }); 98 | 99 | quote! { 100 | #(#args),* 101 | } 102 | } 103 | 104 | /// generate expression to convert constructor into new instance 105 | fn as_constructor_try_to_js(args: &FunctionArgs) -> TokenStream { 106 | let arg_len = args.len(); 107 | let args = (0..arg_len).map(|i| { 108 | let arg_name = arg_ident(i); 109 | quote! { 110 | let #arg_name = self.#arg_name.try_to_js(js_env)?; 111 | } 112 | }); 113 | 114 | quote! { 115 | 116 | #(#args)* 117 | 118 | } 119 | } 120 | 121 | fn as_constructor_invocation(args: &FunctionArgs) -> TokenStream { 122 | let arg_len = args.len(); 123 | let args = (0..arg_len).map(|i| { 124 | let arg_name = arg_ident(i); 125 | quote! { 126 | #arg_name 127 | } 128 | }); 129 | 130 | quote! { 131 | 132 | #(#args),* 133 | 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /nj-derive/src/generator/class/constructor.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use quote::quote; 3 | 4 | use crate::generator::FnGeneratorCtx; 5 | use crate::ast::Method; 6 | 7 | /// generate class constructor 8 | pub fn class_constructor(method: Option<&Method>) -> TokenStream { 9 | use crate::generator::as_arg_token; 10 | use crate::generator::rust_args_input; 11 | 12 | let expansion = if let Some(method) = method { 13 | let method_ident = &method.method_name(); 14 | 15 | let ctx = FnGeneratorCtx::new(&method.method.sig, &method.args, &method.attributes); 16 | let arg_tokens = as_arg_token(&ctx); 17 | 18 | let mut cb_args = vec![]; 19 | let rust_inputs = rust_args_input(&ctx, &mut cb_args); 20 | 21 | quote! { 22 | 23 | #arg_tokens 24 | 25 | let rust_value = Self::#method_ident( #(#rust_inputs),* ); 26 | Ok((rust_value,js_cb)) 27 | 28 | } 29 | } else { 30 | quote! { 31 | let js_cb = js_env.get_cb_info(cb_info,0)?; 32 | Ok((Self::new(),js_cb)) 33 | } 34 | }; 35 | 36 | quote! { 37 | fn create_from_js( 38 | js_env: &ohos_node_bindgen::core::val::JsEnv, 39 | cb_info: ohos_node_bindgen::sys::napi_callback_info) -> 40 | Result<(Self, ohos_node_bindgen::core::val::JsCallback), ohos_node_bindgen::core::NjError> { 41 | 42 | #expansion 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /nj-derive/src/generator/class/mod.rs: -------------------------------------------------------------------------------- 1 | mod constructor; 2 | mod arg; 3 | 4 | use quote::quote; 5 | use proc_macro2::TokenStream; 6 | use syn::ItemImpl; 7 | 8 | use crate::ast::Class; 9 | use crate::util::ident; 10 | use crate::util::lit_str; 11 | 12 | pub fn generate_class(impl_item: ItemImpl) -> TokenStream { 13 | match Class::from_ast(&impl_item) { 14 | Err(err) => err.to_compile_error(), 15 | Ok(class) => { 16 | let class_helper = generate_class_helper(class); 17 | 18 | quote! { 19 | 20 | #impl_item 21 | 22 | #class_helper 23 | 24 | } 25 | } 26 | } 27 | } 28 | 29 | // generate internal module that contains Js class helper 30 | fn generate_class_helper(class: Class) -> TokenStream { 31 | use constructor::class_constructor; 32 | use arg::generate_class_arg; 33 | 34 | let constructor_method = class.constructor(); 35 | let type_name = class.self_ty.ident().unwrap(); 36 | let lifetime = class.self_ty.lifetime(); 37 | let impl_for_block = if let Some(lifetime) = lifetime { 38 | quote! { 39 | #type_name<#lifetime> 40 | } 41 | } else { 42 | quote! { 43 | #type_name 44 | } 45 | }; 46 | 47 | let helper_module_name = ident(&format!("{type_name}_helper").to_lowercase()); 48 | 49 | let class_type_lit = lit_str(&format!("{type_name}")); 50 | let properties = generate_properties(&class); 51 | let constructor_exp = class_constructor(constructor_method); 52 | let class_arg_exp = generate_class_arg(constructor_method, &class); 53 | let construct_name = ident(&format!("{type_name}Constructor")); 54 | 55 | quote! { 56 | 57 | use #helper_module_name::#construct_name; 58 | 59 | mod #helper_module_name { 60 | 61 | use std::ptr; 62 | use ohos_node_bindgen::core::JSClass; 63 | 64 | use super::#type_name; 65 | 66 | static mut CLASS_CONSTRUCTOR: ohos_node_bindgen::sys::napi_ref = ptr::null_mut(); 67 | 68 | impl ohos_node_bindgen::core::JSClass for #impl_for_block { 69 | const CLASS_NAME: &'static str = #class_type_lit; 70 | 71 | fn set_constructor(constructor: ohos_node_bindgen::sys::napi_ref) { 72 | ohos_node_bindgen::core::log::trace!("set constructor"); 73 | unsafe { 74 | CLASS_CONSTRUCTOR = constructor; 75 | } 76 | } 77 | 78 | fn get_constructor() -> ohos_node_bindgen::sys::napi_ref { 79 | unsafe { CLASS_CONSTRUCTOR } 80 | } 81 | 82 | fn properties() -> ohos_node_bindgen::core::PropertiesBuilder { 83 | 84 | vec![ 85 | #(#properties),* 86 | ].into() 87 | } 88 | 89 | #constructor_exp 90 | 91 | } 92 | 93 | #class_arg_exp 94 | 95 | use ohos_node_bindgen::core::submit_register_callback; 96 | 97 | 98 | #[ohos_node_bindgen::core::ctor] 99 | fn register_class() { 100 | ohos_node_bindgen::core::log::debug!(class = stringify!(#type_name),"registering class"); 101 | submit_register_callback(#type_name::js_init); 102 | } 103 | } 104 | } 105 | } 106 | 107 | /// find methods which are defined in ohos_node_bindgen annotation 108 | fn generate_properties(class: &Class) -> Vec { 109 | class 110 | .methods 111 | .iter() 112 | .filter_map(|method| { 113 | if method.attributes.is_constructor() { 114 | None 115 | } else { 116 | let method_ident = &method.method_name(); 117 | 118 | let property_name = method.property_name(); 119 | let napi_name = ident(&format!("napi_{method_ident}")); 120 | 121 | Some(if method.attributes.is_getter() { 122 | quote! { 123 | ohos_node_bindgen::core::Property::new(#property_name).getter(Self::#napi_name) 124 | } 125 | } else if method.attributes.is_setter() { 126 | quote! { 127 | ohos_node_bindgen::core::Property::new(#property_name).setter(Self::#napi_name) 128 | } 129 | } else { 130 | quote! { 131 | ohos_node_bindgen::core::Property::new(#property_name).method(Self::#napi_name) 132 | } 133 | }) 134 | } 135 | }) 136 | .collect() 137 | } 138 | -------------------------------------------------------------------------------- /nj-derive/src/generator/context.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::Span; 2 | use syn::Ident; 3 | use syn::FnArg; 4 | use syn::Signature; 5 | use syn::Receiver; 6 | use syn::LitStr; 7 | use quote::quote; 8 | use proc_macro2::TokenStream; 9 | 10 | use crate::ast::FunctionArgs; 11 | use crate::ast::FunctionAttributes; 12 | use crate::util::ident; 13 | 14 | /// Context for code function code generation 15 | pub struct FnGeneratorCtx<'a> { 16 | pub args: &'a FunctionArgs<'a>, 17 | pub attributes: &'a FunctionAttributes, 18 | pub sig: &'a Signature, 19 | receiver: Option<&'a Receiver>, 20 | } 21 | 22 | impl<'a> FnGeneratorCtx<'a> { 23 | // TODO: is_multi_threaded should be check and return self 24 | pub fn new( 25 | sig: &'a Signature, 26 | args: &'a FunctionArgs<'a>, 27 | attributes: &'a FunctionAttributes, 28 | ) -> Self { 29 | Self { 30 | sig, 31 | args, 32 | attributes, 33 | receiver: find_receiver(sig), 34 | } 35 | } 36 | 37 | /// function name identifier 38 | pub fn fn_name(&self) -> &Ident { 39 | &self.sig.ident 40 | } 41 | 42 | pub fn is_method(&self) -> bool { 43 | self.receiver.is_some() 44 | } 45 | 46 | pub fn is_async(&self) -> bool { 47 | self.sig.asyncness.is_some() 48 | } 49 | 50 | pub fn napi_fn_id(&self) -> Ident { 51 | ident(&format!("napi_{}", self.fn_name())) 52 | } 53 | 54 | pub fn attributes(&self) -> &FunctionAttributes { 55 | self.attributes 56 | } 57 | 58 | /// used for registering in the Napi 59 | pub fn property_name(&self) -> LitStr { 60 | use crate::util::default_function_property_name; 61 | 62 | if let Some(name) = self.attributes().name() { 63 | LitStr::new(name, Span::call_site()) 64 | } else { 65 | LitStr::new( 66 | &default_function_property_name(&self.fn_name().to_string()), 67 | Span::call_site(), 68 | ) 69 | } 70 | } 71 | 72 | // callback type name 73 | pub fn callback_type_name(&self) -> TokenStream { 74 | let callback_type = if self.attributes.is_multi_threaded() { 75 | "JsMultiThreadedCallbackFunction" 76 | } else { 77 | "JsCallbackFunction" 78 | }; 79 | 80 | let ident = Ident::new(callback_type, Span::call_site()); 81 | quote! { #ident } 82 | } 83 | } 84 | 85 | fn find_receiver(sig: &Signature) -> Option<&Receiver> { 86 | sig.inputs.iter().find_map(|arg| match arg { 87 | FnArg::Receiver(rec) => Some(rec), 88 | _ => None, 89 | }) 90 | } 91 | -------------------------------------------------------------------------------- /nj-derive/src/generator/mod.rs: -------------------------------------------------------------------------------- 1 | mod function; 2 | mod property; 3 | mod context; 4 | mod napi; 5 | mod class; 6 | mod derive; 7 | 8 | use property::*; 9 | use context::*; 10 | use napi::*; 11 | use function::*; 12 | 13 | pub use function::generate_function; 14 | pub use class::*; 15 | pub use derive::generate_datatype; 16 | -------------------------------------------------------------------------------- /nj-derive/src/generator/napi.rs: -------------------------------------------------------------------------------- 1 | use quote::quote; 2 | use proc_macro2::TokenStream; 3 | use syn::Ident; 4 | use syn::ItemFn; 5 | 6 | use crate::util::ident; 7 | use super::FnGeneratorCtx; 8 | use super::generate_rust_invocation; 9 | 10 | /// generate native code to be invoked by napi 11 | pub fn generate_napi_code(ctx: &FnGeneratorCtx, input_fn: &ItemFn) -> TokenStream { 12 | let mut cb_args = vec![]; 13 | let rust_invocation = generate_rust_invocation(ctx, &mut cb_args); 14 | let ident_n_api_fn = ident(&format!("napi_{}", ctx.fn_name())); 15 | 16 | if ctx.is_method() { 17 | // if function is method, we can't put rust function inside our napi because we need to preserver self 18 | // in the rust method. 19 | let napi_fn = 20 | raw_napi_function_template(ident_n_api_fn, quote! {}, cb_args, rust_invocation); 21 | 22 | quote! { 23 | #input_fn 24 | 25 | #napi_fn 26 | } 27 | } else { 28 | // otherwise we can put rust function inside to make it tidy 29 | raw_napi_function_template( 30 | ident_n_api_fn, 31 | quote! { #input_fn }, 32 | cb_args, 33 | rust_invocation, 34 | ) 35 | } 36 | } 37 | 38 | /// generate napi function invocation whether it is method or just free standing function 39 | fn raw_napi_function_template( 40 | ident_n_api_fn: Ident, 41 | input_fn: TokenStream, 42 | rust_args_struct: Vec, 43 | rust_invocation: TokenStream, 44 | ) -> TokenStream { 45 | quote! { 46 | 47 | extern "C" fn #ident_n_api_fn(env: ohos_node_bindgen::sys::napi_env,cb_info: ohos_node_bindgen::sys::napi_callback_info) -> ohos_node_bindgen::sys::napi_value 48 | { 49 | use ohos_node_bindgen::core::TryIntoJs; 50 | use ohos_node_bindgen::core::IntoJs; 51 | use ohos_node_bindgen::core::val::JsCallbackFunction; 52 | 53 | ohos_node_bindgen::core::log::debug!( napi_fn = stringify!(#ident_n_api_fn),"invoking napi function"); 54 | 55 | #input_fn 56 | 57 | #(#rust_args_struct)* 58 | 59 | let js_env = ohos_node_bindgen::core::val::JsEnv::new(env); 60 | 61 | #rust_invocation 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /nj-derive/src/generator/property.rs: -------------------------------------------------------------------------------- 1 | use quote::quote; 2 | use proc_macro2::TokenStream; 3 | 4 | use crate::util::ident; 5 | use super::FnGeneratorCtx; 6 | 7 | /// generate code to register this function property to global property 8 | pub fn generate_property_code(ctx: &FnGeneratorCtx) -> TokenStream { 9 | if ctx.is_method() { 10 | return quote! {}; 11 | } 12 | 13 | let ident_n_api_fn = ctx.napi_fn_id(); 14 | let ident_register_fn = ident(&format!("register_{ident_n_api_fn}")); 15 | let property_name_literal = ctx.property_name(); 16 | 17 | quote! { 18 | #[ohos_node_bindgen::core::ctor] 19 | fn #ident_register_fn() { 20 | 21 | let property = ohos_node_bindgen::core::Property::new(#property_name_literal).method(#ident_n_api_fn); 22 | ohos_node_bindgen::core::submit_property(property); 23 | } 24 | 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /nj-derive/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::never_loop)] 2 | extern crate proc_macro; 3 | 4 | mod util; 5 | mod ast; 6 | mod generator; 7 | 8 | use proc_macro::TokenStream; 9 | 10 | /// This turns regular rust function into N-API compatible native module 11 | /// 12 | /// For example; given rust following here 13 | /// ```ignore 14 | /// fn sum(first: i32, second: i32) -> i32 { 15 | /// return first+second 16 | /// } 17 | /// ``` 18 | /// 19 | /// into N-API module 20 | /// ```ignore 21 | /// #[no_mangle] 22 | /// pub extern "C" fn n_sum(env: napi_env, cb_info: napi_callback_info) -> napi_value { 23 | /// fn sum(first: i32, second: i32) -> i32 { 24 | /// return first+second 25 | /// } 26 | /// let js_env = JsEnv::new(env); 27 | /// let js_cb = result_to_napi!(js_env.get_cb_info(cb_info, 2),&js_env); 28 | /// let first = result_to_napi!(js_cb.get_value::(0),&js_env); 29 | /// let second = result_to_napi!(js_cb.get_value::(0),&js_env); 30 | /// sum(msg).into_js(&js_env) 31 | /// } 32 | /// ``` 33 | #[proc_macro_attribute] 34 | pub fn ohos_node_bindgen(args: TokenStream, item: TokenStream) -> TokenStream { 35 | use syn::AttributeArgs; 36 | 37 | use ast::FunctionAttributes; 38 | use ast::NodeItem; 39 | use generator::generate_function; 40 | use generator::generate_class; 41 | use generator::generate_datatype; 42 | 43 | let attribute_args = syn::parse_macro_input!(args as AttributeArgs); 44 | 45 | let attribute: FunctionAttributes = match FunctionAttributes::from_ast(attribute_args) { 46 | Ok(attr) => attr, 47 | Err(err) => return err.to_compile_error().into(), 48 | }; 49 | 50 | let parsed_item = syn::parse_macro_input!(item as NodeItem); 51 | 52 | let out_express = match parsed_item { 53 | NodeItem::Function(fn_item) => generate_function(fn_item, attribute), 54 | NodeItem::Impl(impl_item) => generate_class(impl_item), 55 | NodeItem::Derive(struct_item) => generate_datatype(struct_item), 56 | }; 57 | 58 | // used for debugging, if error occurs println do not work so should uncomment express 59 | // println!("{}", out_express); 60 | // let out_express = quote::quote! {}; 61 | 62 | out_express.into() 63 | } 64 | -------------------------------------------------------------------------------- /nj-derive/src/util.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::Span; 2 | use syn::LitStr; 3 | 4 | /// generate default property name for function which uses camel case 5 | pub fn default_function_property_name(fn_name: &str) -> String { 6 | use inflector::Inflector; 7 | 8 | fn_name.to_camel_case() 9 | } 10 | 11 | pub fn arg_ident(index: usize) -> syn::Ident { 12 | ident(&format!("arg{index}")) 13 | } 14 | 15 | pub fn ident(ident: &str) -> syn::Ident { 16 | syn::Ident::new(ident, Span::call_site()) 17 | } 18 | 19 | pub fn lit_str(ident: &str) -> LitStr { 20 | LitStr::new(ident, Span::call_site()) 21 | } 22 | -------------------------------------------------------------------------------- /nj-derive/tests/parse.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn derive_ui() { 3 | let t = trybuild::TestCases::check_only(); 4 | 5 | t.pass("ui-tests/pass_*.rs"); 6 | t.compile_fail("ui-tests/fail_*.rs"); 7 | } 8 | -------------------------------------------------------------------------------- /nj-derive/ui-tests/fail_attribute_name.rs: -------------------------------------------------------------------------------- 1 | use ohos_node_bindgen::derive::node_bindgen; 2 | 3 | /// name2 is not valid attribute 4 | #[node_bindgen(name2="hello")] 5 | fn example1(count: i32) -> String { 6 | format!("hello world {}", count) 7 | } 8 | 9 | fn main() { 10 | 11 | } -------------------------------------------------------------------------------- /nj-derive/ui-tests/fail_attribute_number.rs: -------------------------------------------------------------------------------- 1 | use ohos_node_bindgen::derive::node_bindgen; 2 | 3 | /// name must be string 4 | #[node_bindgen(name=20)] 5 | fn example2(count: i32) -> i32 { 6 | count 7 | } 8 | 9 | 10 | 11 | fn main() { 12 | 13 | } -------------------------------------------------------------------------------- /nj-derive/ui-tests/fail_attriubte_gibberish.rs: -------------------------------------------------------------------------------- 1 | use ohos_node_bindgen::derive::node_bindgen; 2 | 3 | #[node_bindgen(gibberish)] 4 | fn example3(count: i32) -> i32 { 5 | count 6 | } 7 | 8 | fn main() { 9 | 10 | } -------------------------------------------------------------------------------- /nj-derive/ui-tests/fail_class_attr_name.rs: -------------------------------------------------------------------------------- 1 | use ohos_node_bindgen::derive::node_bindgen; 2 | 3 | 4 | 5 | struct MyObject { 6 | val: f64, 7 | } 8 | 9 | 10 | #[ohos_node_bindgen] 11 | impl MyObject { 12 | 13 | #[node_bindgen(name2="hello")] 14 | fn new(val: f64) -> Self { 15 | Self { val } 16 | } 17 | 18 | } 19 | 20 | 21 | fn main() { 22 | 23 | } -------------------------------------------------------------------------------- /nj-derive/ui-tests/fail_class_attr_number.rs: -------------------------------------------------------------------------------- 1 | use ohos_node_bindgen::derive::node_bindgen; 2 | 3 | 4 | 5 | struct MyObject { 6 | val: f64, 7 | } 8 | 9 | 10 | #[ohos_node_bindgen] 11 | impl MyObject { 12 | 13 | #[node_bindgen(name=20)] 14 | fn new(val: f64) -> Self { 15 | Self { val } 16 | } 17 | 18 | } 19 | 20 | 21 | fn main() { 22 | 23 | } -------------------------------------------------------------------------------- /nj-derive/ui-tests/fail_class_gibberish.rs: -------------------------------------------------------------------------------- 1 | use ohos_node_bindgen::derive::node_bindgen; 2 | 3 | 4 | 5 | struct MyObject { 6 | val: f64, 7 | } 8 | 9 | 10 | #[ohos_node_bindgen] 11 | impl MyObject { 12 | 13 | #[node_bindgen(xyz)] 14 | fn new(val: f64) -> Self { 15 | Self { val } 16 | } 17 | 18 | } 19 | 20 | 21 | fn main() { 22 | 23 | } -------------------------------------------------------------------------------- /nj-derive/ui-tests/fail_non_method.rs: -------------------------------------------------------------------------------- 1 | use ohos_node_bindgen::derive::node_bindgen; 2 | 3 | 4 | #[node_bindgen(constructor)] 5 | fn example2(count: i32) -> i32 { 6 | count 7 | } 8 | 9 | #[node_bindgen(getter)] 10 | fn example3(count: i32) -> i32 { 11 | count 12 | } 13 | 14 | #[node_bindgen(setter)] 15 | fn example4(count: i32) -> i32 { 16 | count 17 | } 18 | 19 | fn main() { 20 | 21 | } -------------------------------------------------------------------------------- /nj-derive/ui-tests/fail_union.rs: -------------------------------------------------------------------------------- 1 | use ohos_node_bindgen::derive::node_bindgen; 2 | 3 | #[ohos_node_bindgen] 4 | union TestUnion { 5 | pub field1: u32, 6 | pub field2: f32 7 | } -------------------------------------------------------------------------------- /nj-derive/ui-tests/fail_unit_struct.rs: -------------------------------------------------------------------------------- 1 | use ohos_node_bindgen::derive::node_bindgen; 2 | 3 | #[ohos_node_bindgen] 4 | struct UnitStruct; -------------------------------------------------------------------------------- /nj-derive/ui-tests/pass_async.rs: -------------------------------------------------------------------------------- 1 | use ohos_node_bindgen::derive::node_bindgen; 2 | 3 | 4 | /// async callback 5 | #[ohos_node_bindgen] 6 | async fn example5( seconds: i32, cb: F) { 7 | } 8 | 9 | 10 | #[ohos_node_bindgen] 11 | async fn example6(arg: f64) -> f64 { 12 | 0.0 13 | } 14 | 15 | 16 | 17 | fn main() { 18 | } -------------------------------------------------------------------------------- /nj-derive/ui-tests/pass_callback.rs: -------------------------------------------------------------------------------- 1 | use ohos_node_bindgen::derive::node_bindgen; 2 | 3 | 4 | #[ohos_node_bindgen] 5 | fn example(cb: F,second: i32) { 6 | cb(second); 7 | } 8 | 9 | fn example2(first: i32,cb: F) { 10 | cb(format!("hello world: {}",first),first as i64); 11 | } 12 | 13 | 14 | 15 | fn main() { 16 | } -------------------------------------------------------------------------------- /nj-derive/ui-tests/pass_class_async.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | use std::io::Error as IoError; 3 | 4 | use fluvio_future::timer::sleep; 5 | 6 | use ohos_node_bindgen::derive::node_bindgen; 7 | 8 | 9 | struct MyObject { 10 | val: f64, 11 | } 12 | 13 | 14 | #[ohos_node_bindgen] 15 | impl MyObject { 16 | 17 | 18 | #[node_bindgen(constructor)] 19 | fn new(val: f64) -> Self { 20 | Self { val } 21 | } 22 | 23 | /// loop and emit event 24 | #[ohos_node_bindgen] 25 | async fn sleep(&self,cb: F) { 26 | 27 | println!("sleeping"); 28 | sleep(Duration::from_secs(1)).await; 29 | let msg = format!("hello world"); 30 | cb(msg); 31 | 32 | } 33 | 34 | 35 | } 36 | 37 | fn main() { 38 | 39 | } 40 | -------------------------------------------------------------------------------- /nj-derive/ui-tests/pass_class_lifetimes.rs: -------------------------------------------------------------------------------- 1 | 2 | use ohos_node_bindgen::derive::node_bindgen; 3 | 4 | struct Inner; 5 | 6 | struct NamedScopeObject<'a>{ 7 | val: &'a Option, 8 | } 9 | 10 | #[ohos_node_bindgen] 11 | impl NamedScopeObject<'_> { 12 | #[node_bindgen(constructor)] 13 | fn new() -> Self { 14 | Self { val: &None } 15 | } 16 | 17 | } 18 | 19 | fn main() { 20 | 21 | } 22 | -------------------------------------------------------------------------------- /nj-derive/ui-tests/pass_class_simple.rs: -------------------------------------------------------------------------------- 1 | use ohos_node_bindgen::derive::node_bindgen; 2 | 3 | 4 | struct MyObject { 5 | val: f64, 6 | } 7 | 8 | 9 | #[ohos_node_bindgen] 10 | impl MyObject { 11 | 12 | 13 | #[node_bindgen(constructor)] 14 | fn new(val: f64) -> Self { 15 | Self { val } 16 | } 17 | 18 | #[ohos_node_bindgen] 19 | fn twice(&self) -> f64 { 20 | self.val * 2.0 21 | } 22 | 23 | 24 | #[node_bindgen(getter)] 25 | fn value(&self) -> f64 { 26 | self.val 27 | } 28 | 29 | 30 | #[node_bindgen(name = "value2",getter)] 31 | fn set_value(&mut self, val: f64) { 32 | self.val = val; 33 | } 34 | 35 | } 36 | 37 | fn main() { 38 | 39 | } -------------------------------------------------------------------------------- /nj-derive/ui-tests/pass_enum.rs: -------------------------------------------------------------------------------- 1 | use ohos_node_bindgen::derive::node_bindgen; 2 | use ohos_node_bindgen::core::TryIntoJs; 3 | 4 | #[ohos_node_bindgen] 5 | enum TestEnum { 6 | Something(usize), 7 | Else { 8 | val: String 9 | }, 10 | UnitVariant 11 | } 12 | 13 | #[ohos_node_bindgen] 14 | enum Generic { 15 | Container(T) 16 | } 17 | 18 | fn main() { 19 | } -------------------------------------------------------------------------------- /nj-derive/ui-tests/pass_function.rs: -------------------------------------------------------------------------------- 1 | use ohos_node_bindgen::derive::node_bindgen; 2 | 3 | 4 | /// no argument and no result 5 | #[ohos_node_bindgen] 6 | fn example1() { 7 | } 8 | 9 | 10 | /// single argument with result 11 | #[ohos_node_bindgen] 12 | fn example2(arg1: i32) -> i32 { 13 | arg1 14 | } 15 | 16 | /// multiple arguments 17 | #[ohos_node_bindgen] 18 | fn example3(_arg1: bool,_arg2: i32,_arg3: String) -> i32 { 19 | 4 20 | } 21 | 22 | 23 | #[node_bindgen(name="hello2")] 24 | fn example4(count: i32) -> i32 { 25 | count 26 | } 27 | 28 | 29 | 30 | fn main() { 31 | 32 | } -------------------------------------------------------------------------------- /nj-derive/ui-tests/pass_struct.rs: -------------------------------------------------------------------------------- 1 | use ohos_node_bindgen::derive::node_bindgen; 2 | 3 | #[ohos_node_bindgen] 4 | struct Something { 5 | pub field: usize 6 | } 7 | 8 | #[ohos_node_bindgen] 9 | pub(crate) struct WithVisibility { 10 | pub field: usize 11 | } 12 | 13 | #[ohos_node_bindgen] 14 | struct Lifetime<'a> { 15 | pub field: &'a usize 16 | } 17 | 18 | #[ohos_node_bindgen] 19 | struct BoundGeneric 20 | where T: Sync + std::fmt::Debug + ohos_node_bindgen::core::TryIntoJs 21 | { 22 | pub field: T 23 | } 24 | 25 | #[ohos_node_bindgen] 26 | struct BoundAndLifetimes<'a, T: Sync + std::fmt::Debug + ohos_node_bindgen::core::TryIntoJs + Clone> { 27 | pub field: &'a T 28 | } 29 | 30 | #[ohos_node_bindgen] 31 | struct Simple { 32 | pub a_string: String, 33 | pub a_number: i64, 34 | pub a_float : f64 35 | } 36 | 37 | #[ohos_node_bindgen] 38 | struct Unnamed(String, f64); 39 | 40 | fn main() { 41 | } -------------------------------------------------------------------------------- /notes.md: -------------------------------------------------------------------------------- 1 | * [ ] napi_create_arraybuffer - NOT HERE 2 | * [ ] napi_create_buffer 3 | * [ ] napi_create_buffer_copy 4 | * [ ] napi_create_date 5 | * [ ] napi_create_external 6 | * [ ] napi_create_external_arraybuffer 7 | * [ ] napi_create_external_buffer 8 | * [ ] napi_create_symbol 9 | * [ ] napi_create_typedarray 10 | * [ ] napi_create_dataview 11 | * [ ] napi_create_uint32 12 | * [ ] napi_create_bigint_int64 13 | * [ ] napi_create_bigint_uint64 14 | * [ ] napi_create_bigint_words 15 | * [ ] napi_create_string_latin1 16 | * [ ] napi_get_array_length 17 | * [ ] napi_get_arraybuffer_info 18 | * [ ] napi_get_buffer_info 19 | * [ ] napi_get_prototype 20 | * [ ] napi_get_typedarray_info 21 | * [ ] napi_get_dataview_info 22 | * [ ] napi_get_value_bool 23 | * [ ] napi_get_value_double 24 | * [ ] napi_get_value_bigint_int64 25 | * [ ] napi_get_value_bigint_uint64 26 | * [ ] napi_get_value_bigint_words 27 | * [ ] napi_get_value_external 28 | * [ ] napi_get_value_int32 29 | * [ ] napi_get_value_int64 30 | * [ ] napi_get_value_string_utf8 31 | * [ ] napi_get_value_uint32 32 | * [ ] napi_get_null 33 | * [ ] napi_coerce_to_bool 34 | * [ ] napi_coerce_to_number 35 | * [ ] napi_coerce_to_string 36 | * [ ] napi_instanceof 37 | * [ ] napi_is_dataview 38 | * [ ] napi_strict_equals 39 | * [ ] napi_detach_arraybuffer 40 | * [ ] napi_is_detached_arraybuffer 41 | * [ ] napi_get_property_names 42 | * [ ] napi_get_all_property_names 43 | * [ ] napi_delete_property 44 | * [ ] napi_set_named_property 45 | * [ ] napi_get_named_property 46 | * [ ] napi_has_named_property 47 | * [ ] napi_has_element 48 | * [ ] napi_delete_element 49 | * [ ] napi_create_function 50 | * [ ] napi_get_version 51 | * [ ] napi_get_uv_event_loop 52 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | reorder_modules = false 2 | reorder_imports = false -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "node")] 2 | pub mod core { 3 | pub use nj_core::*; 4 | } 5 | 6 | #[cfg(feature = "node")] 7 | pub mod sys { 8 | pub use nj_sys::*; 9 | } 10 | 11 | #[cfg(feature = "node")] 12 | pub mod init { 13 | pub use nj_core::init::*; 14 | } 15 | 16 | #[cfg(feature = "node")] 17 | pub mod derive { 18 | pub use nj_derive::*; 19 | } 20 | 21 | #[cfg(feature = "build")] 22 | pub mod build { 23 | pub use nj_build::*; 24 | } 25 | --------------------------------------------------------------------------------