use crate::create_error_result;
use crate::{
create_error,
error::{Error, Result},
};
use fs::File;
use std::fs;
use std::io::Read;
use std::{cell::RefCell, collections::HashMap, path::PathBuf, rc::Rc};
pub trait ImportResolver {
fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>>;
fn load_file_contents(&self, resolved: &PathBuf) -> Result<Rc<str>>;
}
pub struct DummyImportResolver;
impl ImportResolver for DummyImportResolver {
fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>> {
create_error_result(Error::ImportNotSupported(from.clone(), path.clone()))
}
fn load_file_contents(&self, _resolved: &PathBuf) -> Result<Rc<str>> {
panic!("dummy resolver can't load any file")
}
}
impl Default for Box<dyn ImportResolver> {
fn default() -> Self {
Box::new(DummyImportResolver)
}
}
pub struct FileImportResolver {
pub library_paths: Vec<PathBuf>,
}
impl ImportResolver for FileImportResolver {
fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>> {
let mut new_path = from.clone();
new_path.push(path);
if new_path.exists() {
Ok(Rc::new(new_path))
} else {
for library_path in self.library_paths.iter() {
let mut cloned = library_path.clone();
cloned.push(path);
if cloned.exists() {
return Ok(Rc::new(cloned));
}
}
create_error_result(Error::ImportFileNotFound(from.clone(), path.clone()))
}
}
fn load_file_contents(&self, id: &PathBuf) -> Result<Rc<str>> {
let mut file =
File::open(id).map_err(|_e| create_error(Error::ResolvedFileNotFound(id.clone())))?;
let mut out = String::new();
file.read_to_string(&mut out)
.map_err(|_e| create_error(Error::ImportBadFileUtf8(id.clone())))?;
Ok(out.into())
}
}
type ResolutionData = (PathBuf, PathBuf);
pub struct CachingImportResolver {
resolution_cache: RefCell<HashMap<ResolutionData, Result<Rc<PathBuf>>>>,
loading_cache: RefCell<HashMap<PathBuf, Result<Rc<str>>>>,
inner: Box<dyn ImportResolver>,
}
impl ImportResolver for CachingImportResolver {
fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>> {
self.resolution_cache
.borrow_mut()
.entry((from.clone(), path.clone()))
.or_insert_with(|| self.inner.resolve_file(from, path))
.clone()
}
fn load_file_contents(&self, resolved: &PathBuf) -> Result<Rc<str>> {
self.loading_cache
.borrow_mut()
.entry(resolved.clone())
.or_insert_with(|| self.inner.load_file_contents(resolved))
.clone()
}
}