Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor JsonLdOptions to take document loader factory instead of loader #151

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions jsonld/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub use options::*;
pub mod error;
pub use error::*;
pub mod loader;
pub mod loader_factory;
pub mod parser;
pub use parser::*;
pub mod serializer;
Expand Down
111 changes: 111 additions & 0 deletions jsonld/src/loader_factory.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
//! I define trait for loader factories.
//!

use std::{fmt::Display, marker::PhantomData, sync::Arc};

use json_ld::Loader;
use json_syntax::Value;
use locspan::Location;
use sophia_iri::Iri;

/// A trait for factory of document loaders.
pub trait LoaderFactory {
/// Type of loaders this factory yields.
type Loader<'l>: Loader<
Iri<Arc<str>>,
Location<Iri<Arc<str>>>,
Output = Value<Location<Iri<Arc<str>>>>,
Error = Self::LoaderError,
> + Send
+ Sync
+ 'l
where
Self: 'l;

/// Type of loader error.
type LoaderError: Display + Send;

/// Yield a new loader.
fn yield_loader(&self) -> Self::Loader<'_>;
}

/// A loader factory that yields default loaders.
#[derive(Debug, Clone, Default)]
pub struct DefaultLoaderFactory<L> {
_phantom: PhantomData<fn() -> L>,
}

impl<L: Default> DefaultLoaderFactory<L> {
/// Create a new [`DefaultLoaderFactory`].
#[inline]
pub fn new() -> Self {
Self::default()
}
}

impl<L> LoaderFactory for DefaultLoaderFactory<L>
where
L: Loader<Iri<Arc<str>>, Location<Iri<Arc<str>>>, Output = Value<Location<Iri<Arc<str>>>>>
+ Default
+ Send
+ Sync,
L::Error: Display + Send,
{
type Loader<'l> = L
where
Self: 'l;

type LoaderError = L::Error;

#[inline]
fn yield_loader(&self) -> Self::Loader<'_> {
L::default()
}
}

/// A loader factory that delegates to an underlying closure.
#[derive(Debug, Clone)]
pub struct ClosureLoaderFactory<L, F> {
closure: F,
_phantom: PhantomData<fn() -> L>,
}

impl<L, F> ClosureLoaderFactory<L, F> {
/// Create a new [`ClosureLoaderFactory`] with given closure.
#[inline]
pub fn new(closure: F) -> Self {
Self {
closure,
_phantom: PhantomData,
}
}

/// Create a new [`ClosureLoaderFactory`] that yields loaders by cloning a template loader.
#[inline]
pub fn new_cloning(template_loader: L) -> ClosureLoaderFactory<L, impl Fn() -> L>
where
L: Clone,
{
ClosureLoaderFactory::new(move || template_loader.clone())
}
}

impl<L, F> LoaderFactory for ClosureLoaderFactory<L, F>
where
L: Loader<Iri<Arc<str>>, Location<Iri<Arc<str>>>, Output = Value<Location<Iri<Arc<str>>>>>
+ Send
+ Sync,
L::Error: Display + Send,
F: Fn() -> L,
{
type Loader<'l> = L
where
Self: 'l;

type LoaderError = L::Error;

#[inline]
fn yield_loader(&self) -> Self::Loader<'_> {
(self.closure)()
}
}
36 changes: 24 additions & 12 deletions jsonld/src/options.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
//! Defines types for configuring JSON-LD processing.

use std::sync::{LockResult, Mutex, MutexGuard};

use json_ld::expansion::Policy;
pub use json_ld::rdf::RdfDirection;
use json_ld::syntax::context::Value;
use json_ld::NoLoader;
pub use json_ld::Options;
pub use json_ld::ProcessingMode;
use locspan::Location;
use locspan::Span;
use sophia_iri::Iri;

use crate::loader::NoLoader;
use crate::loader_factory::DefaultLoaderFactory;
use crate::loader_factory::LoaderFactory;
use crate::vocabulary::ArcIri;

/// JSON-LD option, as defined by <https://www.w3.org/TR/json-ld11-api/#dom-jsonldoptions-processingmode>.
Expand All @@ -24,24 +24,24 @@ use crate::vocabulary::ArcIri;
///
/// * the generic parameter `L` is the type of the [document loader](`json_ld::Loader`)
#[derive(Default)]
pub struct JsonLdOptions<L> {
pub struct JsonLdOptions<LF> {
inner: InnerOptions,
loader: Mutex<L>,
loader_factory: LF,
use_native_types: bool,
use_rdf_type: bool,
// non standard:
spaces: u16,
compact_context: Option<ContextRef>,
}

impl JsonLdOptions<NoLoader> {
impl JsonLdOptions<DefaultLoaderFactory<NoLoader>> {
/// Build a new JSON-LD options.
pub fn new() -> Self {
Self::default()
}
}

impl<L> JsonLdOptions<L> {
impl<LF> JsonLdOptions<LF> {
/// The [`base`] IRI against which to resolve relative IRIs.
///
/// [`base`]: https://www.w3.org/TR/json-ld11-api/#dom-jsonldoptions-base
Expand All @@ -66,10 +66,11 @@ impl<L> JsonLdOptions<L> {
}

/// The [`documentLoader`] is used to retrieve remote documents and contexts.
/// The returned factory can yield a [`documentLoader`].
///
/// [`documentLoader`]: https://www.w3.org/TR/json-ld11-api/#dom-jsonldoptions-documentloader
pub fn document_loader(&self) -> LockResult<MutexGuard<L>> {
self.loader.lock()
pub fn document_loader_factory(&self) -> &LF {
&self.loader_factory
}

/// [`expandContext`] is a context that is used to initialize the active context when expanding a document.
Expand Down Expand Up @@ -191,10 +192,13 @@ impl<L> JsonLdOptions<L> {
}

/// Change the [`document_loader`](Self::document_loader)
pub fn with_document_loader<L2>(self, document_loader: L2) -> JsonLdOptions<L2> {
pub fn with_document_loader_factory<LF2>(
self,
document_loader_factory: LF2,
) -> JsonLdOptions<LF2> {
JsonLdOptions {
inner: self.inner,
loader: Mutex::new(document_loader),
loader_factory: document_loader_factory,
use_native_types: self.use_native_types,
use_rdf_type: self.use_native_types,
spaces: self.spaces,
Expand Down Expand Up @@ -293,7 +297,15 @@ impl<L> JsonLdOptions<L> {
}
}

impl<L> std::ops::Deref for JsonLdOptions<L> {
impl<LF: LoaderFactory> JsonLdOptions<LF> {
/// The [`documentLoader`] is used to retrieve remote documents and contexts.
/// [`documentLoader`]: https://www.w3.org/TR/json-ld11-api/#dom-jsonldoptions-documentloader
pub fn document_loader(&self) -> LF::Loader<'_> {
self.loader_factory.yield_loader()
}
}

impl<LF> std::ops::Deref for JsonLdOptions<LF> {
type Target = InnerOptions;

fn deref(&self) -> &Self::Target {
Expand Down
48 changes: 16 additions & 32 deletions jsonld/src/parser.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
//! A JSON-LD parser based on Thimothée Haudebourg's [`json_ld`] crate.

use std::{fmt::Display, io::BufRead, ops::DerefMut, sync::Arc};
use std::{io::BufRead, sync::Arc};

use json_ld::{JsonLdProcessor, Loader, RemoteDocument, ToRdfError};
use json_ld::{JsonLdProcessor, RemoteDocument, ToRdfError};
use json_syntax::{Parse, Value};
use locspan::{Location, Span};
use sophia_api::{prelude::QuadParser, quad::Spog};
use sophia_iri::Iri;

use crate::{
loader::NoLoader,
loader_factory::{DefaultLoaderFactory, LoaderFactory},
vocabulary::{ArcIri, ArcVoc},
JsonLdOptions,
};
Expand All @@ -30,17 +31,17 @@ mod test;
///
/// * the generic parameter `L` is the type of the [document loader](`json_ld::Loader`)
/// (determined by the `options` parameters)
pub struct JsonLdParser<L = NoLoader> {
options: JsonLdOptions<L>,
pub struct JsonLdParser<LF = DefaultLoaderFactory<NoLoader>> {
options: JsonLdOptions<LF>,
}

impl Default for JsonLdParser<NoLoader> {
impl Default for JsonLdParser<DefaultLoaderFactory<NoLoader>> {
fn default() -> Self {
Self::new()
}
}

impl JsonLdParser<NoLoader> {
impl JsonLdParser<DefaultLoaderFactory<NoLoader>> {
/// Make a new [`JsonLdParser`] with the default options
pub fn new() -> Self {
JsonLdParser {
Expand All @@ -49,46 +50,36 @@ impl JsonLdParser<NoLoader> {
}
}

impl<L> JsonLdParser<L> {
impl<LF> JsonLdParser<LF> {
/// Make a new [`JsonLdParser`] with the given options
pub fn new_with_options(options: JsonLdOptions<L>) -> Self {
pub fn new_with_options(options: JsonLdOptions<LF>) -> Self {
JsonLdParser { options }
}

/// Borrow the options of this parser
pub fn options(&self) -> &JsonLdOptions<L> {
pub fn options(&self) -> &JsonLdOptions<LF> {
&self.options
}

/// Parse (as RDF) a pre-parsed (as JSON) document
pub fn parse_json(&self, data: &RemoteDocument<ArcIri>) -> JsonLdQuadSource
where
L: Loader<ArcIri, Location<ArcIri>>
+ json_ld::ContextLoader<ArcIri, Location<ArcIri>>
+ Send
+ Sync,
L::Output: Into<Value<Location<ArcIri>>>,
L::Error: Display + Send,
L::Context: Into<json_ld::syntax::context::Value<Location<ArcIri>>>,
L::ContextError: Display + Send,
LF: LoaderFactory,
{
let gen_loc = Location::new(
Iri::new_unchecked(Arc::from("x-bnode-gen://")),
Span::default(),
);
let mut generator = rdf_types::generator::Blank::new().with_metadata(gen_loc);
let mut g_loader = match self.options.document_loader() {
Ok(g) => g,
Err(err) => return JsonLdQuadSource::from_err(err),
};
let loader = g_loader.deref_mut();
let mut loader = self.options.document_loader();
let mut vocab = ArcVoc {};
let options = self.options.inner().clone();
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.expect("Could not build tokio runtime");
match rt.block_on(data.to_rdf_with_using(&mut vocab, &mut generator, loader, options)) {
match rt.block_on(data.to_rdf_with_using(&mut vocab, &mut generator, &mut loader, options))
{
Err(ToRdfError::Expand(err)) => JsonLdQuadSource::from_err(err),
Ok(mut to_rdf) => JsonLdQuadSource::Quads(
to_rdf
Expand All @@ -101,16 +92,9 @@ impl<L> JsonLdParser<L> {
}
}

impl<B: BufRead, L> QuadParser<B> for JsonLdParser<L>
impl<B: BufRead, LF> QuadParser<B> for JsonLdParser<LF>
where
L: Loader<ArcIri, Location<ArcIri>>
+ json_ld::ContextLoader<ArcIri, Location<ArcIri>>
+ Send
+ Sync,
L::Output: Into<Value<Location<ArcIri>>>,
L::Error: Display + Send,
L::Context: Into<json_ld::syntax::context::Value<Location<ArcIri>>>,
L::ContextError: Display + Send,
LF: LoaderFactory,
{
type Source = JsonLdQuadSource;

Expand Down
Loading