1use crate::create_error_result;2use crate::{3 create_error,4 error::{Error, Result},5};6use fs::File;7use std::fs;8use std::io::Read;9use std::{cell::RefCell, collections::HashMap, path::PathBuf, rc::Rc};1011pub trait ImportResolver {12 fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>>;13 fn load_file_contents(&self, resolved: &PathBuf) -> Result<Rc<str>>;14}1516pub struct DummyImportResolver;17impl ImportResolver for DummyImportResolver {18 fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>> {19 create_error_result(Error::ImportNotSupported(from.clone(), path.clone()))20 }21 fn load_file_contents(&self, _resolved: &PathBuf) -> Result<Rc<str>> {22 23 panic!("dummy resolver can't load any file")24 }25}26impl Default for Box<dyn ImportResolver> {27 fn default() -> Self {28 Box::new(DummyImportResolver)29 }30}3132pub struct FileImportResolver {33 pub library_paths: Vec<PathBuf>,34}35impl ImportResolver for FileImportResolver {36 fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>> {37 let mut new_path = from.clone();38 new_path.push(path);39 if new_path.exists() {40 Ok(Rc::new(new_path))41 } else {42 for library_path in self.library_paths.iter() {43 let mut cloned = library_path.clone();44 cloned.push(path);45 if cloned.exists() {46 return Ok(Rc::new(cloned));47 }48 }49 create_error_result(Error::ImportFileNotFound(from.clone(), path.clone()))50 }51 }52 fn load_file_contents(&self, id: &PathBuf) -> Result<Rc<str>> {53 let mut file =54 File::open(id).map_err(|_e| create_error(Error::ResolvedFileNotFound(id.clone())))?;55 let mut out = String::new();56 file.read_to_string(&mut out)57 .map_err(|_e| create_error(Error::ImportBadFileUtf8(id.clone())))?;58 Ok(out.into())59 }60}6162type ResolutionData = (PathBuf, PathBuf);63pub struct CachingImportResolver {64 resolution_cache: RefCell<HashMap<ResolutionData, Result<Rc<PathBuf>>>>,65 loading_cache: RefCell<HashMap<PathBuf, Result<Rc<str>>>>,66 inner: Box<dyn ImportResolver>,67}68impl ImportResolver for CachingImportResolver {69 fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>> {70 self.resolution_cache71 .borrow_mut()72 .entry((from.clone(), path.clone()))73 .or_insert_with(|| self.inner.resolve_file(from, path))74 .clone()75 }76 fn load_file_contents(&self, resolved: &PathBuf) -> Result<Rc<str>> {77 self.loading_cache78 .borrow_mut()79 .entry(resolved.clone())80 .or_insert_with(|| self.inner.load_file_contents(resolved))81 .clone()82 }83}