use json_syntax::JsonValue;
use rdf_syntax::{Iri, IriBuf};
use std::{borrow::Cow, sync::LazyLock};
use crate::{JsonLdDocument, syntax::JsonLdContextDocumentValue};
pub mod chain;
pub mod fs;
pub mod map;
pub mod none;
#[cfg(feature = "reqwest")]
pub mod reqwest;
#[cfg(feature = "tokio")]
pub mod tokio_fs;
#[cfg(feature = "reqwest")]
pub use self::reqwest::JsonLdReqwestLoader;
pub use chain::JsonLdChainLoader;
pub use fs::JsonLdFsLoader;
pub use map::StaticJsonLdLoader;
pub use none::NoJsonLdLoader;
#[cfg(feature = "tokio")]
pub use tokio_fs::JsonLdTokioFsLoader;
pub type RemoteJsonLdContext = RemoteJsonLdDocument<JsonLdContextDocumentValue>;
#[derive(Clone)]
pub enum RemoteJsonLdDocument<T = JsonValue> {
Iri(IriBuf),
Loaded(JsonLdDocument<T>),
}
impl<T> RemoteJsonLdDocument<T> {
pub fn iri(iri: IriBuf) -> Self {
Self::Iri(iri)
}
}
impl<T> RemoteJsonLdDocument<T> {
pub fn url(&self) -> Option<&Iri> {
match self {
Self::Iri(iri) => Some(iri),
Self::Loaded(t) => t.url(),
}
}
}
impl<T> RemoteJsonLdDocument<T>
where
T: TryFrom<JsonValue>,
T::Error: Into<anyhow::Error>,
{
pub async fn load(
self,
loader: &impl AsyncJsonLdLoader,
) -> Result<JsonLdDocument<T>, JsonLdLoadError> {
match self {
Self::Iri(r) => loader
.async_load(&r)
.await?
.try_map(TryInto::try_into)
.map_err(|e| JsonLdLoadError::new(r, e)),
Self::Loaded(doc) => Ok(doc),
}
}
pub async fn loaded(
&self,
loader: &impl AsyncJsonLdLoader,
) -> Result<Cow<'_, JsonLdDocument<T>>, JsonLdLoadError>
where
T: Clone,
{
match self {
Self::Iri(r) => loader
.async_load(r)
.await?
.try_map(TryInto::try_into)
.map_err(|e| JsonLdLoadError::new(r.clone(), e))
.map(Cow::Owned),
Self::Loaded(doc) => Ok(Cow::Borrowed(doc)),
}
}
}
#[derive(Debug, thiserror::Error)]
#[error("loading document `{target}` failed: {cause}")]
pub struct JsonLdLoadError {
pub target: IriBuf,
pub cause: anyhow::Error,
}
impl JsonLdLoadError {
pub fn new(target: IriBuf, cause: impl Into<anyhow::Error>) -> Self {
Self {
target,
cause: cause.into(),
}
}
}
pub trait AsyncJsonLdLoader {
#[allow(async_fn_in_trait)]
async fn async_load(&self, url: &Iri) -> Result<JsonLdDocument, JsonLdLoadError>;
}
impl<L: AsyncJsonLdLoader> AsyncJsonLdLoader for &L {
async fn async_load(&self, url: &Iri) -> Result<JsonLdDocument, JsonLdLoadError> {
L::async_load(self, url).await
}
}
impl<L: AsyncJsonLdLoader> AsyncJsonLdLoader for &mut L {
async fn async_load(&self, url: &Iri) -> Result<JsonLdDocument, JsonLdLoadError> {
L::async_load(self, url).await
}
}
impl<L: AsyncJsonLdLoader, F: FnOnce() -> L> AsyncJsonLdLoader for LazyLock<L, F> {
async fn async_load(&self, url: &Iri) -> Result<JsonLdDocument, JsonLdLoadError> {
L::async_load(self, url).await
}
}
pub trait JsonLdLoader {
fn load(&self, url: &Iri) -> Result<JsonLdDocument, JsonLdLoadError>;
fn as_async_loader(&self) -> &ToAsyncJsonLdLoader<Self> {
ToAsyncJsonLdLoader::from_ref(self)
}
fn into_async_loader(self) -> ToAsyncJsonLdLoader<Self>
where
Self: Sized,
{
ToAsyncJsonLdLoader(self)
}
}
impl<L: JsonLdLoader> JsonLdLoader for &L {
fn load(&self, url: &Iri) -> Result<JsonLdDocument, JsonLdLoadError> {
L::load(self, url)
}
}
impl<L: JsonLdLoader> JsonLdLoader for &mut L {
fn load(&self, url: &Iri) -> Result<JsonLdDocument, JsonLdLoadError> {
L::load(self, url)
}
}
impl<L: JsonLdLoader, F: FnOnce() -> L> JsonLdLoader for LazyLock<L, F> {
fn load(&self, url: &Iri) -> Result<JsonLdDocument, JsonLdLoadError> {
L::load(self, url)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct ToAsyncJsonLdLoader<T: ?Sized>(pub T);
impl<T: ?Sized> ToAsyncJsonLdLoader<T> {
pub fn from_ref(loader: &T) -> &Self {
unsafe {
std::mem::transmute(loader)
}
}
}
impl<T: JsonLdLoader> AsyncJsonLdLoader for ToAsyncJsonLdLoader<T> {
async fn async_load(&self, url: &Iri) -> Result<JsonLdDocument, JsonLdLoadError> {
self.0.load(url)
}
}