1use crate::{2 error::{Error::*, Result},3 throw,4};5use fs::File;6use jrsonnet_interner::IStr;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>>;1718 19 fn load_file_contents(&self, resolved: &PathBuf) -> Result<IStr>;2021 22 23 24 25 26 unsafe fn as_any(&self) -> &dyn Any;27}282930pub struct DummyImportResolver;31impl ImportResolver for DummyImportResolver {32 fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>> {33 throw!(ImportNotSupported(from.clone(), path.clone()))34 }3536 fn load_file_contents(&self, _resolved: &PathBuf) -> Result<IStr> {37 38 panic!("dummy resolver can't load any file")39 }4041 unsafe fn as_any(&self) -> &dyn Any {42 panic!("`as_any($self)` is not supported by dummy resolver")43 }44}45#[allow(clippy::use_self)]46impl Default for Box<dyn ImportResolver> {47 fn default() -> Self {48 Box::new(DummyImportResolver)49 }50}515253#[derive(Default)]54pub struct FileImportResolver {55 56 57 pub library_paths: Vec<PathBuf>,58}59impl ImportResolver for FileImportResolver {60 fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>> {61 let mut new_path = from.clone();62 new_path.push(path);63 if new_path.exists() {64 Ok(Rc::new(new_path))65 } else {66 for library_path in self.library_paths.iter() {67 let mut cloned = library_path.clone();68 cloned.push(path);69 if cloned.exists() {70 return Ok(Rc::new(cloned));71 }72 }73 throw!(ImportFileNotFound(from.clone(), path.clone()))74 }75 }76 fn load_file_contents(&self, id: &PathBuf) -> Result<IStr> {77 let mut file = File::open(id).map_err(|_e| ResolvedFileNotFound(id.clone()))?;78 let mut out = String::new();79 file.read_to_string(&mut out)80 .map_err(|_e| ImportBadFileUtf8(id.clone()))?;81 Ok(out.into())82 }83 unsafe fn as_any(&self) -> &dyn Any {84 panic!("this resolver can't be used as any")85 }86}8788type ResolutionData = (PathBuf, PathBuf);899091pub struct CachingImportResolver {92 resolution_cache: RefCell<HashMap<ResolutionData, Result<Rc<PathBuf>>>>,93 loading_cache: RefCell<HashMap<PathBuf, Result<IStr>>>,94 inner: Box<dyn ImportResolver>,95}96impl ImportResolver for CachingImportResolver {97 fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>> {98 self.resolution_cache99 .borrow_mut()100 .entry((from.clone(), path.clone()))101 .or_insert_with(|| self.inner.resolve_file(from, path))102 .clone()103 }104105 fn load_file_contents(&self, resolved: &PathBuf) -> Result<IStr> {106 self.loading_cache107 .borrow_mut()108 .entry(resolved.clone())109 .or_insert_with(|| self.inner.load_file_contents(resolved))110 .clone()111 }112 unsafe fn as_any(&self) -> &dyn Any {113 panic!("this resolver can't be used as any")114 }115}