use std::collections::BTreeSet;
use std::io::Write;
use oxigraph::model::{
GraphName, GraphNameRef, NamedNode, NamedNodeRef, NamedOrBlankNode, NamedOrBlankNodeRef, Quad,
Term, TermRef, Triple, TripleRef,
};
use crate::io::Serializer;
use crate::world::{BridgeToken, FeatureMap, FeatureValue};
use crate::{
Error, Model, ModelTransaction, Query, QueryResults, Result, StatementMatches,
StatementPattern, World,
};
#[derive(Clone)]
pub struct StorageFacade<'a> {
model: &'a Model,
}
impl<'a> StorageFacade<'a> {
#[must_use]
pub fn new(model: &'a Model) -> Self {
Self { model }
}
#[must_use]
pub fn model(&self) -> &'a Model {
self.model
}
pub fn add_reference(&self) {}
pub fn remove_reference(&self) {}
pub fn close(&self) -> Result<()> {
self.model.sync()
}
pub fn size(&self) -> Result<usize> {
self.model.len()
}
pub fn add_statement(&self, statement: impl Into<Triple>) -> Result<bool> {
self.model.add(statement)
}
pub fn add_statements(&self, quads: impl IntoIterator<Item = Quad>) -> Result<usize> {
self.model.bulk_insert_quads(quads)
}
pub fn remove_statement(&self, statement: impl Into<Triple>) -> Result<bool> {
self.model.remove(statement)
}
pub fn contains_statement(&self, statement: TripleRef<'_>) -> Result<bool> {
self.model.contains(statement)
}
pub fn context_add_statement(
&self,
context: impl Into<GraphName>,
statement: impl Into<Triple>,
) -> Result<bool> {
self.model.add_to_graph(statement, context)
}
pub fn context_add_statements(
&self,
context: impl Into<GraphName>,
statements: impl IntoIterator<Item = Triple>,
) -> Result<usize> {
let context = context.into();
let mut count = 0usize;
for statement in statements {
self.model.add_to_graph(statement, context.clone())?;
count += 1;
}
Ok(count)
}
pub fn context_remove_statement(
&self,
context: impl Into<GraphName>,
statement: impl Into<Triple>,
) -> Result<bool> {
self.model.remove_from_graph(statement, context)
}
pub fn context_remove_statements(&self, context: impl Into<GraphName>) -> Result<()> {
self.model.clear_graph(context)
}
pub fn context_as_stream(&self, context: GraphNameRef<'_>) -> StatementMatches {
self.model.find(StatementPattern {
graph_name: Some(context),
..StatementPattern::default()
})
}
pub fn context_serialise<W: Write>(
&self,
context: GraphNameRef<'_>,
serializer: &Serializer,
writer: W,
) -> Result<W> {
let quads = self
.context_as_stream(context)
.collect::<Result<Vec<_>>>()?;
serializer.serialize_quads_to_writer(writer, quads)
}
pub fn find_statements(&self, pattern: StatementPattern<'_>) -> StatementMatches {
self.model.find(pattern)
}
pub fn find_statements_in_context(
&self,
pattern: StatementPattern<'_>,
context: GraphNameRef<'_>,
) -> StatementMatches {
let mut narrowed = pattern;
narrowed.graph_name = Some(context);
self.model.find(narrowed)
}
pub fn find_statements_with_options(
&self,
pattern: StatementPattern<'_>,
_options: Option<&FeatureMap>,
) -> StatementMatches {
self.find_statements(pattern)
}
pub fn get_sources(
&self,
arc: NamedNodeRef<'_>,
target: TermRef<'_>,
) -> Result<Vec<NamedOrBlankNode>> {
collect_subjects(self.model, None, Some(arc), Some(target))
}
pub fn get_targets(
&self,
source: NamedOrBlankNodeRef<'_>,
arc: NamedNodeRef<'_>,
) -> Result<Vec<Term>> {
collect_objects(self.model, Some(source), Some(arc), None)
}
pub fn get_arcs(
&self,
source: NamedOrBlankNodeRef<'_>,
target: TermRef<'_>,
) -> Result<Vec<NamedNode>> {
collect_predicates(self.model, Some(source), None, Some(target))
}
pub fn get_arcs_in(&self, node: TermRef<'_>) -> Result<Vec<NamedNode>> {
collect_predicates(self.model, None, None, Some(node))
}
pub fn get_arcs_out(&self, node: NamedOrBlankNodeRef<'_>) -> Result<Vec<NamedNode>> {
collect_predicates(self.model, Some(node), None, None)
}
pub fn has_arc_in(&self, node: TermRef<'_>) -> Result<bool> {
Ok(!self.get_arcs_in(node)?.is_empty())
}
pub fn has_arc_out(&self, node: NamedOrBlankNodeRef<'_>) -> Result<bool> {
Ok(!self.get_arcs_out(node)?.is_empty())
}
pub fn get_contexts(&self) -> Result<Vec<GraphName>> {
let mut seen = BTreeSet::new();
let mut out = Vec::new();
for item in self.model.find(StatementPattern::default()) {
let quad = item?;
if matches!(quad.graph_name, GraphName::DefaultGraph) {
continue;
}
let key = quad.graph_name.to_string();
if seen.insert(key) {
out.push(quad.graph_name);
}
}
Ok(out)
}
#[must_use]
pub fn feature(&self, iri: &str) -> Option<FeatureValue> {
self.model.storage_feature(iri)
}
pub fn set_feature(&self, iri: impl Into<String>, value: FeatureValue) {
self.model.set_storage_feature(iri, value);
}
#[must_use]
pub fn instance(&self) -> Option<BridgeToken> {
self.model.storage_instance()
}
pub fn set_instance(&self, token: Option<BridgeToken>) {
self.model.set_storage_instance(token);
}
#[must_use]
pub fn world(&self) -> &World {
self.model.world()
}
pub fn query_execute<'m>(&'m self, query: &Query) -> Result<QueryResults<'m>> {
query.execute(self.model)
}
pub fn serialise<W: Write>(&self, serializer: &Serializer, writer: W) -> Result<W> {
serializer.serialize_model_to_writer(writer, self.model)
}
pub fn transaction<R>(
&self,
f: impl FnOnce(&mut ModelTransaction<'_>) -> Result<R>,
) -> Result<R> {
self.model.transaction(f)
}
pub fn transaction_start<R>(
&self,
f: impl FnOnce(&mut ModelTransaction<'_>) -> Result<R>,
) -> Result<R> {
self.transaction(f)
}
pub fn transaction_start_with_handle<R>(
&self,
_handle: Option<BridgeToken>,
f: impl FnOnce(&mut ModelTransaction<'_>) -> Result<R>,
) -> Result<R> {
self.transaction(f)
}
#[must_use]
pub fn transaction_get_handle(&self) -> Option<BridgeToken> {
None
}
pub fn transaction_commit(&self) -> Result<()> {
Err(Error::Unsupported(
"storage transaction commit outside Model::transaction is unsupported; return Ok from the transaction callback to commit"
.into(),
))
}
pub fn transaction_rollback(&self) -> Result<()> {
Err(Error::Unsupported(
"storage transaction rollback outside Model::transaction is unsupported; return Err from the transaction callback to abort"
.into(),
))
}
pub fn sync(&self) -> Result<()> {
self.model.sync()
}
}
fn collect_subjects(
model: &Model,
subject: Option<NamedOrBlankNodeRef<'_>>,
predicate: Option<NamedNodeRef<'_>>,
object: Option<TermRef<'_>>,
) -> Result<Vec<NamedOrBlankNode>> {
let mut out = Vec::new();
let mut seen = BTreeSet::new();
for item in model.find(StatementPattern {
subject,
predicate,
object,
graph_name: None,
}) {
let quad = item?;
let key = quad.subject.to_string();
if seen.insert(key) {
out.push(quad.subject);
}
}
Ok(out)
}
fn collect_objects(
model: &Model,
subject: Option<NamedOrBlankNodeRef<'_>>,
predicate: Option<NamedNodeRef<'_>>,
object: Option<TermRef<'_>>,
) -> Result<Vec<Term>> {
let mut out = Vec::new();
let mut seen = BTreeSet::new();
for item in model.find(StatementPattern {
subject,
predicate,
object,
graph_name: None,
}) {
let quad = item?;
let key = quad.object.to_string();
if seen.insert(key) {
out.push(quad.object);
}
}
Ok(out)
}
fn collect_predicates(
model: &Model,
subject: Option<NamedOrBlankNodeRef<'_>>,
predicate: Option<NamedNodeRef<'_>>,
object: Option<TermRef<'_>>,
) -> Result<Vec<NamedNode>> {
let mut out = Vec::new();
let mut seen = BTreeSet::new();
for item in model.find(StatementPattern {
subject,
predicate,
object,
graph_name: None,
}) {
let quad = item?;
let key = quad.predicate.as_str().to_owned();
if seen.insert(key) {
out.push(quad.predicate);
}
}
Ok(out)
}