Fork of https://github.com/oxigraph/oxigraph.git for the purpose of NextGraph project
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
oxigraph/js/src/store.rs

202 lines
7.4 KiB

use crate::format_err;
use crate::model::*;
use crate::utils::to_err;
use js_sys::{Array, Map};
use oxigraph::model::NamedOrBlankNode;
use oxigraph::sparql::{PreparedQuery, QueryOptions, QueryResult};
use oxigraph::{DatasetSyntax, Error, FileSyntax, GraphSyntax, MemoryStore};
use std::convert::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(&self.from_js.to_quad(quad)?.try_into()?)
.map_err(to_err)
}
pub fn delete(&self, quad: &JsValue) -> Result<(), JsValue> {
self.store
.remove(&self.from_js.to_quad(quad)?.try_into()?)
.map_err(to_err)
}
pub fn has(&self, quad: &JsValue) -> Result<bool, JsValue> {
Ok(self
.store
.contains(&self.from_js.to_quad(quad)?.try_into()?))
}
#[wasm_bindgen(js_name = match)]
pub fn match_quads(
&self,
subject: &JsValue,
predicate: &JsValue,
object: &JsValue,
graph: &JsValue,
) -> Result<Box<[JsValue]>, JsValue> {
Ok(self
.store
.quads_for_pattern(
match self.from_js.to_optional_term(subject)? {
Some(JsTerm::NamedNode(node)) => Some(node.into()),
Some(JsTerm::BlankNode(node)) => Some(node.into()),
Some(_) => {
return Err(format_err!(
"The match subject parameter should be a named or a blank node",
))
}
None => None,
}.as_ref(),
match self.from_js.to_optional_term(predicate)? {
Some(JsTerm::NamedNode(node)) => Some(node.into()),
Some(_) => {
return Err(format_err!(
"The match predicate parameter should be a named node",
))
}
None => None,
}.as_ref(),
match self.from_js.to_optional_term(object)? {
Some(JsTerm::NamedNode(node)) => Some(node.into()),
Some(JsTerm::BlankNode(node)) => Some(node.into()),
Some(JsTerm::Literal(literal)) => Some(literal.into()),
Some(_) => {
return Err(format_err!(
"The match object parameter should be a named or a blank node or a literal",
))
}
None => None,
}.as_ref(),
match self.from_js.to_optional_term(graph)? {
Some(JsTerm::NamedNode(node)) => Some(Some(node.into())),
Some(JsTerm::BlankNode(node)) => Some(Some(node.into())),
Some(JsTerm::DefaultGraph(_)) => Some(None),
Some(_) => {
return Err(format_err!(
"The match subject parameter should be a named or a blank node or the default graph",
))
}
None => None,
}.as_ref().map(|v| v.as_ref()),
).map(|v| v.map(|v| JsQuad::from(v).into())).collect::<Result<Vec<_>,Error>>().map_err(to_err)?.into_boxed_slice())
}
pub fn query(&self, query: &str) -> Result<JsValue, JsValue> {
let query = self
.store
.prepare_query(query, QueryOptions::default())
.map_err(to_err)?;
let results = query.exec().map_err(to_err)?;
let output = match results {
QueryResult::Bindings(bindings) => {
let (variables, iter) = bindings.destruct();
let variables: Vec<JsValue> =
variables.into_iter().map(|v| v.as_str().into()).collect();
let results = Array::new();
for values in iter {
let values = values.map_err(to_err)?;
let result = Map::new();
for (variable, value) in variables.iter().zip(values) {
if let Some(value) = value {
result.set(variable, &JsTerm::from(value).into());
}
}
results.push(&result.into());
}
results.into()
}
QueryResult::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()
}
QueryResult::Boolean(b) => b.into(),
};
Ok(output)
}
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: Option<NamedOrBlankNode> =
match self.from_js.to_optional_term(to_graph_name)? {
Some(JsTerm::NamedNode(node)) => Some(node.into()),
Some(JsTerm::BlankNode(node)) => Some(node.into()),
Some(JsTerm::DefaultGraph(_)) => None,
Some(_) => {
return Err(format_err!(
"If provided, the target graph name should be a NamedNode or a BlankNode"
))
}
None => None,
};
if let Some(graph_syntax) = GraphSyntax::from_mime_type(mime_type) {
self.store
.load_graph(
Cursor::new(data),
graph_syntax,
to_graph_name.as_ref(),
base_iri.as_deref(),
)
.map_err(to_err)
} else if let Some(dataset_syntax) = DatasetSyntax::from_mime_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_syntax, base_iri.as_deref())
.map_err(to_err)
} else {
Err(format_err!("Not supported MIME type: {}", mime_type))
}
}
}