pub mod local;
pub mod remote;
#[cfg(feature = "source_browser")]
pub mod browser;
use crate::prelude::*;
use iref::InvalidIri;
use taganak_core::{default_graph_view, graphs::GraphView, terms::Iri};
use futures::Stream;
use taganak_orm::GraphORM;
pub trait GraphConfig: Clone {
type Graph: ConfigurableGraph<Config = Self>;
fn into_graph(self) -> Result<Self::Graph, SourceError>
where
Self: Sized,
{
Self::Graph::from_config(self)
}
}
pub trait ConfigurableGraph: Graph {
type Config: GraphConfig<Graph = Self>;
fn from_config(config: Self::Config) -> Result<Self, SourceError>
where
Self: Sized;
}
#[derive(Clone, Debug, PartialEq, Eq, GraphORM, Hash)]
#[rdf(
prefix(ns = "sdk", iri = "http://sdk.taganak.net/vocab/taganak#"),
rdf_type = "sdk:GraphConfigs"
)]
pub enum GraphConfigs {
#[cfg(feature = "source_local_file")]
#[rdf(proxy(rdf_type = "sdk:LocalFileSourceGraphConfig"))]
LocalFile(self::local::persistent::local_file::LocalFileSourceGraphConfig),
#[cfg(feature = "source_graph_store_http")]
#[rdf(proxy(rdf_type = "sdk:GraphStoreHttpSourceGraphConfig"))]
GraphStoreHttp(self::remote::graph_store_http::GraphStoreHttpSourceGraphConfig),
#[cfg(feature = "source_indexeddb")]
#[rdf(proxy(rdf_type = "sdk:IndexedDbSourceGraphConfig"))]
IndexedDb(self::browser::indexeddb::IndexedDbSourceGraphConfig),
#[rdf(proxy(rdf_type = "sdk:DummyGraphConfig"))]
Dummy,
}
impl GraphConfig for GraphConfigs {
type Graph = ConfigurableGraphs;
}
#[derive(Debug)]
pub enum ConfigurableGraphs {
#[cfg(feature = "source_local_file")]
LocalFile(self::local::persistent::local_file::LocalFileSourceGraph),
#[cfg(feature = "source_graph_store_http")]
GraphStoreHttp(self::remote::graph_store_http::GraphStoreHttpSourceGraph),
#[cfg(feature = "source_indexeddb")]
IndexedDb(self::browser::indexeddb::IndexedDbSourceGraph),
}
#[derive(Debug, thiserror::Error)]
pub enum FromGraphError {
#[error("the requested subject is lacking any type")]
NoType,
#[error("the requested subject is not a valid GraphConfigs type {0:?}")]
NotGraph(Arc<Term>),
#[error(
"the requested subject has no <http://sdk.taganak.net/vocab/taganak#SourceIri> predicate"
)]
NoSourceIri,
#[error("the source iri is invalid: `{_0}`")]
InvalidSourceIri(
#[source]
#[from]
InvalidIri<String>,
),
#[error(transparent)]
InvalidGrahIri(#[from] FromIriError),
#[error("the source iri of the provided subject is no literal")]
SourceIriNotLiteral,
#[cfg(feature = "source_graph_store_http")]
#[error(transparent)]
InvalidHttpSourceGraphConfig(#[from] remote::graph_store_http::FromGraphError),
#[cfg(feature = "source_local_file")]
#[error(transparent)]
InvalidLocalFileConfig(#[from] local::persistent::local_file::FromGraphError),
#[error(transparent)]
GraphErr(#[from] GraphError),
}
#[derive(Debug, thiserror::Error)]
pub enum FromIriError {
#[error("Invalid scheme {_0:?}")]
InvalidScheme(iref::iri::SchemeBuf),
}
impl GraphConfigs {
pub fn from_iri(iri: Iri) -> Result<Self, FromIriError> {
let scheme = iri.scheme();
Ok(match scheme.as_str() {
#[cfg(feature = "source_local_file")]
"file" => Self::LocalFile(
self::local::persistent::local_file::LocalFileSourceGraphConfig::new(
iri,
None,
Some("application/n-triples".to_string()),
),
),
#[cfg(feature = "source_graph_store_http")]
"http" | "https" => Self::GraphStoreHttp(
self::remote::graph_store_http::GraphStoreHttpSourceGraphConfig::new(iri),
),
#[cfg(feature = "source_indexeddb")]
"indexeddb" => Self::IndexedDb(
self::browser::indexeddb::IndexedDbSourceGraphConfig::new(iri),
),
_ => return Err(FromIriError::InvalidScheme(scheme.to_owned())),
})
}
}
impl<'g> GraphView for &'g ConfigurableGraphs {
type Stream = Box<dyn Stream<Item = Result<Arc<Triple>, GraphError>> + Unpin + Send + 'g>;
async fn stream(self) -> Result<Self::Stream, GraphError> {
match self {
#[cfg(feature = "source_local_file")]
ConfigurableGraphs::LocalFile(g) => Ok(Box::new(g.stream().await?)),
#[cfg(feature = "source_graph_store_http")]
ConfigurableGraphs::GraphStoreHttp(g) => Ok(Box::new(g.stream().await?)),
#[cfg(feature = "source_browser")]
ConfigurableGraphs::IndexedDb(g) => Ok(Box::new(g.stream().await?)),
_ => unreachable!(),
}
}
default_graph_view!();
}
impl Graph for ConfigurableGraphs {
type View<'g> = &'g Self;
fn view(&self) -> impl core::future::Future<Output = Self::View<'_>> + Send {
core::future::ready(self)
}
async fn add(&mut self, triple: Arc<Triple>) -> Result<(), GraphError> {
match self {
#[cfg(feature = "source_local_file")]
ConfigurableGraphs::LocalFile(g) => g.add(triple.clone()).await,
#[cfg(feature = "source_graph_store_http")]
ConfigurableGraphs::GraphStoreHttp(g) => g.add(triple.clone()).await,
#[cfg(feature = "source_indexeddb")]
ConfigurableGraphs::IndexedDb(g) => g.add(triple.clone()).await,
_ => unreachable!(),
}
}
async fn merge<G>(&mut self, source_graph: G) -> Result<(), GraphError>
where
G: GraphView,
{
match self {
#[cfg(feature = "source_local_file")]
ConfigurableGraphs::LocalFile(g) => g.merge(source_graph).await,
#[cfg(feature = "source_graph_store_http")]
ConfigurableGraphs::GraphStoreHttp(g) => g.merge(source_graph).await,
#[cfg(feature = "source_indexeddb")]
ConfigurableGraphs::IndexedDb(g) => g.merge(source_graph).await,
_ => unreachable!(),
}
}
}
impl ConfigurableGraph for ConfigurableGraphs {
type Config = GraphConfigs;
fn from_config(config: Self::Config) -> Result<Self, SourceError> {
match config {
#[cfg(feature = "source_local_file")]
GraphConfigs::LocalFile(c) => Ok(Self::LocalFile(
self::local::persistent::local_file::LocalFileSourceGraph::from_config(c)?,
)),
#[cfg(feature = "source_graph_store_http")]
GraphConfigs::GraphStoreHttp(c) => Ok(Self::GraphStoreHttp(
self::remote::graph_store_http::GraphStoreHttpSourceGraph::from_config(c)?,
)),
#[cfg(feature = "source_indexeddb")]
GraphConfigs::IndexedDb(c) => Ok(Self::IndexedDb(
self::browser::indexeddb::IndexedDbSourceGraph::from_config(c)?,
)),
_ => unreachable!(),
}
}
}