git.delta.rocks / jrsonnet / refs/commits / 433adfa9b8ae

difftreelog

source

crates/jrsonnet-evaluator/src/import.rs3.6 KiBsourcehistory
1use crate::create_error_result;2use crate::{3	create_error,4	error::{Error, Result},5};6use fs::File;7use std::fs;8use std::io::Read;9use std::{any::Any, cell::RefCell, collections::HashMap, path::PathBuf, rc::Rc};1011/// Implements file resolution logic for `import` and `importStr`12pub trait ImportResolver {13	/// Resolve real file path, i.e14	/// `(/home/user/manifests, b.libsonnet)` can resolve to both `/home/user/manifests/b.libsonnet` and to `/home/user/vendor/b.libsonnet`15	/// (Where vendor is a library path)16	fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>>;17	/// Reads file from filesystem, should be used only with path received from `resolve_file`18	fn load_file_contents(&self, resolved: &PathBuf) -> Result<Rc<str>>;19	/// # Safety20	///21	/// For use in bindings, do not try to use it elsewhere22	/// Implementations, which are not intended to be23	/// used in bindings, should panic in this method24	unsafe fn as_any(&self) -> &dyn Any;25}2627/// Dummy resolver, can't resolve/load any file28pub struct DummyImportResolver;29impl ImportResolver for DummyImportResolver {30	fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>> {31		create_error_result(Error::ImportNotSupported(from.clone(), path.clone()))32	}33	fn load_file_contents(&self, _resolved: &PathBuf) -> Result<Rc<str>> {34		// Can be only caused by library direct consumer, not by supplied jsonnet35		panic!("dummy resolver can't load any file")36	}37	unsafe fn as_any(&self) -> &dyn Any {38		panic!("this resolver can't be used as any")39	}40}41impl Default for Box<dyn ImportResolver> {42	fn default() -> Self {43		Box::new(DummyImportResolver)44	}45}4647/// File resolver, can load file from both FS and library paths48#[derive(Default)]49pub struct FileImportResolver {50	/// Library directories to search for file51	/// In original jsonnet referred as jpath52	pub library_paths: Vec<PathBuf>,53}54impl ImportResolver for FileImportResolver {55	fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>> {56		let mut new_path = from.clone();57		new_path.push(path);58		if new_path.exists() {59			Ok(Rc::new(new_path))60		} else {61			for library_path in self.library_paths.iter() {62				let mut cloned = library_path.clone();63				cloned.push(path);64				if cloned.exists() {65					return Ok(Rc::new(cloned));66				}67			}68			create_error_result(Error::ImportFileNotFound(from.clone(), path.clone()))69		}70	}71	fn load_file_contents(&self, id: &PathBuf) -> Result<Rc<str>> {72		let mut file =73			File::open(id).map_err(|_e| create_error(Error::ResolvedFileNotFound(id.clone())))?;74		let mut out = String::new();75		file.read_to_string(&mut out)76			.map_err(|_e| create_error(Error::ImportBadFileUtf8(id.clone())))?;77		Ok(out.into())78	}79	unsafe fn as_any(&self) -> &dyn Any {80		panic!("this resolver can't be used as any")81	}82}8384type ResolutionData = (PathBuf, PathBuf);8586/// Caches results of underlying resolver implementation87pub struct CachingImportResolver {88	resolution_cache: RefCell<HashMap<ResolutionData, Result<Rc<PathBuf>>>>,89	loading_cache: RefCell<HashMap<PathBuf, Result<Rc<str>>>>,90	inner: Box<dyn ImportResolver>,91}92impl ImportResolver for CachingImportResolver {93	fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>> {94		self.resolution_cache95			.borrow_mut()96			.entry((from.clone(), path.clone()))97			.or_insert_with(|| self.inner.resolve_file(from, path))98			.clone()99	}100	fn load_file_contents(&self, resolved: &PathBuf) -> Result<Rc<str>> {101		self.loading_cache102			.borrow_mut()103			.entry(resolved.clone())104			.or_insert_with(|| self.inner.load_file_contents(resolved))105			.clone()106	}107	unsafe fn as_any(&self) -> &dyn Any {108		panic!("this resolver can't be used as any")109	}110}