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::{any::Any, cell::RefCell, collections::HashMap, path::PathBuf, rc::Rc};101112pub trait ImportResolver {13 14 15 16 fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>>;17 18 fn load_file_contents(&self, resolved: &PathBuf) -> Result<Rc<str>>;19 20 21 22 23 24 unsafe fn as_any(&self) -> &dyn Any;25}262728pub struct DummyImportResolver;29impl ImportResolver for DummyImportResolver {30 fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>> {31 create_error_result(Error::ImportNotSupported(from.clone(), path.clone()))32 }33 fn load_file_contents(&self, _resolved: &PathBuf) -> Result<Rc<str>> {34 35 panic!("dummy resolver can't load any file")36 }37 unsafe fn as_any(&self) -> &dyn Any {38 panic!("this resolver can't be used as any")39 }40}41impl Default for Box<dyn ImportResolver> {42 fn default() -> Self {43 Box::new(DummyImportResolver)44 }45}464748#[derive(Default)]49pub struct FileImportResolver {50 51 52 pub library_paths: Vec<PathBuf>,53}54impl ImportResolver for FileImportResolver {55 fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>> {56 let mut new_path = from.clone();57 new_path.push(path);58 if new_path.exists() {59 Ok(Rc::new(new_path))60 } else {61 for library_path in self.library_paths.iter() {62 let mut cloned = library_path.clone();63 cloned.push(path);64 if cloned.exists() {65 return Ok(Rc::new(cloned));66 }67 }68 create_error_result(Error::ImportFileNotFound(from.clone(), path.clone()))69 }70 }71 fn load_file_contents(&self, id: &PathBuf) -> Result<Rc<str>> {72 let mut file =73 File::open(id).map_err(|_e| create_error(Error::ResolvedFileNotFound(id.clone())))?;74 let mut out = String::new();75 file.read_to_string(&mut out)76 .map_err(|_e| create_error(Error::ImportBadFileUtf8(id.clone())))?;77 Ok(out.into())78 }79 unsafe fn as_any(&self) -> &dyn Any {80 panic!("this resolver can't be used as any")81 }82}8384type ResolutionData = (PathBuf, PathBuf);858687pub struct CachingImportResolver {88 resolution_cache: RefCell<HashMap<ResolutionData, Result<Rc<PathBuf>>>>,89 loading_cache: RefCell<HashMap<PathBuf, Result<Rc<str>>>>,90 inner: Box<dyn ImportResolver>,91}92impl ImportResolver for CachingImportResolver {93 fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>> {94 self.resolution_cache95 .borrow_mut()96 .entry((from.clone(), path.clone()))97 .or_insert_with(|| self.inner.resolve_file(from, path))98 .clone()99 }100 fn load_file_contents(&self, resolved: &PathBuf) -> Result<Rc<str>> {101 self.loading_cache102 .borrow_mut()103 .entry(resolved.clone())104 .or_insert_with(|| self.inner.load_file_contents(resolved))105 .clone()106 }107 unsafe fn as_any(&self) -> &dyn Any {108 panic!("this resolver can't be used as any")109 }110}