use algorithm::{Action, RejectVocab};
pub use json_ld_core::{warning, Context, ProcessingMode};
use json_ld_core::{ExtractContextError, LoadError, Loader};
use json_ld_syntax::ErrorCode;
use rdf_types::VocabularyMut;
use std::{fmt, hash::Hash};
pub mod algorithm;
mod processed;
mod stack;
pub use processed::*;
pub use stack::ProcessingStack;
pub enum Warning {
KeywordLikeTerm(String),
KeywordLikeValue(String),
MalformedIri(String),
}
impl fmt::Display for Warning {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::KeywordLikeTerm(s) => write!(f, "keyword-like term `{s}`"),
Self::KeywordLikeValue(s) => write!(f, "keyword-like value `{s}`"),
Self::MalformedIri(s) => write!(f, "malformed IRI `{s}`"),
}
}
}
impl<N> contextual::DisplayWithContext<N> for Warning {
fn fmt_with(&self, _: &N, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
pub trait WarningHandler<N>: json_ld_core::warning::Handler<N, Warning> {}
impl<N, H> WarningHandler<N> for H where H: json_ld_core::warning::Handler<N, Warning> {}
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("Invalid context nullification")]
InvalidContextNullification,
#[error("Remote document loading failed")]
LoadingDocumentFailed,
#[error("Processing mode conflict")]
ProcessingModeConflict,
#[error("Invalid `@context` entry")]
InvalidContextEntry,
#[error("Invalid `@import` value")]
InvalidImportValue,
#[error("Invalid remote context")]
InvalidRemoteContext,
#[error("Invalid base IRI")]
InvalidBaseIri,
#[error("Invalid vocabulary mapping")]
InvalidVocabMapping,
#[error("Cyclic IRI mapping")]
CyclicIriMapping,
#[error("Invalid term definition")]
InvalidTermDefinition,
#[error("Keyword redefinition")]
KeywordRedefinition,
#[error("Invalid `@protected` value")]
InvalidProtectedValue,
#[error("Invalid type mapping")]
InvalidTypeMapping,
#[error("Invalid reverse property")]
InvalidReverseProperty,
#[error("Invalid IRI mapping")]
InvalidIriMapping,
#[error("Invalid keyword alias")]
InvalidKeywordAlias,
#[error("Invalid container mapping")]
InvalidContainerMapping,
#[error("Invalid scoped context")]
InvalidScopedContext,
#[error("Protected term redefinition")]
ProtectedTermRedefinition,
#[error(transparent)]
ContextLoadingFailed(#[from] LoadError),
#[error("Unable to extract JSON-LD context: {0}")]
ContextExtractionFailed(ExtractContextError),
#[error("Use of forbidden `@vocab`")]
ForbiddenVocab,
}
impl From<RejectVocab> for Error {
fn from(_value: RejectVocab) -> Self {
Self::ForbiddenVocab
}
}
impl Error {
pub fn code(&self) -> ErrorCode {
match self {
Self::InvalidContextNullification => ErrorCode::InvalidContextNullification,
Self::LoadingDocumentFailed => ErrorCode::LoadingDocumentFailed,
Self::ProcessingModeConflict => ErrorCode::ProcessingModeConflict,
Self::InvalidContextEntry => ErrorCode::InvalidContextEntry,
Self::InvalidImportValue => ErrorCode::InvalidImportValue,
Self::InvalidRemoteContext => ErrorCode::InvalidRemoteContext,
Self::InvalidBaseIri => ErrorCode::InvalidBaseIri,
Self::InvalidVocabMapping => ErrorCode::InvalidVocabMapping,
Self::CyclicIriMapping => ErrorCode::CyclicIriMapping,
Self::InvalidTermDefinition => ErrorCode::InvalidTermDefinition,
Self::KeywordRedefinition => ErrorCode::KeywordRedefinition,
Self::InvalidProtectedValue => ErrorCode::InvalidPropagateValue,
Self::InvalidTypeMapping => ErrorCode::InvalidTypeMapping,
Self::InvalidReverseProperty => ErrorCode::InvalidReverseProperty,
Self::InvalidIriMapping => ErrorCode::InvalidIriMapping,
Self::InvalidKeywordAlias => ErrorCode::InvalidKeywordAlias,
Self::InvalidContainerMapping => ErrorCode::InvalidContainerMapping,
Self::InvalidScopedContext => ErrorCode::InvalidScopedContext,
Self::ProtectedTermRedefinition => ErrorCode::ProtectedTermRedefinition,
Self::ContextLoadingFailed(_) => ErrorCode::LoadingRemoteContextFailed,
Self::ContextExtractionFailed(_) => ErrorCode::LoadingRemoteContextFailed,
Self::ForbiddenVocab => ErrorCode::InvalidVocabMapping,
}
}
}
pub type ProcessingResult<'a, T, B> = Result<Processed<'a, T, B>, Error>;
pub trait Process {
#[allow(async_fn_in_trait)]
async fn process_full<N, L, W>(
&self,
vocabulary: &mut N,
active_context: &Context<N::Iri, N::BlankId>,
loader: &L,
base_url: Option<N::Iri>,
options: Options,
warnings: W,
) -> Result<Processed<N::Iri, N::BlankId>, Error>
where
N: VocabularyMut,
N::Iri: Clone + Eq + Hash,
N::BlankId: Clone + PartialEq,
L: Loader,
W: WarningHandler<N>;
#[allow(clippy::type_complexity)]
#[allow(async_fn_in_trait)]
async fn process_with<N, L>(
&self,
vocabulary: &mut N,
active_context: &Context<N::Iri, N::BlankId>,
loader: &L,
base_url: Option<N::Iri>,
options: Options,
) -> Result<Processed<N::Iri, N::BlankId>, Error>
where
N: VocabularyMut,
N::Iri: Clone + Eq + Hash,
N::BlankId: Clone + PartialEq,
L: Loader,
{
self.process_full(
vocabulary,
active_context,
loader,
base_url,
options,
warning::Print,
)
.await
}
#[allow(async_fn_in_trait)]
async fn process<N, L>(
&self,
vocabulary: &mut N,
loader: &L,
base_url: Option<N::Iri>,
) -> Result<Processed<N::Iri, N::BlankId>, Error>
where
N: VocabularyMut,
N::Iri: Clone + Eq + Hash,
N::BlankId: Clone + PartialEq,
L: Loader,
{
let active_context = Context::default();
self.process_full(
vocabulary,
&active_context,
loader,
base_url,
Options::default(),
warning::Print,
)
.await
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Options {
pub processing_mode: ProcessingMode,
pub override_protected: bool,
pub propagate: bool,
pub vocab: Action,
}
impl Options {
#[must_use]
pub fn with_override(&self) -> Options {
let mut opt = *self;
opt.override_protected = true;
opt
}
#[must_use]
pub fn with_no_override(&self) -> Options {
let mut opt = *self;
opt.override_protected = false;
opt
}
#[must_use]
pub fn without_propagation(&self) -> Options {
let mut opt = *self;
opt.propagate = false;
opt
}
}
impl Default for Options {
fn default() -> Options {
Options {
processing_mode: ProcessingMode::default(),
override_protected: false,
propagate: true,
vocab: Action::Keep,
}
}
}