jrsonnet_evaluator/
import.rs1use 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};
16
17pub trait ImportResolver {
19 fn resolve_file(&self, from: &Path, path: &Path) -> Result<Rc<Path>>;
23
24 fn load_file_contents(&self, resolved: &Path) -> Result<IStr>;
26
27 unsafe fn as_any(&self) -> &dyn Any;
33}
34
35pub 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 }
41
42 fn load_file_contents(&self, _resolved: &Path) -> Result<IStr> {
43 panic!("dummy resolver can't load any file")
45 }
46
47 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}
57
58#[derive(Default)]
60pub struct FileImportResolver {
61 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}
93
94type ResolutionData = (PathBuf, PathBuf);
95
96pub 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_cache
105 .borrow_mut()
106 .entry((from.to_owned(), path.to_owned()))
107 .or_insert_with(|| self.inner.resolve_file(from, path))
108 .clone()
109 }
110
111 fn load_file_contents(&self, resolved: &Path) -> Result<IStr> {
112 self.loading_cache
113 .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}