nodejs module
parent
d46e1e3141
commit
659ee3bb2f
@ -0,0 +1,5 @@
|
||||
target
|
||||
index.node
|
||||
**/node_modules
|
||||
**/.DS_Store
|
||||
npm-debug.log*
|
@ -0,0 +1,24 @@
|
||||
[package]
|
||||
name = "cozo-nodejs"
|
||||
version = "0.1.1"
|
||||
description = "Cozo database for NodeJS"
|
||||
authors = ["Ziyang Hu"]
|
||||
license = "MIT/Apache-2.0/BSD-3-Clause"
|
||||
edition = "2021"
|
||||
exclude = ["index.node"]
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
cozo = { version = "0.1.1", path = ".." }
|
||||
miette = { version = "=5.3.0", features = ["fancy"] }
|
||||
serde_json = "1.0.81"
|
||||
lazy_static = "1.4.0"
|
||||
|
||||
[dependencies.neon]
|
||||
version = "0.10"
|
||||
default-features = false
|
||||
features = ["napi-6", "channel-api"]
|
@ -0,0 +1,121 @@
|
||||
# cozo-nodejs
|
||||
|
||||
**cozo-nodejs:** Cozo database for NodeJS
|
||||
|
||||
This project was bootstrapped by [create-neon](https://www.npmjs.com/package/create-neon).
|
||||
|
||||
## Installing cozo-nodejs
|
||||
|
||||
Installing cozo-nodejs requires a [supported version of Node and Rust](https://github.com/neon-bindings/neon#platform-support).
|
||||
|
||||
You can install the project with npm. In the project directory, run:
|
||||
|
||||
```sh
|
||||
$ npm install
|
||||
```
|
||||
|
||||
This fully installs the project, including installing any dependencies and running the build.
|
||||
|
||||
## Building cozo-nodejs
|
||||
|
||||
If you have already installed the project and only want to run the build, run:
|
||||
|
||||
```sh
|
||||
$ npm run build
|
||||
```
|
||||
|
||||
This command uses the [cargo-cp-artifact](https://github.com/neon-bindings/cargo-cp-artifact) utility to run the Rust build and copy the built library into `./index.node`.
|
||||
|
||||
## Exploring cozo-nodejs
|
||||
|
||||
After building cozo-nodejs, you can explore its exports at the Node REPL:
|
||||
|
||||
```sh
|
||||
$ npm install
|
||||
$ node
|
||||
> require('.').hello()
|
||||
"hello node"
|
||||
```
|
||||
|
||||
## Available Scripts
|
||||
|
||||
In the project directory, you can run:
|
||||
|
||||
### `npm install`
|
||||
|
||||
Installs the project, including running `npm run build`.
|
||||
|
||||
### `npm build`
|
||||
|
||||
Builds the Node addon (`index.node`) from source.
|
||||
|
||||
Additional [`cargo build`](https://doc.rust-lang.org/cargo/commands/cargo-build.html) arguments may be passed to `npm build` and `npm build-*` commands. For example, to enable a [cargo feature](https://doc.rust-lang.org/cargo/reference/features.html):
|
||||
|
||||
```
|
||||
npm run build -- --feature=beetle
|
||||
```
|
||||
|
||||
#### `npm build-debug`
|
||||
|
||||
Alias for `npm build`.
|
||||
|
||||
#### `npm build-release`
|
||||
|
||||
Same as [`npm build`](#npm-build) but, builds the module with the [`release`](https://doc.rust-lang.org/cargo/reference/profiles.html#release) profile. Release builds will compile slower, but run faster.
|
||||
|
||||
### `npm test`
|
||||
|
||||
Runs the unit tests by calling `cargo test`. You can learn more about [adding tests to your Rust code](https://doc.rust-lang.org/book/ch11-01-writing-tests.html) from the [Rust book](https://doc.rust-lang.org/book/).
|
||||
|
||||
## Project Layout
|
||||
|
||||
The directory structure of this project is:
|
||||
|
||||
```
|
||||
cozo-nodejs/
|
||||
├── Cargo.toml
|
||||
├── README.md
|
||||
├── index.node
|
||||
├── package.json
|
||||
├── src/
|
||||
| └── lib.rs
|
||||
└── target/
|
||||
```
|
||||
|
||||
### Cargo.toml
|
||||
|
||||
The Cargo [manifest file](https://doc.rust-lang.org/cargo/reference/manifest.html), which informs the `cargo` command.
|
||||
|
||||
### README.md
|
||||
|
||||
This file.
|
||||
|
||||
### index.node
|
||||
|
||||
The Node addon—i.e., a binary Node module—generated by building the project. This is the main module for this package, as dictated by the `"main"` key in `package.json`.
|
||||
|
||||
Under the hood, a [Node addon](https://nodejs.org/api/addons.html) is a [dynamically-linked shared object](https://en.wikipedia.org/wiki/Library_(computing)#Shared_libraries). The `"build"` script produces this file by copying it from within the `target/` directory, which is where the Rust build produces the shared object.
|
||||
|
||||
### package.json
|
||||
|
||||
The npm [manifest file](https://docs.npmjs.com/cli/v7/configuring-npm/package-json), which informs the `npm` command.
|
||||
|
||||
### src/
|
||||
|
||||
The directory tree containing the Rust source code for the project.
|
||||
|
||||
### src/lib.rs
|
||||
|
||||
The Rust library's main module.
|
||||
|
||||
### target/
|
||||
|
||||
Binary artifacts generated by the Rust build.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Neon, see the [Neon documentation](https://neon-bindings.com).
|
||||
|
||||
To learn more about Rust, see the [Rust documentation](https://www.rust-lang.org).
|
||||
|
||||
To learn more about Node, see the [Node documentation](https://nodejs.org).
|
@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "cozo-nodejs",
|
||||
"version": "0.1.1",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "cozo-nodejs",
|
||||
"version": "0.1.1",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"cargo-cp-artifact": "^0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/cargo-cp-artifact": {
|
||||
"version": "0.1.6",
|
||||
"resolved": "https://registry.npmmirror.com/cargo-cp-artifact/-/cargo-cp-artifact-0.1.6.tgz",
|
||||
"integrity": "sha512-CQw0doK/aaF7j041666XzuilHxqMxaKkn+I5vmBsd8SAwS0cO5CqVEVp0xJwOKstyqWZ6WK4Ww3O6p26x/Goyg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"cargo-cp-artifact": "bin/cargo-cp-artifact.js"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"cargo-cp-artifact": {
|
||||
"version": "0.1.6",
|
||||
"resolved": "https://registry.npmmirror.com/cargo-cp-artifact/-/cargo-cp-artifact-0.1.6.tgz",
|
||||
"integrity": "sha512-CQw0doK/aaF7j041666XzuilHxqMxaKkn+I5vmBsd8SAwS0cO5CqVEVp0xJwOKstyqWZ6WK4Ww3O6p26x/Goyg==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "cozo-nodejs",
|
||||
"version": "0.1.1",
|
||||
"description": "Cozo database for NodeJS",
|
||||
"main": "index.node",
|
||||
"scripts": {
|
||||
"build": "cargo-cp-artifact -nc index.node -- cargo build --message-format=json-render-diagnostics",
|
||||
"build-debug": "npm run build --",
|
||||
"build-release": "npm run build -- --release",
|
||||
"install": "npm run build-release",
|
||||
"test": "cargo test"
|
||||
},
|
||||
"author": "Ziyang Hu",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"cargo-cp-artifact": "^0.1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/cozodb/cozo.git"
|
||||
},
|
||||
"keywords": [
|
||||
"database",
|
||||
"datalog",
|
||||
"graph"
|
||||
],
|
||||
"bugs": {
|
||||
"url": "https://github.com/cozodb/cozo/issues"
|
||||
},
|
||||
"homepage": "https://github.com/cozodb/cozo#readme"
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
use std::sync::Mutex;
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use neon::prelude::*;
|
||||
|
||||
use cozo::Db;
|
||||
|
||||
#[derive(Default)]
|
||||
struct Handles {
|
||||
current: AtomicU32,
|
||||
dbs: Mutex<BTreeMap<u32, Db>>,
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref HANDLES: Handles = Handles::default();
|
||||
}
|
||||
|
||||
fn open_db(mut cx: FunctionContext) -> JsResult<JsNumber> {
|
||||
let path = cx.argument::<JsString>(0)?.value(&mut cx);
|
||||
match Db::new(path) {
|
||||
Ok(db) => {
|
||||
let id = HANDLES.current.fetch_add(1, Ordering::AcqRel);
|
||||
let mut dbs = HANDLES.dbs.lock().unwrap();
|
||||
dbs.insert(id, db);
|
||||
Ok(cx.number(id))
|
||||
}
|
||||
Err(err) => {
|
||||
let s = cx.string(format!("{:?}", err));
|
||||
cx.throw(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn close_db(mut cx: FunctionContext) -> JsResult<JsBoolean> {
|
||||
let id = cx.argument::<JsNumber>(0)?.value(&mut cx) as u32;
|
||||
let db = {
|
||||
let mut dbs = HANDLES.dbs.lock().unwrap();
|
||||
dbs.remove(&id)
|
||||
};
|
||||
Ok(cx.boolean(db.is_some()))
|
||||
}
|
||||
|
||||
fn query_db(mut cx: FunctionContext) -> JsResult<JsUndefined> {
|
||||
let id = cx.argument::<JsNumber>(0)?.value(&mut cx) as u32;
|
||||
let db = {
|
||||
let db_ref = {
|
||||
let dbs = HANDLES.dbs.lock().unwrap();
|
||||
dbs.get(&id).cloned()
|
||||
};
|
||||
match db_ref {
|
||||
None => {
|
||||
let s = cx.string("database already closed");
|
||||
cx.throw(s)?
|
||||
}
|
||||
Some(db) => db,
|
||||
}
|
||||
};
|
||||
|
||||
let query = cx.argument::<JsString>(1)?.value(&mut cx);
|
||||
let params_str = cx.argument::<JsString>(2)?.value(&mut cx);
|
||||
|
||||
let params_map: serde_json::Value = match serde_json::from_str(¶ms_str) {
|
||||
Ok(m) => m,
|
||||
Err(_) => {
|
||||
let s = cx.string("the given params argument is not valid JSON");
|
||||
cx.throw(s)?
|
||||
}
|
||||
};
|
||||
let params_arg: BTreeMap<_, _> = match params_map {
|
||||
serde_json::Value::Object(m) => m.into_iter().collect(),
|
||||
_ => {
|
||||
let s = cx.string("the given params argument is not a JSON map");
|
||||
cx.throw(s)?
|
||||
}
|
||||
};
|
||||
|
||||
let callback = cx.argument::<JsFunction>(3)?.root(&mut cx);
|
||||
|
||||
let channel = cx.channel();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
let result = db.run_script(&query, ¶ms_arg);
|
||||
channel.send(move |mut cx| {
|
||||
let callback = callback.into_inner(&mut cx);
|
||||
let this = cx.undefined();
|
||||
let args = match result {
|
||||
Ok(json) => {
|
||||
let json_str = cx.string(json.to_string());
|
||||
vec![cx.null().upcast::<JsValue>(), json_str.upcast()]
|
||||
}
|
||||
Err(err) => {
|
||||
let err = cx.string(format!("{:?}", err));
|
||||
vec![err.upcast::<JsValue>()]
|
||||
}
|
||||
};
|
||||
|
||||
callback.call(&mut cx, this, args)?;
|
||||
|
||||
Ok(())
|
||||
});
|
||||
});
|
||||
|
||||
Ok(cx.undefined())
|
||||
}
|
||||
|
||||
#[neon::main]
|
||||
fn main(mut cx: ModuleContext) -> NeonResult<()> {
|
||||
cx.export_function("open_db", open_db)?;
|
||||
cx.export_function("close_db", close_db)?;
|
||||
cx.export_function("query_db", query_db)?;
|
||||
Ok(())
|
||||
}
|
Loading…
Reference in New Issue