taganak-framework 0.1.0-dev3

Building blocks for applications based on Taganak (Transactional, Aggregating Graph Architecture for Networking and Access to Knowledge)
Documentation
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!(),
        }
    }
}