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;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("no mount point")]
NoMountPoint,
#[error("IO: {0}")]
IO(std::io::Error),
#[error("parse error: {0}")]
Parse(JsonParseError),
}
#[derive(Default)]
pub struct JsonLdFsLoader {
mount_points: Vec<(PathBuf, IriBuf)>,
}
impl JsonLdFsLoader {
pub fn new() -> Self {
Self::default()
}
#[inline(always)]
pub fn mount<P: AsRef<Path>>(&mut self, url: IriBuf, path: P) {
self.mount_points.push((path.as_ref().into(), url));
}
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);
}
}
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)),
}
}
}