From 5d3a9091057d0083cae889f29a226ec99f928e7c Mon Sep 17 00:00:00 2001 From: Tpt Date: Thu, 12 Aug 2021 18:24:47 +0200 Subject: [PATCH] JS: Moves JS terms constuctors to the package root --- js/README.md | 78 ++++++++++---------- js/src/lib.rs | 7 ++ js/src/model.rs | 183 ++++++++++++++++++++++------------------------- js/src/store.rs | 27 +++---- js/test/model.js | 4 +- 5 files changed, 145 insertions(+), 154 deletions(-) diff --git a/js/README.md b/js/README.md index 61907d76..e47769c0 100644 --- a/js/README.md +++ b/js/README.md @@ -28,12 +28,11 @@ const oxigraph = require('oxigraph'); Insert the triple ` "example"` and log the name of `` 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 { ?name }")) { console.log(binding.get("name").value); } @@ -42,38 +41,44 @@ for (binding of store.query("SELECT ?name WHERE { ? quads)` (constructor) +Creates a new store. -#### `MemoryStore(optional sequence? 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 { ?p ?o } WHERE { ?p ?o }")); +const filteredStore = new oxigraph.Store(store.query("CONSTRUCT { ?p ?o } WHERE { ?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 { ?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 `` with the base IRI `http://example.com`: ```js -store.load(" <> .", "text/turtle", "http://example.com", store.dataFactory.namedNode("http://example.com/graph")); +store.load(" <> .", "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 ``: ```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 diff --git a/js/src/lib.rs b/js/src/lib.rs index 9005e09e..b5a42eeb 100644 --- a/js/src/lib.rs +++ b/js/src/lib.rs @@ -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(); +} diff --git a/js/src/model.rs b/js/src/model.rs index 87ef99fc..b69e74d6 100644 --- a/js/src/model.rs +++ b/js/src/model.rs @@ -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 { - 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) -> Result { - 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 { + 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) -> Result { + Ok(if let Some(value) = value { + BlankNode::new(value).map_err(to_err)? + } else { + BlankNode::default() + } + .into()) +} + +#[wasm_bindgen] +pub fn literal( + value: Option, + language_or_datatype: &JsValue, +) -> Result { + 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, - language_or_datatype: &JsValue, - ) -> Result { - 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 { + Ok(Variable::new(value).map_err(to_err)?.into()) +} - #[wasm_bindgen(js_name = variable)] - pub fn variable(&self, value: String) -> Result { - Ok(Variable::new(value).map_err(to_err)?.into()) - } +#[wasm_bindgen(js_name = triple)] +pub fn triple(subject: &JsValue, predicate: &JsValue, object: &JsValue) -> Result { + quad(subject, predicate, object, &JsValue::UNDEFINED) +} - #[wasm_bindgen(js_name = triple)] - pub fn triple( - &self, - subject: &JsValue, - predicate: &JsValue, - object: &JsValue, - ) -> Result { - self.quad(subject, predicate, object, &JsValue::UNDEFINED) - } +#[wasm_bindgen(js_name = quad)] +pub fn quad( + subject: &JsValue, + predicate: &JsValue, + object: &JsValue, + graph: &JsValue, +) -> Result { + 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 { - 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 { - 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 { + 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 { - 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 { + 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 { 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)?)? }, }) } diff --git a/js/src/store.rs b/js/src/store.rs index 5523872d..0b9c1640 100644 --- a/js/src/store.rs +++ b/js/src/store.rs @@ -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 { 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 { 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 diff --git a/js/test/model.js b/js/test/model.js index 8c5765f3..5c8404dd 100644 --- a/js/test/model.js +++ b/js/test/model.js @@ -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)