json-ld 0.22.0

JSON-LD implementation
Documentation
use json_syntax::parse::JsonParseError;
use json_syntax::{JsonParse, JsonValue};
use rdf_syntax::{Iri, IriBuf};
use std::fs::File;
use std::io::{BufReader, Read};
use std::path::{Path, PathBuf};

use crate::{JsonLdDocument, JsonLdLoadError, JsonLdSourceCode};

use super::JsonLdLoader;

/// Loading error.
#[derive(Debug, thiserror::Error)]
pub enum Error {
	/// No mount point found for the given IRI.
	#[error("no mount point")]
	NoMountPoint,

	/// IO error.
	#[error("IO: {0}")]
	IO(std::io::Error),

	/// Parse error.
	#[error("parse error: {0}")]
	Parse(JsonParseError),
}

/// File-system loader.
///
/// This is a special JSON-LD document loader that can load document from the file system by
/// attaching a directory to specific URLs.
///
/// Loaded documents are not cached: a new file system read is made each time
/// an URL is loaded even if it has already been queried before.
#[derive(Default)]
pub struct JsonLdFsLoader {
	mount_points: Vec<(PathBuf, IriBuf)>,
}

impl JsonLdFsLoader {
	/// Creates a new file system loader with the given content `parser`.
	pub fn new() -> Self {
		Self::default()
	}

	/// Bind the given IRI prefix to the given path.
	///
	/// Any document with an IRI matching the given prefix will be loaded from
	/// the referenced local directory.
	#[inline(always)]
	pub fn mount<P: AsRef<Path>>(&mut self, url: IriBuf, path: P) {
		self.mount_points.push((path.as_ref().into(), url));
	}

	/// Returns the local file path associated to the given `url` if any.
	pub fn filepath(&self, url: &Iri) -> Option<PathBuf> {
		for (path, target_url) in &self.mount_points {
			let relative = url.as_iri_ref().relative_to(target_url);
			if relative.as_iri().is_none() {
				let mut filepath = path.clone();
				for seg in relative.path().segments() {
					filepath.push(seg.as_str())
				}

				return Some(filepath);
			}
			// 			if let Some(relative) = url.as_iri_ref().relative_to(target_url) {
			// 				let mut filepath = path.clone();
			// 				for seg in suffix.as_path().segments() {
			// 					filepath.push(seg.as_str())
			// 				}
			//
			// 				return Some(filepath);
			// 			}
		}

		None
	}
}

impl JsonLdLoader for JsonLdFsLoader {
	fn load(&self, url: &Iri) -> Result<JsonLdDocument, JsonLdLoadError> {
		match self.filepath(url) {
			Some(filepath) => {
				let file = File::open(filepath)
					.map_err(|e| JsonLdLoadError::new(url.to_owned(), Error::IO(e)))?;
				let mut buf_reader = BufReader::new(file);
				let mut contents = String::new();
				buf_reader
					.read_to_string(&mut contents)
					.map_err(|e| JsonLdLoadError::new(url.to_owned(), Error::IO(e)))?;
				let (doc, code_map) = JsonValue::parse_str(&contents)
					.map_err(|e| JsonLdLoadError::new(url.to_owned(), Error::Parse(e)))?;
				Ok(JsonLdDocument::new_full(
					Some(url.to_owned()),
					Some("application/ld+json".parse().unwrap()),
					None,
					Default::default(),
					Some(JsonLdSourceCode::new(contents, code_map)),
					doc,
				))
			}
			None => Err(JsonLdLoadError::new(url.to_owned(), Error::NoMountPoint)),
		}
	}
}