diff --git a/docs/arguments.md b/docs/arguments.md deleted file mode 100644 index a2e293e0..00000000 --- a/docs/arguments.md +++ /dev/null @@ -1,149 +0,0 @@ ---- -id: arguments -title: Arguments -sidebar_label: Arguments ---- - -[Examples](https://github.com/neon-bindings/examples/tree/legacy/arguments) - -Neon provides built-in mechanisims for accessing the `arguments` object. - -Arguments can be passed from JS to Rust and be of any type. It is useful to assert that certain values are certain types. - -## Calling Functions by Indexes - -We first start by defining a function and exporting it by the name of `sayHi`: - -```rust -fn say_hi(mut cx: FunctionContext) {} - -register_module!(mut m, { - m.export_function("sayHi", say_hi) -}); -``` - -The following code takes the first argument passed to the `sayHi` function and throws if it cannot be cast to a function - -```rust -fn say_hi(mut cx: FunctionContext) -> JsResult { - let arg0 = cx.argument::(0)?; - // --snip-- -} -``` - -## Asserting Argument Types - -```rust -pub fn foo(mut cx: FunctionContext) -> JsResult { - cx.check_argument::(0)?; - cx.check_argument::(1)?; - Ok(cx.undefined()) -} -``` - -Now in our `./lib/index.js`: - -```js -const { foo } = require('../native'); -foo(); // fails -foo(12); // fails -foo('foobar'); // fails -foo('foobar', 12); // passes! -``` - -## Getting the Value of an Argument - -```rust -fn add1(mut cx: FunctionContext) -> JsResult { - // Attempt to cast the first argument to a JsNumber. Then - // get the value if cast is successul - let x = cx.argument::(0)?.value(); - Ok(cx.number(x + 1.0)) -} - -register_module!(mut m, { - m.export_function("add1", add1) -}); -``` - -## Getting the Number of Arguments - -This is a simple example of getting the length of `arguments` - -```rust -pub fn get_args_len(mut cx: FunctionContext) -> JsResult { - let args_length = cx.len(); - println!("{}", args_length); - Ok(cx.number(args_length)) -} - -register_module!(mut m, { - m.export_function("getArgsLen", get_args_len) -}); -``` - -Now in our `./lib/index.js` we add the following: - -```js -// ./lib/index.js -const { getArgsLen } = require('../native'); -getArgsLen(); // 0 -getArgsLen(1); // 1 -getArgsLen(1, 'foobar'); // 2 -``` - -## Optional Arguments - -Produces the `i`th argument, or `None` if `i` is greater than or equal to `self.len()`. - -```rust -pub fn args_opt(mut cx: FunctionContext) -> JsResult { - match cx.argument_opt(0) { - Some(arg) => { - // Throw if the argument exist and it cannot be downcasted - // to a number - let num = arg.downcast::().or_throw(&mut cx)?.value(); - println!"The 0th argument is {}", num); - }, - None => panic!("0th argument does not exist, out of bounds!") - } - Ok(cx.undefined()) -} -``` - -## Default Values - -Handling default values is similar to handling **Optional Arguments**: - -```rust -// --snip-- - -pub fn default_args(mut cx: FunctionContext) -> JsResult { - let age = match cx.argument_opt(0) { - Some(arg) => arg.downcast::().or_throw(&mut cx)?.value(), - // Default to 12 if no value is given - None => 12 as f64 - }; - - let name = match cx.argument_opt(1) { - Some(arg) => arg.downcast::().or_throw(&mut cx)?.value(), - // Default to 12 if no value is given - None => "John Doe".to_string() - }; - - println!("i am {} years old and my name is {}", age, name); - - Ok(cx.undefined()) -} -``` - -Here's how we'd call those functions: - -```js -// ./lib/index.js -const { defaultArgs } = require('../native'); - -defaultArgs(); // i am 12 years old and my name is John Doe -defaultArgs(22); // i am 22 years old and my name is John Doe -defaultArgs(22, 'Jane Doe'); // i am 22 years old and my name is Jane Doe -``` diff --git a/docs/arrays.md b/docs/arrays.md index 59c2a4d1..e751a7f6 100644 --- a/docs/arrays.md +++ b/docs/arrays.md @@ -4,91 +4,89 @@ title: Arrays sidebar_label: Arrays --- -[Examples](https://github.com/neon-bindings/examples/tree/legacy/arrays) +JavaScript **[arrays](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** are [objects](objects) that support storing properties indexed by integers. Neon exposes access to this class through the [`JsArray`](https://docs.rs/neon/latest/neon/types/struct.JsArray.html) type. -## Converting from `Vec` to `JsArray` +## Creating Arrays -Here is a simple example of converting a Rust `Vec` to a JS `Array` using `JsArray`: +The easiest way to create a new array is through the [`Context::empty_array()`](https://docs.rs/neon/latest/neon/context/trait.Context.html#method.empty_array) method: ```rust -fn convert_vec_to_array(mut cx: FunctionContext) -> JsResult { - let vec: Vec = Vec::with_capacity(100); - - // Create the JS array - let js_array = JsArray::new(&mut cx, vec.len() as u32); - - // Iterate over the Rust Vec and map each value in the Vec to the JS array - for (i, obj) in vec.iter().enumerate() { - let js_string = cx.string(obj); - js_array.set(&mut cx, i as u32, js_string).unwrap(); - } +let a: Handle = cx.empty_array(); +``` - Ok(js_array) -} +This is the equivalent of writing: +```javascript +const a = []; +``` +or +```javascript +const a = new Array(); ``` +in JavaScript. -## Converting from `JsArray` to `Vec` +## Indexed Properties -Converting to `JsArray` to `Vec` is pretty straightforward: +You can get and set _indexed properties_ of an array, i.e., properties with integer property keys, with the [`Object::get()`](https://docs.rs/neon/latest/neon/object/trait.Object.html#method.get) and [`Object::set()`](https://docs.rs/neon/latest/neon/object/trait.Object.html#method.set) methods: ```rust -fn convert_js_array_to_vec(mut cx: FunctionContext) -> JsResult { - // Take the first argument, which must be an array - let js_arr_handle: Handle = cx.argument(0)?; - // Convert a JsArray to a Rust Vec - let vec: Vec> = js_arr_handle.to_vec(&mut cx)?; - // Return the length of the Vec to JS - Ok(cx.number(vec.len() as f64)) -} +let a = cx.empty_array(); + +let s = cx.string("hello!"); + +a.set(&mut cx, 0, s)?; + +let v = a.get(&mut cx, 1)?; ``` -## Returning an empty element +This is equivalent to the JavaScript code: -```rust -pub fn return_js_array(mut cx: FunctionContext) -> JsResult { - Ok(cx.empty_array()) -} +```javascript +const a = []; + +const s = "hello!"; + +a[0] = s; + +const v = a[1]; ``` -## Adding elements to an array +## Extending an Array -This is an example of adding a number to a `JsArray` +The length of a JavaScript array is one more than its largest property index, which can be determined by calling the [`JsArray::len()`](https://docs.rs/neon/latest/neon/types/struct.JsArray.html#method.len) method. You can extend the length of array by adding a property at that index: ```rust -pub fn return_js_array_with_number(mut cx: FunctionContext) -> JsResult { - let array: Handle = JsArray::new(&mut cx, 1); - let n = cx.number(9000.0); - array.set(&mut cx, 0, n)?; - Ok(array) -} +let len = array.len(&mut cx)?; +array.set(&mut cx, len, value)?; ``` -And this is an example of adding a string to a `JsArray` +This is equivalent to the JavaScript code: -```rust -pub fn return_js_array_with_string(mut cx: FunctionContext) -> JsResult { - let array: Handle = JsArray::new(&mut cx, 1); - let s = cx.string("hello node"); - array.set(&mut cx, 0, s)?; - Ok(array) -} +```javascript +const len = array.length; +array[len] = value; ``` -## `ArrayBuffer` +## Converting a Rust Vector to an Array -Neon also provides support for the ES6 [ArrayBuffer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer) as through the [`JsArrayBuffer`](https://docs.rs/neon/*/neon/prelude/struct.JsArrayBuffer.html) struct. It has the same constructor and methods as `JsArray` +An iterable Rust data structure such as `Vec` can be converted to a JavaScript array by looping over the elements. The [`JsArray::new()`](https://docs.rs/neon/latest/neon/types/struct.JsArray.html#method.new) method can be used to preallocate enough capacity for the number of elements. -## Node `Buffer` +```rust +fn vec_to_array<'a, C: Context<'a>>(vec: &Vec, cx: &mut C) -> JsResult<'a, JsArray> { + let a = JsArray::new(cx, vec.len()); + + for (i, s) in vec.iter().enumerate() { + let v = cx.string(s); + a.set(&mut cx, i as u32, v)?; + } -The Node Buffer type is also supported by Neon through the [`JsBuffer`](https://docs.rs/neon/*/neon/prelude/struct.JsBuffer.html) struct. It as the same constructor and methods as `JsArray` + Ok(a) +} +``` -#### Runnable Example +## Converting a JavaScript Array to a Vector -For a working example of using Node's `Buffer` class with Neon, see the [`sharing-binary-data` example](https://github.com/neon-bindings/examples/tree/legacy/sharing-binary-data). You can get started with it by running the following commands: +The [`JsArray::to_vec()`](https://docs.rs/neon/latest/neon/types/struct.JsArray.html#method.to_vec) method makes it easy to convert a JavaScript array to a Rust vector: -```bash -git clone https://github.com/neon-bindings/examples -git checkout legacy -cd sharing-binary-data -npm install +```rust +let vec: Vec> = arr.to_vec(&mut cx); ``` diff --git a/docs/async.md b/docs/async.md deleted file mode 100644 index 6885d89f..00000000 --- a/docs/async.md +++ /dev/null @@ -1,160 +0,0 @@ ---- -id: async -title: Async Tasks -sidebar_label: Async Tasks ---- - -[Examples](https://github.com/neon-bindings/examples/tree/legacy/async) - -Tasks let you execute asynchronous background tasks that run in the Node thread pool. Behind the scenes, Neon is using N-API's microtasks API. Microtasks are the backing implementation of Promises and Callbacks in a JS Engine. For more on microtasks, see ["Tasks, microtasks, queues and schedules"](https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/) - -Let's look at a minimal implementation of an async task: - -```rust -use neon::prelude::*; - -struct BackgroundTask; - -impl Task for BackgroundTask { - type Output = i32; - type Error = String; - type JsEvent = JsNumber; - - fn perform(&self) -> Result { - Ok(17) - } - - fn complete(self, mut cx: TaskContext, result: Result) -> JsResult { - Ok(cx.number(result.unwrap())) - } -} - -pub fn perform_async_task(mut cx: FunctionContext) -> JsResult { - let f = cx.argument::(0)?; - BackgroundTask.schedule(f); - Ok(cx.undefined()) -} - -register_module!(mut m, { - m.export_function("performAsyncTask", perform_async_task) -}); -``` - -### Output - -The task's result type, which is sent back to the main thread to communicate a successful result back to JavaScript. - -### Error - -The task's error type, which is sent back to the main thread to communicate a task failure back to JavaScript. - -### JsEvent - -The type of JavaScript value that gets produced to the asynchronous callback on the main thread after the task is completed. - -### `.perform()` - -Perform the task, producing either a successful `Output` or an unsuccessful `Error`. This method is executed in a background -thread as part of libuv's built-in thread pool. - -### `.complete()` - -Convert the result of the task to a JavaScript value to be passed to the asynchronous callback. This method is executed on the main -thread at some point after the background task is completed. - -### `.schedule(f)` - -Schedule a task to be executed on a background thread. `callback` should have the following signature: - -```js -function callback(err, value) {} -``` - -## Calling Async Tasks - -Now let's look at how we would schedule async task using the `BackgroundTask` struct we created: - -```js -const { performAsyncTask } = require("../native"); - -// Iterate 10,0000 times in background thread -performAsyncTask((err, value) => { - let count = 10; - for (let i = 0; i < 100000; i++) { - count++; - } - console.log(count, "first sum from background thread"); -}); - -// Iterate 10 times -let count = 10; -for (let i = 0; i < 10; i++) { - count++; -} -console.log(count, "second sum from main thread"); -``` - -If you run this code you will get the following results: - -``` -20 'second sum from main thread' -100010 'first sum from background thread' -``` - -If `performAsyncTask()` were executed synchronously then the background thread would finish running before the main thread finishes and the results would be: - -``` -100010 'first sum from background thread' -20 'second sum from main thread' -``` - -## Handling Failing Tasks - -If we wanted our previous example to throw an error we could simple replace the `perform` and `complete` methods with the following: - -```rust -// --snip-- -fn perform(&self) -> Result { - Err(format!("I am a failing task")) -} - -fn complete(self, mut cx: TaskContext, result: Result) -> JsResult { - cx.throw_error(&result.unwrap_err()) -} -// --snip-- -``` - -## Promises - -If we wanted to wrap our task around a promise, we could do that like so: - -```js -// ./lib/index.js -const { performAsyncTask } = require("../native"); - -// Iterate 10,0000 times in background thread -const promisePerformAsyncTask = () => { - return new Promise((resolve, reject) => { - performAsyncTask((err, res) => { - let count = 10; - for (let i = 0; i < 100000; i++) { - count++; - } - console.log(count, "first sum from background thread"); - }); - }); -}; -``` - -## Runnable Example - -For another example of tasks, you can clone and run [fibonacci-async-task](https://github.com/neon-bindings/examples/tree/legacy/fibonacci-async-task): - -```bash -git clone https://github.com/neon-bindings/examples -git checkout legacy -cd fibonacci-async-task -npm install -``` - -This example computes the `100000`th fibonacci number on a background thread while keeping the main thread free diff --git a/docs/classes.md b/docs/classes.md deleted file mode 100644 index 396a25f7..00000000 --- a/docs/classes.md +++ /dev/null @@ -1,275 +0,0 @@ ---- -id: classes -title: Classes -sidebar_label: Classes ---- - -[Examples](https://github.com/neon-bindings/examples/tree/legacy/classes) - -For now, reference this snippet, taken from the tests: - -## Basics - -Let's create a simple struct that our class will use: - -```rust -pub struct Employee { - // Rust struct properties map to JS class properties - id: i32, - name: String -} -``` - -Now let's defines a custom JS class whose instances contain an Employee record. `init` is the constructor for the `JsEmployee` object. The methods that we define are prefixed with `method`. So if we want our JS object to have a method called `alertUser`, our method signature would be `method alertUser(mut cx) {`. All methods need to return types of the `JsValue` so we will need to `upcast` them. Because of this requirement, a method like the following would fail: - -❌ This will not work - -```rust -method talk(mut cx) { - Ok(cx.string("How are you doing?")) -} -``` - -✅ But this will work: - -```rust -method talk(mut cx) { - Ok(cx.string("How are you doing?").upcast()) -} -``` - -### Defining the `constructor` - -Now let's define our class. `init` will construct the class: - -```rust -// --snip-- -declare_types! { - /// JS class wrapping Employee records. - pub class JsEmployee for Employee { - init(mut cx) { - let id = cx.argument::(0)?.value(); - let name: String = cx.argument::(1)?.value(); - - Ok(Employee { - id: id as i32, - name: name, - }) - } - } -} - -// Export the class -register_module!(mut m, { - // tells neon what class we are exporting - // "Employee" is the name of the export that the class is exported as - m.export_class::("Employee")?; - Ok(()) -}); -``` - -### Adding Methods - -Now let's add some methods to our class: - -```rust -// --snip-- -init(mut cx) { - let id = cx.argument::(0)?.value(); - let name: String = cx.argument::(1)?.value(); - - Ok(Employee { - id: id as i32, - name: name, - }) -} - -method name(mut cx) { - let this = cx.this(); - let name = { - let guard = cx.lock(); - this.borrow(&guard).name - }; - println!("{}", &name); - Ok(cx.undefined().upcast()) -} - -method greet(mut cx) { - let this = cx.this(); - let msg = { - let guard = cx.lock(); - let greeter = this.borrow(&guard); - format!("Hi {}!", greeter.name) - }; - println!("{}", &msg); - Ok(cx.string(&msg).upcast()) -} - -method askQuestion(mut cx) { - println!("{}", "How are you?"); - Ok(cx.undefined().upcast()) -} -// --snip-- -``` - -Then you can use instances of this type in JS just like any other object: - -```js -const addon = require('./native'); - -console.log(new addon.Employee()); // fails: TypeError: not enough arguments - -const john = new addon.Employee(1, 'John'); -john.name(); // John -john.greet(); // Hi John! -john.askQuestion(); // How are you? -``` - -Since the methods on `Employee` expect this to have the right binary layout, they check to make sure that they aren’t being called on an inappropriate object type. This means you can’t segfault Node by doing something like: - -```js -Employee.prototype.name.call({}); -``` - -This safely throws a `TypeError` exception just like methods from other native classes like `Date` or `Buffer` do. - -### Getting and Setting Class Properties - -```rust -// --snip-- -let this = cx.this(); -// Downcast the object so we can call .get and .set -let this = this.downcast::().or_throw(&mut cx)?; -let is_raining = this - .get(&mut cx, "raining")? - .downcast::().or_throw(&mut cx)? - .value(); -if is_raining { - let t = cx.boolean(false); - this.set(&mut cx, "shouldGoOutside", t)?; -} -// --snip-- -``` - -### Handling Methods That Take Multiple Types - -Sometimes you may want your function to handle arguments that can be of multiple types. Here's an example showing just that: - -```rust -// --snip-- -method introduce(mut cx) { - let name_or_age = cx.argument::(0)?; - - if name_or_age.is_a::() { - let name = name_or_age - .downcast::() - .or_throw(&mut cx)? - .value(); - println!("Hi, this is {}", name); - } else if name_or_age.is_a::() { - let age = name_or_age - .downcast::() - .or_throw(&mut cx)? - .value(); - println!("Her birthday is on the {}th", age); - } else { - panic!("Name is not a string and age is not a number"); - } - - Ok(cx.undefined().upcast()) -} -// --snip-- -``` - -```js -const addon = require('../native'); - -const john = new addon.Employee(0, 'Lisa'); -john.introduce('Mary'); // Hi, this is Mary -john.introduce(12); // Her birthday is on the 12th -``` - -## Advanced Example - -```rust -#[macro_use] -extern crate neon; - -use neon::prelude::*; - -pub struct User { - id: i32, - first_name: String, - last_name: String, - email: String, -} - -type Unit = (); - -declare_types! { - pub class JsUser for User { - init(mut cx) { - let id = cx.argument::(0)?; - let first_name: Handle = cx.argument::(1)?; - let last_name: Handle = cx.argument::(2)?; - let email: Handle = cx.argument::(3)?; - - Ok(User { - id: id.value() as i32, - first_name: first_name.value(), - last_name: last_name.value(), - email: email.value(), - }) - } - - method get(mut cx) { - let attr: String = cx.argument::(0)?.value(); - - let this = cx.this(); - - match &attr[..] { - "id" => { - let id = { - let guard = cx.lock(); - let user = this.borrow(&guard); - user.id - }; - Ok(cx.number(id).upcast()) - }, - "first_name" => { - let first_name = { - let guard = cx.lock(); - let user = this.borrow(&guard); - user.first_name.clone() - }; - Ok(cx.string(&first_name).upcast()) - }, - "last_name" => { - let last_name = { - let guard = cx.lock(); - let user = this.borrow(&guard); - user.last_name.clone() - }; - Ok(cx.string(&last_name).upcast()) - }, - "email" => { - let email = { - let guard = cx.lock(); - let user = this.borrow(&guard); - user.email.clone() - }; - Ok(cx.string(&email).upcast()) - }, - _ => cx.throw_type_error("property does not exist") - } - } - - method panic(_) { - panic!("User.prototype.panic") - } - } -} -register_module!(mut m, { - m.export_class::("User") -}); -``` diff --git a/docs/electron-apps.md b/docs/electron-apps.md deleted file mode 100644 index 2d935b67..00000000 --- a/docs/electron-apps.md +++ /dev/null @@ -1,69 +0,0 @@ ---- -id: electron-apps -title: Electron Apps -sidebar_label: Electron Apps ---- - -[Examples](https://github.com/neon-bindings/examples/tree/legacy/electron-app) - -Neon can be great for adding native functionality to Electron apps. This guide will walk you through a simple example of adding a Neon dependency to an Electron app. To see the whole example you can look at the [full demo](https://github.com/neon-bindings/examples/tree/legacy/electron-app). - -For the most part, using a Neon package in Electron is as straightforward as adding it to the `package.json` dependencies. However, Electron does bring its own requirements with it for building native modules. - -**We are working on adding Neon support to [electron-rebuild](https://github.com/electron/electron-rebuild)**, so you'll be able to just drop Neon dependencies into your app like any other. For now, there's a tool called [electron-build-env](https://github.com/dherman/electron-build-env) that you can use for building any Neon dependencies of your Electron app. - -In the meantime, we can use Neon modules in an Electron app with just a few lines of configuration in our `package.json`. - -## Setup Electron - -```bash -# Clone the Quick Start repository -git clone https://github.com/electron/electron-quick-start - -# Go into the repository -cd electron-quick-start -``` - -## Adding a Neon Dependency - -First let's add a dependency on a simple Neon module, `neon-hello`, which is already published in npm: - -```bash -npm install @amilajack/neon-hello -``` - -## Adding the Build Dependencies - -Next, we need the `neon-cli` and `electron-build-env` packages in order to build `neon-hello`. Since they're only needed for building, we can add them as development dependencies: - -```bash -npm install electron-build-env neon-cli --save-dev -``` - -## Creating a Build Script - -Finally, we'll add a script to build the Neon dependency: - -```jsonc -"scripts": { - // ... - "build": "electron-build-env neon build @amilajack/neon-hello --release" - // ... -} -``` - -This step uses the `electron-build-env` tool to configure the environment properly to build for Electron. - -## That's It! - -We can then build a production release of the `neon-hello` module: - -```shell -npm run build -``` - -we should have a working Electron app. You can try out the [full working demo](https://github.com/neon-bindings/examples/tree/legacy/electron-app) by building it and running: - -```shell -npm start -``` diff --git a/docs/errors.md b/docs/errors.md deleted file mode 100644 index dcf76185..00000000 --- a/docs/errors.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -id: errors -title: Errors -sidebar_label: Errors ---- - -[Examples](https://github.com/neon-bindings/examples/tree/legacy/errors) - -Neon supports creating and throwing all Error objects in JS. These objects include `Error`, `TypeError`, and `RangeError`. Calling `panic!()` in Neon will throw an `Error` in Node. So `panic!("program errored!")` is equivalent to `throw new Error('program errored!')`. - -## Creating Errors - -The `FunctionContext` trait is used to create and throw Errors. - -## Catching Errors - -:::caution -This section is a work in progress. -::: diff --git a/docs/example-projects.md b/docs/example-projects.md index 31daf64d..07966298 100644 --- a/docs/example-projects.md +++ b/docs/example-projects.md @@ -4,29 +4,28 @@ title: Who's Using Neon sidebar_label: Who's Using Neon --- -Here is a list of projects using Neon +Neon is used to power a growing community of applications and libraries—maybe some you use yourself! -## Node Libraries +## Applications -- [swc: Super-fast Babel alternative written in Rust](https://github.com/swc-project/swc) -- [disk-utility: Disk utility methods for Node using Neon](https://github.com/amilajack/disk-utility) -- [node-fuzzy-phrase: Fast fuzzy phrase matching for Node](https://github.com/mapbox/node-fuzzy-phrase) -- [snips-nlu-js: Quick and dirty Node bindings for snips-nlu-rs](https://github.com/ballwood/snips-nlu-js) -- [reconfix: (Re)Configuration toolkit](https://github.com/resin-io/reconfix) -- [libsodium-neon: Node bindings to libsodium](https://github.com/wireapp/libsodium-neon) -- [ledb: Lightweight embedded database](https://github.com/katyo/ledb) -- [node-crypto: rust-crypto bindings for Node](https://github.com/Brooooooklyn/node-crypto) -- [os-type: Bindings to the os_type Rust library](https://github.com/amilajack/os-type) -- [node-webrender: bindings to webrender](https://github.com/cztomsik/node-webrender) +- **[1password:](https://dteare.medium.com/behind-the-scenes-of-1password-for-linux-d59b19143a23)** Secure password manager +- **[Signal:](https://github.com/signalapp/libsignal-client)** Secure, private messaging +- **[Finda:](https://keminglabs.com/finda/)** Type stuff, find things. No mousing. -And many more! +## Node Libraries -## Electron Apps +- **[disk-utility:](https://github.com/amilajack/disk-utility)** Disk utility methods for Node using Neon +- **[node-fuzzy-phrase:](https://github.com/mapbox/node-fuzzy-phrase)** Fast fuzzy phrase matching for Node +- **[snips-nlu-js:](https://github.com/ballwood/snips-nlu-js)** Quick and dirty Node bindings for snips-nlu-rs +- **[reconfix:](https://github.com/resin-io/reconfix)** (Re)Configuration toolkit +- **[ledb:](https://github.com/katyo/ledb)** Lightweight embedded database +- **[os-type:](https://github.com/amilajack/os-type)** Bindings to the os_type Rust library +- **[big_mac:](https://github.com/ultamatt/big_mac)** Get MAC addresses on the local device -- [finda: Finda finds things — in under 16 milliseconds.](https://keminglabs.com/finda/) +And many more! ## Rust Libraries -- [neon-serde: Easily convert between `JsHandle` and `serde` structs](https://github.com/GabrielCastro/neon-serde) +- **[neon-serde:](https://crates.io/crates/neon-serde2)** Easily convert between Rust and JavaScript datatypes -Want to add a project to this list? Submit a PR! +Want to add a project to this list? [Submit a PR](https://github.com/neon-bindings/website)! diff --git a/docs/functions.md b/docs/functions.md index f50e786e..5153d6b8 100644 --- a/docs/functions.md +++ b/docs/functions.md @@ -4,54 +4,147 @@ title: Functions sidebar_label: Functions --- -[Examples](https://github.com/neon-bindings/examples/tree/legacy/functions) +Neon's main way of connecting Rust and JavaScript is by allowing you to define **[functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Functions)** that are implemented in Rust. -## This +## Defining Functions -In order to call methods on `cx.this`, it must be downcasted to a `JsObject`. For more on [type checking section](type-checking.md#downcasting) for more details +A Neon function looks and acts to JavaScript like a regular JavaScript function, but its behavior is written in Rust. Creating a Neon function requires first defining a Rust function of type `fn(FunctionContext) -> JsResult` where `T` can be any type that implements the [`Value`](https://docs.rs/neon/latest/neon/types/trait.Value.html) trait: ```rust -pub fn require_object_this(mut cx: FunctionContext) -> JsResult { - let this = cx.this(); - // Downcast `this` so .set can be called on it - let this = this.downcast::().or_throw(&mut cx)?; - let t = cx.boolean(true); - // Equivalent to `this.modified = true` in JS - this.set(&mut cx, "modified", t)?; - Ok(cx.undefined()) +fn hello(mut cx: FunctionContext) -> JsResult { + Ok(cx.string("hello")) } ``` -## Calling JS Functions +The `cx` argument is a [`FunctionContext`](https://docs.rs/neon/latest/neon/context/type.FunctionContext.html), which provides the Neon function with access to the JavaScript runtime. The [`JsResult`](https://docs.rs/neon/latest/neon/result/type.JsResult.html) result type indicates that a Neon function may throw a JavaScript error. In this example, we just construct a string and return it, so we immediately wrap the outcome in an `Ok` result. A more involved Neon function might call other functions or interact with objects in ways that could end up triggering errors. -Here we define a JS function that takes a function as the `0`th argument and calls that function. `f.call` takes a `FunctionContext`, the context to call the function from (in this case `null`), and `arguments` +The most common way to define a Neon function from `hello` is to export it from our module using [`ModuleContext::export_function()`](https://docs.rs/neon/latest/neon/context/struct.ModuleContext.html#method.export_function): ```rust -pub fn call_js_function(mut cx: FunctionContext) -> JsResult { - let f = cx.argument::(0)?; - let args: Vec> = vec![cx.number(16.0)]; - let null = cx.null(); - f.call(&mut cx, null, args)?.downcast::().or_throw(&mut cx) +#[neon::main] +pub fn main(mut cx: ModuleContext) -> NeonResult<()> { + cx.export_function("hello", hello)?; + Ok(()) } ``` -## Constructing JS Functions +Notice that the `main` function also returns a result, because it can trigger JavaScript errors. In this code, calling the `export_function()` method could potentially throw an error since it's interacting with the module object. We use Rust's [`?` operator](https://doc.rust-lang.org/reference/expressions/operator-expr.html#the-question-mark-operator) to return early and propagate any errors that get thrown. + +A JavaScript module can then call the `hello` function by importing from the Neon module: + +```javascript +const { hello } = require('./index'); + +console.log(hello()); // prints "hello"! +``` + +## Accessing Arguments + +A Neon function can access its arguments by calling [`FunctionContext::argument()`](https://docs.rs/neon/latest/neon/context/struct.CallContext.html#method.argument): ```rust -pub fn construct_js_function(mut cx: FunctionContext) -> JsResult { - let f = cx.argument::(0)?; - let zero = cx.number(0.0); - let o = f.construct(&mut cx, vec![zero])?; - let get_utc_full_year_method = o.get(&mut cx, "getUTCFullYear")?.downcast::().or_throw(&mut cx)?; - let args: Vec> = vec![]; - get_utc_full_year_method.call(&mut cx, o.upcast::(), args)?.downcast::().or_throw(&mut cx) +fn create_pair(mut cx: FunctionContext) -> JsResult { + let x: Handle = cx.argument(0)?; + let y: Handle = cx.argument(1)?; + + let obj = cx.empty_object(); + + obj.set(&mut cx, "x", x)?; + obj.set(&mut cx, "y", y)?; + + Ok(obj) } ``` -## Returning Functions +## Checking Argument Types + +You can conveniently check the type of a Neon function argument and cast it to the corresponding Rust type by choosing a more specific type than `JsValue`. This example constructs an object representing metadata about a book, first checking the first two arguments to be strings and the third argument to be a number: ```rust -pub fn return_js_function(mut cx: FunctionContext) -> JsResult { - JsFunction::new(&mut cx, add1) +fn create_book(mut cx: FunctionContext) -> JsResult { + let title = cx.argument::(0)?; + let author = cx.argument::(1)?; + let year = cx.argument::(2)?; + + let obj = cx.empty_object(); + + obj.set(&mut cx, "title", title)?; + obj.set(&mut cx, "author", author)?; + obj.set(&mut cx, "year", year)?; + + Ok(obj) +} + +#[neon::main] +pub fn main(mut cx: ModuleContext) -> NeonResult<()> { + cx.export_function("createBook", create_book)?; + Ok(()) +} +``` + +If any of the checks fails, the `createBook` function throws a [`TypeError`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypeError): + +```javascript +const { createBook } = require('./index'); + +try { + createBook(null, null, null); +} catch (e) { + console.log(e); // TypeError } ``` + +## Optional Arguments + +The [`FunctionContext::argument_opt()`](https://docs.rs/neon/latest/neon/context/struct.CallContext.html#method.argument_opt) method makes it possible to extract an optional argument. This example creates an entry in a resume's job history, where the end year may not be present (indicating the person's current job): + +```rust +fn create_job(mut cx: FunctionContext) -> JsResult { + let company = cx.argument::(0)?; + let title = cx.argument::(1)?; + let start_year = cx.argument::(2)?; + let end_year = cx.argument_opt(3); + + let obj = cx.empty_object(); + + obj.set(&mut cx, "company", company)?; + obj.set(&mut cx, "title", title)?; + obj.set(&mut cx, "startYear", start_year)?; + + if let Some(end_year) = end_year { + obj.set(&mut cx, "endYear", end_year)?; + } else { + let null = cx.null(); + obj.set(&mut cx, "endYear", null)?; + } + + Ok(obj) +} +``` + +## Calling Functions + +You can call a JavaScript function from Rust with [`JsFunction::call()`](https://docs.rs/neon/latest/neon/types/struct.JsFunction.html#method.call). This example extracts the [`parseInt`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt) function and calls it on a string: + +```rust +let func = cx.global() + .get(&mut cx, "parseInt")? + .downcast_or_throw::(&mut cx)?; +let null = cx.null(); +let s = cx.string(s); +let result = func.call(&mut cx, null, vec![s])?; +``` + +Notice that `func.call()` takes a runtime context, a value for the `this` binding, and a sequence of arguments. In this case, `parseInt` ignores its `this` value, so we just pass `null`. + +## Calling Constructor Functions + +You can call a JavaScript function as a constructor, as if with the [`new`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new) operator, with [`JsFunction::construct()`](https://docs.rs/neon/latest/neon/types/struct.JsFunction.html#method.construct). This example extracts the [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL) constructor and invokes it with a URL string: + +```rust +let global = cx.global() + .get(&mut cx, "URL")? + .downcast_or_throw::(&mut cx)?; +let url: Handle = cx.string("https://neon-bindings.com"); +let result = ctor.construct(&mut cx, vec![url]); +``` diff --git a/docs/guides.md b/docs/guides.md deleted file mode 100644 index 1690dd6b..00000000 --- a/docs/guides.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -id: guides -title: Guides -sidebar_label: Guides ---- - -## Word Counting - -[Link](word-counting) - -A step by step walkthrough of the word counting demo. In this guide, you will learn how to create a Neon binding that counts all the occurances of the word "thee" in shakespear plays. It then helps you a similar approach with JS and compares the performance of the solutions. diff --git a/docs/hello-world.md b/docs/hello-world.md index 7422234a..556927be 100644 --- a/docs/hello-world.md +++ b/docs/hello-world.md @@ -4,13 +4,11 @@ title: Hello World! sidebar_label: Hello World! --- -[Examples](https://github.com/neon-bindings/examples) +**Full source code:** [examples/cpu-count](https://github.com/neon-bindings/examples/tree/main/examples/cpu-count) -This guide will walk you through writing, building, and running your first Neon project. We'll try to walk you through each step carefully, but if you want to skip ahead, you can always go straight to the [full demo](https://github.com/neon-bindings/examples/tree/main/examples/cpu-count) in the examples repository. +This small example project will be a module that returns the number of processors in the current device. If you're not familiar with fancy systems concepts like processors and CPUs, don't panic! We'll be using the [`num_cpus`](https://crates.io/crates/num_cpus) crate to do all the heavy lifting for us, and we'll just return the number it gives us. -Our first project will be a tiny module that returns a number indicating the number of CPUs in the current machine. If you're not familiar with systems programming, don't panic! We'll be using [Sean McArthur](https://seanmonstar.com/)'s [num_cpus](https://crates.io/crates/num_cpus) crate to do all the heavy lifting for us, and we'll just return the number it gives us. - -Even this simple example already demonstrates some of Neon's usefulness: Rust's [crate ecosystem](https://crates.io/) is younger than npm but, growing quickly and is already full of useful and unique libraries. A library like `num_cpus` could be useful, for example, as a hint for tuning the size of a [Web Worker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers) pool in an [Electron app](./electron-apps/). +The tutorial is short, but it demonstrates some of Neon's power: Rust's [crate ecosystem](https://crates.io/) is growing quickly and full of many useful and unique libraries, often providing low-level capabilities or high-performance data structures that can be hard to find in npm. A library like `num_cpus` could be useful, for example, as a hint for tuning the size of a [Web Worker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers) pool in an [Electron](https://electronjs.org) app. # Creating a New Project @@ -98,7 +96,7 @@ fn get_num_cpus(mut cx: FunctionContext) -> JsResult { A few more things to note about this code: - The `cx` argument to `get_num_cpus`: this contains information about the function call, such as the arguments and the value of `this`. -- The [`JsResult`](https://docs.rs/neon/*/neon/result/type.JsResult.html) output type: this is a Rust [`Result`](https://doc.rust-lang.org/std/result/enum.Result.html) type that indicates whether the function returned (`Ok`) or threw a JavaScript exception (`Err`). You can learn more in the [Errors](errors) docs. It also tracks the lifetime of the returned _handle_. +- The [`JsResult`](https://docs.rs/neon/*/neon/result/type.JsResult.html) output type: this is a Rust [`Result`](https://doc.rust-lang.org/std/result/enum.Result.html) type that indicates whether the function returned (`Ok`) or threw a JavaScript exception (`Err`). It also tracks the lifetime of the returned _handle_. - The `cx.number()` function tells the JavaScript garbage collector that we need to keep the value we allocate alive long enough to return it to the caller of `get_num_cpus`. Finally, we'll modify the code that `npm init neon` created for us to set up the module exports with this function instead of the initial "hello world" function it created for us: diff --git a/docs/intro.md b/docs/introduction.md similarity index 77% rename from docs/intro.md rename to docs/introduction.md index 1572cfd6..2dba2cbf 100644 --- a/docs/intro.md +++ b/docs/introduction.md @@ -1,5 +1,5 @@ --- -id: intro +id: introduction title: Introduction sidebar_label: Introduction --- @@ -16,11 +16,11 @@ With Neon, you can create native Node modules like you might in C or C++, but wi - Raw performance - Threads and parallel programming -- Access to Rust’s growing package ecosystem +- Access to Rust’s [package ecosystem](https://crates.io) - Access to native OS-specific libraries Neon also works hard to make creating native modules easy, with a convenient command-line interface and workflow built around sensible project conventions. This eliminates a lot of the usual hassle of building native Node modules. ## Where Do I Start? -The best place to go next is the [Getting Started guide](getting-started.md), which will help you get Neon installed on your system. From there, try out the [Hello, World! guide](hello-world.md) to write your first native Node module with Neon! +The best place to go next is the [Quick Start guide](quick-start.md), which will help you get Neon installed on your system. From there, try out the [Hello, World! guide](hello-world.md) to write your first native Node module with Neon! diff --git a/docs/json.md b/docs/json.md deleted file mode 100644 index 3334d1ee..00000000 --- a/docs/json.md +++ /dev/null @@ -1,159 +0,0 @@ ---- -id: json -title: JSON -sidebar_label: JSON ---- - -[Examples](https://github.com/neon-bindings/examples/tree/legacy/json) - -Sometimes you just want to convert a struct in Rust to a `JsObject` and back. This can be done with [neon-serde](https://github.com/GabrielCastro/neon-serde). You will need to install the `neon-serde` and `serde_derive` crates. - -For more docs on the `neon-serde` crate, please see its [repo](https://github.com/GabrielCastro/neon-serde) and [docs](https://docs.rs/crate/neon-serde/0.0.3) - -First we install the following crates: - -```toml -# Cargo.toml -# --snip-- -[dependencies] -neon = "0.2.0" -neon-serde = "0.1.1" -serde_derive = "1.0.80" -serde = "1.0.80" -``` - -Then import the necessary libraries and declare a `User` struct that is marked as deserializable with [serde](https://github.com/serde-rs/serde): - -```rust -#[macro_use] -extern crate neon; -#[macro_use] -extern crate neon_serde; -#[macro_use] -extern crate serde_derive; - -#[derive(Serialize)] -struct User { - name: String, - age: u16, -} -``` - -## Serializing - -We can serialize a Rust struct and convert it to a `JsValue` like so: - -```rust -// --snip-- -fn serialize_something(mut cx: FunctionContext) -> JsResult { - let value = AnObject { - a: 1, - b: vec![2f64, 3f64, 4f64], - c: "a string".into() - }; - - let js_value = neon_serde::to_value(&mut cx, &value)?; - Ok(js_value) -} - -register_module!(mut m, { - m.export_function("serialize_something", serialize_something) -}); -``` - -In your `./lib/index.js` you can call your function like so: - -```js -const addon = require('../native'); -addon.serialize_something(); -``` - -## Deserializing - -We need to change the `User` trait to be deserializable as well: - -```rust -// --snip-- -#[derive(Serialize, Deserialize)] -struct User { - name: String, - age: u16, -} -// --snip-- -``` - -Now we can also deserialize a `JsObject` struct and convert it to a `JsValue` like so: - -```rust -// --snip-- -fn deserialize_something(mut cx: FunctionContext) -> JsResult { - let arg0 = cx.argument::(0)?; - - let arg0_value: AnObject = neon_serde::from_value(&mut cx, arg0)?; - println!("{:?}", arg0_value); - - Ok(cx.undefined().upcast()) -} - -register_module!(mut m, { - m.export_function("serialize_something", serialize_something)?; - m.export_function("deserialize_something", deserialize_something)?; - Ok(()) -}); -``` - -In your `./lib/index.js` you can call your function like so: - -```js -const addon = require('../native'); -addon.deserialize_something(); -``` - -## Macros - -`neon-serde` provides some macros for simplifying some of the type signatures of functions. It also handles exporting our functions so we don't have to use the `register_module!` macro manually. - -```rust -#[macro_use] -extern crate neon; -#[macro_use] -extern crate neon_serde; -#[macro_use] -extern crate serde_derive; - -export! { - fn say_hello(name: String) -> String { - format!("Hello, {}!", name) - } - - fn greet(user: User) -> String { - format!("{} is {} years old", user.name, user.age) - } - - fn fibonacci(n: i32) -> i32 { - match n { - 1 | 2 => 1, - n => fibonacci(n - 1) + fibonacci(n - 2) - } - } -} -``` - -In our JS we simply import the methods and call the functions. Note that type checks are written for us by the macro: - -```js -const addon = require('../native'); - -// Calling the function with incorrect arguments will fail -// console.log(addon.say_hello()); -// fails: TypeError: not enough arguments - -console.log(addon.say_hello('john')); -// Hello, john! - -// Calling the function with incorrect arguments will fail -// console.log(addon.greet({ name: "afsd" })); -// Error(Msg("missing field `age`"), State { next_error: None, backtrace: None }) - -console.log(addon.fibonacci(32)); -``` diff --git a/docs/learning-resources.md b/docs/learning-resources.md deleted file mode 100644 index 5351e8bb..00000000 --- a/docs/learning-resources.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -id: learning-resources -title: Learning Resources -sidebar_label: Learning Resources ---- - -:::caution -Content in these posts and videos are using outdated versions of Neon -::: - -## Blog Posts - -- [Building a fast Electron app with Rust - ](https://keminglabs.com/blog/building-a-fast-electron-app-with-rust/) -- [Speeding up crypto on Wire desktop apps](https://medium.com/@wireapp/speeding-up-crypto-on-wire-desktop-apps-3ff37fc98c3f) -- [Blazing fast, low requirements, computationally intensive operations on Node.js using Rust](https://itnext.io/rust-node-js-are-awesome-a50d63411773) -- [Writing fast and safe native Node.js modules with Rust](https://blog.risingstack.com/node-js-native-modules-with-rust/) -- [Neon: Node + Rust = 💖](http://calculist.org/blog/2015/12/23/neon-node-rust/) -- [Native JS Classes in Neon - ](http://calculist.org/blog/2016/04/01/native-js-classes-in-neon/) - -## Videos - -- [Dave Herman: Rust + Node = Neon](https://youtu.be/jINMIAicaS0?t=765) -- [Péter Czibik - Fast & Safe Native node.js Modules with Rust](https://youtu.be/zz1Gie9FkbI) -- [Pumping up Node js modules with Rust!](https://youtu.be/Zqzwkiii2NE) -- [Writing Node.js Modules in Rust An introduction to Neon](https://youtu.be/yj2nD9hB3D0) diff --git a/docs/modules.md b/docs/modules.md deleted file mode 100644 index 5a15dbf2..00000000 --- a/docs/modules.md +++ /dev/null @@ -1,56 +0,0 @@ ---- -id: modules -title: Modules -sidebar_label: Modules ---- - -[Examples](https://github.com/neon-bindings/examples/tree/legacy/modules) - -In Node, it is common to export values, functions and classes. These data structures can also be exported from Neon modules as well with a few extra steps. - -## Exporting Values, Functions, and Classes - -Consider the following JS: - -```js -// ./my-exports.js -export const foo = 'foo'; - -export function bar() {} - -export class Baz {} -``` - -The Neon equivalent would be the following: - -```rust -// --snip-- -fn bar(mut cx: FunctionContext) -> JsResult { - Ok(cx.undefined()) -} - -pub struct Baz; - -declare_types! { - pub class JsBaz for Baz { - // --snip-- - } -} - -register_module!(mut m, { - let foo = m.string("foo"); - // Export `foo' - m.export_value("foo", foo)?; - // Export the `bar` function - m.export_function("bar", bar)?; - // Export the `Baz` class - m.export_class::("Baz")?; - Ok(()) -}); -``` - -## Default Exports - -:::caution -This section is a work in progress. -::: diff --git a/docs/objects.md b/docs/objects.md index 924f79c9..09e9f24c 100644 --- a/docs/objects.md +++ b/docs/objects.md @@ -4,63 +4,120 @@ title: Objects sidebar_label: Objects --- -[Examples](https://github.com/neon-bindings/examples/tree/legacy/objects) +Most types of data in JavaScript are considered **[objects](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Basics)**. (In fact, everything that isn't a [primitive type](primitive-types) is an object type.) In Neon, all object types implement the [`Object`](https://docs.rs/neon/latest/neon/object/trait.Object.html) trait, which allows you interact with an object's properties. -`JsObject`'s are used to create objects on the JS heap. `JsObject` structs have two methods: `get` and `set`: +The JavaScript [`Object`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) class, sometimes referred to as "vanilla objects" or "simple objects," is one of the most common ways to construct objects in JavaScript, and is available to Neon through the [`JsObject`](https://docs.rs/neon/latest/neon/types/struct.JsObject.html) type. + +## Creating Objects + +The [`Context::empty_object()`](https://docs.rs/neon/latest/neon/context/trait.Context.html#method.empty_object) method creates a new `JsObject`: + +```rust +let obj: Handle = cx.empty_object(); +``` ## Getting Properties -`.get()` is used to get a property of a JS object at runtime: +The [`Object::get()`](https://docs.rs/neon/latest/neon/object/trait.Object.html#method.get) method accesses a property of an object at runtime: ```rust -// --snip-- -let js_object = JsObject::new(&mut cx); -js_object - .get(&mut cx, "myProperty")? - .downcast::() - .or_throw(&mut cx)?; -// --snip-- +// Create an empty object: +let obj: Handle = cx.empty_object(); + +// Get the `toString` property of the object: +let prop: Handle = obj.get(&mut cx, "toString")?; ``` -`.downcast()` will attempt to cast the property to a `JsFunction`. `.or_throw()` will error if downcasting the propety is not possible. +Notice that this example extracts the `toString` property from an empty object, which will typically be _inherited_ from the object's [prototype chain](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain). ## Setting Properties -`.set()` requires a `FunctionContext`, the name of the property you want to set, and the value you want to set the property to: +The [`Object::set()`](https://docs.rs/neon/latest/neon/object/trait.Object.html#method.set) method sets a property of an object at runtime: ```rust -let js_object = JsObject::new(&mut cx); -let js_string = cx.string("foobar"); +let obj = cx.empty_object(); +let age = cx.number(35); -js_object.set(&mut cx, "myProperty", js_string)?; +obj.set(&mut cx, "age", age)?; ``` -## Mapping a `struct` to a `JsObject` +## Converting Rust data to JavaScript -Here is a simple example of converting a Rust `struct` to a JS `Object` using `JsObject`. We first start by defining the `struct`: +Here is a simple example of converting a Rust `struct` to a JavaScript object. First, let's define a Rust type describing, say, about a book in a library catalog: ```rust -struct Foo { - pub bar: u64, - pub baz: String +struct Book { + pub title: String, + pub author: String, + pub year: u32, } ``` -And then define a function which creates an instance of the `Foo` struct + +To copy a `Book` into a JavaScript object, we'll define a conversion function. To make it idiomatically Rusty, let's define it as a [_method_](https://doc.rust-lang.org/book/ch05-03-method-syntax.html) of the `Book` type, so that callers of our API can use a pleasant method call syntax: + ```rust -fn convert_struct_to_js_object(mut cx: FunctionContext) -> JsResult { - let foo = Foo { - bar: 1234, - baz: "baz".to_string() - }; - let object = JsObject::new(&mut cx); - let js_string = cx.string(&foo.baz); - let js_number = cx.number(foo.bar as f64); - object.set(&mut cx, "myStringProperty", js_string).unwrap(); - object.set(&mut cx, "myNumberProperty", js_number).unwrap(); - Ok(object) +let obj = book.to_object(&mut cx)?; +``` + +First let's look at the signature of `Book::to_object()`, which we define as a method using Rust's `impl Book` syntax and a `&self` parameter: + +```rust +impl Book { + fn to_object<'a>(&self, cx: &mut FunctionContext<'a>) -> JsResult<'a, JsObject> { + // ... + } +} +``` + +This is our first example using a _[lifetime annotation](https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html)_ `'a`. This allows the Rust compiler to ensure that our code never accidentally makes an unsafe reference to JavaScript values managed by the Node runtime. Specifically, this signature tells Neon that the result object returned by this function (which has lifetime `'a`) is managed by the runtime context that was passed in as an argument (which also has that same lifetime `'a`). + +If you've never seen lifetimes before or are not yet confident using them, don't worry! For now, you can use this code as a template, and know that the Rust compiler will keep you safe. + +Now here is the full implementation: + +```rust + fn to_object<'a>(&self, cx: &mut FunctionContext<'a>) -> JsResult<'a, JsObject> { + let obj = cx.empty_object(); + + let title = cx.string(self.title); + obj.set(&mut cx, "title", title)?; + + let author = cx.string(self.author); + obj.set(&mut cx, "author", author)?; + + let year = cx.number(self.year); + obj.set(&mut cx, "year", year)?; + + Ok(obj) + } +} +``` + +Let's walk through the implementation. First, it constructs a new empty JavaScript object, which will serve as the result, converts each of the fields to a [primitive type](primitive-types) and sets the relevant property on the object to its value. Finally, the method returns the new object, wrapped in an `Ok` value to signal success. + +One thing worth noticing about this function is that it doesn't use anything specific about the `FunctionContext` type other than the generic methods of the [`Context`](https://docs.rs/neon/latest/neon/context/trait.Context.html) trait. To make our function even more powerful, we can make it _generic_ and accept any implementation of `Context`: + +```rust +impl Book { + fn to_object<'a>(&self, cx: &mut impl Context<'a>) -> JsResult<'a, JsObject> { + // same as before... + } } +``` + +This allows us to use our method in more places, such as with a [`ModuleContext`](https://docs.rs/neon/latest/neon/context/struct.ModuleContext.html): -register_module!(mut m, { - m.export_function("convertStructToJsObject", convert_struct_to_js_object) -}); +```rust +#[neon::main] +pub fn main(mut cx: ModuleContext) -> NeonResult<()> { + let book = Book { + title: "Chadwick the Crab".to_string(), + author: "Priscilla Cummings".to_string(), + year: 2009, + }; + + let obj = book.to_object(&mut cx)?; + cx.export_value("chadwick", obj)?; + Ok(()) +} ``` diff --git a/docs/parallisim.md b/docs/parallisim.md deleted file mode 100644 index 1086aefc..00000000 --- a/docs/parallisim.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -id: parallelism -title: Parallelism -sidebar_label: Parallelism ---- - -## Simple Example - -```rust - -``` - -## Using Rayon - -```rust - -``` - -For another example with [Rayon](https://github.com/rayon-rs/rayon), see the [Word Counting guide](word-counting.md) diff --git a/docs/primitive-types.md b/docs/primitive-types.md new file mode 100644 index 00000000..553885f0 --- /dev/null +++ b/docs/primitive-types.md @@ -0,0 +1,57 @@ +--- +id: primitive-types +title: Primitive Types +sidebar_label: Primitive Types +--- + +In JavaScript, the **[primitive types](https://developer.mozilla.org/en-US/docs/Glossary/Primitive)** are types of values that aren't objects. This page gives some simple examples of how to construct instances of various JavaScript primitive types, and you can follow the links to more detailed information in the API documentation. + +## Numbers + +The [`Context::number()`](https://docs.rs/neon/latest/neon/context/trait.Context.html#method.number) method constructs a JavaScript number from any Rust number compatible with the [`f64`](https://doc.rust-lang.org/std/primitive.f64.html) type. This includes both integers and floating-point numbers, so you can conveniently construct a JavaScript number from a literal number: + +```rust +let i: Handle = cx.number(42); +let f: Handle = cx.number(3.14); +``` + +For types that aren't implicitly convertible to `f64`, you can explicitly cast a number with Rust's [`as`](https://doc.rust-lang.org/std/keyword.as.html) operator: + +```rust +let size: usize = std::mem::size_of::(); +let n = cx.number(size as f64) +``` + +Keep in mind that for some types, explicit casts to `f64` might be lossy, so you'll want to check whether the conversion you're doing is appropriate. Another option is to use the [`TryFrom`](https://doc.rust-lang.org/std/convert/trait.TryFrom.html) API, which can help avoid loss of precision. + +## Strings + +The [`Context::string()`](https://docs.rs/neon/latest/neon/context/trait.Context.html#method.string) method constructs a JavaScript string from a reference to a Rust string. + +```rust +let s: Handle = cx.string("foobar"); +``` + +## Booleans + +The [`Context::boolean()`](https://docs.rs/neon/latest/neon/context/trait.Context.html#method.boolean) method constructs a JavaScript Boolean value. + +```rust +let b: Handle = cx.boolean(true); +``` + +## Undefined + +The [`Context::undefined()`](https://docs.rs/neon/latest/neon/context/trait.Context.html#method.undefined) method constructs a JavaScript `undefined` value. + +```rust +let u: Handle = cx.undefined(); +``` + +## Null + +The [`Context::null()`](https://docs.rs/neon/latest/neon/context/trait.Context.html#method.null) method constructs a JavaScript `null` value. + +```rust +let n: Handle = cx.null(); +``` diff --git a/docs/primitives.md b/docs/primitives.md deleted file mode 100644 index d37cd9b9..00000000 --- a/docs/primitives.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -id: primitives -title: Primitives -sidebar_label: Primitives ---- - -[Examples](https://github.com/neon-bindings/examples/tree/legacy/primitives) - -## Numbers - -Note that all numbers must be casted to a `f64` since these are the only types of numbers that a JS engine supports - -```rust -fn js_number(mut cx: FunctionContext) -> JsResult { - // Either use function context to create number - let number = cx.number(23 as f64); - // or use JsNumber struct - Ok(number) -} -``` - -Other primitives follow a similar pattern: - -## Strings - -```rust -// --snip-- -let string = cx.string("foobar"); -// --snip-- -``` - -## Booleans - -```rust -// --snip-- -let boolean = cx.boolean(true); -// --snip-- -``` - -## Undefined - -```rust -// --snip-- -let undefined = cx.undefined(); -// --snip-- -``` - -## Null - -```rust -// --snip-- -let null = cx.null(); -// --snip-- -``` diff --git a/docs/publishing.md b/docs/publishing.md deleted file mode 100644 index bd70152f..00000000 --- a/docs/publishing.md +++ /dev/null @@ -1,144 +0,0 @@ ---- -id: publishing -title: Publishing -sidebar_label: Publishing ---- - -Publishing native modules is a critical part of native modules. The user of a native node module that is compatible with the architecture of their machine. The Node community has devised two solutions for this: - -### 1. Upload and downloading native compiled modules - -> This method requires you to have a Travis CI account. If you do not have one, [please create one](https://travis-ci.com). - -[Example](https://github.com/amilajack/disk-utility) - -Library authors can compile the native module to all multiple targets (windows, macOS, linux) and then upload those targets. Users of the module will then download these targets. - -To implement this solution, add the following `.travis.yml` to the root directory of your project: - -```yaml -os: - - osx - - linux - -language: node_js - -node_js: - - node - - 10 - - 9 - - 8 - -cache: cargo - -before_install: - # Install Rust and Cargo - - curl https://sh.rustup.rs -sSf > /tmp/rustup.sh - - sh /tmp/rustup.sh -y - - export PATH="$HOME/.cargo/bin:$PATH" - - source "$HOME/.cargo/env" - # Install NPM packages - - node -v - - npm -v - - npm install - -script: - - npm test - # Publish when using '[publish binary]' keywords - - COMMIT_MESSAGE=$(git log --format=%B --no-merges -n 1 | tr -d '\n') - - if [[ ${COMMIT_MESSAGE} =~ "[publish binary]" ]]; then yarn upload-binary || exit 0; fi; -``` - -Then add the following lines to your `package.json` to tell NPM to only publish the `lib` directory. Make sure to change the `type` and `url` properties in the `repository` object: - -```json - "repository": { - "type": "git", - "url": "git+https://github.com/your-username-here/your-project-here.git" - }, - "files": [ - "lib" - ], -``` - -Then install [`node-pre-gyp`](https://github.com/mapbox/node-pre-gyp) and [`node-pre-gyp-github`](https://github.com/bchr02/node-pre-gyp-github). Note that the master branch of `node-pre-gyp` does not work with Neon so you must use a branch of a fork which adds support for Neon: - -```bash -# NPM -npm install node-pre-gyp -npm install node-pre-gyp-github --save-dev -# Yarn -yarn add node-pre-gyp -yarn add node-pre-gyp-github --dev -``` - -Then make the following changes to the `scripts` section of your `package.json`: -```diff -- "install": "neon build --release", -+ "install": "node-pre-gyp install --fallback-to-build=false || neon build --release", -+ "package": "node-pre-gyp package", -+ "upload-binary": "node-pre-gyp package && node-pre-gyp-github publish", -``` - -`node-pre-gyp install --fallback-to-build=false` will attempt to download a binary and not fallback to a building the project with `node-pre-gyp`. The following part, ` || neon build --release`, builds a production relase using Neon if the previous script throws an error. - -Finally, add the following to the root of your `package.json`: - -```json -"binary": { - "module_name": "index", - "host": "https://github.com/your-username-here/your-repo-here/releases/download/", - "remote_path": "{version}", - "package_name": "{node_abi}-{platform}-{arch}.tar.gz", - "module_path": "./native", - "pkg_path": "." -}, -``` - -This configures `node-pre-gyp` by telling it where our `native/index.node` lives and where to upload the binaries to. Make sure to replace `your-username-here` and `your-repo-here` with the actual values. - -Note: DO NOT replace `{version}` with actual version. - -GitHub needs permission to publish releases on your behalf so you'll need to create a Personal Access Token: - -1. Go to [Developer Settings](https://github.com/settings/developers) -2. Click `Personal access tokens` -3. Click `Generate new token` -4. Select `"public_repo"` and `"repo_deployment"` -5. Generate Token -6. Copy the key that's generated and set NODE_PRE_GYP_GITHUB_TOKEN environment variable to it. Within your command prompt: - -Then add the key to the Travis CI settings of your repo. - -1. Open your project on Travis CI -2. Click `More options` dropdown -3. Click `Settings` -4. Go to `Environment Variables` and add `NODE_PRE_GYP_GITHUB_TOKEN` as `Name` and your generated token as the `Value` of your ENV variables - -For an **example of a Neon project with publishing capabilities**, see [amilajack/disk-utility](https://github.com/amilajack/disk-utility) -For more details on [`node-pre-gyp-github`'s `README`](https://github.com/bchr02/node-pre-gyp-github) for more details on publishing options - -Now you can publish binaries only on a specific commit. To do this you could borrow from the Travis CI idea of commit keywords and add special handling for commit messages with [publish binary]: - -```bash -git commit -a -m "[publish binary]" -``` - -Or, if you don't have any changes to make simply run: - -```bash -git commit --allow-empty -m "[publish binary]" -``` - -Note that these will run the `upload-binary` script, which will package and upload binaries for the current version of the package. A typical workflow of publishing a Neon module would include the following the following: - -```bash -git commit -a -m "[publish binary]" -npm publish -``` - -### 2. Shipping compiling native modules - -Another solution is shipping all the compiled native code targets with the npm module. In other words, this means publishing the package with the windows, macOS, and linux binaries. This solution is considered more secure that the approach of uploading and download modules because they prevent users of a module from [wormhole attacks](https://www.kb.cert.org/vuls/id/319816/). - -This feature is still a work in progress as the [`neon build --target` feature](https://github.com/neon-bindings/rfcs/issues/16) is a work in progress. diff --git a/docs/getting-started.md b/docs/quick-start.md similarity index 86% rename from docs/getting-started.md rename to docs/quick-start.md index 2f2aef84..69b106ae 100644 --- a/docs/getting-started.md +++ b/docs/quick-start.md @@ -1,7 +1,7 @@ --- -id: getting-started -title: Getting Started -sidebar_label: Getting Started +id: quick-start +title: Quick Start +sidebar_label: Quick Start --- This guide will help you get your system set up for building Neon projects. If you already have everything installed and are looking to write your first Neon code, try the [Hello, World!](hello-world.md) guide. @@ -14,4 +14,4 @@ Neon requires Node.js and supports **the latest Node version and all LTS release Neon requires Rust for development. If you don't already have Rust installed, or don't have a supported version, go to the [Rust web site](https://www.rust-lang.org/install.html) for installation instructions. -Rust may have additional dependencies depending on your target platform. For example, Visual Studio on Windows. +Rust may have additional dependencies depending on your target platform (for example, Visual Studio on Windows). diff --git a/docs/roadmap.md b/docs/roadmap.md deleted file mode 100644 index 867112e1..00000000 --- a/docs/roadmap.md +++ /dev/null @@ -1,55 +0,0 @@ ---- -id: roadmap -title: Roadmap -sidebar_label: Roadmap ---- - -To see what's in progress, see [our RFC's](https://github.com/neon-bindings/rfcs/pulls) - -## N-API - -#### What is [N-API](https://nodejs.org/api/n-api.html)? - -> It is independent from the underlying JavaScript runtime (ex V8) and is maintained as part of Node.js itself. This API will be Application Binary Interface (ABI) stable across versions of Node.js. It is intended to insulate Addons from changes in the underlying JavaScript engine and allow modules compiled for one major version to run on later major versions of Node.js without recompilation - -See [nodejs.org/api/n-api](https://nodejs.org/api/n-api.html) for more details - -Currently Neon uses the `v8` API for all JS interaction with the JS runtime. Migrating to N-API would give us the following: - -- ABI stability -- Remove dependency on `node-gyp` - -## Cross Compilation Flags - -Currently, Neon only supports building for the host target. We are looking to add support for compiling to multiple targets, similar to how Cargo lets you do this: - -```bash -# Cargo -cargo build --target i686-pc-windows-msvc -# Neon -neon build --target i686-pc-windows-msvc -``` - -See the [issue ticket](https://github.com/neon-bindings/rfcs/issues/16) - -## JS Standard Library Integration - -This includes supporting more ergonomic Neon APIs for JS APIs. One of these API's, for example, is the `Date` API. While it is possible to use the JS `Date` object in Neon, the methods for using it are low level. We plan to add a higher level API for the `Date` object and other basic standard library objects in JS including the following: `RegEx`, `TypedArray`, `Promise`, and other objects. - -Here's an example of how you could instantiate a JS `Date` object in Neon with the higher level API: - -```rust -let date: Handle = JsDate::now(scope)?; -let time: SystemTime = date.as_time(); -``` - -To learn more, see these RFC's: - -* [Date API](https://github.com/neon-bindings/rfcs/blob/26f10abccf49dd880449f043868b0968b137096a/text/0000-date-api.md) -* [stdlib API](https://github.com/neon-bindings/rfcs/issues/10) - -## Threadsafe Callbacks on Main Thread - -The main motivation of this is to provide a way to schedule the execution of JavaScript from any (Rust) thread. While `neon::Task` allows to perform a background task and call a JavaScript callback after it's completion, there is currently no way to propagate the progress of a task to JavaScript. - -[See the RFC](https://github.com/geovie/rfcs/blob/0f1963c1010253408229f5d0d3ee0cc7049765fa/text/0000-threadsafe-callback.md) for more details diff --git a/docs/sdl2-crate-guide.md b/docs/sdl2-crate-guide.md deleted file mode 100644 index 397b7f08..00000000 --- a/docs/sdl2-crate-guide.md +++ /dev/null @@ -1,113 +0,0 @@ ---- -id: sdl2-guide -title: SDL2 Bindings -sidebar_label: SDL2 Bindings ---- - -**This doc is currently a work in progress** - -For now, reference this snippet, taken from this comment: - -## Native - -```rust -// lib.rs -#[macro_use] -extern crate neon; -extern crate sdl2; - -use neon::context::Context; -use neon::types::{JsNumber, JsUndefined}; - -use sdl2::pixels::Color; -use sdl2::render::WindowCanvas; - -pub struct Canvas(WindowCanvas); - -declare_types! { - pub class JsCanvas for Canvas { - init(mut cx) { - let sdl_context = sdl2::init() - .or_else(|err| cx.throw_error(err.to_string()))?; - - let video_subsystem = sdl_context.video() - .or_else(|err| cx.throw_error(err.to_string()))?; - - let window = video_subsystem.window("rust-sdl2 demo", 800, 600) - .position_centered() - .build() - .or_else(|err| cx.throw_error(err.to_string()))?; - - let mut canvas = window.into_canvas().build() - .or_else(|err| cx.throw_error(err.to_string()))?; - - canvas.set_draw_color(Color::RGB(0, 255, 255)); - canvas.clear(); - canvas.present(); - - Ok(Canvas(canvas)) - } - - method set_draw_color(mut cx) { - let r = cx.argument::(0)?.value() as u8; - let g = cx.argument::(1)?.value() as u8; - let b = cx.argument::(2)?.value() as u8; - - let color = Color::RGB(r, g, b); - let mut this = cx.this(); - - cx.borrow_mut(&mut this, |mut canvas| canvas.0.set_draw_color(color)); - - Ok(cx.undefined().upcast()) - } - - method present(mut cx) { - let mut this = cx.this(); - - cx.borrow_mut(&mut this, |mut canvas| canvas.0.present()); - - Ok(cx.undefined().upcast()) - } - - method clear(mut cx) { - let mut this = cx.this(); - - cx.borrow_mut(&mut this, |mut canvas| canvas.0.clear()); - - Ok(cx.undefined().upcast()) - } - } -} - -register_module!(mut m, { - m.export_class::("Canvas")?; - Ok(()) -}); -``` - -## Node - -```js -// index.js -const { Canvas } = require('../native'); - -function sleep(n) { - return new Promise(resolve => setTimeout(resolve, n)); -} - -async function run() { - const canvas = new Canvas(); - - for (let i = 0; i < 1000; i++) { - const m = i % 255; - - canvas.set_draw_color(m, 64, 255 - m); - canvas.clear(); - canvas.present(); - - await sleep(10); - } -} - -run(); -``` diff --git a/docs/tooling.md b/docs/tooling.md deleted file mode 100644 index d3602645..00000000 --- a/docs/tooling.md +++ /dev/null @@ -1,65 +0,0 @@ ---- -id: tooling -title: Tooling -sidebar_label: Tooling ---- - -Here is a list of tooling configuration for Neon projects - -## `.travis.yml` - -This `.travis.yml` config tests against multiple node versions, tests against macOS and Linux, and installs Rust - -```yaml -os: - - osx - - linux - -language: node_js - -node_js: - - node - - 10 - - 9 - - 8 - -cache: cargo - -before_install: - # Install Rust and Cargo - - curl https://sh.rustup.rs -sSf > /tmp/rustup.sh - - sh /tmp/rustup.sh -y - - export PATH="$HOME/.cargo/bin:$PATH" - - source "$HOME/.cargo/env" - # Install NPM packages - - node -v - - npm -v - - npm install -``` - -## `.editorconfig` - -This `.editorconfig` follows the JS convention of using 2 spaces for indenting JS files. It also follows the Rust convention by indending Rust files with 4 spaces. - -``` -root = true - -[*] -indent_style = space -indent_size = 2 -end_of_line = lf -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true - -[*.md] -trim_trailing_whitespace = false - -[*.rs] -indent_style = space -indent_size = 4 - -[*.toml] -indent_style = space -indent_size = 4 -``` diff --git a/docs/type-checking.md b/docs/type-checking.md deleted file mode 100644 index ad01c087..00000000 --- a/docs/type-checking.md +++ /dev/null @@ -1,51 +0,0 @@ ---- -id: type-checking -title: Type Checking -sidebar_label: Type Checking ---- - -If we can declare functions with Rust that can be called from JS then we need to know the type of the argument that was passed to the argument in order to work with in Rust. This is where casting comes into play. **Upcasting** makes a type less specific while **Downcasting** makes a type more specific. A `JsValue`, which represents an arbitrary JS value that we do not know the type of. We can cast this value to something more specific like a `JsNumber` so that we can use it in Rust as if it were a number. Downcasting is useful when we want to pass values back to the JS engine. See the [classes section](classes.md) for more on this. - -## Upcasting - -Every method of a JS class implicity returns a `JsValue`. No type more or less specific than a `JsValue` can be returned. - -For example, the following class method would fail to compile: - -```rust -declare_types! { - /// JS class wrapping Employee records. - pub class JsEmployee for Employee { - method talk(mut cx) { - Ok(cx.string("Hello").upcast()) - } - } -} -``` - -Safely upcast a handle to a supertype. -This method does not require an execution context because it only copies a handle. - -## Downcasting - -Attempts to downcast a handle to another type, which may fail. A failure to downcast does not throw a JavaScript exception, so it's OK to continue interacting with the JS engine if this method produces an `Err` result. - -```rust -// --snip -cx.number(17).downcast(); -cx.number(17).downcast_or_throw(); -// --snip-- -``` - -## Checking Types - -Test whether this value is an instance of the given type. - -```rust -// --snip-- -let v: Handle = cx.number(17).upcast(); -v.is_a::(); // false -v.is_a::(); // true -v.is_a::(); // true -// --snip-- -``` diff --git a/docs/word-counting.md b/docs/word-counting.md deleted file mode 100644 index 532d495b..00000000 --- a/docs/word-counting.md +++ /dev/null @@ -1,130 +0,0 @@ ---- -id: word-counting -title: Word Counting -sidebar_label: Word Counting ---- - -In this tutorial, we're going to recreate the [word counting demo Dave Herman gave in his talk about neon](https://youtu.be/jINMIAicaS0?t=789). - -First start by creating a new project with `neon-cli`: - -```bash -neon new hello -cd hello -npm install # OR `yarn install` -node -e 'require("./")' -``` - -When installing the dependencies, npm will run `neon build --release` and build a release build of our code in `./native`. - -## I Take Thee at thy Word - -To illustrate what you can do with Neon, we will a little word counting demo. The demo is simple: read in the complete plays of Shakespeare and count the total number of occurrences of the word “thee”. First lets implement this demo in pure JS. The top-level code splits the corpus into lines, and sums up the counts for each line: - -```js -function search(corpus, search) { - const ls = lines(corpus); - const total = 0; - for (let i = 0, n = ls.length; i < n; i++) { - total += wcLine(ls[i], search); - } - return total; -} -``` - -Searching an individual line involves splitting the line up into word and matching each word against the search string: - -```js -function wcLine(line, search) { - const words = line.split(' '); - const total = 0; - for (let i = 0, n = words.length; i < n; i++) { - if (matches(words[i], search)) { - total++; - } - } - return total; -} -``` - -The rest of the details are pretty straightforward but definitely check out the code—it’s small and self-contained. - -## Fall Into our Rustic Revelry - -One of the amazing things about Rust is that highly efficient code can still be remarkably compact and readable. In the Rust version of the algorithm, the code for summing up the counts for all the lines looks pretty similar to the JS code: - -```rust -let mut total = 0; -for word in line.split(' ') { - if matches(word, search) { - total += 1; - } -} -total // in Rust you can omit `return` for a trailing expression -``` - -In fact, that same code can be written at a higher level of abstraction without losing performance, using iteration methods like filter and fold (similar to Array.prototype.filter and Array.prototype.reduce in JS): - -```rust -line.split(' ') - .filter(|word| matches(word, search)) - .fold(0, |sum, _| sum + 1) -``` - -In my quick experiments, that even seems to shave a few milliseconds off the total running time. I think this is a nice demonstration of the power of Rust’s zero-cost abstractions, where idiomatic and high-level abstractions produce the same or sometimes even better performance (by making additional optimizations possible, like eliminating bounds checks) than lower-level, more obscure code. - -On my machine, the simple Rust translation runs in about 80 – 85ms. Not bad—about 3x as fast just from using Rust, and in roughly the same number of lines of code (60 in JS, 70 in Rust). By the way, I’m being approximate here with the numbers, because this isn’t a remotely scientific benchmark. My goal is just to demonstrate that you can get significant performance improvements from using Rust; in any given situation, the particular details will of course matter. - -## Their Thread of Life is Spun - -We’re not done yet, though! Rust enables something even cooler for Node: we can easily and safely parallelize this code—and I mean without the night-sweats and palpitations usually associated with multithreading. Here’s a quick look at the top level logic in the Rust implementation of the demo: - -```rust -let total = cx.borrow(&buffer, |data| { - let corpus = str::from_utf8(data.as_slice()).ok().unwrap(); - wc_parallel(&lines(corpus), search) -}); -``` - -The `cx.borrow` API lets Neon safely expose the raw bytes of a Node Buffer object (i.e., a typed array) to Rust threads, by preventing JS from running in the meantime. And Rust’s concurrency model makes programming with threads actually fun. - -To demonstrate how easy this can be, I used Niko Matsakis’s new Rayon crate of beautiful data parallelism abstractions. Changing the demo to use Rayon is as simple as replacing the into_iter/map/fold/ lines above with: - -```diff -+ lines.into_par_iter() - .map(|line| wc_line(line, search)) - .sum() -``` - -Keep in mind, Rayon wasn’t designed with Neon in mind—its generic primitives match the iteration protocols of Rust, so Neon was able to just pull it off the shelf. - -With that simple change, on my two-core MacBook Air, the demo goes from about 85ms down to about 50ms. - -## Bridge Most Valiantly, with Excellent Discipline - -I’ve worked on making the integration as seamless as possible. From the Rust side, Neon functions follow a simple protocol, taking a Call object and returning a JavaScript value: - -```rust -fn search(mut cx: FunctionContext) -> JsResult { - // ... - Ok(cx.number(total)) -} -``` - -`cx`, a `FunctionContext` struct, safely tracks handles into V8’s garbage-collected heap. The Neon API uses the Rust type system to guarantee that your native module can’t crash your app by mismanaging object handles. - -From the JS side, loading the native module is straightforward: - -```js -const myNeonModule = require('neon-bridge').load(); -``` - -## Wherefore’s this Noise? - -I hope this demo is enough to get people interested. Beyond the sheer fun of it, I think the strongest reasons for using Rust in Node are performance and parallelism. As the Rust ecosystem grows, it’ll also be a way to give Node access to cool Rust libraries. Beyond that, I’m hoping that Neon can make a nice abstraction layer that just makes writing native Node modules less painful. With projects like node-uwp it might even be worth exploring evolving Neon towards a JS-engine-agnostic abstraction layer. - -There are lots of possibilities, but I need help! If you want to get involved, I’ve created a community slack (grab an invite from the Slackin app) and a #neon IRC channel on Mozilla IRC (irc.mozilla.org). - -## Source - -Here is the [source code](https://github.com/neon-bindings/examples/tree/legacy/word-counting) for this guide. diff --git a/docusaurus.config.js b/docusaurus.config.js index 43f3886c..c83e06c4 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -30,12 +30,7 @@ module.exports = { src: "logo/letter-logo-alpha.png", }, items: [ - { position: "left", to: "docs/intro", label: "Docs" }, - { - position: "left", - label: "Examples", - href: "https://github.com/neon-bindings/examples/tree/legacy#table-of-contents", - }, + { position: "left", to: "docs/introduction", label: "Docs" }, { position: "left", label: "API Reference", @@ -46,14 +41,13 @@ module.exports = { items: [ { label: "GitHub", href: CORE_REPO_URL }, { - label: "Help", + label: "Slack", href: "https://rust-bindings-slackin.herokuapp.com", }, { label: "Twitter", - to: "https://twitter.com/rustneon", + href: "https://twitter.com/rustneon", }, - { label: "Roadmap", to: "docs/roadmap" }, ], label: "Community", }, @@ -68,7 +62,7 @@ module.exports = { items: [ { label: "Introduction", - to: "docs/intro", + to: "docs/introduction", }, { label: "Examples", diff --git a/sidebars.js b/sidebars.js index e88169c3..45846b77 100644 --- a/sidebars.js +++ b/sidebars.js @@ -1,22 +1,12 @@ module.exports = { docs: { - Introduction: ["intro", "getting-started", "hello-world"], - Guides: [ - "primitives", - "arrays", + "Get Started": ["introduction", "quick-start", "hello-world"], + "How To": [ + "primitive-types", "objects", - "arguments", - "type-checking", + "arrays", "functions", - "classes", - "modules", - "json", - "errors", - "async", - "electron-apps", - "publishing", ], - Tutorials: ["word-counting"], - Other: ["roadmap", "tooling", "example-projects", "learning-resources"], + Community: ["example-projects"], }, }; diff --git a/src/pages/index.js b/src/pages/index.js index 499742b2..ec7675d0 100755 --- a/src/pages/index.js +++ b/src/pages/index.js @@ -125,7 +125,7 @@ function Home() { sm={{ size: 3 }}> - +