Skip to main content

json_ld/core/loader/
fs.rs

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