From 29ae4cfcc8342847c1e171776a9ff66a7e46bf84 Mon Sep 17 00:00:00 2001 From: David Herman Date: Wed, 8 Sep 2021 08:38:55 -0700 Subject: [PATCH 01/16] WIP --- docs/arguments.md | 149 ----------- docs/arrays.md | 2 - docs/async.md | 160 ------------ docs/classes.md | 275 -------------------- docs/electron-apps.md | 69 ----- docs/errors.md | 19 -- docs/functions.md | 144 +++++++++- docs/guides.md | 11 - docs/hello-world.md | 8 +- docs/{intro.md => introduction.md} | 6 +- docs/json.md | 159 ----------- docs/learning-resources.md | 27 -- docs/modules.md | 56 ---- docs/objects.md | 2 - docs/parallisim.md | 19 -- docs/primitive-types.md | 53 ++++ docs/primitives.md | 54 ---- docs/publishing.md | 144 ---------- docs/{getting-started.md => quick-start.md} | 8 +- docs/roadmap.md | 55 ---- docs/sdl2-crate-guide.md | 113 -------- docs/tooling.md | 65 ----- docs/type-checking.md | 51 ---- docs/word-counting.md | 130 --------- docusaurus.config.js | 10 +- sidebars.js | 18 +- 26 files changed, 211 insertions(+), 1596 deletions(-) delete mode 100644 docs/arguments.md delete mode 100644 docs/async.md delete mode 100644 docs/classes.md delete mode 100644 docs/electron-apps.md delete mode 100644 docs/errors.md delete mode 100644 docs/guides.md rename docs/{intro.md => introduction.md} (77%) delete mode 100644 docs/json.md delete mode 100644 docs/learning-resources.md delete mode 100644 docs/modules.md delete mode 100644 docs/parallisim.md create mode 100644 docs/primitive-types.md delete mode 100644 docs/primitives.md delete mode 100644 docs/publishing.md rename docs/{getting-started.md => quick-start.md} (86%) delete mode 100644 docs/roadmap.md delete mode 100644 docs/sdl2-crate-guide.md delete mode 100644 docs/tooling.md delete mode 100644 docs/type-checking.md delete mode 100644 docs/word-counting.md 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..2e0caaaf 100644 --- a/docs/arrays.md +++ b/docs/arrays.md @@ -4,8 +4,6 @@ title: Arrays sidebar_label: Arrays --- -[Examples](https://github.com/neon-bindings/examples/tree/legacy/arrays) - ## Converting from `Vec` to `JsArray` Here is a simple example of converting a Rust `Vec` to a JS `Array` using `JsArray`: 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/functions.md b/docs/functions.md index f50e786e..8ea5a9e7 100644 --- a/docs/functions.md +++ b/docs/functions.md @@ -4,8 +4,6 @@ title: Functions sidebar_label: Functions --- -[Examples](https://github.com/neon-bindings/examples/tree/legacy/functions) - ## This 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 @@ -55,3 +53,145 @@ pub fn return_js_function(mut cx: FunctionContext) -> JsResult { JsFunction::new(&mut cx, add1) } ``` + +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/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..b672d65f 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 simple example project will be a tiny 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/). +Even this simple project demonstrates some of Neon's power: Rust's [crate ecosystem](https://crates.io/) is younger than npm but growing quickly and full of many 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/). # Creating a New Project 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..873e4871 100644 --- a/docs/objects.md +++ b/docs/objects.md @@ -4,8 +4,6 @@ title: Objects sidebar_label: Objects --- -[Examples](https://github.com/neon-bindings/examples/tree/legacy/objects) - `JsObject`'s are used to create objects on the JS heap. `JsObject` structs have two methods: `get` and `set`: ## Getting Properties 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..cb4a6851 --- /dev/null +++ b/docs/primitive-types.md @@ -0,0 +1,53 @@ +--- +id: primitive-types +title: Primitive Types +sidebar_label: Primitive Types +--- + +## 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 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) +``` + +## 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 + +```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/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..250952b2 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -31,11 +31,6 @@ module.exports = { }, 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", 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", }, diff --git a/sidebars.js b/sidebars.js index e88169c3..2e7c4de7 100644 --- a/sidebars.js +++ b/sidebars.js @@ -1,22 +1,12 @@ module.exports = { docs: { - Introduction: ["intro", "getting-started", "hello-world"], - Guides: [ - "primitives", + "Get Started": ["introduction", "quick-start", "hello-world"], + "How To": [ + "primitive-types", "arrays", "objects", - "arguments", - "type-checking", "functions", - "classes", - "modules", - "json", - "errors", - "async", - "electron-apps", - "publishing", ], - Tutorials: ["word-counting"], - Other: ["roadmap", "tooling", "example-projects", "learning-resources"], + Community: ["example-projects"], }, }; From a519c6e5ba9f9cd21fbb5d7aa0f44c53f9889cad Mon Sep 17 00:00:00 2001 From: David Herman Date: Wed, 8 Sep 2021 08:53:51 -0700 Subject: [PATCH 02/16] Fix broken links --- docs/functions.md | 2 +- docs/hello-world.md | 4 ++-- docusaurus.config.js | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/functions.md b/docs/functions.md index 8ea5a9e7..8f6fb723 100644 --- a/docs/functions.md +++ b/docs/functions.md @@ -6,7 +6,7 @@ sidebar_label: Functions ## This -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 +In order to call methods on `cx.this`, it must be downcasted to a `JsObject`. ```rust pub fn require_object_this(mut cx: FunctionContext) -> JsResult { diff --git a/docs/hello-world.md b/docs/hello-world.md index b672d65f..5928b749 100644 --- a/docs/hello-world.md +++ b/docs/hello-world.md @@ -8,7 +8,7 @@ sidebar_label: Hello World! This simple example project will be a tiny 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. -Even this simple project demonstrates some of Neon's power: Rust's [crate ecosystem](https://crates.io/) is younger than npm but growing quickly and full of many 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/). +Even this simple project demonstrates some of Neon's power: Rust's [crate ecosystem](https://crates.io/) is younger than npm but growing quickly and full of many 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](https://electronjs.org) app. # Creating a New Project @@ -96,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/docusaurus.config.js b/docusaurus.config.js index 250952b2..c83e06c4 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -30,7 +30,7 @@ module.exports = { src: "logo/letter-logo-alpha.png", }, items: [ - { position: "left", to: "docs/intro", label: "Docs" }, + { position: "left", to: "docs/introduction", label: "Docs" }, { position: "left", label: "API Reference", @@ -62,7 +62,7 @@ module.exports = { items: [ { label: "Introduction", - to: "docs/intro", + to: "docs/introduction", }, { label: "Examples", From 0a46f6b946246c778c043760bcbebbfc6d079c10 Mon Sep 17 00:00:00 2001 From: David Herman Date: Wed, 8 Sep 2021 12:33:47 -0700 Subject: [PATCH 03/16] Rewrote the Primitive Types and Objects docs. --- docs/example-projects.md | 6 +-- docs/objects.md | 91 +++++++++++++++++++++++++--------------- docs/primitive-types.md | 22 +++++----- sidebars.js | 2 +- src/pages/index.js | 2 +- 5 files changed, 74 insertions(+), 49 deletions(-) diff --git a/docs/example-projects.md b/docs/example-projects.md index 31daf64d..8d6a3460 100644 --- a/docs/example-projects.md +++ b/docs/example-projects.md @@ -8,7 +8,6 @@ Here is a list of projects using Neon ## Node Libraries -- [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) @@ -21,12 +20,13 @@ Here is a list of projects using Neon And many more! -## Electron Apps +## Applications +- [1password](https://dteare.medium.com/behind-the-scenes-of-1password-for-linux-d59b19143a23) - [finda: Finda finds things — in under 16 milliseconds.](https://keminglabs.com/finda/) ## Rust Libraries -- [neon-serde: Easily convert between `JsHandle` and `serde` structs](https://github.com/GabrielCastro/neon-serde) +- [neon-serde: Easily convert between `JsHandle` and `serde` structs](https://crates.io/crates/neon-serde2) Want to add a project to this list? Submit a PR! diff --git a/docs/objects.md b/docs/objects.md index 873e4871..030fb43f 100644 --- a/docs/objects.md +++ b/docs/objects.md @@ -4,61 +4,84 @@ title: Objects sidebar_label: Objects --- -`JsObject`'s are used to create objects on the JS heap. `JsObject` structs have two methods: `get` and `set`: +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. + +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: u64, } ``` -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. Just for fun and to make it a little more idiomatically Rusty, we'll define it as a method of the `Book` type: + ```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) +impl Book { + fn to_object(&self, mut cx: FunctionContext) -> JsResult { + 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) + } } +``` -register_module!(mut m, { - m.export_function("convertStructToJsObject", convert_struct_to_js_object) -}); +Let's walk through the implementation a step at a time. The `to_object` method takes a reference to `self` and a runtime context. Next, 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(&self, mut cx: impl Context) -> JsResult { + // same as before... + } +} ``` diff --git a/docs/primitive-types.md b/docs/primitive-types.md index cb4a6851..278ec988 100644 --- a/docs/primitive-types.md +++ b/docs/primitive-types.md @@ -4,6 +4,8 @@ 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: @@ -13,7 +15,7 @@ let i: Handle = cx.number(42); let f: Handle = cx.number(3.14); ``` -For types that aren't implicitly convertible to `f64`, you can cast a number with Rust's [`as`](https://doc.rust-lang.org/std/keyword.as.html) operator: +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::(); @@ -30,24 +32,24 @@ 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 -// --snip-- -let boolean = cx.boolean(true); -// --snip-- +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 -// --snip-- -let undefined = cx.undefined(); -// --snip-- +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 -// --snip-- -let null = cx.null(); -// --snip-- +let n: Handle = cx.null(); ``` diff --git a/sidebars.js b/sidebars.js index 2e7c4de7..45846b77 100644 --- a/sidebars.js +++ b/sidebars.js @@ -3,8 +3,8 @@ module.exports = { "Get Started": ["introduction", "quick-start", "hello-world"], "How To": [ "primitive-types", - "arrays", "objects", + "arrays", "functions", ], 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 }}> - + From 4fd03be704776bd21eddda7b8a3bfc672d969183 Mon Sep 17 00:00:00 2001 From: David Herman Date: Wed, 8 Sep 2021 22:25:08 -0700 Subject: [PATCH 04/16] Rewrote the arrays doc --- docs/arrays.md | 114 ++++++++++++++++++++++++------------------------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/docs/arrays.md b/docs/arrays.md index 2e0caaaf..e751a7f6 100644 --- a/docs/arrays.md +++ b/docs/arrays.md @@ -4,89 +4,89 @@ title: Arrays sidebar_label: Arrays --- -## Converting from `Vec` to `JsArray` +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. -Here is a simple example of converting a Rust `Vec` to a JS `Array` using `JsArray`: +## Creating Arrays -```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); +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: - // 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(); - } +```rust +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); ``` From e61ae99db74518db0544289d24a73803296c8e72 Mon Sep 17 00:00:00 2001 From: David Herman Date: Fri, 10 Sep 2021 19:43:36 -0700 Subject: [PATCH 05/16] Code examples for the functions doc --- docs/functions.md | 216 ++++++++++++++-------------------------------- 1 file changed, 66 insertions(+), 150 deletions(-) diff --git a/docs/functions.md b/docs/functions.md index 8f6fb723..6c587556 100644 --- a/docs/functions.md +++ b/docs/functions.md @@ -4,194 +4,110 @@ title: Functions sidebar_label: Functions --- -## This - -In order to call methods on `cx.this`, it must be downcasted to a `JsObject`. - -```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()) -} -``` - -## Calling JS Functions - -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` +## Defining Functions ```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) +fn hello(mut cx: FunctionContext) -> JsResult { + Ok(cx.string("hello")) } -``` - -## Constructing JS Functions -```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) +#[neon::main] +pub fn main(mut cx: ModuleContext) -> NeonResult<()> { + cx.export_function("hello", hello)?; + Ok(()) } ``` -## Returning Functions +## Accessing Arguments ```rust -pub fn return_js_function(mut cx: FunctionContext) -> JsResult { - JsFunction::new(&mut cx, add1) -} -``` +fn create_pair(mut cx: FunctionContext) -> JsResult { + let x: Handle = cx.argument(0)?; + let y: Handle = cx.argument(1)?; -Neon provides built-in mechanisims for accessing the `arguments` object. + let obj = cx.empty_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. + obj.set(&mut cx, "x", x)?; + obj.set(&mut cx, "y", y)?; -## 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 + Ok(obj) +} -```rust -fn say_hi(mut cx: FunctionContext) -> JsResult { - let arg0 = cx.argument::(0)?; - // --snip-- +#[neon::main] +pub fn main(mut cx: ModuleContext) -> NeonResult<()> { + cx.export_function("createPair", create_pair)?; + Ok(()) } ``` -## Asserting Argument Types +## Checking Argument Types ```rust -pub fn foo(mut cx: FunctionContext) -> JsResult { - cx.check_argument::(0)?; - cx.check_argument::(1)?; - Ok(cx.undefined()) -} -``` +fn create_book(mut cx: FunctionContext) -> JsResult { + let title = cx.argument::(0)?; + let author = cx.argument::(1)?; + let year = cx.argument::(2)?; -Now in our `./lib/index.js`: + let obj = cx.empty_object(); -```js -const { foo } = require('../native'); -foo(); // fails -foo(12); // fails -foo('foobar'); // fails -foo('foobar', 12); // passes! -``` + obj.set(&mut cx, "title", title)?; + obj.set(&mut cx, "author", author)?; + obj.set(&mut cx, "year", year)?; -## 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)) + Ok(obj) } -register_module!(mut m, { - m.export_function("add1", add1) -}); +#[neon::main] +pub fn main(mut cx: ModuleContext) -> NeonResult<()> { + cx.export_function("createBook", create_book)?; + Ok(()) +} ``` -## Getting the Number of Arguments - -This is a simple example of getting the length of `arguments` +## Optional Arguments ```rust -pub fn get_args_len(mut cx: FunctionContext) -> JsResult { - let args_length = cx.len(); - println!("{}", args_length); - Ok(cx.number(args_length)) -} +fn create_entry(mut cx: FunctionContext) -> JsResult { + let company = cx.argument::(0)?; + let job = cx.argument::(1)?; + let start_year = cx.argument::(2)?; + let end_year = cx.argument_opt(3); -register_module!(mut m, { - m.export_function("getArgsLen", get_args_len) -}); -``` - -Now in our `./lib/index.js` we add the following: + let obj = cx.empty_object(); -```js -// ./lib/index.js -const { getArgsLen } = require('../native'); -getArgsLen(); // 0 -getArgsLen(1); // 1 -getArgsLen(1, 'foobar'); // 2 -``` + obj.set(&mut cx, "company", company)?; + obj.set(&mut cx, "job", job)?; + obj.set(&mut cx, "startYear", start_year)?; -## Optional Arguments + if let Some(end_year) = end_year { + obj.set(&mut cx, "endYear", end_year)?; + } -Produces the `i`th argument, or `None` if `i` is greater than or equal to `self.len()`. + Ok(obj) +} -```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()) +#[neon::main] +pub fn main(mut cx: ModuleContext) -> NeonResult<()> { + cx.export_function("createEntry", create_entry)?; + Ok(()) } ``` -## Default Values - -Handling default values is similar to handling **Optional Arguments**: +## Calling Functions ```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()) -} +let global = cx.global(); +let func: Handle = global.get(&mut cx, "parseInt")?; +let null = cx.null(); +let s = cx.string(s); +let result = func.call(&mut cx, null, vec![s])?; ``` -Here's how we'd call those functions: +## Calling Constructor 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 +```rust +let global = cx.global(); +let ctor: Handle = global.get(&mut cx, "URL")?; +let url: Handle = cx.string("https://neon-bindings.com"); +let result = ctor.construct(&mut cx, vec![url]); ``` From b1b059832ec4ddd7544676c34d4905c78dc7ee04 Mon Sep 17 00:00:00 2001 From: David Herman Date: Fri, 10 Sep 2021 22:47:40 -0700 Subject: [PATCH 06/16] First draft of Functions doc --- docs/functions.md | 57 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 45 insertions(+), 12 deletions(-) diff --git a/docs/functions.md b/docs/functions.md index 6c587556..0116ac6e 100644 --- a/docs/functions.md +++ b/docs/functions.md @@ -4,13 +4,23 @@ title: Functions sidebar_label: 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. + ## Defining Functions +A Neon function looks and acts to JavaScript like a regular JavaScript function, but its behavior is defined in pure Rust. Defining 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 fn hello(mut cx: FunctionContext) -> JsResult { Ok(cx.string("hello")) } +``` + +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 exception. In this simple example, we just construct a simple string and return it, so we immediately wrap the outcome in an `Ok` result. +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 #[neon::main] pub fn main(mut cx: ModuleContext) -> NeonResult<()> { cx.export_function("hello", hello)?; @@ -18,8 +28,18 @@ pub fn main(mut cx: ModuleContext) -> NeonResult<()> { } ``` +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 fn create_pair(mut cx: FunctionContext) -> JsResult { let x: Handle = cx.argument(0)?; @@ -32,16 +52,12 @@ fn create_pair(mut cx: FunctionContext) -> JsResult { Ok(obj) } - -#[neon::main] -pub fn main(mut cx: ModuleContext) -> NeonResult<()> { - cx.export_function("createPair", create_pair)?; - Ok(()) -} ``` ## 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 fn create_book(mut cx: FunctionContext) -> JsResult { let title = cx.argument::(0)?; @@ -64,8 +80,22 @@ pub fn main(mut cx: ModuleContext) -> NeonResult<()> { } ``` +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_entry(mut cx: FunctionContext) -> JsResult { let company = cx.argument::(0)?; @@ -81,20 +111,19 @@ fn create_entry(mut cx: FunctionContext) -> JsResult { 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) } - -#[neon::main] -pub fn main(mut cx: ModuleContext) -> NeonResult<()> { - cx.export_function("createEntry", create_entry)?; - Ok(()) -} ``` ## 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 global = cx.global(); let func: Handle = global.get(&mut cx, "parseInt")?; @@ -103,8 +132,12 @@ 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(); let ctor: Handle = global.get(&mut cx, "URL")?; From dac06d7d3f10738778d0ae8fce907241a6f4661c Mon Sep 17 00:00:00 2001 From: David Herman Date: Fri, 10 Sep 2021 23:00:34 -0700 Subject: [PATCH 07/16] Bold the first use of "functions" to be consistent with the other docs --- docs/functions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/functions.md b/docs/functions.md index 0116ac6e..3bd77181 100644 --- a/docs/functions.md +++ b/docs/functions.md @@ -4,7 +4,7 @@ title: Functions sidebar_label: 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. +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. ## Defining Functions From 6f8c675ee8d5addda7b981241048f4d59065dead Mon Sep 17 00:00:00 2001 From: David Herman Date: Fri, 10 Sep 2021 23:04:16 -0700 Subject: [PATCH 08/16] Tweak variable names for the resume/job history example --- docs/functions.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/functions.md b/docs/functions.md index 3bd77181..9b583cb2 100644 --- a/docs/functions.md +++ b/docs/functions.md @@ -97,16 +97,16 @@ try { 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_entry(mut cx: FunctionContext) -> JsResult { +fn create_job(mut cx: FunctionContext) -> JsResult { let company = cx.argument::(0)?; - let job = cx.argument::(1)?; + 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, "job", job)?; + obj.set(&mut cx, "title", title)?; obj.set(&mut cx, "startYear", start_year)?; if let Some(end_year) = end_year { From ea1d4cdfc6f197ccd2ad3244ea4dfd8ba9777f83 Mon Sep 17 00:00:00 2001 From: David Herman Date: Sat, 11 Sep 2021 00:25:38 -0700 Subject: [PATCH 09/16] Update the "who's using Neon" list --- docs/example-projects.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/example-projects.md b/docs/example-projects.md index 8d6a3460..602cb029 100644 --- a/docs/example-projects.md +++ b/docs/example-projects.md @@ -17,16 +17,18 @@ Here is a list of projects using Neon - [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) +- [big_mac: get MAC addresses on the local device](https://github.com/ultamatt/big_mac) -And many more! +And many more! ## Applications -- [1password](https://dteare.medium.com/behind-the-scenes-of-1password-for-linux-d59b19143a23) +- [1password: Secure password manager](https://dteare.medium.com/behind-the-scenes-of-1password-for-linux-d59b19143a23) +- [Signal: Secure, private messaging](https://github.com/signalapp/libsignal-client) - [finda: Finda finds things — in under 16 milliseconds.](https://keminglabs.com/finda/) ## Rust Libraries -- [neon-serde: Easily convert between `JsHandle` and `serde` structs](https://crates.io/crates/neon-serde2) +- [neon-serde: Easily convert between Rust and JavaScript datatypes](https://crates.io/crates/neon-serde2) Want to add a project to this list? Submit a PR! From 2f8d8790ea1bcc7b50dce15155a2ce32a8c3eb0a Mon Sep 17 00:00:00 2001 From: David Herman Date: Sat, 11 Sep 2021 00:37:19 -0700 Subject: [PATCH 10/16] More editing of the "Who's Using Neon" page --- docs/example-projects.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/example-projects.md b/docs/example-projects.md index 602cb029..c18f3762 100644 --- a/docs/example-projects.md +++ b/docs/example-projects.md @@ -4,7 +4,13 @@ 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 large community of applications and libraries—maybe some you use yourself! + +## Applications + +- [1password: Secure password manager](https://dteare.medium.com/behind-the-scenes-of-1password-for-linux-d59b19143a23) +- [Signal: Secure, private messaging](https://github.com/signalapp/libsignal-client) +- [finda: Type stuff, find things. No mousing.](https://keminglabs.com/finda/) ## Node Libraries @@ -19,16 +25,10 @@ Here is a list of projects using Neon - [node-webrender: bindings to webrender](https://github.com/cztomsik/node-webrender) - [big_mac: get MAC addresses on the local device](https://github.com/ultamatt/big_mac) -And many more! - -## Applications - -- [1password: Secure password manager](https://dteare.medium.com/behind-the-scenes-of-1password-for-linux-d59b19143a23) -- [Signal: Secure, private messaging](https://github.com/signalapp/libsignal-client) -- [finda: Finda finds things — in under 16 milliseconds.](https://keminglabs.com/finda/) +And many more! ## Rust Libraries - [neon-serde: Easily convert between Rust and JavaScript datatypes](https://crates.io/crates/neon-serde2) -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)! From 88806298236e1d2f669a319f438f3787acd1ca3f Mon Sep 17 00:00:00 2001 From: David Herman Date: Sat, 11 Sep 2021 00:41:13 -0700 Subject: [PATCH 11/16] Capitalize Finda --- docs/example-projects.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/example-projects.md b/docs/example-projects.md index c18f3762..2a8401f0 100644 --- a/docs/example-projects.md +++ b/docs/example-projects.md @@ -10,7 +10,7 @@ Neon is used to power a large community of applications and libraries—maybe so - [1password: Secure password manager](https://dteare.medium.com/behind-the-scenes-of-1password-for-linux-d59b19143a23) - [Signal: Secure, private messaging](https://github.com/signalapp/libsignal-client) -- [finda: Type stuff, find things. No mousing.](https://keminglabs.com/finda/) +- [Finda: Type stuff, find things. No mousing.](https://keminglabs.com/finda/) ## Node Libraries From d1bf5fdbf4889f6de4cdb658d9e88612b5791559 Mon Sep 17 00:00:00 2001 From: David Herman Date: Sat, 11 Sep 2021 01:04:38 -0700 Subject: [PATCH 12/16] Tidy up formatting on "Who's Using Neon" page --- docs/example-projects.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/example-projects.md b/docs/example-projects.md index 2a8401f0..cb89fb28 100644 --- a/docs/example-projects.md +++ b/docs/example-projects.md @@ -8,27 +8,27 @@ Neon is used to power a large community of applications and libraries—maybe so ## Applications -- [1password: Secure password manager](https://dteare.medium.com/behind-the-scenes-of-1password-for-linux-d59b19143a23) -- [Signal: Secure, private messaging](https://github.com/signalapp/libsignal-client) -- [Finda: Type stuff, find things. No mousing.](https://keminglabs.com/finda/) +- **[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. ## Node Libraries -- [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) -- [big_mac: get MAC addresses on the local device](https://github.com/ultamatt/big_mac) +- **[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 +- **[libsodium-neon:](https://github.com/wireapp/libsodium-neon)** Node bindings to libsodium +- **[ledb:](https://github.com/katyo/ledb)** Lightweight embedded database +- **[node-crypto:](https://github.com/Brooooooklyn/node-crypto)** rust-crypto bindings for Node +- **[os-type:](https://github.com/amilajack/os-type)** Bindings to the os_type Rust library +- **[node-webrender:](https://github.com/cztomsik/node-webrender)** bindings to webrender +- **[big_mac:](https://github.com/ultamatt/big_mac)** get MAC addresses on the local device And many more! ## Rust Libraries -- [neon-serde: Easily convert between Rust and JavaScript datatypes](https://crates.io/crates/neon-serde2) +- **[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](https://github.com/neon-bindings/website)! From b884f0edb5295dfc06b80b00e3f11aebb40de897 Mon Sep 17 00:00:00 2001 From: David Herman Date: Sat, 11 Sep 2021 01:13:12 -0700 Subject: [PATCH 13/16] Drop some older libraries from "Who's Using Neon" --- docs/example-projects.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/docs/example-projects.md b/docs/example-projects.md index cb89fb28..07966298 100644 --- a/docs/example-projects.md +++ b/docs/example-projects.md @@ -4,7 +4,7 @@ title: Who's Using Neon sidebar_label: Who's Using Neon --- -Neon is used to power a large community of applications and libraries—maybe some you use yourself! +Neon is used to power a growing community of applications and libraries—maybe some you use yourself! ## Applications @@ -18,12 +18,9 @@ Neon is used to power a large community of applications and libraries—maybe so - **[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 -- **[libsodium-neon:](https://github.com/wireapp/libsodium-neon)** Node bindings to libsodium - **[ledb:](https://github.com/katyo/ledb)** Lightweight embedded database -- **[node-crypto:](https://github.com/Brooooooklyn/node-crypto)** rust-crypto bindings for Node - **[os-type:](https://github.com/amilajack/os-type)** Bindings to the os_type Rust library -- **[node-webrender:](https://github.com/cztomsik/node-webrender)** bindings to webrender -- **[big_mac:](https://github.com/ultamatt/big_mac)** get MAC addresses on the local device +- **[big_mac:](https://github.com/ultamatt/big_mac)** Get MAC addresses on the local device And many more! From 526c66409a01e5285f24a7b598d4bf991c31d3f7 Mon Sep 17 00:00:00 2001 From: Dave Herman Date: Sat, 11 Sep 2021 07:33:54 -0700 Subject: [PATCH 14/16] Copy edit Remove redundant "simple" Co-authored-by: K.J. Valencik --- docs/functions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/functions.md b/docs/functions.md index 9b583cb2..1212b348 100644 --- a/docs/functions.md +++ b/docs/functions.md @@ -16,7 +16,7 @@ fn hello(mut cx: FunctionContext) -> JsResult { } ``` -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 exception. In this simple example, we just construct a simple string and return it, so we immediately wrap the outcome in an `Ok` result. +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 exception. In this simple example, we just construct a string and return it, so we immediately wrap the outcome in an `Ok` result. 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): From bdbfbfaa6ed6dd806c4d082a6bb555baae02a3e0 Mon Sep 17 00:00:00 2001 From: David Herman Date: Sat, 11 Sep 2021 11:36:09 -0700 Subject: [PATCH 15/16] Address issues found in KJ's review --- docs/functions.md | 10 +++++---- docs/hello-world.md | 4 ++-- docs/objects.md | 46 ++++++++++++++++++++++++++++++++++++----- docs/primitive-types.md | 2 ++ 4 files changed, 51 insertions(+), 11 deletions(-) diff --git a/docs/functions.md b/docs/functions.md index 1212b348..d9c43a68 100644 --- a/docs/functions.md +++ b/docs/functions.md @@ -8,7 +8,7 @@ Neon's main way of connecting Rust and JavaScript is by allowing you to define * ## Defining Functions -A Neon function looks and acts to JavaScript like a regular JavaScript function, but its behavior is defined in pure Rust. Defining 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: +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 fn hello(mut cx: FunctionContext) -> JsResult { @@ -16,7 +16,7 @@ fn hello(mut cx: FunctionContext) -> JsResult { } ``` -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 exception. In this simple example, we just construct a string and return it, so we immediately wrap the outcome in an `Ok` result. +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. 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): @@ -28,6 +28,8 @@ pub fn main(mut cx: ModuleContext) -> NeonResult<()> { } ``` +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 @@ -126,7 +128,7 @@ You can call a JavaScript function from Rust with [`JsFunction::call()`](https:/ ```rust let global = cx.global(); -let func: Handle = global.get(&mut cx, "parseInt")?; +let func = global.get(&mut cx, "parseInt")?.downcast_or_throw::()?; let null = cx.null(); let s = cx.string(s); let result = func.call(&mut cx, null, vec![s])?; @@ -140,7 +142,7 @@ You can call a JavaScript function as a constructor, as if with the [`new`](http ```rust let global = cx.global(); -let ctor: Handle = global.get(&mut cx, "URL")?; +let ctor = global.get(&mut cx, "URL")?.downcast_or_throw::()?; let url: Handle = cx.string("https://neon-bindings.com"); let result = ctor.construct(&mut cx, vec![url]); ``` diff --git a/docs/hello-world.md b/docs/hello-world.md index 5928b749..556927be 100644 --- a/docs/hello-world.md +++ b/docs/hello-world.md @@ -6,9 +6,9 @@ sidebar_label: Hello World! **Full source code:** [examples/cpu-count](https://github.com/neon-bindings/examples/tree/main/examples/cpu-count) -This simple example project will be a tiny 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. +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. -Even this simple project demonstrates some of Neon's power: Rust's [crate ecosystem](https://crates.io/) is younger than npm but growing quickly and full of many 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](https://electronjs.org) app. +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 diff --git a/docs/objects.md b/docs/objects.md index 030fb43f..09e9f24c 100644 --- a/docs/objects.md +++ b/docs/objects.md @@ -49,15 +49,34 @@ Here is a simple example of converting a Rust `struct` to a JavaScript object. F struct Book { pub title: String, pub author: String, - pub year: u64, + pub year: u32, } ``` -To copy a `Book` into a JavaScript object, we'll define a conversion function. Just for fun and to make it a little more idiomatically Rusty, we'll define it as a method of the `Book` type: +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 +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(&self, mut cx: FunctionContext) -> JsResult { + 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); @@ -74,14 +93,31 @@ impl Book { } ``` -Let's walk through the implementation a step at a time. The `to_object` method takes a reference to `self` and a runtime context. Next, 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. +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(&self, mut cx: impl Context) -> JsResult { + 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): + +```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/primitive-types.md b/docs/primitive-types.md index 278ec988..553885f0 100644 --- a/docs/primitive-types.md +++ b/docs/primitive-types.md @@ -22,6 +22,8 @@ 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. From cc5108a156970993aeeaef05742fbc1822c6b97f Mon Sep 17 00:00:00 2001 From: David Herman Date: Sat, 11 Sep 2021 14:27:33 -0700 Subject: [PATCH 16/16] Fix downcast_or_throw invocations --- docs/functions.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/functions.md b/docs/functions.md index d9c43a68..5153d6b8 100644 --- a/docs/functions.md +++ b/docs/functions.md @@ -127,8 +127,9 @@ fn create_job(mut cx: FunctionContext) -> JsResult { 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 global = cx.global(); -let func = global.get(&mut cx, "parseInt")?.downcast_or_throw::()?; +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])?; @@ -141,8 +142,9 @@ Notice that `func.call()` takes a runtime context, a value for the `this` bindin 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(); -let ctor = global.get(&mut cx, "URL")?.downcast_or_throw::()?; +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]); ```