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::{10 any::Any,11 cell::RefCell,12 collections::HashMap,13 path::{Path, PathBuf},14 rc::Rc,15};161718pub trait ImportResolver {19 20 21 22 fn resolve_file(&self, from: &Path, path: &Path) -> Result<Rc<Path>>;2324 25 fn load_file_contents(&self, resolved: &Path) -> Result<IStr>;2627 28 29 30 31 32 unsafe fn as_any(&self) -> &dyn Any;33}343536pub struct DummyImportResolver;37impl ImportResolver for DummyImportResolver {38 fn resolve_file(&self, from: &Path, path: &Path) -> Result<Rc<Path>> {39 throw!(ImportNotSupported(from.into(), path.into()))40 }4142 fn load_file_contents(&self, _resolved: &Path) -> Result<IStr> {43 44 panic!("dummy resolver can't load any file")45 }4647 unsafe fn as_any(&self) -> &dyn Any {48 panic!("`as_any($self)` is not supported by dummy resolver")49 }50}51#[allow(clippy::use_self)]52impl Default for Box<dyn ImportResolver> {53 fn default() -> Self {54 Box::new(DummyImportResolver)55 }56}575859#[derive(Default)]60pub struct FileImportResolver {61 62 63 pub library_paths: Vec<PathBuf>,64}65impl ImportResolver for FileImportResolver {66 fn resolve_file(&self, from: &Path, path: &Path) -> Result<Rc<Path>> {67 let mut direct = from.to_path_buf();68 direct.push(path);69 if direct.exists() {70 Ok(direct.into())71 } else {72 for library_path in self.library_paths.iter() {73 let mut cloned = library_path.clone();74 cloned.push(path);75 if cloned.exists() {76 return Ok(cloned.into());77 }78 }79 throw!(ImportFileNotFound(from.to_owned(), path.to_owned()))80 }81 }82 fn load_file_contents(&self, id: &Path) -> Result<IStr> {83 let mut file = File::open(id).map_err(|_e| ResolvedFileNotFound(id.to_owned()))?;84 let mut out = String::new();85 file.read_to_string(&mut out)86 .map_err(|_e| ImportBadFileUtf8(id.to_owned()))?;87 Ok(out.into())88 }89 unsafe fn as_any(&self) -> &dyn Any {90 panic!("this resolver can't be used as any")91 }92}9394type ResolutionData = (PathBuf, PathBuf);959697pub struct CachingImportResolver {98 resolution_cache: RefCell<HashMap<ResolutionData, Result<Rc<Path>>>>,99 loading_cache: RefCell<HashMap<PathBuf, Result<IStr>>>,100 inner: Box<dyn ImportResolver>,101}102impl ImportResolver for CachingImportResolver {103 fn resolve_file(&self, from: &Path, path: &Path) -> Result<Rc<Path>> {104 self.resolution_cache105 .borrow_mut()106 .entry((from.to_owned(), path.to_owned()))107 .or_insert_with(|| self.inner.resolve_file(from, path))108 .clone()109 }110111 fn load_file_contents(&self, resolved: &Path) -> Result<IStr> {112 self.loading_cache113 .borrow_mut()114 .entry(resolved.to_owned())115 .or_insert_with(|| self.inner.load_file_contents(resolved))116 .clone()117 }118 unsafe fn as_any(&self) -> &dyn Any {119 panic!("this resolver can't be used as any")120 }121}