JS: Moves JS terms constuctors to the package root

pull/171/head
Tpt 3 years ago
parent 3ca149fa97
commit 5d3a909105
  1. 78
      js/README.md
  2. 7
      js/src/lib.rs
  3. 183
      js/src/model.rs
  4. 27
      js/src/store.rs
  5. 4
      js/test/model.js

@ -28,12 +28,11 @@ const oxigraph = require('oxigraph');
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")));
const oxigraph = require('oxigraph');
const store = new oxigraph.Store();
const ex = oxigraph.namedNode("http://example/");
const schemaName = oxigraph.namedNode("http://schema.org/name");
store.add(oxigraph.triple(ex, schemaName, oxigraph.literal("example")));
for (binding of store.query("SELECT ?name WHERE { <http://example/> <http://schema.org/name> ?name }")) {
console.log(binding.get("name").value);
}
@ -42,38 +41,44 @@ for (binding of store.query("SELECT ?name WHERE { <http://example/> <http://sche
## API
Oxigraph currently provides a simple JS API.
It is centered around the `MemoryStore` class.
The `NamedNode`, `BlankNode`, `Literal`, `DefaultGraph`, `Variable`, `Quad` and `DataFactory` types
are following the [RDF/JS datamodel specification](https://rdf.js.org/data-model-spec/).
### RDF data model
Oxigraph implements the [RDF/JS datamodel specification](https://rdf.js.org/data-model-spec/).
To import `MemoryStore` using Node:
For that, the `oxigraph` module implements the [RDF/JS `DataFactory` interface](http://rdf.js.org/data-model-spec/#datafactory-interface).
Example:
```js
const { MemoryStore } = require('oxigraph');
const oxigraph = require('oxigraph');
const ex = oxigraph.namedNode("http://example.com");
const blank = oxigraph.blankNode();
const foo = oxigraph.literal("foo");
const quad = oxigraph.quad(blank, ex, foo);
```
### `MemoryStore`
### `Store`
Oxigraph API is centered around the `Store` class.
A store contains an [RDF dataset](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-dataset) and allows to query and update them using SPARQL.
#### `Store(optional sequence<Quad>? quads)` (constructor)
Creates a new store.
#### `MemoryStore(optional sequence<Quad>? quads)` (constructor)
```js
const store = new MemoryStore();
const oxigraph = require('oxigraph');
const store = new oxigraph.Store();
```
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/).
If provided, the `Store` will be initialized with a sequence of quads.
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);
const oxigraph = require('oxigraph');
const store = new oxigraph.Store([oxigraph.quad(blank, ex, foo)]);
```
#### `MemoryStore.prototype.add(Quad quad)`
#### `Store.prototype.add(Quad quad)`
Inserts a quad in the store.
Example:
@ -81,7 +86,7 @@ Example:
store.add(quad);
```
#### `MemoryStore.prototype.delete(Quad quad)`
#### `Store.prototype.delete(Quad quad)`
Removes a quad from the store.
Example:
@ -89,7 +94,7 @@ Example:
store.delete(quad);
```
#### `MemoryStore.prototype.has(Quad quad)`
#### `Store.prototype.has(Quad quad)`
Returns a boolean stating if the store contains the quad.
Example:
@ -97,12 +102,12 @@ Example:
store.has(quad);
```
#### `MemoryStore.prototype.match(optional Term? subject, optional Term? predicate, optional Term? object, optional Term? graph)`
#### `Store.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());
store.match(ex, null, null, oxigraph.defaultGraph());
```
Example to get all quads:
@ -110,7 +115,7 @@ Example to get all quads:
store.match();
```
#### `MemoryStore.prototype.query(String query)`
#### `Store.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`.
@ -125,7 +130,7 @@ for (binding of store.query("SELECT DISTINCT ?s WHERE { ?s ?p ?o }")) {
Example of CONSTRUCT query:
```js
const filteredStore = new MemoryStore(store.query("CONSTRUCT { <http:/example.com/> ?p ?o } WHERE { <http:/example.com/> ?p ?o }"));
const filteredStore = new oxigraph.Store(store.query("CONSTRUCT { <http:/example.com/> ?p ?o } WHERE { <http:/example.com/> ?p ?o }"));
```
Example of ASK query:
@ -135,7 +140,7 @@ if (store.query("ASK { ?s ?s ?s }")) {
}
```
#### `MemoryStore.prototype.update(String query)`
#### `Store.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.
@ -144,7 +149,7 @@ Example of update:
store.update("DELETE WHERE { <http://example.com/s> ?p ?o }")
```
### `MemoryStore.prototype.load(String data, String mimeType, NamedNode|String? baseIRI, NamedNode|BlankNode|DefaultGraph? toNamedGraph)`
#### `Store.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:
@ -162,11 +167,10 @@ The available formats are:
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"));
store.load("<http://example.com> <http://example.com> <> .", "text/turtle", "http://example.com", oxigraph.namedNode("http://example.com/graph"));
```
### `MemoryStore.prototype.dump(String mimeType, NamedNode|BlankNode|DefaultGraph? fromNamedGraph)`
#### `Store.prototype.dump(String mimeType, NamedNode|BlankNode|DefaultGraph? fromNamedGraph)`
Returns serialized RDF triples or quad from the store.
The method arguments are:
@ -182,7 +186,7 @@ The available formats are:
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"));
store.dump("text/turtle", oxigraph.namedNode("http://example.com/graph"));
```
## How to contribute

@ -1,3 +1,10 @@
use wasm_bindgen::prelude::*;
mod model;
mod store;
mod utils;
#[wasm_bindgen(start)]
pub fn main() {
console_error_panic_hook::set_once();
}

@ -7,104 +7,91 @@ use std::convert::TryFrom;
use std::sync::Arc;
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()
}
thread_local! {
pub static FROM_JS: FromJsConverter = FromJsConverter::default();
}
#[wasm_bindgen(js_name = namedNode)]
pub fn named_node(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(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(
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) = FROM_JS.with(|c| c.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]
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() -> JsDefaultGraph {
JsDefaultGraph {}
}
#[wasm_bindgen(js_name = defaultGraph)]
pub fn default_graph(&self) -> JsDefaultGraph {
JsDefaultGraph {}
}
#[wasm_bindgen(js_name = variable)]
pub fn variable(value: String) -> Result<JsVariable, JsValue> {
Ok(Variable::new(value).map_err(to_err)?.into())
}
#[wasm_bindgen(js_name = variable)]
pub fn variable(&self, value: String) -> Result<JsVariable, JsValue> {
Ok(Variable::new(value).map_err(to_err)?.into())
}
#[wasm_bindgen(js_name = triple)]
pub fn triple(subject: &JsValue, predicate: &JsValue, object: &JsValue) -> Result<JsQuad, JsValue> {
quad(subject, predicate, object, &JsValue::UNDEFINED)
}
#[wasm_bindgen(js_name = triple)]
pub fn triple(
&self,
subject: &JsValue,
predicate: &JsValue,
object: &JsValue,
) -> Result<JsQuad, JsValue> {
self.quad(subject, predicate, object, &JsValue::UNDEFINED)
}
#[wasm_bindgen(js_name = quad)]
pub fn quad(
subject: &JsValue,
predicate: &JsValue,
object: &JsValue,
graph: &JsValue,
) -> Result<JsQuad, JsValue> {
Ok(FROM_JS
.with(|c| c.to_quad_from_parts(subject, predicate, object, graph))?
.into())
}
#[wasm_bindgen(js_name = quad)]
pub fn quad(
&self,
subject: &JsValue,
predicate: &JsValue,
object: &JsValue,
graph: &JsValue,
) -> Result<JsQuad, JsValue> {
Ok(self
.from_js
.to_quad_from_parts(subject, predicate, object, graph)?
.into())
}
#[wasm_bindgen(js_name = fromTerm)]
pub fn convert_term(&self, original: &JsValue) -> Result<JsValue, JsValue> {
Ok(if original.is_null() {
JsValue::NULL
} else {
self.from_js.to_term(original)?.into()
})
}
#[wasm_bindgen(js_name = fromTerm)]
pub fn from_term(original: &JsValue) -> Result<JsValue, JsValue> {
Ok(if original.is_null() {
JsValue::NULL
} else {
FROM_JS.with(|c| c.to_term(original))?.into()
})
}
#[wasm_bindgen(js_name = fromQuad)]
pub fn convert_quad(&self, original: &JsValue) -> Result<JsValue, JsValue> {
Ok(if original.is_null() {
JsValue::NULL
} else {
JsQuad::from(self.from_js.to_quad(original)?).into()
})
}
#[wasm_bindgen(js_name = fromQuad)]
pub fn from_quad(original: &JsValue) -> Result<JsValue, JsValue> {
Ok(if original.is_null() {
JsValue::NULL
} else {
JsQuad::from(FROM_JS.with(|c| c.to_quad(original))?).into()
})
}
#[wasm_bindgen(js_name = NamedNode)]
@ -340,7 +327,7 @@ impl JsVariable {
pub fn equals(&self, other: &JsValue) -> bool {
if let Ok(Some(JsTerm::Variable(other))) =
FromJsConverter::default().to_optional_term(&other)
FromJsConverter::default().to_optional_term(other)
{
self == &other
} else {
@ -400,7 +387,7 @@ impl JsQuad {
}
pub fn equals(&self, other: &JsValue) -> bool {
if let Ok(Some(JsTerm::Quad(other))) = FromJsConverter::default().to_optional_term(&other) {
if let Ok(Some(JsTerm::Quad(other))) = FromJsConverter::default().to_optional_term(other) {
self == &other
} else {
false
@ -724,7 +711,7 @@ impl FromJsConverter {
}
"DefaultGraph" => Ok(JsTerm::DefaultGraph(JsDefaultGraph {})),
"Variable" => Ok(Variable::new(
&Reflect::get(&value, &self.value)?
&Reflect::get(value, &self.value)?
.as_string()
.ok_or_else(|| format_err!("Variable should have a string value"))?,
)
@ -763,10 +750,10 @@ impl FromJsConverter {
pub fn to_quad(&self, value: &JsValue) -> Result<Quad, JsValue> {
self.to_quad_from_parts(
&Reflect::get(&value, &self.subject)?,
&Reflect::get(&value, &self.predicate)?,
&Reflect::get(&value, &self.object)?,
&Reflect::get(&value, &self.graph)?,
&Reflect::get(value, &self.subject)?,
&Reflect::get(value, &self.predicate)?,
&Reflect::get(value, &self.object)?,
&Reflect::get(value, &self.graph)?,
)
}
@ -784,7 +771,7 @@ impl FromJsConverter {
graph_name: if graph_name.is_undefined() {
GraphName::DefaultGraph
} else {
GraphName::try_from(self.to_term(&graph_name)?)?
GraphName::try_from(self.to_term(graph_name)?)?
},
})
}

@ -13,7 +13,6 @@ use wasm_bindgen::prelude::*;
#[wasm_bindgen(js_name = Store)]
pub struct JsStore {
store: Store,
from_js: FromJsConverter,
}
#[wasm_bindgen(js_class = Store)]
@ -24,7 +23,6 @@ impl JsStore {
let store = Self {
store: Store::new().map_err(to_err)?,
from_js: FromJsConverter::default(),
};
if let Some(quads) = quads {
for quad in quads.iter() {
@ -34,28 +32,23 @@ impl JsStore {
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(&self.from_js.to_quad(quad)?.into())
.insert(&FROM_JS.with(|c| c.to_quad(quad))?)
.map_err(to_err)?;
Ok(())
}
pub fn delete(&self, quad: &JsValue) -> Result<(), JsValue> {
self.store
.remove(&self.from_js.to_quad(quad)?)
.remove(&FROM_JS.with(|c| c.to_quad(quad))?)
.map_err(to_err)?;
Ok(())
}
pub fn has(&self, quad: &JsValue) -> Result<bool, JsValue> {
self.store
.contains(&self.from_js.to_quad(quad)?)
.contains(&FROM_JS.with(|c| c.to_quad(quad))?)
.map_err(to_err)
}
@ -75,28 +68,28 @@ impl JsStore {
Ok(self
.store
.quads_for_pattern(
if let Some(subject) = self.from_js.to_optional_term(subject)? {
if let Some(subject) = FROM_JS.with(|c| c.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)? {
if let Some(predicate) = FROM_JS.with(|c| c.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)? {
if let Some(object) = FROM_JS.with(|c| c.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)? {
if let Some(graph_name) = FROM_JS.with(|c| c.to_optional_term(graph_name))? {
Some(graph_name.try_into()?)
} else {
None
@ -158,7 +151,7 @@ impl JsStore {
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)? {
} else if let JsTerm::NamedNode(base_iri) = FROM_JS.with(|c| c.to_term(base_iri))? {
Some(base_iri.value())
} else {
return Err(format_err!(
@ -167,7 +160,7 @@ impl JsStore {
};
let to_graph_name =
if let Some(graph_name) = self.from_js.to_optional_term(to_graph_name)? {
if let Some(graph_name) = FROM_JS.with(|c| c.to_optional_term(to_graph_name))? {
Some(graph_name.try_into()?)
} else {
None
@ -198,7 +191,7 @@ impl JsStore {
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)? {
if let Some(graph_name) = FROM_JS.with(|c| c.to_optional_term(from_graph_name))? {
Some(graph_name.try_into()?)
} else {
None

@ -1,2 +1,2 @@
const { Store } = require('../pkg/oxigraph.js')
require('../node_modules/@rdfjs/data-model/test/index.js')((new Store()).dataFactory)
const oxigraph = require('../pkg/oxigraph.js')
require('../node_modules/@rdfjs/data-model/test/index.js')(oxigraph)

Loading…
Cancel
Save