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}44#[allow(clippy::use_self)]45impl Default for Box<dyn ImportResolver> {46 fn default() -> Self {47 Box::new(DummyImportResolver)48 }49}505152#[derive(Default)]53pub struct FileImportResolver {54 55 56 pub library_paths: Vec<PathBuf>,57}58impl ImportResolver for FileImportResolver {59 fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>> {60 let mut new_path = from.clone();61 new_path.push(path);62 if new_path.exists() {63 Ok(Rc::new(new_path))64 } else {65 for library_path in self.library_paths.iter() {66 let mut cloned = library_path.clone();67 cloned.push(path);68 if cloned.exists() {69 return Ok(Rc::new(cloned));70 }71 }72 throw!(ImportFileNotFound(from.clone(), path.clone()))73 }74 }75 fn load_file_contents(&self, id: &PathBuf) -> Result<Rc<str>> {76 let mut file = File::open(id).map_err(|_e| ResolvedFileNotFound(id.clone()))?;77 let mut out = String::new();78 file.read_to_string(&mut out)79 .map_err(|_e| ImportBadFileUtf8(id.clone()))?;80 Ok(out.into())81 }82 unsafe fn as_any(&self) -> &dyn Any {83 panic!("this resolver can't be used as any")84 }85}8687type ResolutionData = (PathBuf, PathBuf);888990pub struct CachingImportResolver {91 resolution_cache: RefCell<HashMap<ResolutionData, Result<Rc<PathBuf>>>>,92 loading_cache: RefCell<HashMap<PathBuf, Result<Rc<str>>>>,93 inner: Box<dyn ImportResolver>,94}95impl ImportResolver for CachingImportResolver {96 fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>> {97 self.resolution_cache98 .borrow_mut()99 .entry((from.clone(), path.clone()))100 .or_insert_with(|| self.inner.resolve_file(from, path))101 .clone()102 }103104 fn load_file_contents(&self, resolved: &PathBuf) -> Result<Rc<str>> {105 self.loading_cache106 .borrow_mut()107 .entry(resolved.clone())108 .or_insert_with(|| self.inner.load_file_contents(resolved))109 .clone()110 }111 unsafe fn as_any(&self) -> &dyn Any {112 panic!("this resolver can't be used as any")113 }114}