git.delta.rocks / jrsonnet / refs/commits / 38dfcbef8382

difftreelog

source

crates/jrsonnet-evaluator/src/import.rs3.5 KiBsourcehistory
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};910/// Implements file resolution logic for `import` and `importStr`11pub trait ImportResolver {12	/// Resolve real file path, i.e13	/// `(/home/user/manifests, b.libsonnet)` can resolve to both `/home/user/manifests/b.libsonnet` and to `/home/user/vendor/b.libsonnet`14	/// (Where vendor is a library path)15	fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>>;16	/// Reads file from filesystem, should be used only with path received from `resolve_file`17	fn load_file_contents(&self, resolved: &PathBuf) -> Result<Rc<str>>;18	/// # Safety19	///20	/// For use in bindings, do not try to use it elsewhere21	/// Implementations, which are not intended to be22	/// used in bindings, should panic in this method23	unsafe fn as_any(&self) -> &dyn Any;24}2526/// Dummy resolver, can't resolve/load any file27pub struct DummyImportResolver;28impl ImportResolver for DummyImportResolver {29	fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>> {30		throw!(ImportNotSupported(from.clone(), path.clone()))31	}32	fn load_file_contents(&self, _resolved: &PathBuf) -> Result<Rc<str>> {33		// Can be only caused by library direct consumer, not by supplied jsonnet34		panic!("dummy resolver can't load any file")35	}36	unsafe fn as_any(&self) -> &dyn Any {37		panic!("this resolver can't be used as any")38	}39}40impl Default for Box<dyn ImportResolver> {41	fn default() -> Self {42		Box::new(DummyImportResolver)43	}44}4546/// File resolver, can load file from both FS and library paths47#[derive(Default)]48pub struct FileImportResolver {49	/// Library directories to search for file50	/// In original jsonnet referred as jpath51	pub library_paths: Vec<PathBuf>,52}53impl ImportResolver for FileImportResolver {54	fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>> {55		let mut new_path = from.clone();56		new_path.push(path);57		if new_path.exists() {58			Ok(Rc::new(new_path))59		} else {60			for library_path in self.library_paths.iter() {61				let mut cloned = library_path.clone();62				cloned.push(path);63				if cloned.exists() {64					return Ok(Rc::new(cloned));65				}66			}67			throw!(ImportFileNotFound(from.clone(), path.clone()))68		}69	}70	fn load_file_contents(&self, id: &PathBuf) -> Result<Rc<str>> {71		let mut file = File::open(id).map_err(|_e| ResolvedFileNotFound(id.clone()))?;72		let mut out = String::new();73		file.read_to_string(&mut out)74			.map_err(|_e| ImportBadFileUtf8(id.clone()))?;75		Ok(out.into())76	}77	unsafe fn as_any(&self) -> &dyn Any {78		panic!("this resolver can't be used as any")79	}80}8182type ResolutionData = (PathBuf, PathBuf);8384/// Caches results of underlying resolver implementation85pub struct CachingImportResolver {86	resolution_cache: RefCell<HashMap<ResolutionData, Result<Rc<PathBuf>>>>,87	loading_cache: RefCell<HashMap<PathBuf, Result<Rc<str>>>>,88	inner: Box<dyn ImportResolver>,89}90impl ImportResolver for CachingImportResolver {91	fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>> {92		self.resolution_cache93			.borrow_mut()94			.entry((from.clone(), path.clone()))95			.or_insert_with(|| self.inner.resolve_file(from, path))96			.clone()97	}98	fn load_file_contents(&self, resolved: &PathBuf) -> Result<Rc<str>> {99		self.loading_cache100			.borrow_mut()101			.entry(resolved.clone())102			.or_insert_with(|| self.inner.load_file_contents(resolved))103			.clone()104	}105	unsafe fn as_any(&self) -> &dyn Any {106		panic!("this resolver can't be used as any")107	}108}