json_ld_core/loader/
fs.rs

1use super::{Loader, RemoteDocument};
2use crate::{LoadError, LoadingResult};
3use iref::{Iri, IriBuf};
4use json_syntax::Parse;
5use std::fs::File;
6use std::io::{BufReader, Read};
7use std::path::{Path, PathBuf};
8
9/// Loading error.
10#[derive(Debug, thiserror::Error)]
11pub enum Error {
12	/// No mount point found for the given IRI.
13	#[error("no mount point")]
14	NoMountPoint,
15
16	/// IO error.
17	#[error("IO: {0}")]
18	IO(std::io::Error),
19
20	/// Parse error.
21	#[error("parse error: {0}")]
22	Parse(json_syntax::parse::Error),
23}
24
25/// File-system loader.
26///
27/// This is a special JSON-LD document loader that can load document from the file system by
28/// attaching a directory to specific URLs.
29///
30/// Loaded documents are not cached: a new file system read is made each time
31/// an URL is loaded even if it has already been queried before.
32#[derive(Default)]
33pub struct FsLoader {
34	mount_points: Vec<(PathBuf, IriBuf)>,
35}
36
37impl FsLoader {
38	/// Creates a new file system loader with the given content `parser`.
39	pub fn new() -> Self {
40		Self::default()
41	}
42
43	/// Bind the given IRI prefix to the given path.
44	///
45	/// Any document with an IRI matching the given prefix will be loaded from
46	/// the referenced local directory.
47	#[inline(always)]
48	pub fn mount<P: AsRef<Path>>(&mut self, url: IriBuf, path: P) {
49		self.mount_points.push((path.as_ref().into(), url));
50	}
51
52	/// Returns the local file path associated to the given `url` if any.
53	pub fn filepath(&self, url: &Iri) -> Option<PathBuf> {
54		for (path, target_url) in &self.mount_points {
55			if let Some((suffix, _, _)) = url.as_iri_ref().suffix(target_url) {
56				let mut filepath = path.clone();
57				for seg in suffix.as_path().segments() {
58					filepath.push(seg.as_str())
59				}
60
61				return Some(filepath);
62			}
63		}
64
65		None
66	}
67}
68
69impl Loader for FsLoader {
70	async fn load(&self, url: &Iri) -> LoadingResult<IriBuf> {
71		match self.filepath(url) {
72			Some(filepath) => {
73				let file = File::open(filepath)
74					.map_err(|e| LoadError::new(url.to_owned(), Error::IO(e)))?;
75				let mut buf_reader = BufReader::new(file);
76				let mut contents = String::new();
77				buf_reader
78					.read_to_string(&mut contents)
79					.map_err(|e| LoadError::new(url.to_owned(), Error::IO(e)))?;
80				let (doc, _) = json_syntax::Value::parse_str(&contents)
81					.map_err(|e| LoadError::new(url.to_owned(), Error::Parse(e)))?;
82				Ok(RemoteDocument::new(
83					Some(url.to_owned()),
84					Some("application/ld+json".parse().unwrap()),
85					doc,
86				))
87			}
88			None => Err(LoadError::new(url.to_owned(), Error::NoMountPoint)),
89		}
90	}
91}