#![allow(
clippy::module_name_repetitions,
clippy::min_ident_chars,
clippy::pattern_type_mismatch
)]
use anyhow::Context;
use sophia::api::graph::{GTripleSource, Graph};
use sophia::api::ns::NsTerm;
use sophia::api::MownStr;
use sophia::api::{prelude::*, term::SimpleTerm};
use sophia::inmem::graph::FastGraph;
use std::iter;
pub struct StelaeGraph {
pub fast_graph: FastGraph,
}
impl Default for StelaeGraph {
fn default() -> Self {
Self::new()
}
}
impl StelaeGraph {
#[must_use]
pub fn new() -> Self {
Self {
fast_graph: FastGraph::new(),
}
}
pub fn literal_from_triple_matching(
&self,
subject: Option<&SimpleTerm>,
predicate: Option<NsTerm>,
object: Option<NsTerm>,
) -> anyhow::Result<String> {
let triple = self.get_next_triples_matching(subject, predicate, object)?;
let literal = self.term_to_literal(&triple)?;
Ok(literal)
}
pub fn term_to_literal(&self, term: &[&SimpleTerm<'_>; 3]) -> anyhow::Result<String> {
match &term.o() {
SimpleTerm::LiteralDatatype(literal, _) | SimpleTerm::LiteralLanguage(literal, _) => {
Ok(literal.to_string())
}
SimpleTerm::Iri(_)
| SimpleTerm::BlankNode(_)
| SimpleTerm::Triple(_)
| SimpleTerm::Variable(_) => {
anyhow::bail!("Expected literal language, got - {:?}", term)
}
}
}
pub fn all_literals_from_triple_matching(
&self,
subject: Option<&SimpleTerm>,
predicate: Option<NsTerm>,
object: Option<NsTerm>,
) -> anyhow::Result<Vec<String>> {
let mut literals = Vec::new();
let triples_iter = self.triples_matching_inner(subject, predicate, object);
for term in triples_iter {
literals.push(self.term_to_literal(&term?)?);
}
Ok(literals)
}
pub fn iri_from_triple_matching<'graph>(
&'graph self,
subject: Option<&'graph SimpleTerm>,
predicate: Option<NsTerm<'graph>>,
object: Option<NsTerm<'graph>>,
) -> anyhow::Result<SimpleTerm> {
let triple = self.get_next_triples_matching(subject, predicate, object)?;
let SimpleTerm::Iri(iri) = &triple.o() else {
anyhow::bail!("Expected literal language, got - {:?}", triple.o());
};
Ok(SimpleTerm::Iri(iri.clone()))
}
fn get_next_triples_matching<'graph>(
&'graph self,
subject: Option<&'graph SimpleTerm>,
predicate: Option<NsTerm<'graph>>,
object: Option<NsTerm<'graph>>,
) -> anyhow::Result<[&'graph SimpleTerm<'_>; 3]> {
let triple = self
.triples_matching_inner(subject, predicate, object)
.next()
.context(format!(
"Expected to find triple matching s={subject:?}, p={predicate:?}, o={object:?}"
))?;
Ok(triple?)
}
fn triples_matching_inner<'graph>(
&'graph self,
subject: Option<&'graph SimpleTerm>,
predicate: Option<NsTerm<'graph>>,
object: Option<NsTerm<'graph>>,
) -> GTripleSource<'graph, FastGraph> {
let triple = match (subject, predicate, object) {
(Some(s), None, None) => self.fast_graph.triples_matching([s], Any, Any),
(None, Some(p), None) => self.fast_graph.triples_matching(Any, [p], Any),
(None, None, Some(o)) => self.fast_graph.triples_matching(Any, Any, [o]),
(Some(s), Some(p), None) => self.fast_graph.triples_matching([s], [p], Any),
(Some(s), None, Some(o)) => self.fast_graph.triples_matching([s], Any, [o]),
(None, Some(p), Some(o)) => self.fast_graph.triples_matching(Any, [p], [o]),
(Some(s), Some(p), Some(o)) => self.fast_graph.triples_matching([s], [p], [o]),
(None, None, None) => Box::new(iter::empty()),
};
triple
}
pub fn all_iris_from_triple_matching<'graph>(
&'graph self,
subject: Option<&'graph SimpleTerm>,
predicate: Option<NsTerm<'graph>>,
object: Option<NsTerm<'graph>>,
) -> anyhow::Result<Vec<&SimpleTerm>> {
let triples_iter = self.triples_matching_inner(subject, predicate, object);
let iris = triples_iter
.into_iter()
.filter_map(|triple| {
let found_triple = triple.ok()?;
let subj = found_triple.s();
Some(subj)
})
.collect();
Ok(iris)
}
}
pub struct Bag<'graph> {
uri: SimpleTerm<'graph>,
graph: &'graph StelaeGraph,
}
impl Bag<'_> {
#[must_use]
pub const fn new<'graph>(graph: &'graph StelaeGraph, uri: SimpleTerm<'graph>) -> Bag<'graph> {
Bag { uri, graph }
}
#[allow(clippy::separated_literal_suffix)]
pub fn items(&self) -> anyhow::Result<Vec<SimpleTerm>> {
let container = &self.uri;
let mut i = 1_u32;
let mut items = vec![];
loop {
let el_uri = format!("http://www.w3.org/1999/02/22-rdf-syntax-ns#_{i}");
let elem_iri = SimpleTerm::Iri(IriRef::new_unchecked(MownStr::from_str(&el_uri)));
let item_response = self
.graph
.fast_graph
.triples_matching([container], Some(elem_iri), Any)
.next();
if let Some(found_item) = item_response {
i += 1;
let item = found_item
.context(format!("Expected to find item in {container:?}"))?
.o()
.clone();
items.push(item);
} else {
break;
}
}
Ok(items)
}
}