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