├── .gitignore ├── .travis.yml ├── Cargo.toml ├── LICENSE.md ├── Readme.md ├── examples └── hello-world │ ├── .gitignore │ ├── Cargo.toml │ ├── index.js │ ├── package.json │ └── src │ └── lib.rs ├── node-api-sys ├── .gitignore ├── Cargo.toml └── src │ └── lib.rs ├── src ├── error.rs ├── futures.rs ├── lib.rs ├── napi.rs ├── napi_futures.rs └── napi_value.rs └── tests ├── .gitignore ├── Cargo.toml ├── index.js ├── package-lock.json ├── package.json ├── src └── lib.rs └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | **/*.rs.bk 3 | Cargo.lock 4 | package-lock.json 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | sudo: required 3 | dist: trusty 4 | 5 | matrix: 6 | include: 7 | - os: linux 8 | rust: nightly 9 | node_js: head 10 | - os: osx 11 | osx_image: xcode8 12 | rust: nightly 13 | node_js: head 14 | 15 | install: | 16 | if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then 17 | git clone https://github.com/nodejs/node.git 18 | pushd node 19 | ./configure 20 | make -j 4 21 | sudo make install 22 | popd 23 | elif [[ "$TRAVIS_OS_NAME" == "osx" ]]; then 24 | brew reinstall -v --HEAD nodejs 25 | fi 26 | 27 | 28 | script: 29 | - nvm use system 30 | - cd tests 31 | - npm test 32 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "node-api" 3 | version = "0.1.0" 4 | authors = ["Jupp Müller "] 5 | 6 | [lib] 7 | crate-type = ["lib"] 8 | 9 | [dependencies] 10 | node-api-sys = {path = "./node-api-sys"} 11 | futures = "0.1" 12 | 13 | [workspace] 14 | members = [ 15 | "examples/hello-world", 16 | "node-api-sys", 17 | "tests", 18 | ] -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Jupp Müller 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Node-API [![Build Status](https://travis-ci.org/jupp0r/node-api.svg?branch=master)](https://travis-ci.org/jupp0r/node-api) 2 | 3 | Using this crate, you'll be easily able to write native node modules in Rust! 4 | 5 | ## Design 6 | 7 | I opted for using the 8 | new 9 | [stable, vm-agnostic node api](https://github.com/nodejs/abi-stable-node). It 10 | is still in an experimental stage and will probably change quite a 11 | bit. However, there are plenty of good reasons for using it: 12 | 13 | * it's a plain C api, making the creation of Rust bindings a breeze 14 | * once it's stable, ABI compatibility between node versions will make 15 | life for module maintainers easy 16 | * the VM agnostic nature of the API enables use on more platforms (ChakraCore, SpiderMonkey) 17 | 18 | Furthermore, this crate will make writing asynchronous modules much 19 | easier. Due to the nature of NodeJS' event loop, there is almost no 20 | point in providing synchronous native modules. Native modules either do 21 | 22 | * compute-intensive work, in which case operations have to be performed in their own thread 23 | * operations involving IO 24 | 25 | In both cases, it's undesirable to block the node event loop. 26 | Unfortunately, that's what most binding generators focus on. This 27 | crate uses [futures-rs](https://github.com/alexcrichton/futures-rs) to 28 | make writing asynchronous modules for compute or IO tasks much easier. 29 | 30 | ## Usage 31 | This is a [cargo workspace](https://rust-lang.github.io/book/second-edition/ch14-03-cargo-workspaces.html). Simply run `cargo build --all` 32 | 33 | ## Status 34 | pre-alpha, some parts work in a proof-of-concept way, but the crate cannot be consumed yet. 35 | 36 | ## License 37 | MIT 38 | -------------------------------------------------------------------------------- /examples/hello-world/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | yarn-error.log 3 | node-api 4 | target/ 5 | **/*.rs.bk 6 | Cargo.lock 7 | -------------------------------------------------------------------------------- /examples/hello-world/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello-world" 3 | version = "0.1.0" 4 | authors = ["Jupp Müller "] 5 | 6 | [lib] 7 | crate-type = ["cdylib"] 8 | 9 | [dependencies] 10 | node-api = { path = "../.." } 11 | -------------------------------------------------------------------------------- /examples/hello-world/index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | const hello = require('./node-api'); 3 | 4 | console.log(hello.foo) 5 | -------------------------------------------------------------------------------- /examples/hello-world/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tests", 3 | "version": "1.0.0", 4 | "description": "tests for the nodi-api crate", 5 | "main": "index.js", 6 | "repository": "https://github.com/jupp0r/node-api", 7 | "author": "Jupp Müller ", 8 | "license": "MIT", 9 | "scripts": { 10 | "preinstall": "cargo build", 11 | "postinstall": "mkdir -p node-api && (cp ./target/debug/libhello_world.dylib node-api/index.node || cp ./target/debug/libhello_world.so node-api/index.node || cp ./target/debug/libhello_world.dll node-api/index.node)", 12 | "prestart": "npm install", 13 | "start": "node --napi-modules index.js" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/hello-world/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(link_args)] 2 | #[macro_use(napi_module)] 3 | extern crate node_api; 4 | 5 | napi_module!("testmod", register); 6 | 7 | use node_api::{NapiEnv, NapiValue, FromNapiValues, IntoNapiValue}; 8 | use node_api::{create_function, set_named_property, create_object}; 9 | use node_api::error::*; 10 | 11 | #[no_mangle] 12 | pub extern "C" fn register(env: NapiEnv, exports: NapiValue, _module: NapiValue, _priv: *mut std::os::raw::c_void) { 13 | let function = create_function(env, "foo", |_: NapiEnv, _: NapiValue, ()| { 14 | HelloReturn { 15 | foo: "hello".to_string(), 16 | bar: 42, 17 | } 18 | }) 19 | .expect("error creating function"); 20 | set_named_property(env, exports, "hello", function).expect("error attaching function"); 21 | } 22 | 23 | struct HelloArgs {} 24 | impl FromNapiValues for HelloArgs { 25 | fn from_napi_values(_: NapiEnv, _: NapiValue, _: &[NapiValue]) -> Result { 26 | Ok(HelloArgs {}) 27 | } 28 | } 29 | 30 | struct HelloReturn { 31 | pub foo: String, 32 | pub bar: u64, 33 | } 34 | 35 | impl IntoNapiValue for HelloReturn { 36 | fn into_napi_value(self, env: NapiEnv) -> Result { 37 | let object = create_object(env)?; 38 | let foo = self.foo.into_napi_value(env)?; 39 | let bar = self.bar.into_napi_value(env)?; 40 | set_named_property(env, object, "foo", foo)?; 41 | set_named_property(env, object, "bar", bar)?; 42 | Ok(object) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /node-api-sys/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /node-api-sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "node-api-sys" 3 | version = "0.1.1" 4 | authors = ["Jupp Müller "] 5 | license = "MIT" 6 | description = "Bindings for nodejs NAPI" 7 | homepage = "https://github.com/jupp0r/node-api-sys" 8 | 9 | [dependencies] 10 | -------------------------------------------------------------------------------- /node-api-sys/src/lib.rs: -------------------------------------------------------------------------------- 1 | /* automatically generated by rust-bindgen */ 2 | 3 | pub type intmax_t = ::std::os::raw::c_long; 4 | pub type uintmax_t = ::std::os::raw::c_ulong; 5 | pub type char16_t = u16; 6 | #[repr(C)] 7 | #[derive(Debug, Copy, Clone)] 8 | pub struct napi_env__ { 9 | _unused: [u8; 0], 10 | } 11 | pub type napi_env = u64; 12 | #[repr(C)] 13 | #[derive(Debug, Copy, Clone)] 14 | pub struct napi_value__ { 15 | _unused: [u8; 0], 16 | } 17 | pub type napi_value = u64; 18 | #[repr(C)] 19 | #[derive(Debug, Copy, Clone)] 20 | pub struct napi_ref__ { 21 | _unused: [u8; 0], 22 | } 23 | pub type napi_ref = *mut napi_ref__; 24 | #[repr(C)] 25 | #[derive(Debug, Copy, Clone)] 26 | pub struct napi_handle_scope__ { 27 | _unused: [u8; 0], 28 | } 29 | pub type napi_handle_scope = *mut napi_handle_scope__; 30 | #[repr(C)] 31 | #[derive(Debug, Copy, Clone)] 32 | pub struct napi_escapable_handle_scope__ { 33 | _unused: [u8; 0], 34 | } 35 | pub type napi_escapable_handle_scope = *mut napi_escapable_handle_scope__; 36 | #[repr(C)] 37 | #[derive(Debug, Copy, Clone)] 38 | pub struct napi_callback_info__ { 39 | _unused: [u8; 0], 40 | } 41 | pub type napi_callback_info = *mut napi_callback_info__; 42 | #[repr(C)] 43 | #[derive(Debug, Copy, Clone)] 44 | pub struct napi_async_work__ { 45 | _unused: [u8; 0], 46 | } 47 | pub type napi_async_work = *mut napi_async_work__; 48 | #[repr(u32)] 49 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] 50 | pub enum napi_property_attributes { 51 | napi_default = 0, 52 | napi_writable = 1, 53 | napi_enumerable = 2, 54 | napi_configurable = 4, 55 | napi_static = 1024, 56 | } 57 | #[repr(u32)] 58 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] 59 | pub enum napi_valuetype { 60 | napi_undefined = 0, 61 | napi_null = 1, 62 | napi_boolean = 2, 63 | napi_number = 3, 64 | napi_string = 4, 65 | napi_symbol = 5, 66 | napi_object = 6, 67 | napi_function = 7, 68 | napi_external = 8, 69 | } 70 | #[repr(u32)] 71 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] 72 | pub enum napi_typedarray_type { 73 | napi_int8_array = 0, 74 | napi_uint8_array = 1, 75 | napi_uint8_clamped_array = 2, 76 | napi_int16_array = 3, 77 | napi_uint16_array = 4, 78 | napi_int32_array = 5, 79 | napi_uint32_array = 6, 80 | napi_float32_array = 7, 81 | napi_float64_array = 8, 82 | } 83 | #[repr(u32)] 84 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] 85 | pub enum napi_status { 86 | napi_ok = 0, 87 | napi_invalid_arg = 1, 88 | napi_object_expected = 2, 89 | napi_string_expected = 3, 90 | napi_name_expected = 4, 91 | napi_function_expected = 5, 92 | napi_number_expected = 6, 93 | napi_boolean_expected = 7, 94 | napi_array_expected = 8, 95 | napi_generic_failure = 9, 96 | napi_pending_exception = 10, 97 | napi_cancelled = 11, 98 | napi_status_last = 12, 99 | } 100 | pub type napi_callback = 101 | ::std::option::Option napi_value>; 104 | pub type napi_finalize = 105 | ::std::option::Option; 110 | pub type napi_async_execute_callback = 111 | ::std::option::Option; 114 | pub type napi_async_complete_callback = 115 | ::std::option::Option; 119 | #[repr(C)] 120 | #[derive(Debug, Copy)] 121 | pub struct napi_property_descriptor { 122 | pub utf8name: *const ::std::os::raw::c_char, 123 | pub name: napi_value, 124 | pub method: napi_callback, 125 | pub getter: napi_callback, 126 | pub setter: napi_callback, 127 | pub value: napi_value, 128 | pub attributes: napi_property_attributes, 129 | pub data: *mut ::std::os::raw::c_void, 130 | } 131 | #[test] 132 | fn bindgen_test_layout_napi_property_descriptor() { 133 | assert_eq!(::std::mem::size_of::() , 64usize , 134 | concat ! ( 135 | "Size of: " , stringify ! ( napi_property_descriptor ) )); 136 | assert_eq! (::std::mem::align_of::() , 8usize , 137 | concat ! ( 138 | "Alignment of " , stringify ! ( napi_property_descriptor ) )); 139 | assert_eq! (unsafe { 140 | & ( * ( 0 as * const napi_property_descriptor ) ) . utf8name 141 | as * const _ as usize } , 0usize , concat ! ( 142 | "Alignment of field: " , stringify ! ( 143 | napi_property_descriptor ) , "::" , stringify ! ( utf8name ) 144 | )); 145 | assert_eq! (unsafe { 146 | & ( * ( 0 as * const napi_property_descriptor ) ) . name as * 147 | const _ as usize } , 8usize , concat ! ( 148 | "Alignment of field: " , stringify ! ( 149 | napi_property_descriptor ) , "::" , stringify ! ( name ) )); 150 | assert_eq! (unsafe { 151 | & ( * ( 0 as * const napi_property_descriptor ) ) . method as 152 | * const _ as usize } , 16usize , concat ! ( 153 | "Alignment of field: " , stringify ! ( 154 | napi_property_descriptor ) , "::" , stringify ! ( method ) )); 155 | assert_eq! (unsafe { 156 | & ( * ( 0 as * const napi_property_descriptor ) ) . getter as 157 | * const _ as usize } , 24usize , concat ! ( 158 | "Alignment of field: " , stringify ! ( 159 | napi_property_descriptor ) , "::" , stringify ! ( getter ) )); 160 | assert_eq! (unsafe { 161 | & ( * ( 0 as * const napi_property_descriptor ) ) . setter as 162 | * const _ as usize } , 32usize , concat ! ( 163 | "Alignment of field: " , stringify ! ( 164 | napi_property_descriptor ) , "::" , stringify ! ( setter ) )); 165 | assert_eq! (unsafe { 166 | & ( * ( 0 as * const napi_property_descriptor ) ) . value as * 167 | const _ as usize } , 40usize , concat ! ( 168 | "Alignment of field: " , stringify ! ( 169 | napi_property_descriptor ) , "::" , stringify ! ( value ) )); 170 | assert_eq! (unsafe { 171 | & ( * ( 0 as * const napi_property_descriptor ) ) . attributes 172 | as * const _ as usize } , 48usize , concat ! ( 173 | "Alignment of field: " , stringify ! ( 174 | napi_property_descriptor ) , "::" , stringify ! ( attributes ) 175 | )); 176 | assert_eq! (unsafe { 177 | & ( * ( 0 as * const napi_property_descriptor ) ) . data as * 178 | const _ as usize } , 56usize , concat ! ( 179 | "Alignment of field: " , stringify ! ( 180 | napi_property_descriptor ) , "::" , stringify ! ( data ) )); 181 | } 182 | impl Clone for napi_property_descriptor { 183 | fn clone(&self) -> Self { *self } 184 | } 185 | #[repr(C)] 186 | #[derive(Debug, Copy)] 187 | pub struct napi_extended_error_info { 188 | pub error_message: *const ::std::os::raw::c_char, 189 | pub engine_reserved: *mut ::std::os::raw::c_void, 190 | pub engine_error_code: u32, 191 | pub error_code: napi_status, 192 | } 193 | #[test] 194 | fn bindgen_test_layout_napi_extended_error_info() { 195 | assert_eq!(::std::mem::size_of::() , 24usize , 196 | concat ! ( 197 | "Size of: " , stringify ! ( napi_extended_error_info ) )); 198 | assert_eq! (::std::mem::align_of::() , 8usize , 199 | concat ! ( 200 | "Alignment of " , stringify ! ( napi_extended_error_info ) )); 201 | assert_eq! (unsafe { 202 | & ( * ( 0 as * const napi_extended_error_info ) ) . 203 | error_message as * const _ as usize } , 0usize , concat ! ( 204 | "Alignment of field: " , stringify ! ( 205 | napi_extended_error_info ) , "::" , stringify ! ( 206 | error_message ) )); 207 | assert_eq! (unsafe { 208 | & ( * ( 0 as * const napi_extended_error_info ) ) . 209 | engine_reserved as * const _ as usize } , 8usize , concat ! ( 210 | "Alignment of field: " , stringify ! ( 211 | napi_extended_error_info ) , "::" , stringify ! ( 212 | engine_reserved ) )); 213 | assert_eq! (unsafe { 214 | & ( * ( 0 as * const napi_extended_error_info ) ) . 215 | engine_error_code as * const _ as usize } , 16usize , concat ! 216 | ( 217 | "Alignment of field: " , stringify ! ( 218 | napi_extended_error_info ) , "::" , stringify ! ( 219 | engine_error_code ) )); 220 | assert_eq! (unsafe { 221 | & ( * ( 0 as * const napi_extended_error_info ) ) . error_code 222 | as * const _ as usize } , 20usize , concat ! ( 223 | "Alignment of field: " , stringify ! ( 224 | napi_extended_error_info ) , "::" , stringify ! ( error_code ) 225 | )); 226 | } 227 | impl Clone for napi_extended_error_info { 228 | fn clone(&self) -> Self { *self } 229 | } 230 | pub type napi_addon_register_func = 231 | ::std::option::Option; 236 | #[repr(C)] 237 | #[derive(Debug, Copy)] 238 | pub struct napi_module { 239 | pub nm_version: ::std::os::raw::c_int, 240 | pub nm_flags: ::std::os::raw::c_uint, 241 | pub nm_filename: *const ::std::os::raw::c_char, 242 | pub nm_register_func: napi_addon_register_func, 243 | pub nm_modname: *const ::std::os::raw::c_char, 244 | pub nm_priv: *mut ::std::os::raw::c_void, 245 | pub reserved: [*mut ::std::os::raw::c_void; 4usize], 246 | } 247 | #[test] 248 | fn bindgen_test_layout_napi_module() { 249 | assert_eq!(::std::mem::size_of::() , 72usize , concat ! ( 250 | "Size of: " , stringify ! ( napi_module ) )); 251 | assert_eq! (::std::mem::align_of::() , 8usize , concat ! ( 252 | "Alignment of " , stringify ! ( napi_module ) )); 253 | assert_eq! (unsafe { 254 | & ( * ( 0 as * const napi_module ) ) . nm_version as * const _ 255 | as usize } , 0usize , concat ! ( 256 | "Alignment of field: " , stringify ! ( napi_module ) , "::" , 257 | stringify ! ( nm_version ) )); 258 | assert_eq! (unsafe { 259 | & ( * ( 0 as * const napi_module ) ) . nm_flags as * const _ 260 | as usize } , 4usize , concat ! ( 261 | "Alignment of field: " , stringify ! ( napi_module ) , "::" , 262 | stringify ! ( nm_flags ) )); 263 | assert_eq! (unsafe { 264 | & ( * ( 0 as * const napi_module ) ) . nm_filename as * const 265 | _ as usize } , 8usize , concat ! ( 266 | "Alignment of field: " , stringify ! ( napi_module ) , "::" , 267 | stringify ! ( nm_filename ) )); 268 | assert_eq! (unsafe { 269 | & ( * ( 0 as * const napi_module ) ) . nm_register_func as * 270 | const _ as usize } , 16usize , concat ! ( 271 | "Alignment of field: " , stringify ! ( napi_module ) , "::" , 272 | stringify ! ( nm_register_func ) )); 273 | assert_eq! (unsafe { 274 | & ( * ( 0 as * const napi_module ) ) . nm_modname as * const _ 275 | as usize } , 24usize , concat ! ( 276 | "Alignment of field: " , stringify ! ( napi_module ) , "::" , 277 | stringify ! ( nm_modname ) )); 278 | assert_eq! (unsafe { 279 | & ( * ( 0 as * const napi_module ) ) . nm_priv as * const _ as 280 | usize } , 32usize , concat ! ( 281 | "Alignment of field: " , stringify ! ( napi_module ) , "::" , 282 | stringify ! ( nm_priv ) )); 283 | assert_eq! (unsafe { 284 | & ( * ( 0 as * const napi_module ) ) . reserved as * const _ 285 | as usize } , 40usize , concat ! ( 286 | "Alignment of field: " , stringify ! ( napi_module ) , "::" , 287 | stringify ! ( reserved ) )); 288 | } 289 | impl Clone for napi_module { 290 | fn clone(&self) -> Self { *self } 291 | } 292 | 293 | extern "C" { 294 | pub fn napi_module_register(mod_: *mut napi_module); 295 | } 296 | extern "C" { 297 | pub fn napi_get_last_error_info(env: napi_env, 298 | result: 299 | *mut *const napi_extended_error_info) 300 | -> napi_status; 301 | } 302 | extern "C" { 303 | pub fn napi_get_undefined(env: napi_env, result: *mut napi_value) 304 | -> napi_status; 305 | } 306 | extern "C" { 307 | pub fn napi_get_null(env: napi_env, result: *mut napi_value) 308 | -> napi_status; 309 | } 310 | extern "C" { 311 | pub fn napi_get_global(env: napi_env, result: *mut napi_value) 312 | -> napi_status; 313 | } 314 | extern "C" { 315 | pub fn napi_get_boolean(env: napi_env, value: bool, 316 | result: *mut napi_value) -> napi_status; 317 | } 318 | extern "C" { 319 | pub fn napi_create_object(env: napi_env, result: *mut napi_value) 320 | -> napi_status; 321 | } 322 | extern "C" { 323 | pub fn napi_create_array(env: napi_env, result: *mut napi_value) 324 | -> napi_status; 325 | } 326 | extern "C" { 327 | pub fn napi_create_array_with_length(env: napi_env, length: usize, 328 | result: *mut napi_value) 329 | -> napi_status; 330 | } 331 | extern "C" { 332 | pub fn napi_create_number(env: napi_env, value: f64, 333 | result: *mut napi_value) -> napi_status; 334 | } 335 | extern "C" { 336 | pub fn napi_create_string_latin1(env: napi_env, 337 | str: *const ::std::os::raw::c_char, 338 | length: usize, result: *mut napi_value) 339 | -> napi_status; 340 | } 341 | extern "C" { 342 | pub fn napi_create_string_utf8(env: napi_env, 343 | str: *const ::std::os::raw::c_char, 344 | length: usize, result: *mut napi_value) 345 | -> napi_status; 346 | } 347 | extern "C" { 348 | pub fn napi_create_string_utf16(env: napi_env, str: *const char16_t, 349 | length: usize, result: *mut napi_value) 350 | -> napi_status; 351 | } 352 | extern "C" { 353 | pub fn napi_create_symbol(env: napi_env, description: napi_value, 354 | result: *mut napi_value) -> napi_status; 355 | } 356 | extern "C" { 357 | pub fn napi_create_function(env: napi_env, 358 | utf8name: *const ::std::os::raw::c_char, 359 | cb: napi_callback, 360 | data: *mut ::std::os::raw::c_void, 361 | result: *mut napi_value) -> napi_status; 362 | } 363 | extern "C" { 364 | pub fn napi_create_error(env: napi_env, msg: napi_value, 365 | result: *mut napi_value) -> napi_status; 366 | } 367 | extern "C" { 368 | pub fn napi_create_type_error(env: napi_env, msg: napi_value, 369 | result: *mut napi_value) -> napi_status; 370 | } 371 | extern "C" { 372 | pub fn napi_create_range_error(env: napi_env, msg: napi_value, 373 | result: *mut napi_value) -> napi_status; 374 | } 375 | extern "C" { 376 | pub fn napi_typeof(env: napi_env, value: napi_value, 377 | result: *mut napi_valuetype) -> napi_status; 378 | } 379 | extern "C" { 380 | pub fn napi_get_value_double(env: napi_env, value: napi_value, 381 | result: *mut f64) -> napi_status; 382 | } 383 | extern "C" { 384 | pub fn napi_get_value_int32(env: napi_env, value: napi_value, 385 | result: *mut i32) -> napi_status; 386 | } 387 | extern "C" { 388 | pub fn napi_get_value_uint32(env: napi_env, value: napi_value, 389 | result: *mut u32) -> napi_status; 390 | } 391 | extern "C" { 392 | pub fn napi_get_value_int64(env: napi_env, value: napi_value, 393 | result: *mut i64) -> napi_status; 394 | } 395 | extern "C" { 396 | pub fn napi_get_value_bool(env: napi_env, value: napi_value, 397 | result: *mut bool) -> napi_status; 398 | } 399 | extern "C" { 400 | pub fn napi_get_value_string_latin1(env: napi_env, value: napi_value, 401 | buf: *mut ::std::os::raw::c_char, 402 | bufsize: usize, result: *mut usize) 403 | -> napi_status; 404 | } 405 | extern "C" { 406 | pub fn napi_get_value_string_utf8(env: napi_env, value: napi_value, 407 | buf: *mut ::std::os::raw::c_char, 408 | bufsize: usize, result: *mut usize) 409 | -> napi_status; 410 | } 411 | extern "C" { 412 | pub fn napi_get_value_string_utf16(env: napi_env, value: napi_value, 413 | buf: *mut char16_t, bufsize: usize, 414 | result: *mut usize) -> napi_status; 415 | } 416 | extern "C" { 417 | pub fn napi_coerce_to_bool(env: napi_env, value: napi_value, 418 | result: *mut napi_value) -> napi_status; 419 | } 420 | extern "C" { 421 | pub fn napi_coerce_to_number(env: napi_env, value: napi_value, 422 | result: *mut napi_value) -> napi_status; 423 | } 424 | extern "C" { 425 | pub fn napi_coerce_to_object(env: napi_env, value: napi_value, 426 | result: *mut napi_value) -> napi_status; 427 | } 428 | extern "C" { 429 | pub fn napi_coerce_to_string(env: napi_env, value: napi_value, 430 | result: *mut napi_value) -> napi_status; 431 | } 432 | extern "C" { 433 | pub fn napi_get_prototype(env: napi_env, object: napi_value, 434 | result: *mut napi_value) -> napi_status; 435 | } 436 | extern "C" { 437 | pub fn napi_get_property_names(env: napi_env, object: napi_value, 438 | result: *mut napi_value) -> napi_status; 439 | } 440 | extern "C" { 441 | pub fn napi_set_property(env: napi_env, object: napi_value, 442 | key: napi_value, value: napi_value) 443 | -> napi_status; 444 | } 445 | extern "C" { 446 | pub fn napi_has_property(env: napi_env, object: napi_value, 447 | key: napi_value, result: *mut bool) 448 | -> napi_status; 449 | } 450 | extern "C" { 451 | pub fn napi_get_property(env: napi_env, object: napi_value, 452 | key: napi_value, result: *mut napi_value) 453 | -> napi_status; 454 | } 455 | extern "C" { 456 | pub fn napi_set_named_property(env: napi_env, object: napi_value, 457 | utf8name: *const ::std::os::raw::c_char, 458 | value: napi_value) -> napi_status; 459 | } 460 | extern "C" { 461 | pub fn napi_has_named_property(env: napi_env, object: napi_value, 462 | utf8name: *const ::std::os::raw::c_char, 463 | result: *mut bool) -> napi_status; 464 | } 465 | extern "C" { 466 | pub fn napi_get_named_property(env: napi_env, object: napi_value, 467 | utf8name: *const ::std::os::raw::c_char, 468 | result: *mut napi_value) -> napi_status; 469 | } 470 | extern "C" { 471 | pub fn napi_set_element(env: napi_env, object: napi_value, index: u32, 472 | value: napi_value) -> napi_status; 473 | } 474 | extern "C" { 475 | pub fn napi_has_element(env: napi_env, object: napi_value, index: u32, 476 | result: *mut bool) -> napi_status; 477 | } 478 | extern "C" { 479 | pub fn napi_get_element(env: napi_env, object: napi_value, index: u32, 480 | result: *mut napi_value) -> napi_status; 481 | } 482 | extern "C" { 483 | pub fn napi_define_properties(env: napi_env, object: napi_value, 484 | property_count: usize, 485 | properties: *const napi_property_descriptor) 486 | -> napi_status; 487 | } 488 | extern "C" { 489 | pub fn napi_is_array(env: napi_env, value: napi_value, result: *mut bool) 490 | -> napi_status; 491 | } 492 | extern "C" { 493 | pub fn napi_get_array_length(env: napi_env, value: napi_value, 494 | result: *mut u32) -> napi_status; 495 | } 496 | extern "C" { 497 | pub fn napi_strict_equals(env: napi_env, lhs: napi_value, rhs: napi_value, 498 | result: *mut bool) -> napi_status; 499 | } 500 | extern "C" { 501 | pub fn napi_call_function(env: napi_env, recv: napi_value, 502 | func: napi_value, argc: usize, 503 | argv: *const napi_value, 504 | result: *mut napi_value) -> napi_status; 505 | } 506 | extern "C" { 507 | pub fn napi_new_instance(env: napi_env, constructor: napi_value, 508 | argc: usize, argv: *const napi_value, 509 | result: *mut napi_value) -> napi_status; 510 | } 511 | extern "C" { 512 | pub fn napi_instanceof(env: napi_env, object: napi_value, 513 | constructor: napi_value, result: *mut bool) 514 | -> napi_status; 515 | } 516 | extern "C" { 517 | pub fn napi_make_callback(env: napi_env, recv: napi_value, 518 | func: napi_value, argc: usize, 519 | argv: *const napi_value, 520 | result: *mut napi_value) -> napi_status; 521 | } 522 | extern "C" { 523 | pub fn napi_get_cb_info(env: napi_env, cbinfo: napi_callback_info, 524 | argc: *mut usize, argv: *mut napi_value, 525 | this_arg: *mut napi_value, 526 | data: *mut *mut ::std::os::raw::c_void) 527 | -> napi_status; 528 | } 529 | extern "C" { 530 | pub fn napi_is_construct_call(env: napi_env, cbinfo: napi_callback_info, 531 | result: *mut bool) -> napi_status; 532 | } 533 | extern "C" { 534 | pub fn napi_define_class(env: napi_env, 535 | utf8name: *const ::std::os::raw::c_char, 536 | constructor: napi_callback, 537 | data: *mut ::std::os::raw::c_void, 538 | property_count: usize, 539 | properties: *const napi_property_descriptor, 540 | result: *mut napi_value) -> napi_status; 541 | } 542 | extern "C" { 543 | pub fn napi_wrap(env: napi_env, js_object: napi_value, 544 | native_object: *mut ::std::os::raw::c_void, 545 | finalize_cb: napi_finalize, 546 | finalize_hint: *mut ::std::os::raw::c_void, 547 | result: *mut napi_ref) -> napi_status; 548 | } 549 | extern "C" { 550 | pub fn napi_unwrap(env: napi_env, js_object: napi_value, 551 | result: *mut *mut ::std::os::raw::c_void) 552 | -> napi_status; 553 | } 554 | extern "C" { 555 | pub fn napi_create_external(env: napi_env, 556 | data: *mut ::std::os::raw::c_void, 557 | finalize_cb: napi_finalize, 558 | finalize_hint: *mut ::std::os::raw::c_void, 559 | result: *mut napi_value) -> napi_status; 560 | } 561 | extern "C" { 562 | pub fn napi_get_value_external(env: napi_env, value: napi_value, 563 | result: *mut *mut ::std::os::raw::c_void) 564 | -> napi_status; 565 | } 566 | extern "C" { 567 | pub fn napi_create_reference(env: napi_env, value: napi_value, 568 | initial_refcount: u32, result: *mut napi_ref) 569 | -> napi_status; 570 | } 571 | extern "C" { 572 | pub fn napi_delete_reference(env: napi_env, ref_: napi_ref) 573 | -> napi_status; 574 | } 575 | extern "C" { 576 | pub fn napi_reference_ref(env: napi_env, ref_: napi_ref, result: *mut u32) 577 | -> napi_status; 578 | } 579 | extern "C" { 580 | pub fn napi_reference_unref(env: napi_env, ref_: napi_ref, 581 | result: *mut u32) -> napi_status; 582 | } 583 | extern "C" { 584 | pub fn napi_get_reference_value(env: napi_env, ref_: napi_ref, 585 | result: *mut napi_value) -> napi_status; 586 | } 587 | extern "C" { 588 | pub fn napi_open_handle_scope(env: napi_env, 589 | result: *mut napi_handle_scope) 590 | -> napi_status; 591 | } 592 | extern "C" { 593 | pub fn napi_close_handle_scope(env: napi_env, scope: napi_handle_scope) 594 | -> napi_status; 595 | } 596 | extern "C" { 597 | pub fn napi_open_escapable_handle_scope(env: napi_env, 598 | result: 599 | *mut napi_escapable_handle_scope) 600 | -> napi_status; 601 | } 602 | extern "C" { 603 | pub fn napi_close_escapable_handle_scope(env: napi_env, 604 | scope: 605 | napi_escapable_handle_scope) 606 | -> napi_status; 607 | } 608 | extern "C" { 609 | pub fn napi_escape_handle(env: napi_env, 610 | scope: napi_escapable_handle_scope, 611 | escapee: napi_value, result: *mut napi_value) 612 | -> napi_status; 613 | } 614 | extern "C" { 615 | pub fn napi_throw(env: napi_env, error: napi_value) -> napi_status; 616 | } 617 | extern "C" { 618 | pub fn napi_throw_error(env: napi_env, msg: *const ::std::os::raw::c_char) 619 | -> napi_status; 620 | } 621 | extern "C" { 622 | pub fn napi_throw_type_error(env: napi_env, 623 | msg: *const ::std::os::raw::c_char) 624 | -> napi_status; 625 | } 626 | extern "C" { 627 | pub fn napi_throw_range_error(env: napi_env, 628 | msg: *const ::std::os::raw::c_char) 629 | -> napi_status; 630 | } 631 | extern "C" { 632 | pub fn napi_is_error(env: napi_env, value: napi_value, result: *mut bool) 633 | -> napi_status; 634 | } 635 | extern "C" { 636 | pub fn napi_is_exception_pending(env: napi_env, result: *mut bool) 637 | -> napi_status; 638 | } 639 | extern "C" { 640 | pub fn napi_get_and_clear_last_exception(env: napi_env, 641 | result: *mut napi_value) 642 | -> napi_status; 643 | } 644 | extern "C" { 645 | pub fn napi_create_buffer(env: napi_env, length: usize, 646 | data: *mut *mut ::std::os::raw::c_void, 647 | result: *mut napi_value) -> napi_status; 648 | } 649 | extern "C" { 650 | pub fn napi_create_external_buffer(env: napi_env, length: usize, 651 | data: *mut ::std::os::raw::c_void, 652 | finalize_cb: napi_finalize, 653 | finalize_hint: 654 | *mut ::std::os::raw::c_void, 655 | result: *mut napi_value) 656 | -> napi_status; 657 | } 658 | extern "C" { 659 | pub fn napi_create_buffer_copy(env: napi_env, length: usize, 660 | data: *const ::std::os::raw::c_void, 661 | result_data: 662 | *mut *mut ::std::os::raw::c_void, 663 | result: *mut napi_value) -> napi_status; 664 | } 665 | extern "C" { 666 | pub fn napi_is_buffer(env: napi_env, value: napi_value, result: *mut bool) 667 | -> napi_status; 668 | } 669 | extern "C" { 670 | pub fn napi_get_buffer_info(env: napi_env, value: napi_value, 671 | data: *mut *mut ::std::os::raw::c_void, 672 | length: *mut usize) -> napi_status; 673 | } 674 | extern "C" { 675 | pub fn napi_is_arraybuffer(env: napi_env, value: napi_value, 676 | result: *mut bool) -> napi_status; 677 | } 678 | extern "C" { 679 | pub fn napi_create_arraybuffer(env: napi_env, byte_length: usize, 680 | data: *mut *mut ::std::os::raw::c_void, 681 | result: *mut napi_value) -> napi_status; 682 | } 683 | extern "C" { 684 | pub fn napi_create_external_arraybuffer(env: napi_env, 685 | external_data: 686 | *mut ::std::os::raw::c_void, 687 | byte_length: usize, 688 | finalize_cb: napi_finalize, 689 | finalize_hint: 690 | *mut ::std::os::raw::c_void, 691 | result: *mut napi_value) 692 | -> napi_status; 693 | } 694 | extern "C" { 695 | pub fn napi_get_arraybuffer_info(env: napi_env, arraybuffer: napi_value, 696 | data: *mut *mut ::std::os::raw::c_void, 697 | byte_length: *mut usize) -> napi_status; 698 | } 699 | extern "C" { 700 | pub fn napi_is_typedarray(env: napi_env, value: napi_value, 701 | result: *mut bool) -> napi_status; 702 | } 703 | extern "C" { 704 | pub fn napi_create_typedarray(env: napi_env, type_: napi_typedarray_type, 705 | length: usize, arraybuffer: napi_value, 706 | byte_offset: usize, result: *mut napi_value) 707 | -> napi_status; 708 | } 709 | extern "C" { 710 | pub fn napi_get_typedarray_info(env: napi_env, typedarray: napi_value, 711 | type_: *mut napi_typedarray_type, 712 | length: *mut usize, 713 | data: *mut *mut ::std::os::raw::c_void, 714 | arraybuffer: *mut napi_value, 715 | byte_offset: *mut usize) -> napi_status; 716 | } 717 | extern "C" { 718 | pub fn napi_create_async_work(env: napi_env, 719 | execute: napi_async_execute_callback, 720 | complete: napi_async_complete_callback, 721 | data: *mut ::std::os::raw::c_void, 722 | result: *mut napi_async_work) 723 | -> napi_status; 724 | } 725 | extern "C" { 726 | pub fn napi_delete_async_work(env: napi_env, work: napi_async_work) 727 | -> napi_status; 728 | } 729 | extern "C" { 730 | pub fn napi_queue_async_work(env: napi_env, work: napi_async_work) 731 | -> napi_status; 732 | } 733 | extern "C" { 734 | pub fn napi_cancel_async_work(env: napi_env, work: napi_async_work) 735 | -> napi_status; 736 | } 737 | #[repr(C)] 738 | #[derive(Debug, Copy)] 739 | pub struct __va_list_tag { 740 | pub gp_offset: ::std::os::raw::c_uint, 741 | pub fp_offset: ::std::os::raw::c_uint, 742 | pub overflow_arg_area: *mut ::std::os::raw::c_void, 743 | pub reg_save_area: *mut ::std::os::raw::c_void, 744 | } 745 | #[test] 746 | fn bindgen_test_layout___va_list_tag() { 747 | assert_eq!(::std::mem::size_of::<__va_list_tag>() , 24usize , concat ! ( 748 | "Size of: " , stringify ! ( __va_list_tag ) )); 749 | assert_eq! (::std::mem::align_of::<__va_list_tag>() , 8usize , concat ! ( 750 | "Alignment of " , stringify ! ( __va_list_tag ) )); 751 | assert_eq! (unsafe { 752 | & ( * ( 0 as * const __va_list_tag ) ) . gp_offset as * const 753 | _ as usize } , 0usize , concat ! ( 754 | "Alignment of field: " , stringify ! ( __va_list_tag ) , "::" 755 | , stringify ! ( gp_offset ) )); 756 | assert_eq! (unsafe { 757 | & ( * ( 0 as * const __va_list_tag ) ) . fp_offset as * const 758 | _ as usize } , 4usize , concat ! ( 759 | "Alignment of field: " , stringify ! ( __va_list_tag ) , "::" 760 | , stringify ! ( fp_offset ) )); 761 | assert_eq! (unsafe { 762 | & ( * ( 0 as * const __va_list_tag ) ) . overflow_arg_area as 763 | * const _ as usize } , 8usize , concat ! ( 764 | "Alignment of field: " , stringify ! ( __va_list_tag ) , "::" 765 | , stringify ! ( overflow_arg_area ) )); 766 | assert_eq! (unsafe { 767 | & ( * ( 0 as * const __va_list_tag ) ) . reg_save_area as * 768 | const _ as usize } , 16usize , concat ! ( 769 | "Alignment of field: " , stringify ! ( __va_list_tag ) , "::" 770 | , stringify ! ( reg_save_area ) )); 771 | } 772 | impl Clone for __va_list_tag { 773 | fn clone(&self) -> Self { *self } 774 | } 775 | pub type __builtin_va_list = [__va_list_tag; 1usize]; 776 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use std::{result, ffi, string}; 2 | use std::ffi::CStr; 3 | 4 | use node_api_sys::*; 5 | pub type Result = result::Result; 6 | 7 | fn make_generic_napi_error(message: &str) -> NapiError { 8 | NapiError { 9 | error_message: message.to_string(), 10 | engine_error_code: 0, 11 | error_code: NapiErrorType::GenericFailure, 12 | } 13 | } 14 | 15 | #[derive(Debug, Clone)] 16 | pub struct NapiError { 17 | pub error_message: String, 18 | pub engine_error_code: u32, 19 | pub error_code: NapiErrorType, 20 | } 21 | 22 | impl From for NapiError { 23 | fn from(error: napi_extended_error_info) -> Self { 24 | unsafe { 25 | Self { 26 | error_message: CStr::from_ptr(error.error_message) 27 | .to_string_lossy() 28 | .into_owned(), 29 | engine_error_code: error.engine_error_code, 30 | error_code: NapiErrorType::from(error.error_code), 31 | } 32 | } 33 | } 34 | } 35 | 36 | impl From for NapiError { 37 | fn from(_: ffi::NulError) -> Self { 38 | make_generic_napi_error("string must not contain 0 byte") 39 | } 40 | } 41 | 42 | impl From for NapiError { 43 | fn from(err: string::FromUtf8Error) -> Self { 44 | make_generic_napi_error(&format!("{:?}", err)) 45 | } 46 | } 47 | 48 | #[derive(Debug, Clone, PartialEq)] 49 | pub enum NapiErrorType { 50 | InvalidArg, 51 | ObjectExpected, 52 | StringExpected, 53 | NameExpected, 54 | FunctionExpected, 55 | NumberExpected, 56 | BooleanExpected, 57 | ArrayExpected, 58 | GenericFailure, 59 | PendingException, 60 | Cancelled, 61 | StatusLast, 62 | } 63 | 64 | impl From for NapiErrorType { 65 | fn from(s: napi_status) -> Self { 66 | match s { 67 | napi_status::napi_invalid_arg => NapiErrorType::InvalidArg, 68 | napi_status::napi_object_expected => NapiErrorType::ObjectExpected, 69 | napi_status::napi_string_expected => NapiErrorType::StringExpected, 70 | napi_status::napi_name_expected => NapiErrorType::NameExpected, 71 | napi_status::napi_function_expected => NapiErrorType::FunctionExpected, 72 | napi_status::napi_number_expected => NapiErrorType::NumberExpected, 73 | napi_status::napi_boolean_expected => NapiErrorType::BooleanExpected, 74 | napi_status::napi_array_expected => NapiErrorType::ArrayExpected, 75 | napi_status::napi_generic_failure => NapiErrorType::GenericFailure, 76 | napi_status::napi_pending_exception => NapiErrorType::PendingException, 77 | napi_status::napi_cancelled => NapiErrorType::Cancelled, 78 | napi_status::napi_status_last => NapiErrorType::StatusLast, 79 | _ => NapiErrorType::GenericFailure, 80 | } 81 | } 82 | } -------------------------------------------------------------------------------- /src/futures.rs: -------------------------------------------------------------------------------- 1 | use std::boxed::Box; 2 | 3 | use napi::{NapiEnv, NapiValue, Result}; 4 | use napi_value::{ToNapiValue, FromNapiValues}; 5 | 6 | struct FutureContext {} 7 | 8 | struct ThenArgs { 9 | on_fulfilled: Box R>, 10 | on_rejected: Box, 11 | } 12 | 13 | impl FromNapiValues for ThenArgs { 14 | fn from_napi_values(env: NapiEnv, values: &[NapiValue]) -> Result { 15 | Ok(ThenArgs { 16 | on_fulfilled: Box::new(|| {}), 17 | on_rejected: Box::new(|| {}), 18 | }) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(link_args)] 2 | 3 | extern crate node_api_sys; 4 | extern crate futures; 5 | 6 | mod napi; 7 | mod napi_value; 8 | mod napi_futures; 9 | pub mod error; 10 | 11 | pub use napi::*; 12 | pub use napi_value::{FromNapiValues, IntoNapiValue}; 13 | 14 | #[macro_export] 15 | macro_rules! napi_module { 16 | ($module:expr, $register_func:ident) => { 17 | const NAPI_MODULE_VERSION: std::os::raw::c_int = 1; 18 | 19 | #[cfg_attr(target_os = "macos", link_args = "-Wl,-undefined,dynamic_lookup")] 20 | extern "C" {} 21 | 22 | #[cfg_attr(target_os = "linux", link_section = ".ctors")] 23 | #[cfg_attr(target_os = "macos", link_section = "__DATA,__mod_init_func")] 24 | #[cfg_attr(target_os = "windows", link_section = ".CRT$XCU")] 25 | pub static REGISTER_FOO: extern "C" fn() = { 26 | extern "C" fn __load_napi_module() { 27 | node_api::module_register(node_api::NapiModule { 28 | version: NAPI_MODULE_VERSION, 29 | flags: 0, 30 | filename: $module.to_string(), 31 | register_func: Some($register_func), 32 | modname: $module.to_string(), 33 | }) 34 | .expect("error registering module"); 35 | } 36 | __load_napi_module 37 | }; 38 | }} 39 | 40 | #[cfg(test)] 41 | mod tests { 42 | #[test] 43 | fn it_works() {} 44 | } 45 | -------------------------------------------------------------------------------- /src/napi.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::CString; 2 | use std::{ptr,mem,f64,usize,os}; 3 | use std::result::Result as StdResult; 4 | use node_api_sys::*; 5 | 6 | use napi_value::{FromNapiValues, IntoNapiValue}; 7 | use error::*; 8 | 9 | pub type NapiEnv = napi_env; 10 | pub type NapiRef = napi_ref; 11 | pub type NapiValue = napi_value; 12 | 13 | #[derive(Debug, Clone)] 14 | pub struct NapiModule { 15 | pub version: i32, 16 | pub flags: u32, 17 | pub filename: String, 18 | pub register_func: napi_addon_register_func, 19 | pub modname: String, 20 | } 21 | 22 | 23 | #[derive(Debug, Clone, PartialEq)] 24 | pub enum NapiValueType { 25 | Undefined, 26 | Null, 27 | Boolean, 28 | Number, 29 | String, 30 | Symbol, 31 | Object, 32 | Function, 33 | External, 34 | } 35 | 36 | impl From for NapiValueType { 37 | fn from(s: napi_valuetype) -> Self { 38 | match s { 39 | napi_valuetype::napi_undefined => NapiValueType::Undefined, 40 | napi_valuetype::napi_null => NapiValueType::Null, 41 | napi_valuetype::napi_boolean => NapiValueType::Boolean, 42 | napi_valuetype::napi_number => NapiValueType::Number, 43 | napi_valuetype::napi_string => NapiValueType::String, 44 | napi_valuetype::napi_symbol => NapiValueType::Symbol, 45 | napi_valuetype::napi_object => NapiValueType::Object, 46 | napi_valuetype::napi_function => NapiValueType::Function, 47 | napi_valuetype::napi_external => NapiValueType::External, 48 | } 49 | } 50 | } 51 | 52 | pub fn napi_either(env: NapiEnv, status: napi_status, val: T) -> Result { 53 | match status { 54 | napi_status::napi_ok => Ok(val), 55 | _err => Err(get_last_napi_error(env).expect("error fetching last napi error")), 56 | } 57 | } 58 | 59 | fn get_last_error_info(env: napi_env) -> StdResult { 60 | unsafe { 61 | let mut info: *const napi_extended_error_info = 62 | Box::into_raw(Box::new(mem::uninitialized())); 63 | let status = napi_get_last_error_info(env, &mut info); 64 | match status { 65 | napi_status::napi_ok => Ok(*info), 66 | _ => Err(status), 67 | } 68 | } 69 | } 70 | 71 | fn get_last_napi_error(env: NapiEnv) -> StdResult { 72 | get_last_error_info(env) 73 | .map(|res| NapiError::from(res)) 74 | .map_err(|err| NapiErrorType::from(err)) 75 | } 76 | 77 | pub fn module_register(mod_: NapiModule) -> StdResult<(), NapiError> { 78 | let module = &mut napi_module { 79 | nm_version: mod_.version, 80 | nm_flags: mod_.flags, 81 | nm_filename: CString::new(mod_.filename)?.as_ptr(), 82 | nm_register_func: mod_.register_func, 83 | nm_modname: try!(CString::new(mod_.modname)).as_ptr(), 84 | nm_priv: ptr::null_mut(), 85 | reserved: [ptr::null_mut(), 86 | ptr::null_mut(), 87 | ptr::null_mut(), 88 | ptr::null_mut()], 89 | }; 90 | unsafe { 91 | napi_module_register(module); 92 | } 93 | Ok(()) 94 | } 95 | 96 | pub fn get_undefined(env: NapiEnv) -> Result { 97 | unsafe { 98 | let mut napi_val: NapiValue = mem::uninitialized(); 99 | let status = napi_get_undefined(env, &mut napi_val); 100 | napi_either(env, status, napi_val) 101 | } 102 | } 103 | 104 | pub fn get_null(env: NapiEnv) -> Result { 105 | unsafe { 106 | let mut napi_val: NapiValue = mem::uninitialized(); 107 | let status = napi_get_null(env, &mut napi_val); 108 | napi_either(env, status, napi_val) 109 | } 110 | } 111 | 112 | pub fn get_global(env: NapiEnv) -> Result { 113 | unsafe { 114 | let mut napi_val: NapiValue = mem::uninitialized(); 115 | let status = napi_get_global(env, &mut napi_val); 116 | napi_either(env, status, napi_val) 117 | } 118 | } 119 | 120 | pub fn get_boolean(env: NapiEnv, value: bool) -> Result { 121 | unsafe { 122 | let mut napi_val: NapiValue = mem::uninitialized(); 123 | let status = napi_get_boolean(env, value, &mut napi_val); 124 | napi_either(env, status, napi_val) 125 | } 126 | } 127 | 128 | pub fn create_object(env: NapiEnv) -> Result { 129 | unsafe { 130 | let mut napi_val: NapiValue = mem::uninitialized(); 131 | let status = napi_create_object(env, &mut napi_val); 132 | napi_either(env, status, napi_val) 133 | } 134 | } 135 | 136 | pub fn create_array(env: NapiEnv) -> Result { 137 | unsafe { 138 | let mut napi_val: NapiValue = mem::uninitialized(); 139 | let status = napi_create_array(env, &mut napi_val); 140 | napi_either(env, status, napi_val) 141 | } 142 | } 143 | 144 | pub fn array_with_length(env: NapiEnv, size: usize) -> Result { 145 | unsafe { 146 | let mut napi_val: NapiValue = mem::uninitialized(); 147 | let status = napi_create_array_with_length(env, size, &mut napi_val); 148 | napi_either(env, status, napi_val) 149 | } 150 | } 151 | 152 | pub fn create_number(env: NapiEnv, value: f64) -> Result { 153 | unsafe { 154 | let mut napi_val: NapiValue = mem::uninitialized(); 155 | let status = napi_create_number(env, value, &mut napi_val); 156 | napi_either(env, status, napi_val) 157 | } 158 | } 159 | 160 | pub fn create_string_utf8(env: NapiEnv, val: T) -> Result 161 | where T: AsRef 162 | { 163 | let mut napi_val: NapiValue = 0; 164 | let converted_value = CString::new(val.as_ref())?; 165 | let status = unsafe { 166 | napi_create_string_utf8(env, 167 | converted_value.as_ptr(), 168 | usize::MAX, // indicates 0-terminated string 169 | &mut napi_val) 170 | }; 171 | napi_either(env, status, napi_val) 172 | } 173 | 174 | // pub fn napi_create_symbol(env: napi_env, description: napi_value, 175 | // result: *mut napi_value) -> napi_status; 176 | 177 | pub fn create_function(env: NapiEnv, utf8name: &str, f: F) -> Result 178 | where F: Fn(NapiEnv, NapiValue, T) -> R, 179 | T: FromNapiValues, 180 | R: IntoNapiValue 181 | { 182 | unsafe extern "C" fn wrapper(env: NapiEnv, cbinfo: napi_callback_info) -> NapiValue 183 | where F: Fn(NapiEnv, NapiValue, T) -> R, 184 | T: FromNapiValues, 185 | R: IntoNapiValue 186 | { 187 | let mut argc: usize = 16; 188 | let mut argv: [NapiValue; 16] = mem::uninitialized(); 189 | let mut user_data = ptr::null_mut(); 190 | let mut this: NapiValue = 0; 191 | let status = napi_get_cb_info(env, 192 | cbinfo, 193 | &mut argc, 194 | argv.as_mut_ptr(), 195 | &mut this, 196 | &mut user_data); 197 | assert!(status == napi_status::napi_ok); 198 | assert!(user_data != ptr::null_mut()); 199 | 200 | let args = 201 | T::from_napi_values(env, this, &argv[0..argc]).expect("cannot convert arguments"); 202 | 203 | let callback: Box> = Box::from_raw(user_data as *mut Option); 204 | 205 | let return_value = callback.expect("no callback found")(env, this, args); 206 | return_value 207 | .into_napi_value(env) 208 | .unwrap_or(get_undefined(env).unwrap()) 209 | } 210 | 211 | let boxed_f = Box::new(Some(f)); 212 | let user_data = Box::into_raw(boxed_f) as *mut os::raw::c_void; 213 | let mut napi_val: NapiValue = 0; 214 | let name = CString::new(utf8name)?; 215 | let status = unsafe { 216 | napi_create_function(env, 217 | name.into_raw(), 218 | Some(wrapper::), 219 | user_data, 220 | &mut napi_val) 221 | }; 222 | napi_either(env, status, napi_val) 223 | } 224 | 225 | 226 | // pub fn napi_create_error(env: napi_env, msg: napi_value, 227 | // result: *mut napi_value) -> napi_status; 228 | 229 | 230 | // pub fn napi_create_type_error(env: napi_env, msg: napi_value, 231 | // result: *mut napi_value) -> napi_status; 232 | 233 | 234 | // pub fn napi_create_range_error(env: napi_env, msg: napi_value, 235 | // result: *mut napi_value) -> napi_status; 236 | 237 | 238 | pub fn type_of(env: NapiEnv, napi_value: NapiValue) -> Result { 239 | let mut napi_value_type = napi_valuetype::napi_undefined; 240 | let status = unsafe { napi_typeof(env, napi_value, &mut napi_value_type) }; 241 | napi_either(env, status, NapiValueType::from(napi_value_type)) 242 | } 243 | 244 | pub fn get_value_double(env: NapiEnv, value: NapiValue) -> Result { 245 | let mut result: f64 = f64::NAN; 246 | let status = unsafe { napi_get_value_double(env, value, &mut result) }; 247 | napi_either(env, status, result) 248 | } 249 | 250 | // pub fn napi_get_value_int32(env: napi_env, value: napi_value, 251 | // result: *mut i32) -> napi_status; 252 | 253 | 254 | pub fn get_value_uint32(env: NapiEnv, value: NapiValue) -> Result { 255 | let mut result: u32 = 0; 256 | let status = unsafe { napi_get_value_uint32(env, value, &mut result) }; 257 | napi_either(env, status, result) 258 | } 259 | 260 | pub fn get_value_int64(env: NapiEnv, value: NapiValue) -> Result { 261 | let mut result: i64 = 0; 262 | let status = unsafe { napi_get_value_int64(env, value, &mut result) }; 263 | napi_either(env, status, result) 264 | } 265 | 266 | 267 | 268 | pub fn get_value_bool(env: NapiEnv, value: NapiValue) -> Result { 269 | let mut result = false; 270 | let status = unsafe { napi_get_value_bool(env, value, &mut result) }; 271 | napi_either(env, status, result) 272 | } 273 | 274 | // pub fn napi_get_value_string_latin1(env: napi_env, value: napi_value, 275 | // buf: *mut ::std::os::raw::c_char, 276 | // bufsize: usize, result: *mut usize) 277 | // -> napi_status; 278 | 279 | 280 | pub fn get_value_string_utf8(env: NapiEnv, value: NapiValue) -> Result { 281 | let mut size: usize = 0; 282 | // obtain string length in bytes to determine buffer size 283 | let size_status = 284 | unsafe { napi_get_value_string_utf8(env, value, ptr::null_mut(), 0, &mut size) }; 285 | napi_either(env, size_status, size)?; 286 | let mut buffer: Vec = Vec::with_capacity(size + 1); 287 | let mut written: usize = 0; 288 | let status = unsafe { 289 | napi_get_value_string_utf8(env, 290 | value, 291 | buffer.as_mut_ptr() as *mut i8, 292 | size + 1, 293 | &mut written) 294 | }; 295 | match written == size { 296 | true => { 297 | napi_either(env, status, unsafe { 298 | buffer.set_len(size); 299 | String::from_utf8(buffer)? 300 | }) 301 | } 302 | false => { 303 | Err(NapiError { 304 | error_message: format!("buffer size mismatch, expected {}, got {}", 305 | size, 306 | written), 307 | engine_error_code: 0, 308 | error_code: NapiErrorType::GenericFailure, 309 | }) 310 | } 311 | } 312 | } 313 | 314 | 315 | 316 | // pub fn napi_get_value_string_utf16(env: napi_env, value: napi_value, 317 | // buf: *mut char16_t, bufsize: usize, 318 | // result: *mut usize) -> napi_status; 319 | 320 | 321 | // pub fn napi_coerce_to_bool(env: napi_env, value: napi_value, 322 | // result: *mut napi_value) -> napi_status; 323 | 324 | 325 | // pub fn napi_coerce_to_number(env: napi_env, value: napi_value, 326 | // result: *mut napi_value) -> napi_status; 327 | 328 | 329 | // pub fn napi_coerce_to_object(env: napi_env, value: napi_value, 330 | // result: *mut napi_value) -> napi_status; 331 | 332 | 333 | // pub fn napi_coerce_to_string(env: napi_env, value: napi_value, 334 | // result: *mut napi_value) -> napi_status; 335 | 336 | 337 | // pub fn napi_get_prototype(env: napi_env, object: napi_value, 338 | // result: *mut napi_value) -> napi_status; 339 | 340 | 341 | // pub fn napi_get_property_names(env: napi_env, object: napi_value, 342 | // result: *mut napi_value) -> napi_status; 343 | 344 | 345 | // pub fn napi_set_property(env: napi_env, object: napi_value, 346 | // key: napi_value, value: napi_value) 347 | // -> napi_status; 348 | 349 | 350 | // pub fn napi_has_property(env: napi_env, object: napi_value, 351 | // key: napi_value, result: *mut bool) 352 | // -> napi_status; 353 | 354 | 355 | // pub fn napi_get_property(env: napi_env, object: napi_value, 356 | // key: napi_value, result: *mut napi_value) 357 | // -> napi_status; 358 | 359 | 360 | // pub fn napi_set_named_property(env: napi_env, object: napi_value, 361 | // utf8name: *const ::std::os::raw::c_char, 362 | // value: napi_value) -> napi_status; 363 | pub fn set_named_property(env: NapiEnv, 364 | target: NapiValue, 365 | name: &str, 366 | value: NapiValue) 367 | -> Result<()> { 368 | let status = 369 | unsafe { napi_set_named_property(env, target, CString::new(name)?.as_ptr(), value) }; 370 | napi_either(env, status, ()) 371 | } 372 | 373 | // pub fn napi_has_named_property(env: napi_env, object: napi_value, 374 | // utf8name: *const ::std::os::raw::c_char, 375 | // result: *mut bool) -> napi_status; 376 | 377 | pub fn get_named_property(env: NapiEnv, object: NapiValue, name: &str) -> Result { 378 | let mut result: NapiValue = 0; 379 | let status = 380 | unsafe { napi_get_named_property(env, object, CString::new(name)?.as_ptr(), &mut result) }; 381 | napi_either(env, status, result) 382 | } 383 | 384 | pub fn set_element(env: NapiEnv, array: NapiValue, index: usize, value: NapiValue) -> Result<()> { 385 | let status = unsafe { napi_set_element(env, array, index as u32, value) }; 386 | napi_either(env, status, ()) 387 | } 388 | 389 | // pub fn napi_has_element(env: napi_env, object: napi_value, index: u32, 390 | // result: *mut bool) -> napi_status; 391 | 392 | 393 | pub fn get_element(env: NapiEnv, array: NapiValue, index: usize) -> Result { 394 | let mut result: NapiValue = 0; 395 | let status = unsafe { napi_get_element(env, array, index as u32, &mut result) }; 396 | napi_either(env, status, result) 397 | } 398 | 399 | // pub fn napi_define_properties(env: napi_env, object: napi_value, 400 | // property_count: usize, 401 | // properties: *const napi_property_descriptor) 402 | // -> napi_status; 403 | 404 | 405 | pub fn is_array(env: NapiEnv, value: NapiValue) -> Result { 406 | let mut result: bool = false; 407 | let status = unsafe { napi_is_array(env, value, &mut result) }; 408 | napi_either(env, status, result) 409 | } 410 | 411 | pub fn get_array_length(env: NapiEnv, value: NapiValue) -> Result { 412 | let mut result: u32 = 0; 413 | let status = unsafe { napi_get_array_length(env, value, &mut result) }; 414 | napi_either(env, status, result as usize) 415 | } 416 | 417 | // pub fn napi_strict_equals(env: napi_env, lhs: napi_value, rhs: napi_value, 418 | // result: *mut bool) -> napi_status; 419 | 420 | 421 | // pub fn napi_call_function(env: napi_env, recv: napi_value, 422 | // func: napi_value, argc: usize, 423 | // argv: *const napi_value, 424 | // result: *mut napi_value) -> napi_status; 425 | pub fn call_function(env: NapiEnv, 426 | recv: NapiValue, 427 | func: NapiValue, 428 | args: &[NapiValue]) 429 | -> Result { 430 | let mut result: NapiValue = 0; 431 | let status = 432 | unsafe { napi_call_function(env, recv, func, args.len(), args.as_ptr(), &mut result) }; 433 | napi_either(env, status, result) 434 | } 435 | 436 | 437 | 438 | // pub fn napi_new_instance(env: napi_env, constructor: napi_value, 439 | // argc: usize, argv: *const napi_value, 440 | // result: *mut napi_value) -> napi_status; 441 | 442 | 443 | // pub fn napi_instanceof(env: napi_env, object: napi_value, 444 | // constructor: napi_value, result: *mut bool) 445 | // -> napi_status; 446 | 447 | 448 | // pub fn napi_make_callback(env: napi_env, recv: napi_value, 449 | // func: napi_value, argc: usize, 450 | // argv: *const napi_value, 451 | // result: *mut napi_value) -> napi_status; 452 | 453 | 454 | // pub fn napi_get_cb_info(env: napi_env, cbinfo: napi_callback_info, 455 | // argc: *mut usize, argv: *mut napi_value, 456 | // this_arg: *mut napi_value, 457 | // data: *mut *mut ::std::os::raw::c_void) 458 | // -> napi_status; 459 | 460 | 461 | // pub fn napi_is_construct_call(env: napi_env, cbinfo: napi_callback_info, 462 | // result: *mut bool) -> napi_status; 463 | 464 | 465 | // pub fn napi_define_class(env: napi_env, 466 | // utf8name: *const ::std::os::raw::c_char, 467 | // constructor: napi_callback, 468 | // data: *mut ::std::os::raw::c_void, 469 | // property_count: usize, 470 | // properties: *const napi_property_descriptor, 471 | // result: *mut napi_value) -> napi_status; 472 | 473 | 474 | // pub fn napi_wrap(env: napi_env, js_object: napi_value, 475 | // native_object: *mut ::std::os::raw::c_void, 476 | // finalize_cb: napi_finalize, 477 | // finalize_hint: *mut ::std::os::raw::c_void, 478 | // result: *mut napi_ref) -> napi_status; 479 | pub fn wrap(env: NapiEnv, js_object: NapiValue, native_object: Box) -> Result { 480 | let mut result: NapiRef = unsafe { mem::uninitialized() }; 481 | let status = unsafe { 482 | napi_wrap(env, 483 | js_object, 484 | Box::into_raw(native_object) as *mut ::std::os::raw::c_void, 485 | Some(finalize_box::), 486 | ptr::null_mut(), 487 | &mut result) 488 | }; 489 | napi_either(env, status, result) 490 | } 491 | 492 | // pub fn napi_unwrap(env: napi_env, js_object: napi_value, 493 | // result: *mut *mut ::std::os::raw::c_void) 494 | // -> napi_status; 495 | pub fn unwrap(env: NapiEnv, js_object: NapiValue) -> Result> { 496 | let mut result = ptr::null_mut(); 497 | let status = unsafe { napi_unwrap(env, js_object, &mut result) }; 498 | napi_either(env, status, unsafe { Box::::from_raw(result as *mut T) }) 499 | } 500 | 501 | // pub fn napi_create_external(env: napi_env, 502 | // data: *mut ::std::os::raw::c_void, 503 | // finalize_cb: napi_finalize, 504 | // finalize_hint: *mut ::std::os::raw::c_void, 505 | // result: *mut napi_value) -> napi_status; 506 | pub fn create_external(env: NapiEnv, t: Box) -> Result { 507 | 508 | let mut result: NapiValue = 0; 509 | let status = unsafe { 510 | napi_create_external(env, 511 | Box::into_raw(t) as *mut ::std::os::raw::c_void, 512 | Some(finalize_box::), 513 | ptr::null_mut(), 514 | &mut result) 515 | }; 516 | napi_either(env, status, result) 517 | } 518 | 519 | unsafe extern "C" fn finalize_box(_env: NapiEnv, 520 | finalize_data: *mut ::std::os::raw::c_void, 521 | _finalize_hint: *mut ::std::os::raw::c_void) { 522 | // move ownership into transient box in order to handle Drop, etc 523 | Box::from_raw(finalize_data as *mut T); 524 | } 525 | 526 | // pub fn napi_get_value_external(env: napi_env, value: napi_value, 527 | // result: *mut *mut ::std::os::raw::c_void) 528 | // -> napi_status; 529 | pub fn get_value_external(env: NapiEnv, value: NapiValue) -> Result> { 530 | let mut result = ptr::null_mut(); 531 | let status = unsafe { napi_get_value_external(env, value, &mut result) }; 532 | napi_either(env, status, unsafe { Box::::from_raw(result as *mut T) }) 533 | } 534 | 535 | 536 | // pub fn napi_create_reference(env: napi_env, value: napi_value, 537 | // initial_refcount: u32, result: *mut napi_ref) 538 | // -> napi_status; 539 | 540 | 541 | // pub fn napi_delete_reference(env: napi_env, ref_: napi_ref) 542 | // -> napi_status; 543 | 544 | 545 | // pub fn napi_reference_ref(env: napi_env, ref_: napi_ref, result: *mut u32) 546 | // -> napi_status; 547 | 548 | 549 | // pub fn napi_reference_unref(env: napi_env, ref_: napi_ref, 550 | // result: *mut u32) -> napi_status; 551 | 552 | 553 | // pub fn napi_get_reference_value(env: napi_env, ref_: napi_ref, 554 | // result: *mut napi_value) -> napi_status; 555 | 556 | 557 | // pub fn napi_open_handle_scope(env: napi_env, 558 | // result: *mut napi_handle_scope) 559 | // -> napi_status; 560 | 561 | 562 | // pub fn napi_close_handle_scope(env: napi_env, scope: napi_handle_scope) 563 | // -> napi_status; 564 | 565 | 566 | // pub fn napi_open_escapable_handle_scope(env: napi_env, 567 | // result: 568 | // *mut napi_escapable_handle_scope) 569 | // -> napi_status; 570 | 571 | 572 | // pub fn napi_close_escapable_handle_scope(env: napi_env, 573 | // scope: 574 | // napi_escapable_handle_scope) 575 | // -> napi_status; 576 | 577 | 578 | // pub fn napi_escape_handle(env: napi_env, 579 | // scope: napi_escapable_handle_scope, 580 | // escapee: napi_value, result: *mut napi_value) 581 | // -> napi_status; 582 | 583 | 584 | // pub fn napi_throw(env: napi_env, error: napi_value) -> napi_status; 585 | 586 | 587 | // pub fn napi_throw_error(env: napi_env, msg: *const ::std::os::raw::c_char) 588 | // -> napi_status; 589 | 590 | 591 | // pub fn napi_throw_type_error(env: napi_env, 592 | // msg: *const ::std::os::raw::c_char) 593 | // -> napi_status; 594 | 595 | 596 | // pub fn napi_throw_range_error(env: napi_env, 597 | // msg: *const ::std::os::raw::c_char) 598 | // -> napi_status; 599 | 600 | 601 | // pub fn napi_is_error(env: napi_env, value: napi_value, result: *mut bool) 602 | // -> napi_status; 603 | 604 | 605 | // pub fn napi_is_exception_pending(env: napi_env, result: *mut bool) 606 | // -> napi_status; 607 | 608 | 609 | // pub fn napi_get_and_clear_last_exception(env: napi_env, 610 | // result: *mut napi_value) 611 | // -> napi_status; 612 | 613 | 614 | // pub fn napi_create_buffer(env: napi_env, length: usize, 615 | // data: *mut *mut ::std::os::raw::c_void, 616 | // result: *mut napi_value) -> napi_status; 617 | 618 | 619 | // pub fn napi_create_external_buffer(env: napi_env, length: usize, 620 | // data: *mut ::std::os::raw::c_void, 621 | // finalize_cb: napi_finalize, 622 | // finalize_hint: 623 | // *mut ::std::os::raw::c_void, 624 | // result: *mut napi_value) 625 | // -> napi_status; 626 | 627 | 628 | // pub fn napi_create_buffer_copy(env: napi_env, length: usize, 629 | // data: *const ::std::os::raw::c_void, 630 | // result_data: 631 | // *mut *mut ::std::os::raw::c_void, 632 | // result: *mut napi_value) -> napi_status; 633 | 634 | 635 | // pub fn napi_is_buffer(env: napi_env, value: napi_value, result: *mut bool) 636 | // -> napi_status; 637 | 638 | 639 | // pub fn napi_get_buffer_info(env: napi_env, value: napi_value, 640 | // data: *mut *mut ::std::os::raw::c_void, 641 | // length: *mut usize) -> napi_status; 642 | 643 | 644 | // pub fn napi_is_arraybuffer(env: napi_env, value: napi_value, 645 | // result: *mut bool) -> napi_status; 646 | 647 | 648 | // pub fn napi_create_arraybuffer(env: napi_env, byte_length: usize, 649 | // data: *mut *mut ::std::os::raw::c_void, 650 | // result: *mut napi_value) -> napi_status; 651 | 652 | 653 | // pub fn napi_create_external_arraybuffer(env: napi_env, 654 | // external_data: 655 | // *mut ::std::os::raw::c_void, 656 | // byte_length: usize, 657 | // finalize_cb: napi_finalize, 658 | // finalize_hint: 659 | // *mut ::std::os::raw::c_void, 660 | // result: *mut napi_value) 661 | // -> napi_status; 662 | 663 | 664 | // pub fn napi_get_arraybuffer_info(env: napi_env, arraybuffer: napi_value, 665 | // data: *mut *mut ::std::os::raw::c_void, 666 | // byte_length: *mut usize) -> napi_status; 667 | 668 | 669 | // pub fn napi_is_typedarray(env: napi_env, value: napi_value, 670 | // result: *mut bool) -> napi_status; 671 | 672 | 673 | // pub fn napi_create_typedarray(env: napi_env, type_: napi_typedarray_type, 674 | // length: usize, arraybuffer: napi_value, 675 | // byte_offset: usize, result: *mut napi_value) 676 | // -> napi_status; 677 | 678 | 679 | // pub fn napi_get_typedarray_info(env: napi_env, typedarray: napi_value, 680 | // type_: *mut napi_typedarray_type, 681 | // length: *mut usize, 682 | // data: *mut *mut ::std::os::raw::c_void, 683 | // arraybuffer: *mut napi_value, 684 | // byte_offset: *mut usize) -> napi_status; 685 | 686 | 687 | // pub fn napi_create_async_work(env: napi_env, 688 | // execute: napi_async_execute_callback, 689 | // complete: napi_async_complete_callback, 690 | // data: *mut ::std::os::raw::c_void, 691 | // result: *mut napi_async_work) 692 | // -> napi_status; 693 | 694 | 695 | // pub fn napi_delete_async_work(env: napi_env, work: napi_async_work) 696 | // -> napi_status; 697 | 698 | 699 | // pub fn napi_queue_async_work(env: napi_env, work: napi_async_work) 700 | // -> napi_status; 701 | 702 | 703 | // pub fn napi_cancel_async_work(env: napi_env, work: napi_async_work) 704 | // -> napi_status; 705 | -------------------------------------------------------------------------------- /src/napi_futures.rs: -------------------------------------------------------------------------------- 1 | use std::boxed::Box; 2 | 3 | use napi::{NapiEnv, NapiValue, call_function}; 4 | use napi_value::{IntoNapiValue, FromNapiValues}; 5 | use error::Result; 6 | 7 | pub struct ThenArgs { 8 | pub on_fulfilled: Box, 9 | pub on_rejected: Box, 10 | } 11 | 12 | #[allow(unused_variables, unused_must_use)] 13 | impl FromNapiValues for ThenArgs 14 | where T: IntoNapiValue 15 | { 16 | fn from_napi_values(env: NapiEnv, this: NapiValue, values: &[NapiValue]) -> Result { 17 | let fulfilled_function = values[0].clone(); 18 | Ok(ThenArgs { 19 | on_fulfilled: Box::new(move |env, this, args| { 20 | let napi_args = [args.into_napi_value(env).unwrap()]; 21 | call_function(env, this, fulfilled_function, &napi_args); 22 | }), 23 | on_rejected: Box::new(|_, _, _| { 24 | println!("onrejected called"); 25 | }), 26 | }) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/napi_value.rs: -------------------------------------------------------------------------------- 1 | use napi; 2 | use futures::future; 3 | use futures::future::Future; 4 | 5 | use napi::NapiValueType; 6 | use error::{Result, NapiError, NapiErrorType}; 7 | use napi_futures; 8 | 9 | pub trait FromNapiValue: Sized { 10 | fn from_napi_values(napi::NapiEnv, napi::NapiValue) -> Result; 11 | } 12 | 13 | pub trait FromNapiValues: Sized { 14 | fn from_napi_values(napi::NapiEnv, napi::NapiValue, &[napi::NapiValue]) -> Result; 15 | } 16 | 17 | 18 | macro_rules! impl_from_napi_values { 19 | ($t:ty, $from:expr, $get_value:expr) => { 20 | impl FromNapiValues for $t { 21 | fn from_napi_values(env: napi::NapiEnv, _: napi::NapiValue, napi_values: &[napi::NapiValue]) -> Result<$t> { 22 | check_napi_args_length(env, napi_values, 1)?; 23 | let value = napi_values[0]; 24 | check_napi_type(env, $from, value)?; 25 | $get_value(env, value) 26 | } 27 | } 28 | } 29 | } 30 | 31 | impl_from_napi_values!(String, NapiValueType::String, napi::get_value_string_utf8); 32 | impl_from_napi_values!(i64, NapiValueType::Number, napi::get_value_int64); 33 | impl_from_napi_values!(u64, NapiValueType::Number, get_value_uint64); 34 | impl_from_napi_values!(bool, NapiValueType::Boolean, napi::get_value_bool); 35 | impl_from_napi_values!(f64, NapiValueType::Number, napi::get_value_double); 36 | 37 | 38 | fn get_value_uint64(env: napi::NapiEnv, value: napi::NapiValue) -> Result { 39 | napi::get_value_uint32(env, value).map(|x| x as u64) 40 | } 41 | 42 | impl FromNapiValues for () { 43 | fn from_napi_values(_: napi::NapiEnv, _: napi::NapiValue, _: &[napi::NapiValue]) -> Result { 44 | Ok(()) 45 | } 46 | } 47 | 48 | impl FromNapiValues for Vec where T: FromNapiValues { 49 | fn from_napi_values(env: napi::NapiEnv, this: napi::NapiValue, napi_values: &[napi::NapiValue]) -> Result { 50 | check_napi_args_length(env, napi_values, 1)?; 51 | let value = napi_values[0]; 52 | 53 | if !napi::is_array(env, value)? { 54 | Err(NapiError{error_message: "expected array".to_string(), 55 | engine_error_code: 0, 56 | error_code: NapiErrorType::InvalidArg, 57 | }) 58 | } else { 59 | let size = napi::get_array_length(env, value)?; 60 | let mut result = Vec::with_capacity(size); 61 | for i in 0..size { 62 | let ival = napi::get_element(env, value, i)?; 63 | result.push(FromNapiValues::from_napi_values(env, this, &[ival])?); 64 | } 65 | Ok(result) 66 | } 67 | } 68 | } 69 | 70 | fn check_napi_args_length(_env: napi::NapiEnv, napi_values: &[napi::NapiValue], expected_length: usize) -> Result<()> { 71 | let values_length = napi_values.len(); 72 | if values_length == expected_length { 73 | Ok(()) 74 | } else { 75 | Err(NapiError { 76 | error_message: format!("expected {} argument, got {}", expected_length, values_length), 77 | engine_error_code: 0, 78 | error_code: NapiErrorType::InvalidArg, 79 | }) 80 | } 81 | } 82 | 83 | fn check_napi_type(env: napi::NapiEnv, expected_type: NapiValueType, value: napi::NapiValue) -> Result<()> { 84 | let value_type = napi::type_of(env, value)?; 85 | if expected_type == value_type { 86 | Ok(()) 87 | } else { 88 | Err(NapiError { 89 | error_message: format!("expected argument to be of type {:?}, but found it to be of type {:?}", expected_type, value_type), 90 | engine_error_code: 0, 91 | error_code: NapiErrorType::InvalidArg, 92 | }) 93 | } 94 | } 95 | 96 | 97 | pub trait IntoNapiValue { 98 | fn into_napi_value(self, env: napi::NapiEnv) -> Result; 99 | } 100 | 101 | macro_rules! impl_into_napi_values { 102 | ($t:ty, $get_value:expr) => { 103 | impl IntoNapiValue for $t { 104 | fn into_napi_value(self, env: napi::NapiEnv) -> Result { 105 | $get_value(env, self) 106 | } 107 | } 108 | } 109 | } 110 | 111 | impl_into_napi_values!((), |env, _| napi::get_undefined(env)); 112 | impl_into_napi_values!(String, napi::create_string_utf8); 113 | 114 | impl<'a> IntoNapiValue for &'a str { 115 | fn into_napi_value(self, env: napi::NapiEnv) -> Result { 116 | napi::create_string_utf8(env, self) 117 | } 118 | } 119 | 120 | impl_into_napi_values!(u8, |env, s| napi::create_number(env, s as f64)); 121 | impl_into_napi_values!(u16, |env, s| napi::create_number(env, s as f64)); 122 | impl_into_napi_values!(u32, |env, s| napi::create_number(env, s as f64)); 123 | impl_into_napi_values!(u64, |env, s| napi::create_number(env, s as f64)); 124 | 125 | impl_into_napi_values!(i8, |env, s| napi::create_number(env, s as f64)); 126 | impl_into_napi_values!(i16, |env, s| napi::create_number(env, s as f64)); 127 | impl_into_napi_values!(i32, |env, s| napi::create_number(env, s as f64)); 128 | impl_into_napi_values!(i64, |env, s| napi::create_number(env, s as f64)); 129 | 130 | impl_into_napi_values!(f32, |env, s| napi::create_number(env, s as f64)); 131 | impl_into_napi_values!(f64, |env, s| napi::create_number(env, s as f64)); 132 | 133 | impl_into_napi_values!(bool, napi::get_boolean); 134 | 135 | 136 | impl<'a, T> IntoNapiValue for &'a [T] 137 | where T: IntoNapiValue + Clone 138 | { 139 | fn into_napi_value(self, env: napi::NapiEnv) -> Result { 140 | let array = napi::array_with_length(env, self.len())?; 141 | 142 | let mut index: usize = 0; 143 | for item in self.into_iter() { 144 | let converted_item = item.clone().into_napi_value(env)?; 145 | napi::set_element(env, array, index, converted_item)?; 146 | index = index + 1; 147 | } 148 | Ok(array) 149 | } 150 | } 151 | 152 | impl IntoNapiValue for Vec 153 | where T: IntoNapiValue 154 | { 155 | fn into_napi_value(self, env: napi::NapiEnv) -> Result { 156 | let array = napi::array_with_length(env, self.len())?; 157 | 158 | let mut index: usize = 0; 159 | for item in self.into_iter() { 160 | let converted_item = item.into_napi_value(env)?; 161 | napi::set_element(env, array, index, converted_item)?; 162 | index = index + 1; 163 | } 164 | Ok(array) 165 | } 166 | } 167 | 168 | impl IntoNapiValue for future::BoxFuture 169 | where T: IntoNapiValue + 'static, 170 | E: IntoNapiValue + 'static, 171 | { 172 | fn into_napi_value(self, env: napi::NapiEnv) -> Result { 173 | let obj = napi::create_object(env)?; 174 | let state = napi::create_external(env, Box::new(self))?; 175 | let then = napi::create_function(env, "then", move |env, this, then_args: napi_futures::ThenArgs| { 176 | let state = napi::get_named_property(env, this, "state").unwrap(); 177 | let future: Box> = napi::get_value_external(env, state).unwrap(); 178 | future.then(move |result| { 179 | match result { 180 | Ok(val) => (then_args.on_fulfilled)(env, this, val), 181 | Err(err) => (then_args.on_rejected)(env, this, err) 182 | } 183 | future::result::<(),()>(Ok(())).boxed() 184 | }).boxed().wait().unwrap(); 185 | })?; 186 | napi::set_named_property(env, obj, "then", then)?; 187 | napi::set_named_property(env, obj, "state", state)?; 188 | Ok(obj) 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /tests/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | yarn-error.log 3 | node-api 4 | -------------------------------------------------------------------------------- /tests/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tests" 3 | version = "0.1.0" 4 | authors = ["Jupp Müller "] 5 | license = "MIT" 6 | 7 | [lib] 8 | name = "tests" 9 | crate-type = ["cdylib"] 10 | 11 | [dependencies] 12 | node-api = {path = ".."} 13 | futures = "0.1.13" 14 | tokio-core = "0.1" 15 | -------------------------------------------------------------------------------- /tests/index.js: -------------------------------------------------------------------------------- 1 | const nt = require('./node-api'); 2 | const expect = require('chai').expect; 3 | 4 | describe("node-api", function() { 5 | describe("function returns", function() { 6 | it("can return objects", function() { 7 | expect(nt.returns_objects()).to.deep.equal({'foo': 'hello', bar: 42}); 8 | }); 9 | it("can return strings", function() { 10 | expect(nt.returns_strings()).to.equal("returned_string"); 11 | }); 12 | it("can return numbers", function() { 13 | expect(nt.returns_numbers()).to.equal(42); 14 | }); 15 | it("can return booleans", function() { 16 | expect(nt.returns_booleans()).to.equal(true); 17 | }); 18 | it("can return arrays", function() { 19 | expect(nt.returns_arrays()).to.deep.equal(["one", "two", "three"]); 20 | }); 21 | }); 22 | describe("function arguments", function() { 23 | it("can receive objects", function() { 24 | const object = {'foo': 'hello', bar: 42}; 25 | expect(nt.receives_objects(object)).to.deep.equal(object); 26 | }); 27 | it("can receive strings", function() { 28 | const str = "hello world!"; 29 | expect(nt.receives_strings(str)).to.deep.equal(str); 30 | }); 31 | it("can receive booleans", function() { 32 | const b = true; 33 | expect(nt.receives_booleans(b)).to.deep.equal(b); 34 | }); 35 | it("can receive f64", function() { 36 | const n = 1.1; 37 | expect(nt.receives_f64(n)).to.deep.equal(n); 38 | }); 39 | it("can receive u64", function() { 40 | const n = 123; 41 | expect(nt.receives_u64(n)).to.deep.equal(n); 42 | }); 43 | it("can receive i64", function() { 44 | const i = -42; 45 | expect(nt.receives_i64(i)).to.deep.equal(i); 46 | }); 47 | it("can receive arrays", function() { 48 | const arr = ["one", "two", "three"]; 49 | expect(nt.receives_arrays(arr)).to.deep.equal(arr); 50 | }); 51 | }); 52 | describe("promises", function() { 53 | it("returns a void promise", function(done) { 54 | const p = nt.returns_promises(); 55 | p.then(function() { 56 | done(); 57 | }); 58 | }); 59 | }); 60 | }); 61 | -------------------------------------------------------------------------------- /tests/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tests", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "dependencies": { 6 | "assertion-error": { 7 | "version": "1.0.2", 8 | "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.2.tgz", 9 | "integrity": "sha1-E8pRXYYgbaC6xm6DTdOX2HWBCUw=" 10 | }, 11 | "balanced-match": { 12 | "version": "0.4.2", 13 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", 14 | "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=" 15 | }, 16 | "brace-expansion": { 17 | "version": "1.1.7", 18 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.7.tgz", 19 | "integrity": "sha1-Pv/DxQ4ABTH7cg6v+A8K6O8jz1k=" 20 | }, 21 | "browser-stdout": { 22 | "version": "1.3.0", 23 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", 24 | "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=" 25 | }, 26 | "chai": { 27 | "version": "3.5.0", 28 | "resolved": "https://registry.npmjs.org/chai/-/chai-3.5.0.tgz", 29 | "integrity": "sha1-TQJjewZ/6Vi9v906QOxW/vc3Mkc=" 30 | }, 31 | "commander": { 32 | "version": "2.9.0", 33 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", 34 | "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=" 35 | }, 36 | "concat-map": { 37 | "version": "0.0.1", 38 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 39 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 40 | }, 41 | "debug": { 42 | "version": "2.6.0", 43 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.0.tgz", 44 | "integrity": "sha1-vFlryr52F/Edn6FTYe3tVgi4SZs=" 45 | }, 46 | "deep-eql": { 47 | "version": "0.1.3", 48 | "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", 49 | "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=", 50 | "dependencies": { 51 | "type-detect": { 52 | "version": "0.1.1", 53 | "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz", 54 | "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=" 55 | } 56 | } 57 | }, 58 | "diff": { 59 | "version": "3.2.0", 60 | "resolved": "https://registry.npmjs.org/diff/-/diff-3.2.0.tgz", 61 | "integrity": "sha1-yc45Okt8vQsFinJck98pkCeGj/k=" 62 | }, 63 | "escape-string-regexp": { 64 | "version": "1.0.5", 65 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 66 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" 67 | }, 68 | "fs.realpath": { 69 | "version": "1.0.0", 70 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 71 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" 72 | }, 73 | "glob": { 74 | "version": "7.1.1", 75 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", 76 | "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=" 77 | }, 78 | "graceful-readlink": { 79 | "version": "1.0.1", 80 | "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", 81 | "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=" 82 | }, 83 | "growl": { 84 | "version": "1.9.2", 85 | "resolved": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz", 86 | "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=" 87 | }, 88 | "has-flag": { 89 | "version": "1.0.0", 90 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", 91 | "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" 92 | }, 93 | "inflight": { 94 | "version": "1.0.6", 95 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 96 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=" 97 | }, 98 | "inherits": { 99 | "version": "2.0.3", 100 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 101 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 102 | }, 103 | "json3": { 104 | "version": "3.3.2", 105 | "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", 106 | "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=" 107 | }, 108 | "lodash._baseassign": { 109 | "version": "3.2.0", 110 | "resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz", 111 | "integrity": "sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4=" 112 | }, 113 | "lodash._basecopy": { 114 | "version": "3.0.1", 115 | "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", 116 | "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=" 117 | }, 118 | "lodash._basecreate": { 119 | "version": "3.0.3", 120 | "resolved": "https://registry.npmjs.org/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz", 121 | "integrity": "sha1-G8ZhYU2qf8MRt9A78WgGoCE8+CE=" 122 | }, 123 | "lodash._getnative": { 124 | "version": "3.9.1", 125 | "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", 126 | "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=" 127 | }, 128 | "lodash._isiterateecall": { 129 | "version": "3.0.9", 130 | "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", 131 | "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=" 132 | }, 133 | "lodash.create": { 134 | "version": "3.1.1", 135 | "resolved": "https://registry.npmjs.org/lodash.create/-/lodash.create-3.1.1.tgz", 136 | "integrity": "sha1-1/KEnw29p+BGgruM1yqwIkYd6+c=" 137 | }, 138 | "lodash.isarguments": { 139 | "version": "3.1.0", 140 | "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", 141 | "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=" 142 | }, 143 | "lodash.isarray": { 144 | "version": "3.0.4", 145 | "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", 146 | "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=" 147 | }, 148 | "lodash.keys": { 149 | "version": "3.1.2", 150 | "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", 151 | "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=" 152 | }, 153 | "minimatch": { 154 | "version": "3.0.4", 155 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 156 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==" 157 | }, 158 | "minimist": { 159 | "version": "0.0.8", 160 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 161 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" 162 | }, 163 | "mkdirp": { 164 | "version": "0.5.1", 165 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 166 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=" 167 | }, 168 | "mocha": { 169 | "version": "github:jupp0r/mocha#63f0cc03240e9b9235b7f7cd899389fd7fdc9f90" 170 | }, 171 | "ms": { 172 | "version": "0.7.2", 173 | "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", 174 | "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=" 175 | }, 176 | "once": { 177 | "version": "1.4.0", 178 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 179 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=" 180 | }, 181 | "path-is-absolute": { 182 | "version": "1.0.1", 183 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 184 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" 185 | }, 186 | "supports-color": { 187 | "version": "3.1.2", 188 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.1.2.tgz", 189 | "integrity": "sha1-cqJiiU2dQIuVbKBf83su2KbiotU=" 190 | }, 191 | "type-detect": { 192 | "version": "1.0.0", 193 | "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-1.0.0.tgz", 194 | "integrity": "sha1-diIXzAbbJY7EiQihKY6LlRIejqI=" 195 | }, 196 | "wrappy": { 197 | "version": "1.0.2", 198 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 199 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 200 | } 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /tests/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tests", 3 | "version": "1.0.0", 4 | "description": "tests for the nodi-api crate", 5 | "main": "index.js", 6 | "repository": "https://github.com/jupp0r/node-api", 7 | "author": "Jupp Müller ", 8 | "license": "MIT", 9 | "dependencies": { 10 | "chai": "^3.5.0", 11 | "mocha": "jupp0r/mocha#feature/enable-napi-modules" 12 | }, 13 | "scripts": { 14 | "preinstall": "cargo build", 15 | "postinstall": "mkdir -p node-api && (cp ../target/debug/libtests.dylib node-api/index.node || cp ../target/debug/libtests.so node-api/index.node || cp ../target/debug/libtests.dll node-api/index.node)", 16 | "pretest": "npm install", 17 | "test": " mocha --napi-modules index.js" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tests/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(link_args)] 2 | #[macro_use(napi_module)] 3 | extern crate node_api; 4 | extern crate futures; 5 | extern crate tokio_core; 6 | 7 | use node_api::{NapiEnv, NapiValue, FromNapiValues, IntoNapiValue}; 8 | use node_api::error::*; 9 | use node_api::{create_function, get_named_property, set_named_property, create_object, 10 | create_external}; 11 | 12 | use futures::future; 13 | use futures::Future; 14 | 15 | use tokio_core::reactor::Core; 16 | 17 | napi_module!("tests", register); 18 | 19 | #[no_mangle] 20 | pub extern "C" fn register(env: NapiEnv, 21 | exports: NapiValue, 22 | module: NapiValue, 23 | _priv: *mut std::os::raw::c_void) { 24 | // create_and_attach_event_loop(env, module); 25 | 26 | register_test(env, "returns_objects", exports, &returns_objects); 27 | register_test(env, "returns_strings", exports, &returns_strings); 28 | register_test(env, "returns_numbers", exports, &returns_numbers); 29 | register_test(env, "returns_booleans", exports, &returns_booleans); 30 | register_test(env, "returns_arrays", exports, &returns_arrays); 31 | 32 | register_test(env, "receives_objects", exports, &receives_objects); 33 | register_test(env, "receives_strings", exports, &receives_strings); 34 | register_test(env, "receives_booleans", exports, &receives_booleans); 35 | register_test(env, "receives_f64", exports, &receives_f64); 36 | register_test(env, "receives_u64", exports, &receives_u64); 37 | register_test(env, "receives_i64", exports, &receives_i64); 38 | register_test(env, "receives_arrays", exports, &receives_arrays); 39 | 40 | register_test(env, "returns_promises", exports, &returns_promises); 41 | } 42 | 43 | // fn create_and_attach_event_loop(env: NapiEnv, module: NapiValue) { 44 | // let core = Box::new(Core::new().unwrap()); 45 | // let core_js = node_api::create_external(env, core).unwrap(); 46 | //} 47 | 48 | fn register_test(env: NapiEnv, name: &str, exports: NapiValue, f: F) 49 | where F: Fn(NapiEnv, NapiValue, A) -> R, 50 | A: FromNapiValues, 51 | R: IntoNapiValue 52 | { 53 | let test = create_function(env, name, f).unwrap(); 54 | set_named_property(env, exports, name, test).unwrap(); 55 | } 56 | 57 | // returns objects 58 | fn returns_objects(_: NapiEnv, _: NapiValue, _: ()) -> Object { 59 | Object { 60 | foo: "hello".to_string(), 61 | bar: 42, 62 | } 63 | } 64 | 65 | #[derive(Debug)] 66 | struct Object { 67 | pub foo: String, 68 | pub bar: u64, 69 | } 70 | 71 | impl IntoNapiValue for Object { 72 | fn into_napi_value(self, env: NapiEnv) -> Result { 73 | let object = create_object(env)?; 74 | let foo = self.foo.into_napi_value(env)?; 75 | let bar = self.bar.into_napi_value(env)?; 76 | set_named_property(env, object, "foo", foo)?; 77 | set_named_property(env, object, "bar", bar)?; 78 | Ok(object) 79 | } 80 | } 81 | 82 | impl FromNapiValues for Object { 83 | fn from_napi_values(env: NapiEnv, 84 | this: NapiValue, 85 | napi_values: &[NapiValue]) 86 | -> Result { 87 | match napi_values.len() { 88 | 1 => { 89 | let object = napi_values[0]; 90 | let foo_property = get_named_property(env, object, "foo")?; 91 | let bar_property = get_named_property(env, object, "bar")?; 92 | Ok(Object { 93 | foo: FromNapiValues::from_napi_values(env, this, &[foo_property])?, 94 | bar: FromNapiValues::from_napi_values(env, this, &[bar_property])?, 95 | }) 96 | } 97 | n => { 98 | Err(NapiError { 99 | error_message: "expected one argument, got ".to_string() + &n.to_string(), 100 | engine_error_code: 0, 101 | error_code: NapiErrorType::InvalidArg, 102 | }) 103 | } 104 | } 105 | } 106 | } 107 | 108 | #[derive(Debug)] 109 | struct ReceivesObjectsArgs { 110 | pub arg0: Object, 111 | } 112 | 113 | impl FromNapiValues for ReceivesObjectsArgs { 114 | fn from_napi_values(env: NapiEnv, 115 | this: NapiValue, 116 | napi_values: &[NapiValue]) 117 | -> Result { 118 | let arg0 = Object::from_napi_values(env, this, napi_values)?; 119 | Ok(ReceivesObjectsArgs { arg0: arg0 }) 120 | } 121 | } 122 | 123 | fn returns_strings(_: NapiEnv, _: NapiValue, _: ()) -> String { 124 | "returned_string".to_string() 125 | } 126 | 127 | fn returns_numbers(_: NapiEnv, _: NapiValue, _: ()) -> u64 { 128 | 42 129 | } 130 | 131 | fn returns_booleans(_: NapiEnv, _: NapiValue, _: ()) -> bool { 132 | true 133 | } 134 | 135 | fn returns_arrays(_: NapiEnv, _: NapiValue, _: ()) -> Vec<&'static str> { 136 | vec!["one", "two", "three"] 137 | } 138 | 139 | fn receives_objects(_: NapiEnv, _: NapiValue, args: ReceivesObjectsArgs) -> Object { 140 | args.arg0 141 | } 142 | 143 | fn receives_strings(_: NapiEnv, _: NapiValue, arg: String) -> String { 144 | arg 145 | } 146 | 147 | fn receives_booleans(_: NapiEnv, _: NapiValue, arg: bool) -> bool { 148 | arg 149 | } 150 | 151 | fn receives_f64(_: NapiEnv, _: NapiValue, arg: f64) -> f64 { 152 | arg 153 | } 154 | 155 | fn receives_u64(_: NapiEnv, _: NapiValue, arg: u64) -> u64 { 156 | arg 157 | } 158 | 159 | fn receives_i64(_: NapiEnv, _: NapiValue, arg: i64) -> i64 { 160 | arg 161 | } 162 | 163 | fn receives_arrays(_: NapiEnv, _: NapiValue, arg: Vec) -> Vec { 164 | arg 165 | } 166 | 167 | fn returns_promises(_: NapiEnv, _: NapiValue, _arg: ()) -> futures::BoxFuture<(), ()> { 168 | future::ok(()).boxed() 169 | } 170 | -------------------------------------------------------------------------------- /tests/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | assertion-error@^1.0.1: 6 | version "1.0.2" 7 | resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.0.2.tgz#13ca515d86206da0bac66e834dd397d87581094c" 8 | 9 | balanced-match@^0.4.1: 10 | version "0.4.2" 11 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" 12 | 13 | brace-expansion@^1.1.7: 14 | version "1.1.7" 15 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.7.tgz#3effc3c50e000531fb720eaff80f0ae8ef23cf59" 16 | dependencies: 17 | balanced-match "^0.4.1" 18 | concat-map "0.0.1" 19 | 20 | browser-stdout@1.3.0: 21 | version "1.3.0" 22 | resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.0.tgz#f351d32969d32fa5d7a5567154263d928ae3bd1f" 23 | 24 | chai@^3.5.0: 25 | version "3.5.0" 26 | resolved "https://registry.yarnpkg.com/chai/-/chai-3.5.0.tgz#4d02637b067fe958bdbfdd3a40ec56fef7373247" 27 | dependencies: 28 | assertion-error "^1.0.1" 29 | deep-eql "^0.1.3" 30 | type-detect "^1.0.0" 31 | 32 | commander@2.9.0: 33 | version "2.9.0" 34 | resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" 35 | dependencies: 36 | graceful-readlink ">= 1.0.0" 37 | 38 | concat-map@0.0.1: 39 | version "0.0.1" 40 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 41 | 42 | debug@2.6.0: 43 | version "2.6.0" 44 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.0.tgz#bc596bcabe7617f11d9fa15361eded5608b8499b" 45 | dependencies: 46 | ms "0.7.2" 47 | 48 | deep-eql@^0.1.3: 49 | version "0.1.3" 50 | resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-0.1.3.tgz#ef558acab8de25206cd713906d74e56930eb69f2" 51 | dependencies: 52 | type-detect "0.1.1" 53 | 54 | diff@3.2.0: 55 | version "3.2.0" 56 | resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9" 57 | 58 | escape-string-regexp@1.0.5: 59 | version "1.0.5" 60 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 61 | 62 | fs.realpath@^1.0.0: 63 | version "1.0.0" 64 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 65 | 66 | glob@7.1.1: 67 | version "7.1.1" 68 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" 69 | dependencies: 70 | fs.realpath "^1.0.0" 71 | inflight "^1.0.4" 72 | inherits "2" 73 | minimatch "^3.0.2" 74 | once "^1.3.0" 75 | path-is-absolute "^1.0.0" 76 | 77 | "graceful-readlink@>= 1.0.0": 78 | version "1.0.1" 79 | resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" 80 | 81 | growl@1.9.2: 82 | version "1.9.2" 83 | resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f" 84 | 85 | has-flag@^1.0.0: 86 | version "1.0.0" 87 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" 88 | 89 | inflight@^1.0.4: 90 | version "1.0.6" 91 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 92 | dependencies: 93 | once "^1.3.0" 94 | wrappy "1" 95 | 96 | inherits@2: 97 | version "2.0.3" 98 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" 99 | 100 | json3@3.3.2: 101 | version "3.3.2" 102 | resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" 103 | 104 | lodash._baseassign@^3.0.0: 105 | version "3.2.0" 106 | resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e" 107 | dependencies: 108 | lodash._basecopy "^3.0.0" 109 | lodash.keys "^3.0.0" 110 | 111 | lodash._basecopy@^3.0.0: 112 | version "3.0.1" 113 | resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" 114 | 115 | lodash._basecreate@^3.0.0: 116 | version "3.0.3" 117 | resolved "https://registry.yarnpkg.com/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz#1bc661614daa7fc311b7d03bf16806a0213cf821" 118 | 119 | lodash._getnative@^3.0.0: 120 | version "3.9.1" 121 | resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" 122 | 123 | lodash._isiterateecall@^3.0.0: 124 | version "3.0.9" 125 | resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c" 126 | 127 | lodash.create@3.1.1: 128 | version "3.1.1" 129 | resolved "https://registry.yarnpkg.com/lodash.create/-/lodash.create-3.1.1.tgz#d7f2849f0dbda7e04682bb8cd72ab022461debe7" 130 | dependencies: 131 | lodash._baseassign "^3.0.0" 132 | lodash._basecreate "^3.0.0" 133 | lodash._isiterateecall "^3.0.0" 134 | 135 | lodash.isarguments@^3.0.0: 136 | version "3.1.0" 137 | resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" 138 | 139 | lodash.isarray@^3.0.0: 140 | version "3.0.4" 141 | resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" 142 | 143 | lodash.keys@^3.0.0: 144 | version "3.1.2" 145 | resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" 146 | dependencies: 147 | lodash._getnative "^3.0.0" 148 | lodash.isarguments "^3.0.0" 149 | lodash.isarray "^3.0.0" 150 | 151 | minimatch@^3.0.2: 152 | version "3.0.4" 153 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" 154 | dependencies: 155 | brace-expansion "^1.1.7" 156 | 157 | minimist@0.0.8: 158 | version "0.0.8" 159 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" 160 | 161 | mkdirp@0.5.1: 162 | version "0.5.1" 163 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" 164 | dependencies: 165 | minimist "0.0.8" 166 | 167 | mocha@jupp0r/mocha#feature/enable-napi-modules: 168 | version "3.4.1" 169 | resolved "https://codeload.github.com/jupp0r/mocha/tar.gz/63f0cc03240e9b9235b7f7cd899389fd7fdc9f90" 170 | dependencies: 171 | browser-stdout "1.3.0" 172 | commander "2.9.0" 173 | debug "2.6.0" 174 | diff "3.2.0" 175 | escape-string-regexp "1.0.5" 176 | glob "7.1.1" 177 | growl "1.9.2" 178 | json3 "3.3.2" 179 | lodash.create "3.1.1" 180 | mkdirp "0.5.1" 181 | supports-color "3.1.2" 182 | 183 | ms@0.7.2: 184 | version "0.7.2" 185 | resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.2.tgz#ae25cf2512b3885a1d95d7f037868d8431124765" 186 | 187 | once@^1.3.0: 188 | version "1.4.0" 189 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 190 | dependencies: 191 | wrappy "1" 192 | 193 | path-is-absolute@^1.0.0: 194 | version "1.0.1" 195 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 196 | 197 | supports-color@3.1.2: 198 | version "3.1.2" 199 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.1.2.tgz#72a262894d9d408b956ca05ff37b2ed8a6e2a2d5" 200 | dependencies: 201 | has-flag "^1.0.0" 202 | 203 | type-detect@0.1.1: 204 | version "0.1.1" 205 | resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-0.1.1.tgz#0ba5ec2a885640e470ea4e8505971900dac58822" 206 | 207 | type-detect@^1.0.0: 208 | version "1.0.0" 209 | resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-1.0.0.tgz#762217cc06db258ec48908a1298e8b95121e8ea2" 210 | 211 | wrappy@1: 212 | version "1.0.2" 213 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 214 | --------------------------------------------------------------------------------