parent
7668f032f4
commit
122db6a2c3
@ -1,23 +0,0 @@ |
|||||||
[package] |
|
||||||
name = "oxigraph_js" |
|
||||||
version = "0.2.3" |
|
||||||
authors = ["Tpt <thomas@pellissier-tanon.fr>"] |
|
||||||
license = "MIT OR Apache-2.0" |
|
||||||
readme = "README.md" |
|
||||||
keywords = ["RDF", "N-Triples", "Turtle", "RDF/XML", "SPARQL"] |
|
||||||
repository = "https://github.com/oxigraph/oxigraph/tree/master/js" |
|
||||||
description = "JavaScript bindings of Oxigraph" |
|
||||||
edition = "2018" |
|
||||||
|
|
||||||
[lib] |
|
||||||
crate-type = ["cdylib"] |
|
||||||
name = "oxigraph" |
|
||||||
|
|
||||||
[dependencies] |
|
||||||
oxigraph = { version = "0.2", path="../lib" } |
|
||||||
wasm-bindgen = "0.2" |
|
||||||
js-sys = "0.3" |
|
||||||
console_error_panic_hook = "0.1" |
|
||||||
|
|
||||||
[dev-dependencies] |
|
||||||
wasm-bindgen-test = "0.3" |
|
@ -1,211 +0,0 @@ |
|||||||
Oxigraph for JavaScript |
|
||||||
======================= |
|
||||||
|
|
||||||
[![npm](https://img.shields.io/npm/v/oxigraph)](https://www.npmjs.com/package/oxigraph) |
|
||||||
[![actions status](https://github.com/oxigraph/oxigraph/workflows/build/badge.svg)](https://github.com/oxigraph/oxigraph/actions) |
|
||||||
[![Gitter](https://badges.gitter.im/oxigraph/community.svg)](https://gitter.im/oxigraph/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) |
|
||||||
|
|
||||||
This package provides a JavaScript API on top of [Oxigraph](https://crates.io/crates/oxigraph), compiled with WebAssembly. |
|
||||||
|
|
||||||
Oxigraph is a graph database written in Rust implementing the [SPARQL](https://www.w3.org/TR/sparql11-overview/) standard. |
|
||||||
|
|
||||||
Oxigraph for JavaScript is a work in progress and currently offers a simple in-memory store with [SPARQL 1.1 Query](https://www.w3.org/TR/sparql11-query/) and [SPARQL 1.1 Update](https://www.w3.org/TR/sparql11-update/) capabilities. |
|
||||||
|
|
||||||
The store is also able to load RDF serialized in [Turtle](https://www.w3.org/TR/turtle/), [TriG](https://www.w3.org/TR/trig/), [N-Triples](https://www.w3.org/TR/n-triples/), [N-Quads](https://www.w3.org/TR/n-quads/) and [RDF/XML](https://www.w3.org/TR/rdf-syntax-grammar/). |
|
||||||
|
|
||||||
|
|
||||||
It is distributed using a [a NPM package](https://www.npmjs.com/package/oxigraph) that should work with nodeJS 12+. |
|
||||||
|
|
||||||
```bash |
|
||||||
npm install oxigraph |
|
||||||
``` |
|
||||||
|
|
||||||
```js |
|
||||||
const oxigraph = require('oxigraph'); |
|
||||||
``` |
|
||||||
|
|
||||||
## Example |
|
||||||
|
|
||||||
Insert the triple `<http://example/> <http://schema.org/name> "example"` and log the name of `<http://example/>` in SPARQL: |
|
||||||
```js |
|
||||||
const { MemoryStore } = require('oxigraph'); |
|
||||||
const store = new MemoryStore(); |
|
||||||
const dataFactory = store.dataFactory; |
|
||||||
const ex = dataFactory.namedNode("http://example/"); |
|
||||||
const schemaName = dataFactory.namedNode("http://schema.org/name"); |
|
||||||
store.add(dataFactory.triple(ex, schemaName, dataFactory.literal("example"))); |
|
||||||
for (binding of store.query("SELECT ?name WHERE { <http://example/> <http://schema.org/name> ?name }")) { |
|
||||||
console.log(binding.get("name").value); |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
## API |
|
||||||
|
|
||||||
Oxigraph currently provides a simple JS API. |
|
||||||
It is centered around the `MemoryStore` class. |
|
||||||
|
|
||||||
The `NamedNode`, `BlankNode`, `Literal`, `DefaultGraph`, `Quad` and `DataFactory` types |
|
||||||
are following the [RDF/JS datamodel specification](https://rdf.js.org/data-model-spec/). |
|
||||||
|
|
||||||
To import `MemoryStore` using Node: |
|
||||||
```js |
|
||||||
const { MemoryStore } = require('oxigraph'); |
|
||||||
``` |
|
||||||
|
|
||||||
### `MemoryStore` |
|
||||||
|
|
||||||
#### `MemoryStore(optional sequence<Quad>? quads)` (constructor) |
|
||||||
```js |
|
||||||
const store = new MemoryStore(); |
|
||||||
``` |
|
||||||
|
|
||||||
If provided, the `MemoryStore` will be initialized with a sequence of quads. |
|
||||||
|
|
||||||
#### `MemoryStore.dataFactory` |
|
||||||
Returns a `DataFactory` following [RDF/JS datamodel specification](https://rdf.js.org/data-model-spec/). |
|
||||||
|
|
||||||
Example: |
|
||||||
```js |
|
||||||
const store = new MemoryStore(); |
|
||||||
const ex = store.dataFactory.namedNode("http://example.com"); |
|
||||||
const blank = store.dataFactory.blankNode(); |
|
||||||
const foo = store.dataFactory.literal("foo"); |
|
||||||
const quad = store.dataFactory.quad(blank, ex, foo); |
|
||||||
``` |
|
||||||
|
|
||||||
#### `MemoryStore.prototype.add(Quad quad)` |
|
||||||
Inserts a quad in the store. |
|
||||||
|
|
||||||
Example: |
|
||||||
```js |
|
||||||
store.add(quad); |
|
||||||
``` |
|
||||||
|
|
||||||
#### `MemoryStore.prototype.delete(Quad quad)` |
|
||||||
Removes a quad from the store. |
|
||||||
|
|
||||||
Example: |
|
||||||
```js |
|
||||||
store.delete(quad); |
|
||||||
``` |
|
||||||
|
|
||||||
#### `MemoryStore.prototype.has(Quad quad)` |
|
||||||
Returns a boolean stating if the store contains the quad. |
|
||||||
|
|
||||||
Example: |
|
||||||
```js |
|
||||||
store.has(quad); |
|
||||||
``` |
|
||||||
|
|
||||||
#### `MemoryStore.prototype.match(optional Term? subject, optional Term? predicate, optional Term? object, optional Term? graph)` |
|
||||||
Returns an array with all the quads matching a given quad pattern. |
|
||||||
|
|
||||||
Example to get all quads in the default graph with `ex` for subject: |
|
||||||
```js |
|
||||||
store.match(ex, null, null, store.dataFactory.defaultGraph()); |
|
||||||
``` |
|
||||||
|
|
||||||
Example to get all quads: |
|
||||||
```js |
|
||||||
store.match(); |
|
||||||
``` |
|
||||||
|
|
||||||
#### `MemoryStore.prototype.query(String query)` |
|
||||||
Executes a [SPARQL 1.1 Query](https://www.w3.org/TR/sparql11-query/). |
|
||||||
For `SELECT` queries the return type is an array of `Map` which keys are the bound variables and values are the values the result is bound to. |
|
||||||
For `CONSTRUCT` and `ÐESCRIBE` queries the return type is an array of `Quad`. |
|
||||||
For `ASK` queries the return type is a boolean. |
|
||||||
|
|
||||||
Example of SELECT query: |
|
||||||
```js |
|
||||||
for (binding of store.query("SELECT DISTINCT ?s WHERE { ?s ?p ?o }")) { |
|
||||||
console.log(binding.get("s").value); |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
Example of CONSTRUCT query: |
|
||||||
```js |
|
||||||
const filteredStore = new MemoryStore(store.query("CONSTRUCT { <http:/example.com/> ?p ?o } WHERE { <http:/example.com/> ?p ?o }")); |
|
||||||
``` |
|
||||||
|
|
||||||
Example of ASK query: |
|
||||||
```js |
|
||||||
if (store.query("ASK { ?s ?s ?s }")) { |
|
||||||
console.log("there is a triple with same subject, predicate and object"); |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
#### `MemoryStore.prototype.update(String query)` |
|
||||||
Executes a [SPARQL 1.1 Update](https://www.w3.org/TR/sparql11-update/). |
|
||||||
The [`LOAD` operation](https://www.w3.org/TR/sparql11-update/#load) is not supported yet. |
|
||||||
|
|
||||||
Example of update: |
|
||||||
```js |
|
||||||
store.update("DELETE WHERE { <http://example.com/s> ?p ?o }") |
|
||||||
``` |
|
||||||
|
|
||||||
### `MemoryStore.prototype.load(String data, String mimeType, NamedNode|String? baseIRI, NamedNode|BlankNode|DefaultGraph? toNamedGraph)` |
|
||||||
|
|
||||||
Loads serialized RDF triples or quad into the store. |
|
||||||
The method arguments are: |
|
||||||
1. `data`: the serialized RDF triples or quads. |
|
||||||
2. `mimeType`: the MIME type of the serialization. See below for the supported mime types. |
|
||||||
3. `baseIRI`: the base IRI to use to resolve the relative IRIs in the serialization. |
|
||||||
4. `toNamedGraph`: for triple serialization formats, the name of the named graph the triple should be loaded to. |
|
||||||
|
|
||||||
The available formats are: |
|
||||||
* [Turtle](https://www.w3.org/TR/turtle/): `text/turtle` |
|
||||||
* [TriG](https://www.w3.org/TR/trig/): `application/trig` |
|
||||||
* [N-Triples](https://www.w3.org/TR/n-triples/): `application/n-triples` |
|
||||||
* [N-Quads](https://www.w3.org/TR/n-quads/): `application/n-quads` |
|
||||||
* [RDF/XML](https://www.w3.org/TR/rdf-syntax-grammar/): `application/rdf+xml` |
|
||||||
|
|
||||||
Example of loading a Turtle file into the named graph `<http://example.com/graph>` with the base IRI `http://example.com`: |
|
||||||
```js |
|
||||||
store.load("<http://example.com> <http://example.com> <> .", "text/turtle", "http://example.com", store.dataFactory.namedNode("http://example.com/graph")); |
|
||||||
``` |
|
||||||
|
|
||||||
|
|
||||||
### `MemoryStore.prototype.dump(String mimeType, NamedNode|BlankNode|DefaultGraph? fromNamedGraph)` |
|
||||||
|
|
||||||
Returns serialized RDF triples or quad from the store. |
|
||||||
The method arguments are: |
|
||||||
1. `mimeType`: the MIME type of the serialization. See below for the supported mime types. |
|
||||||
2. `fromNamedGraph`: for triple serialization formats, the name of the named graph the triple should be loaded from. |
|
||||||
|
|
||||||
The available formats are: |
|
||||||
* [Turtle](https://www.w3.org/TR/turtle/): `text/turtle` |
|
||||||
* [TriG](https://www.w3.org/TR/trig/): `application/trig` |
|
||||||
* [N-Triples](https://www.w3.org/TR/n-triples/): `application/n-triples` |
|
||||||
* [N-Quads](https://www.w3.org/TR/n-quads/): `application/n-quads` |
|
||||||
* [RDF/XML](https://www.w3.org/TR/rdf-syntax-grammar/): `application/rdf+xml` |
|
||||||
|
|
||||||
Example of building a Turtle file from the named graph `<http://example.com/graph>`: |
|
||||||
```js |
|
||||||
store.dump("text/turtle", store.dataFactory.namedNode("http://example.com/graph")); |
|
||||||
``` |
|
||||||
|
|
||||||
## How to contribute |
|
||||||
|
|
||||||
The Oxigraph bindings are written in Rust using [the Rust WASM toolkit](https://rustwasm.github.io/docs.html). |
|
||||||
|
|
||||||
The [The Rust Wasm Book](https://rustwasm.github.io/docs/book/) is a great tutorial to get started. |
|
||||||
|
|
||||||
To run the tests of the JS bindings written in JS run `npm test`. |
|
||||||
|
|
||||||
|
|
||||||
## License |
|
||||||
|
|
||||||
This project is licensed under either of |
|
||||||
|
|
||||||
* Apache License, Version 2.0, ([LICENSE-APACHE](../LICENSE-APACHE) or |
|
||||||
http://www.apache.org/licenses/LICENSE-2.0) |
|
||||||
* MIT license ([LICENSE-MIT](../LICENSE-MIT) or |
|
||||||
http://opensource.org/licenses/MIT) |
|
||||||
|
|
||||||
at your option. |
|
||||||
|
|
||||||
|
|
||||||
### Contribution |
|
||||||
|
|
||||||
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in Futures by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. |
|
@ -1,15 +0,0 @@ |
|||||||
{ |
|
||||||
"name": "oxigraph_tests", |
|
||||||
"description": "Oxigraph JS build and tests", |
|
||||||
"private": true, |
|
||||||
"devDependencies": { |
|
||||||
"mocha": "^8.0.1", |
|
||||||
"@rdfjs/data-model": "1.1.2", |
|
||||||
"standard": "^16.0.0" |
|
||||||
}, |
|
||||||
"scripts": { |
|
||||||
"test": "standard test/*.js && wasm-pack build --dev --target nodejs && mocha", |
|
||||||
"build": "wasm-pack build --release --target nodejs && sed -i 's/oxigraph_js/oxigraph/g' pkg/package.json", |
|
||||||
"release": "wasm-pack pack && wasm-pack publish" |
|
||||||
} |
|
||||||
} |
|
@ -1,3 +0,0 @@ |
|||||||
mod model; |
|
||||||
mod store; |
|
||||||
mod utils; |
|
@ -1,597 +0,0 @@ |
|||||||
use crate::format_err; |
|
||||||
use crate::utils::to_err; |
|
||||||
use js_sys::{Reflect, UriError}; |
|
||||||
use oxigraph::model::*; |
|
||||||
use std::convert::TryFrom; |
|
||||||
use wasm_bindgen::prelude::*; |
|
||||||
|
|
||||||
#[wasm_bindgen(js_name = DataFactory)] |
|
||||||
#[derive(Default)] |
|
||||||
pub struct JsDataFactory { |
|
||||||
from_js: FromJsConverter, |
|
||||||
} |
|
||||||
|
|
||||||
#[wasm_bindgen(js_class = DataFactory)] |
|
||||||
impl JsDataFactory { |
|
||||||
#[wasm_bindgen(js_name = namedNode)] |
|
||||||
pub fn named_node(&self, value: String) -> Result<JsNamedNode, JsValue> { |
|
||||||
NamedNode::new(value) |
|
||||||
.map(|v| v.into()) |
|
||||||
.map_err(|v| UriError::new(&v.to_string()).into()) |
|
||||||
} |
|
||||||
|
|
||||||
#[wasm_bindgen(js_name = blankNode)] |
|
||||||
pub fn blank_node(&self, value: Option<String>) -> Result<JsBlankNode, JsValue> { |
|
||||||
Ok(if let Some(value) = value { |
|
||||||
BlankNode::new(value).map_err(to_err)? |
|
||||||
} else { |
|
||||||
BlankNode::default() |
|
||||||
} |
|
||||||
.into()) |
|
||||||
} |
|
||||||
|
|
||||||
#[wasm_bindgen] |
|
||||||
pub fn literal( |
|
||||||
&self, |
|
||||||
value: Option<String>, |
|
||||||
language_or_datatype: &JsValue, |
|
||||||
) -> Result<JsLiteral, JsValue> { |
|
||||||
if language_or_datatype.is_null() || language_or_datatype.is_undefined() { |
|
||||||
Ok(Literal::new_simple_literal(value.unwrap_or_else(String::new)).into()) |
|
||||||
} else if language_or_datatype.is_string() { |
|
||||||
Ok(Literal::new_language_tagged_literal( |
|
||||||
value.unwrap_or_else(String::new), |
|
||||||
language_or_datatype.as_string().unwrap_or_else(String::new), |
|
||||||
) |
|
||||||
.map_err(to_err)? |
|
||||||
.into()) |
|
||||||
} else if let JsTerm::NamedNode(datatype) = self.from_js.to_term(language_or_datatype)? { |
|
||||||
Ok(Literal::new_typed_literal(value.unwrap_or_else(String::new), datatype).into()) |
|
||||||
} else { |
|
||||||
Err(format_err!("The literal datatype should be a NamedNode")) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
#[wasm_bindgen(js_name = defaultGraph)] |
|
||||||
pub fn default_graph(&self) -> JsDefaultGraph { |
|
||||||
JsDefaultGraph {} |
|
||||||
} |
|
||||||
|
|
||||||
#[wasm_bindgen(js_name = triple)] |
|
||||||
pub fn triple( |
|
||||||
&self, |
|
||||||
subject: &JsValue, |
|
||||||
predicate: &JsValue, |
|
||||||
object: &JsValue, |
|
||||||
) -> Result<JsQuad, JsValue> { |
|
||||||
Ok(JsQuad { |
|
||||||
subject: self.from_js.to_term(subject)?, |
|
||||||
predicate: self.from_js.to_term(predicate)?, |
|
||||||
object: self.from_js.to_term(object)?, |
|
||||||
graph_name: JsTerm::DefaultGraph(JsDefaultGraph {}), |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
#[wasm_bindgen(js_name = quad)] |
|
||||||
pub fn quad( |
|
||||||
&self, |
|
||||||
subject: &JsValue, |
|
||||||
predicate: &JsValue, |
|
||||||
object: &JsValue, |
|
||||||
graph: &JsValue, |
|
||||||
) -> Result<JsQuad, JsValue> { |
|
||||||
Ok(JsQuad { |
|
||||||
subject: self.from_js.to_term(subject)?, |
|
||||||
predicate: self.from_js.to_term(predicate)?, |
|
||||||
object: self.from_js.to_term(object)?, |
|
||||||
graph_name: if graph.is_undefined() || graph.is_null() { |
|
||||||
JsTerm::DefaultGraph(JsDefaultGraph {}) |
|
||||||
} else { |
|
||||||
self.from_js.to_term(&graph)? |
|
||||||
}, |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
#[wasm_bindgen(js_name = fromTerm)] |
|
||||||
pub fn convert_term(&self, original: &JsValue) -> Result<JsValue, JsValue> { |
|
||||||
Ok(self.from_js.to_term(original)?.into()) |
|
||||||
} |
|
||||||
|
|
||||||
#[wasm_bindgen(js_name = fromQuad)] |
|
||||||
pub fn convert_quad(&self, original: &JsValue) -> Result<JsQuad, JsValue> { |
|
||||||
self.from_js.to_quad(original) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
#[wasm_bindgen(js_name = NamedNode)] |
|
||||||
#[derive(Eq, PartialEq, Debug, Clone, Hash)] |
|
||||||
pub struct JsNamedNode { |
|
||||||
inner: NamedNode, |
|
||||||
} |
|
||||||
|
|
||||||
#[wasm_bindgen(js_class = NamedNode)] |
|
||||||
impl JsNamedNode { |
|
||||||
#[wasm_bindgen(getter = termType)] |
|
||||||
pub fn term_type(&self) -> String { |
|
||||||
"NamedNode".to_owned() |
|
||||||
} |
|
||||||
|
|
||||||
#[wasm_bindgen(getter)] |
|
||||||
pub fn value(&self) -> String { |
|
||||||
self.inner.as_str().to_owned() |
|
||||||
} |
|
||||||
|
|
||||||
pub fn equals(&self, other: &JsValue) -> bool { |
|
||||||
if let Ok(Some(JsTerm::NamedNode(other))) = |
|
||||||
FromJsConverter::default().to_optional_term(&other) |
|
||||||
{ |
|
||||||
self == &other |
|
||||||
} else { |
|
||||||
false |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
impl From<NamedNode> for JsNamedNode { |
|
||||||
fn from(inner: NamedNode) -> Self { |
|
||||||
Self { inner } |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
impl From<JsNamedNode> for NamedNode { |
|
||||||
fn from(node: JsNamedNode) -> Self { |
|
||||||
node.inner |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
impl From<JsNamedNode> for NamedOrBlankNode { |
|
||||||
fn from(node: JsNamedNode) -> Self { |
|
||||||
node.inner.into() |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
impl From<JsNamedNode> for Term { |
|
||||||
fn from(node: JsNamedNode) -> Self { |
|
||||||
node.inner.into() |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
impl From<JsNamedNode> for GraphName { |
|
||||||
fn from(node: JsNamedNode) -> Self { |
|
||||||
node.inner.into() |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
#[wasm_bindgen(js_name = BlankNode)] |
|
||||||
#[derive(Eq, PartialEq, Debug, Clone, Hash)] |
|
||||||
pub struct JsBlankNode { |
|
||||||
inner: BlankNode, |
|
||||||
} |
|
||||||
|
|
||||||
#[wasm_bindgen(js_class = BlankNode)] |
|
||||||
impl JsBlankNode { |
|
||||||
#[wasm_bindgen(getter = termType)] |
|
||||||
pub fn term_type(&self) -> String { |
|
||||||
"BlankNode".to_owned() |
|
||||||
} |
|
||||||
|
|
||||||
#[wasm_bindgen(getter)] |
|
||||||
pub fn value(&self) -> String { |
|
||||||
self.inner.as_str().to_owned() |
|
||||||
} |
|
||||||
|
|
||||||
pub fn equals(&self, other: &JsValue) -> bool { |
|
||||||
if let Ok(Some(JsTerm::BlankNode(other))) = |
|
||||||
FromJsConverter::default().to_optional_term(&other) |
|
||||||
{ |
|
||||||
self == &other |
|
||||||
} else { |
|
||||||
false |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
impl From<BlankNode> for JsBlankNode { |
|
||||||
fn from(inner: BlankNode) -> Self { |
|
||||||
Self { inner } |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
impl From<JsBlankNode> for BlankNode { |
|
||||||
fn from(node: JsBlankNode) -> Self { |
|
||||||
node.inner |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
impl From<JsBlankNode> for NamedOrBlankNode { |
|
||||||
fn from(node: JsBlankNode) -> Self { |
|
||||||
node.inner.into() |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
impl From<JsBlankNode> for Term { |
|
||||||
fn from(node: JsBlankNode) -> Self { |
|
||||||
node.inner.into() |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
impl From<JsBlankNode> for GraphName { |
|
||||||
fn from(node: JsBlankNode) -> Self { |
|
||||||
node.inner.into() |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
#[wasm_bindgen(js_name = Literal)] |
|
||||||
#[derive(Eq, PartialEq, Debug, Clone, Hash)] |
|
||||||
pub struct JsLiteral { |
|
||||||
inner: Literal, |
|
||||||
} |
|
||||||
|
|
||||||
#[wasm_bindgen(js_class = Literal)] |
|
||||||
impl JsLiteral { |
|
||||||
#[wasm_bindgen(getter = termType)] |
|
||||||
pub fn term_type(&self) -> String { |
|
||||||
"Literal".to_owned() |
|
||||||
} |
|
||||||
|
|
||||||
#[wasm_bindgen(getter)] |
|
||||||
pub fn value(&self) -> String { |
|
||||||
self.inner.value().to_owned() |
|
||||||
} |
|
||||||
|
|
||||||
#[wasm_bindgen(getter)] |
|
||||||
pub fn language(&self) -> String { |
|
||||||
self.inner.language().unwrap_or("").to_owned() |
|
||||||
} |
|
||||||
|
|
||||||
#[wasm_bindgen(getter)] |
|
||||||
pub fn datatype(&self) -> JsNamedNode { |
|
||||||
self.inner.datatype().into_owned().into() |
|
||||||
} |
|
||||||
|
|
||||||
pub fn equals(&self, other: &JsValue) -> bool { |
|
||||||
if let Ok(Some(JsTerm::Literal(other))) = |
|
||||||
FromJsConverter::default().to_optional_term(&other) |
|
||||||
{ |
|
||||||
self == &other |
|
||||||
} else { |
|
||||||
false |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
impl From<Literal> for JsLiteral { |
|
||||||
fn from(inner: Literal) -> Self { |
|
||||||
Self { inner } |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
impl From<JsLiteral> for Literal { |
|
||||||
fn from(node: JsLiteral) -> Self { |
|
||||||
node.inner |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
impl From<JsLiteral> for Term { |
|
||||||
fn from(node: JsLiteral) -> Self { |
|
||||||
node.inner.into() |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
#[wasm_bindgen(js_name = DefaultGraph)] |
|
||||||
#[derive(Eq, PartialEq, Debug, Clone, Hash)] |
|
||||||
pub struct JsDefaultGraph {} |
|
||||||
|
|
||||||
#[wasm_bindgen(js_class = DefaultGraph)] |
|
||||||
impl JsDefaultGraph { |
|
||||||
#[wasm_bindgen(getter = termType)] |
|
||||||
pub fn term_type(&self) -> String { |
|
||||||
"DefaultGraph".to_owned() |
|
||||||
} |
|
||||||
|
|
||||||
#[wasm_bindgen(getter)] |
|
||||||
pub fn value(&self) -> String { |
|
||||||
"".to_owned() |
|
||||||
} |
|
||||||
|
|
||||||
pub fn equals(&self, other: &JsValue) -> bool { |
|
||||||
if let Ok(Some(JsTerm::DefaultGraph(other))) = |
|
||||||
FromJsConverter::default().to_optional_term(&other) |
|
||||||
{ |
|
||||||
self == &other |
|
||||||
} else { |
|
||||||
false |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
#[derive(Eq, PartialEq, Debug, Clone, Hash)] |
|
||||||
pub enum JsTerm { |
|
||||||
NamedNode(JsNamedNode), |
|
||||||
BlankNode(JsBlankNode), |
|
||||||
Literal(JsLiteral), |
|
||||||
DefaultGraph(JsDefaultGraph), |
|
||||||
} |
|
||||||
|
|
||||||
impl From<JsTerm> for JsValue { |
|
||||||
fn from(value: JsTerm) -> Self { |
|
||||||
match value { |
|
||||||
JsTerm::NamedNode(v) => v.into(), |
|
||||||
JsTerm::BlankNode(v) => v.into(), |
|
||||||
JsTerm::Literal(v) => v.into(), |
|
||||||
JsTerm::DefaultGraph(v) => v.into(), |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
impl From<NamedNode> for JsTerm { |
|
||||||
fn from(node: NamedNode) -> Self { |
|
||||||
JsTerm::NamedNode(node.into()) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
impl From<BlankNode> for JsTerm { |
|
||||||
fn from(node: BlankNode) -> Self { |
|
||||||
JsTerm::BlankNode(node.into()) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
impl From<Literal> for JsTerm { |
|
||||||
fn from(literal: Literal) -> Self { |
|
||||||
JsTerm::Literal(literal.into()) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
impl From<NamedOrBlankNode> for JsTerm { |
|
||||||
fn from(node: NamedOrBlankNode) -> Self { |
|
||||||
match node { |
|
||||||
NamedOrBlankNode::NamedNode(node) => node.into(), |
|
||||||
NamedOrBlankNode::BlankNode(node) => node.into(), |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
impl From<Term> for JsTerm { |
|
||||||
fn from(term: Term) -> Self { |
|
||||||
match term { |
|
||||||
Term::NamedNode(node) => node.into(), |
|
||||||
Term::BlankNode(node) => node.into(), |
|
||||||
Term::Literal(literal) => literal.into(), |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
impl From<GraphName> for JsTerm { |
|
||||||
fn from(name: GraphName) -> Self { |
|
||||||
match name { |
|
||||||
GraphName::NamedNode(node) => node.into(), |
|
||||||
GraphName::BlankNode(node) => node.into(), |
|
||||||
GraphName::DefaultGraph => JsTerm::DefaultGraph(JsDefaultGraph {}), |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
impl TryFrom<JsTerm> for NamedNode { |
|
||||||
type Error = JsValue; |
|
||||||
|
|
||||||
fn try_from(value: JsTerm) -> Result<Self, JsValue> { |
|
||||||
match value { |
|
||||||
JsTerm::NamedNode(node) => Ok(node.into()), |
|
||||||
JsTerm::BlankNode(node) => Err(format_err!( |
|
||||||
"The blank node {} is not a named node", |
|
||||||
node.inner |
|
||||||
)), |
|
||||||
JsTerm::Literal(literal) => Err(format_err!( |
|
||||||
"The literal {} is not a named node", |
|
||||||
literal.inner |
|
||||||
)), |
|
||||||
JsTerm::DefaultGraph(_) => Err(format_err!("The default graph is not a named node")), |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
impl TryFrom<JsTerm> for NamedOrBlankNode { |
|
||||||
type Error = JsValue; |
|
||||||
|
|
||||||
fn try_from(value: JsTerm) -> Result<Self, JsValue> { |
|
||||||
match value { |
|
||||||
JsTerm::NamedNode(node) => Ok(node.into()), |
|
||||||
JsTerm::BlankNode(node) => Ok(node.into()), |
|
||||||
JsTerm::Literal(literal) => Err(format_err!( |
|
||||||
"The literal {} is not a possible named or blank node term", |
|
||||||
literal.inner |
|
||||||
)), |
|
||||||
JsTerm::DefaultGraph(_) => { |
|
||||||
Err(format_err!("The default graph is not a possible RDF term")) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
impl TryFrom<JsTerm> for Term { |
|
||||||
type Error = JsValue; |
|
||||||
|
|
||||||
fn try_from(value: JsTerm) -> Result<Self, JsValue> { |
|
||||||
match value { |
|
||||||
JsTerm::NamedNode(node) => Ok(node.into()), |
|
||||||
JsTerm::BlankNode(node) => Ok(node.into()), |
|
||||||
JsTerm::Literal(literal) => Ok(literal.into()), |
|
||||||
JsTerm::DefaultGraph(_) => { |
|
||||||
Err(format_err!("The default graph is not a possible RDF term")) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
impl TryFrom<JsTerm> for GraphName { |
|
||||||
type Error = JsValue; |
|
||||||
|
|
||||||
fn try_from(value: JsTerm) -> Result<Self, JsValue> { |
|
||||||
match value { |
|
||||||
JsTerm::NamedNode(node) => Ok(node.into()), |
|
||||||
JsTerm::BlankNode(node) => Ok(node.into()), |
|
||||||
JsTerm::Literal(literal) => Err(format_err!( |
|
||||||
"The literal {} is not a possible graph name", |
|
||||||
literal.inner |
|
||||||
)), |
|
||||||
JsTerm::DefaultGraph(_) => Ok(GraphName::DefaultGraph), |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
#[wasm_bindgen(js_name = Quad)] |
|
||||||
#[derive(Eq, PartialEq, Debug, Clone, Hash)] |
|
||||||
pub struct JsQuad { |
|
||||||
subject: JsTerm, |
|
||||||
predicate: JsTerm, |
|
||||||
object: JsTerm, |
|
||||||
graph_name: JsTerm, |
|
||||||
} |
|
||||||
|
|
||||||
#[wasm_bindgen(js_class = Quad)] |
|
||||||
impl JsQuad { |
|
||||||
#[wasm_bindgen(getter = subject)] |
|
||||||
pub fn subject(&self) -> JsValue { |
|
||||||
self.subject.clone().into() |
|
||||||
} |
|
||||||
|
|
||||||
#[wasm_bindgen(getter = predicate)] |
|
||||||
pub fn predicate(&self) -> JsValue { |
|
||||||
self.predicate.clone().into() |
|
||||||
} |
|
||||||
|
|
||||||
#[wasm_bindgen(getter = object)] |
|
||||||
pub fn object(&self) -> JsValue { |
|
||||||
self.object.clone().into() |
|
||||||
} |
|
||||||
|
|
||||||
#[wasm_bindgen(getter = graph)] |
|
||||||
pub fn graph(&self) -> JsValue { |
|
||||||
self.graph_name.clone().into() |
|
||||||
} |
|
||||||
|
|
||||||
pub fn equals(&self, other: &JsValue) -> bool { |
|
||||||
FromJsConverter::default() |
|
||||||
.to_quad(&other) |
|
||||||
.map_or(false, |other| self == &other) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
impl From<Quad> for JsQuad { |
|
||||||
fn from(quad: Quad) -> Self { |
|
||||||
Self { |
|
||||||
subject: quad.subject.into(), |
|
||||||
predicate: quad.predicate.into(), |
|
||||||
object: quad.object.into(), |
|
||||||
graph_name: quad.graph_name.into(), |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
impl TryFrom<JsQuad> for Quad { |
|
||||||
type Error = JsValue; |
|
||||||
|
|
||||||
fn try_from(quad: JsQuad) -> Result<Self, JsValue> { |
|
||||||
Ok(Quad { |
|
||||||
subject: NamedOrBlankNode::try_from(quad.subject)?, |
|
||||||
predicate: NamedNode::try_from(quad.predicate)?, |
|
||||||
object: Term::try_from(quad.object)?, |
|
||||||
graph_name: GraphName::try_from(quad.graph_name)?, |
|
||||||
}) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
pub struct FromJsConverter { |
|
||||||
term_type: JsValue, |
|
||||||
value: JsValue, |
|
||||||
language: JsValue, |
|
||||||
datatype: JsValue, |
|
||||||
subject: JsValue, |
|
||||||
predicate: JsValue, |
|
||||||
object: JsValue, |
|
||||||
graph: JsValue, |
|
||||||
} |
|
||||||
|
|
||||||
impl Default for FromJsConverter { |
|
||||||
fn default() -> Self { |
|
||||||
Self { |
|
||||||
term_type: JsValue::from_str("termType"), |
|
||||||
value: JsValue::from_str("value"), |
|
||||||
language: JsValue::from_str("language"), |
|
||||||
datatype: JsValue::from_str("datatype"), |
|
||||||
subject: JsValue::from_str("subject"), |
|
||||||
predicate: JsValue::from_str("predicate"), |
|
||||||
object: JsValue::from_str("object"), |
|
||||||
graph: JsValue::from_str("graph"), |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
impl FromJsConverter { |
|
||||||
pub fn to_term(&self, value: &JsValue) -> Result<JsTerm, JsValue> { |
|
||||||
let term_type = Reflect::get(&value, &self.term_type)?; |
|
||||||
if let Some(term_type) = term_type.as_string() { |
|
||||||
match term_type.as_str() { |
|
||||||
"NamedNode" => Ok(NamedNode::new( |
|
||||||
Reflect::get(&value, &self.value)? |
|
||||||
.as_string() |
|
||||||
.ok_or_else(|| format_err!("NamedNode should have a string value"))?, |
|
||||||
) |
|
||||||
.map_err(|v| UriError::new(&v.to_string()))? |
|
||||||
.into()), |
|
||||||
"BlankNode" => Ok(BlankNode::new( |
|
||||||
&Reflect::get(&value, &self.value)? |
|
||||||
.as_string() |
|
||||||
.ok_or_else(|| format_err!("BlankNode should have a string value"))?, |
|
||||||
) |
|
||||||
.map_err(to_err)? |
|
||||||
.into()), |
|
||||||
"Literal" => { |
|
||||||
if let JsTerm::NamedNode(datatype) = |
|
||||||
self.to_term(&Reflect::get(&value, &self.datatype)?)? |
|
||||||
{ |
|
||||||
let datatype = NamedNode::from(datatype); |
|
||||||
let literal_value = Reflect::get(&value, &self.value)? |
|
||||||
.as_string() |
|
||||||
.ok_or_else(|| format_err!("Literal should have a string value"))?; |
|
||||||
Ok(match datatype.as_str() { |
|
||||||
"http://www.w3.org/2001/XMLSchema#string" => Literal::new_simple_literal(literal_value), |
|
||||||
"http://www.w3.org/1999/02/22-rdf-syntax-ns#langString" => Literal::new_language_tagged_literal(literal_value, Reflect::get(&value, &self.language)?.as_string().ok_or_else( |
|
||||||
|| format_err!("Literal with rdf:langString datatype should have a language"), |
|
||||||
)?).map_err(to_err)?, |
|
||||||
_ => Literal::new_typed_literal(literal_value, datatype) |
|
||||||
}.into()) |
|
||||||
} else { |
|
||||||
Err(format_err!( |
|
||||||
"Literal should have a datatype that is a NamedNode" |
|
||||||
)) |
|
||||||
} |
|
||||||
} |
|
||||||
"DefaultGraph" => Ok(JsTerm::DefaultGraph(JsDefaultGraph {})), |
|
||||||
_ => Err(format_err!( |
|
||||||
"The termType {} is not supported by Oxigraph", |
|
||||||
term_type |
|
||||||
)), |
|
||||||
} |
|
||||||
} else { |
|
||||||
Err(format_err!("The object termType field should be a string")) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
pub fn to_optional_term(&self, value: &JsValue) -> Result<Option<JsTerm>, JsValue> { |
|
||||||
if value.is_null() || value.is_undefined() { |
|
||||||
Ok(None) |
|
||||||
} else { |
|
||||||
self.to_term(value).map(Some) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
pub fn to_quad(&self, value: &JsValue) -> Result<JsQuad, JsValue> { |
|
||||||
Ok(JsQuad { |
|
||||||
subject: self.to_term(&Reflect::get(&value, &self.subject)?)?, |
|
||||||
predicate: self.to_term(&Reflect::get(&value, &self.predicate)?)?, |
|
||||||
object: self.to_term(&Reflect::get(&value, &self.object)?)?, |
|
||||||
graph_name: self.to_term(&Reflect::get(&value, &self.graph)?)?, |
|
||||||
}) |
|
||||||
} |
|
||||||
} |
|
@ -1,221 +0,0 @@ |
|||||||
use crate::format_err; |
|
||||||
use crate::model::*; |
|
||||||
use crate::utils::to_err; |
|
||||||
use js_sys::{Array, Map}; |
|
||||||
use oxigraph::io::{DatasetFormat, GraphFormat}; |
|
||||||
use oxigraph::model::*; |
|
||||||
use oxigraph::sparql::QueryResults; |
|
||||||
use oxigraph::MemoryStore; |
|
||||||
use std::convert::{TryFrom, TryInto}; |
|
||||||
use std::io::Cursor; |
|
||||||
use wasm_bindgen::prelude::*; |
|
||||||
|
|
||||||
#[wasm_bindgen(js_name = MemoryStore)] |
|
||||||
#[derive(Default)] |
|
||||||
pub struct JsMemoryStore { |
|
||||||
store: MemoryStore, |
|
||||||
from_js: FromJsConverter, |
|
||||||
} |
|
||||||
|
|
||||||
#[wasm_bindgen(js_class = MemoryStore)] |
|
||||||
impl JsMemoryStore { |
|
||||||
#[wasm_bindgen(constructor)] |
|
||||||
pub fn new(quads: Option<Box<[JsValue]>>) -> Result<JsMemoryStore, JsValue> { |
|
||||||
console_error_panic_hook::set_once(); |
|
||||||
|
|
||||||
let store = Self::default(); |
|
||||||
if let Some(quads) = quads { |
|
||||||
for quad in quads.iter() { |
|
||||||
store.add(quad)?; |
|
||||||
} |
|
||||||
} |
|
||||||
Ok(store) |
|
||||||
} |
|
||||||
|
|
||||||
#[wasm_bindgen(js_name = dataFactory, getter)] |
|
||||||
pub fn data_factory(&self) -> JsDataFactory { |
|
||||||
JsDataFactory::default() |
|
||||||
} |
|
||||||
|
|
||||||
pub fn add(&self, quad: &JsValue) -> Result<(), JsValue> { |
|
||||||
self.store |
|
||||||
.insert(Quad::try_from(self.from_js.to_quad(quad)?)?); |
|
||||||
Ok(()) |
|
||||||
} |
|
||||||
|
|
||||||
pub fn delete(&self, quad: &JsValue) -> Result<(), JsValue> { |
|
||||||
self.store.remove(&self.from_js.to_quad(quad)?.try_into()?); |
|
||||||
Ok(()) |
|
||||||
} |
|
||||||
|
|
||||||
pub fn has(&self, quad: &JsValue) -> Result<bool, JsValue> { |
|
||||||
Ok(self |
|
||||||
.store |
|
||||||
.contains(&self.from_js.to_quad(quad)?.try_into()?)) |
|
||||||
} |
|
||||||
|
|
||||||
#[wasm_bindgen(getter=size)] |
|
||||||
pub fn size(&self) -> usize { |
|
||||||
self.store.len() |
|
||||||
} |
|
||||||
|
|
||||||
#[wasm_bindgen(js_name = match)] |
|
||||||
pub fn match_quads( |
|
||||||
&self, |
|
||||||
subject: &JsValue, |
|
||||||
predicate: &JsValue, |
|
||||||
object: &JsValue, |
|
||||||
graph_name: &JsValue, |
|
||||||
) -> Result<Box<[JsValue]>, JsValue> { |
|
||||||
Ok(self |
|
||||||
.store |
|
||||||
.quads_for_pattern( |
|
||||||
if let Some(subject) = self.from_js.to_optional_term(subject)? { |
|
||||||
Some(subject.try_into()?) |
|
||||||
} else { |
|
||||||
None |
|
||||||
} |
|
||||||
.as_ref() |
|
||||||
.map(|t: &NamedOrBlankNode| t.into()), |
|
||||||
if let Some(predicate) = self.from_js.to_optional_term(predicate)? { |
|
||||||
Some(NamedNode::try_from(predicate)?) |
|
||||||
} else { |
|
||||||
None |
|
||||||
} |
|
||||||
.as_ref() |
|
||||||
.map(|t: &NamedNode| t.into()), |
|
||||||
if let Some(object) = self.from_js.to_optional_term(object)? { |
|
||||||
Some(object.try_into()?) |
|
||||||
} else { |
|
||||||
None |
|
||||||
} |
|
||||||
.as_ref() |
|
||||||
.map(|t: &Term| t.into()), |
|
||||||
if let Some(graph_name) = self.from_js.to_optional_term(graph_name)? { |
|
||||||
Some(graph_name.try_into()?) |
|
||||||
} else { |
|
||||||
None |
|
||||||
} |
|
||||||
.as_ref() |
|
||||||
.map(|t: &GraphName| t.into()), |
|
||||||
) |
|
||||||
.map(|v| JsQuad::from(v).into()) |
|
||||||
.collect::<Vec<_>>() |
|
||||||
.into_boxed_slice()) |
|
||||||
} |
|
||||||
|
|
||||||
pub fn query(&self, query: &str) -> Result<JsValue, JsValue> { |
|
||||||
let results = self.store.query(query).map_err(to_err)?; |
|
||||||
let output = match results { |
|
||||||
QueryResults::Solutions(solutions) => { |
|
||||||
let results = Array::new(); |
|
||||||
for solution in solutions { |
|
||||||
let solution = solution.map_err(to_err)?; |
|
||||||
let result = Map::new(); |
|
||||||
for (variable, value) in solution.iter() { |
|
||||||
result.set( |
|
||||||
&variable.as_str().into(), |
|
||||||
&JsTerm::from(value.clone()).into(), |
|
||||||
); |
|
||||||
} |
|
||||||
results.push(&result.into()); |
|
||||||
} |
|
||||||
results.into() |
|
||||||
} |
|
||||||
QueryResults::Graph(quads) => { |
|
||||||
let results = Array::new(); |
|
||||||
for quad in quads { |
|
||||||
results.push(&JsQuad::from(quad.map_err(to_err)?.in_graph(None)).into()); |
|
||||||
} |
|
||||||
results.into() |
|
||||||
} |
|
||||||
QueryResults::Boolean(b) => b.into(), |
|
||||||
}; |
|
||||||
Ok(output) |
|
||||||
} |
|
||||||
|
|
||||||
pub fn update(&self, update: &str) -> Result<(), JsValue> { |
|
||||||
self.store.update(update).map_err(to_err) |
|
||||||
} |
|
||||||
|
|
||||||
pub fn load( |
|
||||||
&self, |
|
||||||
data: &str, |
|
||||||
mime_type: &str, |
|
||||||
base_iri: &JsValue, |
|
||||||
to_graph_name: &JsValue, |
|
||||||
) -> Result<(), JsValue> { |
|
||||||
let base_iri = if base_iri.is_null() || base_iri.is_undefined() { |
|
||||||
None |
|
||||||
} else if base_iri.is_string() { |
|
||||||
base_iri.as_string() |
|
||||||
} else if let JsTerm::NamedNode(base_iri) = self.from_js.to_term(&base_iri)? { |
|
||||||
Some(base_iri.value()) |
|
||||||
} else { |
|
||||||
return Err(format_err!( |
|
||||||
"If provided, the base IRI should be a NamedNode or a string" |
|
||||||
)); |
|
||||||
}; |
|
||||||
|
|
||||||
let to_graph_name = |
|
||||||
if let Some(graph_name) = self.from_js.to_optional_term(to_graph_name)? { |
|
||||||
Some(graph_name.try_into()?) |
|
||||||
} else { |
|
||||||
None |
|
||||||
}; |
|
||||||
|
|
||||||
if let Some(graph_format) = GraphFormat::from_media_type(mime_type) { |
|
||||||
self.store |
|
||||||
.load_graph( |
|
||||||
Cursor::new(data), |
|
||||||
graph_format, |
|
||||||
&to_graph_name.unwrap_or(GraphName::DefaultGraph), |
|
||||||
base_iri.as_deref(), |
|
||||||
) |
|
||||||
.map_err(to_err) |
|
||||||
} else if let Some(dataset_format) = DatasetFormat::from_media_type(mime_type) { |
|
||||||
if to_graph_name.is_some() { |
|
||||||
return Err(format_err!( |
|
||||||
"The target graph name parameter is not available for dataset formats" |
|
||||||
)); |
|
||||||
} |
|
||||||
self.store |
|
||||||
.load_dataset(Cursor::new(data), dataset_format, base_iri.as_deref()) |
|
||||||
.map_err(to_err) |
|
||||||
} else { |
|
||||||
Err(format_err!("Not supported MIME type: {}", mime_type)) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
pub fn dump(&self, mime_type: &str, from_graph_name: &JsValue) -> Result<String, JsValue> { |
|
||||||
let from_graph_name = |
|
||||||
if let Some(graph_name) = self.from_js.to_optional_term(from_graph_name)? { |
|
||||||
Some(graph_name.try_into()?) |
|
||||||
} else { |
|
||||||
None |
|
||||||
}; |
|
||||||
|
|
||||||
let mut buffer = Vec::new(); |
|
||||||
if let Some(graph_format) = GraphFormat::from_media_type(mime_type) { |
|
||||||
self.store |
|
||||||
.dump_graph( |
|
||||||
&mut buffer, |
|
||||||
graph_format, |
|
||||||
&from_graph_name.unwrap_or(GraphName::DefaultGraph), |
|
||||||
) |
|
||||||
.map_err(to_err)?; |
|
||||||
} else if let Some(dataset_format) = DatasetFormat::from_media_type(mime_type) { |
|
||||||
if from_graph_name.is_some() { |
|
||||||
return Err(format_err!( |
|
||||||
"The target graph name parameter is not available for dataset formats" |
|
||||||
)); |
|
||||||
} |
|
||||||
self.store |
|
||||||
.dump_dataset(&mut buffer, dataset_format) |
|
||||||
.map_err(to_err)?; |
|
||||||
} else { |
|
||||||
return Err(format_err!("Not supported MIME type: {}", mime_type)); |
|
||||||
} |
|
||||||
String::from_utf8(buffer).map_err(to_err) |
|
||||||
} |
|
||||||
} |
|
@ -1,16 +0,0 @@ |
|||||||
use js_sys::Error; |
|
||||||
use wasm_bindgen::JsValue; |
|
||||||
|
|
||||||
#[macro_export] |
|
||||||
macro_rules! format_err { |
|
||||||
($msg:literal $(,)?) => { |
|
||||||
::wasm_bindgen::JsValue::from(::js_sys::Error::new($msg)) |
|
||||||
}; |
|
||||||
($fmt:literal, $($arg:tt)*) => { |
|
||||||
::wasm_bindgen::JsValue::from(::js_sys::Error::new(&format!($fmt, $($arg)*))) |
|
||||||
}; |
|
||||||
} |
|
||||||
|
|
||||||
pub fn to_err(e: impl ToString) -> JsValue { |
|
||||||
JsValue::from(Error::new(&e.to_string())) |
|
||||||
} |
|
@ -1,2 +0,0 @@ |
|||||||
const { MemoryStore } = require('../pkg/oxigraph.js') |
|
||||||
require('../node_modules/@rdfjs/data-model/test/index.js')((new MemoryStore()).dataFactory) |
|
@ -1,156 +0,0 @@ |
|||||||
/* global describe, it */ |
|
||||||
|
|
||||||
const { MemoryStore } = require('../pkg/oxigraph.js') |
|
||||||
const assert = require('assert') |
|
||||||
const dataFactory = require('@rdfjs/data-model') |
|
||||||
|
|
||||||
const ex = dataFactory.namedNode('http://example.com') |
|
||||||
|
|
||||||
describe('MemoryStore', function () { |
|
||||||
describe('#add()', function () { |
|
||||||
it('an added quad should be in the store', function () { |
|
||||||
const store = new MemoryStore() |
|
||||||
store.add(dataFactory.triple(ex, ex, ex)) |
|
||||||
assert(store.has(dataFactory.triple(ex, ex, ex))) |
|
||||||
}) |
|
||||||
}) |
|
||||||
|
|
||||||
describe('#delete()', function () { |
|
||||||
it('an removed quad should not be in the store anymore', function () { |
|
||||||
const store = new MemoryStore([dataFactory.triple(ex, ex, ex)]) |
|
||||||
assert(store.has(dataFactory.triple(ex, ex, ex))) |
|
||||||
store.delete(dataFactory.triple(ex, ex, ex)) |
|
||||||
assert(!store.has(dataFactory.triple(ex, ex, ex))) |
|
||||||
}) |
|
||||||
}) |
|
||||||
|
|
||||||
describe('#has()', function () { |
|
||||||
it('an added quad should be in the store', function () { |
|
||||||
const store = new MemoryStore([dataFactory.triple(ex, ex, ex)]) |
|
||||||
assert(store.has(dataFactory.triple(ex, ex, ex))) |
|
||||||
}) |
|
||||||
}) |
|
||||||
|
|
||||||
describe('#size()', function () { |
|
||||||
it('A store with one quad should have 1 for size', function () { |
|
||||||
const store = new MemoryStore([dataFactory.triple(ex, ex, ex)]) |
|
||||||
assert.strictEqual(1, store.size) |
|
||||||
}) |
|
||||||
}) |
|
||||||
|
|
||||||
describe('#match_quads()', function () { |
|
||||||
it('blank pattern should return all quads', function () { |
|
||||||
const store = new MemoryStore([dataFactory.triple(ex, ex, ex)]) |
|
||||||
const results = store.match() |
|
||||||
assert.strictEqual(1, results.length) |
|
||||||
assert(dataFactory.triple(ex, ex, ex).equals(results[0])) |
|
||||||
}) |
|
||||||
}) |
|
||||||
|
|
||||||
describe('#query()', function () { |
|
||||||
it('ASK true', function () { |
|
||||||
const store = new MemoryStore([dataFactory.triple(ex, ex, ex)]) |
|
||||||
assert.strictEqual(true, store.query('ASK { ?s ?s ?s }')) |
|
||||||
}) |
|
||||||
|
|
||||||
it('ASK false', function () { |
|
||||||
const store = new MemoryStore() |
|
||||||
assert.strictEqual(false, store.query('ASK { FILTER(false)}')) |
|
||||||
}) |
|
||||||
|
|
||||||
it('CONSTRUCT', function () { |
|
||||||
const store = new MemoryStore([dataFactory.triple(ex, ex, ex)]) |
|
||||||
const results = store.query('CONSTRUCT { ?s ?p ?o } WHERE { ?s ?p ?o }') |
|
||||||
assert.strictEqual(1, results.length) |
|
||||||
assert(dataFactory.triple(ex, ex, ex).equals(results[0])) |
|
||||||
}) |
|
||||||
|
|
||||||
it('SELECT', function () { |
|
||||||
const store = new MemoryStore([dataFactory.triple(ex, ex, ex)]) |
|
||||||
const results = store.query('SELECT ?s WHERE { ?s ?p ?o }') |
|
||||||
assert.strictEqual(1, results.length) |
|
||||||
assert(ex.equals(results[0].get('s'))) |
|
||||||
}) |
|
||||||
|
|
||||||
it('SELECT with NOW()', function () { |
|
||||||
const store = new MemoryStore([dataFactory.triple(ex, ex, ex)]) |
|
||||||
const results = store.query('SELECT (YEAR(NOW()) AS ?y) WHERE {}') |
|
||||||
assert.strictEqual(1, results.length) |
|
||||||
}) |
|
||||||
|
|
||||||
it('SELECT with RAND()', function () { |
|
||||||
const store = new MemoryStore([dataFactory.triple(ex, ex, ex)]) |
|
||||||
const results = store.query('SELECT (RAND() AS ?y) WHERE {}') |
|
||||||
assert.strictEqual(1, results.length) |
|
||||||
}) |
|
||||||
}) |
|
||||||
|
|
||||||
describe('#update()', function () { |
|
||||||
it('INSERT DATA', function () { |
|
||||||
const store = new MemoryStore() |
|
||||||
store.update('INSERT DATA { <http://example.com> <http://example.com> <http://example.com> }') |
|
||||||
assert.strictEqual(1, store.size) |
|
||||||
}) |
|
||||||
|
|
||||||
it('DELETE DATA', function () { |
|
||||||
const store = new MemoryStore([dataFactory.triple(ex, ex, ex)]) |
|
||||||
store.update('DELETE DATA { <http://example.com> <http://example.com> <http://example.com> }') |
|
||||||
assert.strictEqual(0, store.size) |
|
||||||
}) |
|
||||||
|
|
||||||
it('DELETE WHERE', function () { |
|
||||||
const store = new MemoryStore([dataFactory.triple(ex, ex, ex)]) |
|
||||||
store.update('DELETE WHERE { ?v ?v ?v }') |
|
||||||
assert.strictEqual(0, store.size) |
|
||||||
}) |
|
||||||
}) |
|
||||||
|
|
||||||
describe('#load()', function () { |
|
||||||
it('load NTriples in the default graph', function () { |
|
||||||
const store = new MemoryStore() |
|
||||||
store.load('<http://example.com> <http://example.com> <http://example.com> .', 'application/n-triples') |
|
||||||
assert(store.has(dataFactory.triple(ex, ex, ex))) |
|
||||||
}) |
|
||||||
|
|
||||||
it('load NTriples in an other graph', function () { |
|
||||||
const store = new MemoryStore() |
|
||||||
store.load('<http://example.com> <http://example.com> <http://example.com> .', 'application/n-triples', null, ex) |
|
||||||
assert(store.has(dataFactory.quad(ex, ex, ex, ex))) |
|
||||||
}) |
|
||||||
|
|
||||||
it('load Turtle with a base IRI', function () { |
|
||||||
const store = new MemoryStore() |
|
||||||
store.load('<http://example.com> <http://example.com> <> .', 'text/turtle', 'http://example.com') |
|
||||||
assert(store.has(dataFactory.triple(ex, ex, ex))) |
|
||||||
}) |
|
||||||
|
|
||||||
it('load NQuads', function () { |
|
||||||
const store = new MemoryStore() |
|
||||||
store.load('<http://example.com> <http://example.com> <http://example.com> <http://example.com> .', 'application/n-quads') |
|
||||||
assert(store.has(dataFactory.quad(ex, ex, ex, ex))) |
|
||||||
}) |
|
||||||
|
|
||||||
it('load TriG with a base IRI', function () { |
|
||||||
const store = new MemoryStore() |
|
||||||
store.load('GRAPH <> { <http://example.com> <http://example.com> <> }', 'application/trig', 'http://example.com') |
|
||||||
assert(store.has(dataFactory.quad(ex, ex, ex, ex))) |
|
||||||
}) |
|
||||||
}) |
|
||||||
|
|
||||||
describe('#dump()', function () { |
|
||||||
it('dump dataset content', function () { |
|
||||||
const store = new MemoryStore([dataFactory.quad(ex, ex, ex, ex)]) |
|
||||||
assert.strictEqual('<http://example.com> <http://example.com> <http://example.com> <http://example.com> .\n', store.dump('application/n-quads')) |
|
||||||
}) |
|
||||||
|
|
||||||
it('dump named graph content', function () { |
|
||||||
const store = new MemoryStore([dataFactory.quad(ex, ex, ex, ex)]) |
|
||||||
assert.strictEqual('<http://example.com> <http://example.com> <http://example.com> .\n', store.dump('application/n-triples', ex)) |
|
||||||
}) |
|
||||||
|
|
||||||
it('dump default graph content', function () { |
|
||||||
const store = new MemoryStore([dataFactory.quad(ex, ex, ex, ex)]) |
|
||||||
assert.strictEqual('', store.dump('application/n-triples')) |
|
||||||
}) |
|
||||||
}) |
|
||||||
}) |
|
Loading…
Reference in new issue