1use crate::{2 error::{Error::*, Result},3 throw,4};5use fs::File;6use std::fs;7use std::io::Read;8use std::{any::Any, cell::RefCell, collections::HashMap, path::PathBuf, rc::Rc};91011pub trait ImportResolver {12 13 14 15 fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>>;1617 18 fn load_file_contents(&self, resolved: &PathBuf) -> Result<Rc<str>>;1920 21 22 23 24 25 unsafe fn as_any(&self) -> &dyn Any;26}272829pub struct DummyImportResolver;30impl ImportResolver for DummyImportResolver {31 fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>> {32 throw!(ImportNotSupported(from.clone(), path.clone()))33 }3435 fn load_file_contents(&self, _resolved: &PathBuf) -> Result<Rc<str>> {36 37 panic!("dummy resolver can't load any file")38 }3940 unsafe fn as_any(&self) -> &dyn Any {41 panic!("`as_any($self)` is not supported by dummy resolver")42 }43}44impl Default for Box<dyn ImportResolver> {45 fn default() -> Self {46 Box::new(DummyImportResolver)47 }48}495051#[derive(Default)]52pub struct FileImportResolver {53 54 55 pub library_paths: Vec<PathBuf>,56}57impl ImportResolver for FileImportResolver {58 fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>> {59 let mut new_path = from.clone();60 new_path.push(path);61 if new_path.exists() {62 Ok(Rc::new(new_path))63 } else {64 for library_path in self.library_paths.iter() {65 let mut cloned = library_path.clone();66 cloned.push(path);67 if cloned.exists() {68 return Ok(Rc::new(cloned));69 }70 }71 throw!(ImportFileNotFound(from.clone(), path.clone()))72 }73 }74 fn load_file_contents(&self, id: &PathBuf) -> Result<Rc<str>> {75 let mut file = File::open(id).map_err(|_e| ResolvedFileNotFound(id.clone()))?;76 let mut out = String::new();77 file.read_to_string(&mut out)78 .map_err(|_e| ImportBadFileUtf8(id.clone()))?;79 Ok(out.into())80 }81 unsafe fn as_any(&self) -> &dyn Any {82 panic!("this resolver can't be used as any")83 }84}8586type ResolutionData = (PathBuf, PathBuf);878889pub struct CachingImportResolver {90 resolution_cache: RefCell<HashMap<ResolutionData, Result<Rc<PathBuf>>>>,91 loading_cache: RefCell<HashMap<PathBuf, Result<Rc<str>>>>,92 inner: Box<dyn ImportResolver>,93}94impl ImportResolver for CachingImportResolver {95 fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>> {96 self.resolution_cache97 .borrow_mut()98 .entry((from.clone(), path.clone()))99 .or_insert_with(|| self.inner.resolve_file(from, path))100 .clone()101 }102103 fn load_file_contents(&self, resolved: &PathBuf) -> Result<Rc<str>> {104 self.loading_cache105 .borrow_mut()106 .entry(resolved.clone())107 .or_insert_with(|| self.inner.load_file_contents(resolved))108 .clone()109 }110 unsafe fn as_any(&self) -> &dyn Any {111 panic!("this resolver can't be used as any")112 }113}