git.delta.rocks / jrsonnet / refs/commits / e46de0a3e23e

difftreelog

source

crates/jrsonnet-evaluator/src/import.rs5.3 KiBsourcehistory
1use std::{2	any::Any,3	env::current_dir,4	fs,5	io::{ErrorKind, Read},6	path::{Path, PathBuf},7};89use fs::File;10use jrsonnet_gcmodule::Trace;11use jrsonnet_interner::IBytes;12use jrsonnet_parser::{SourceDirectory, SourceFifo, SourceFile, SourcePath};1314use crate::{15	bail,16	error::{ErrorKind::*, Result},17};1819/// Implements file resolution logic for `import` and `importStr`20pub trait ImportResolver: Trace {21	/// Resolves file path, e.g. `(/home/user/manifests, b.libjsonnet)` can correspond22	/// both to `/home/user/manifests/b.libjsonnet` and to `/home/user/${vendor}/b.libjsonnet`23	/// where `${vendor}` is a library path.24	///25	/// `from` should only be returned from [`ImportResolver::resolve`], or from other defined file, any other value26	/// may result in panic27	fn resolve_from(&self, from: &SourcePath, path: &str) -> Result<SourcePath> {28		bail!(ImportNotSupported(from.clone(), path.into()))29	}30	fn resolve_from_default(&self, path: &str) -> Result<SourcePath> {31		self.resolve_from(&SourcePath::default(), path)32	}33	/// Resolves absolute path, doesn't supports jpath and other fancy things34	fn resolve(&self, path: &Path) -> Result<SourcePath> {35		bail!(AbsoluteImportNotSupported(path.to_owned()))36	}3738	/// Load resolved file39	/// This should only be called with value returned from [`ImportResolver::resolve_file`]/[`ImportResolver::resolve`],40	/// this cannot be resolved using associated type, as evaluator uses object instead of generic for [`ImportResolver`]41	fn load_file_contents(&self, resolved: &SourcePath) -> Result<Vec<u8>>;4243	// For downcasts, will be removed after trait_upcasting_coercion44	// stabilization.45	fn as_any(&self) -> &dyn Any;46	fn as_any_mut(&mut self) -> &mut dyn Any;47}4849/// Dummy resolver, can't resolve/load any file50#[derive(Trace)]51pub struct DummyImportResolver;52impl ImportResolver for DummyImportResolver {53	fn load_file_contents(&self, _resolved: &SourcePath) -> Result<Vec<u8>> {54		panic!("dummy resolver can't load any file")55	}5657	fn as_any(&self) -> &dyn Any {58		self59	}60	fn as_any_mut(&mut self) -> &mut dyn Any {61		self62	}63}64#[allow(clippy::use_self)]65impl Default for Box<dyn ImportResolver> {66	fn default() -> Self {67		Box::new(DummyImportResolver)68	}69}7071/// File resolver, can load file from both FS and library paths72#[derive(Default, Trace)]73pub struct FileImportResolver {74	/// Library directories to search for file.75	/// Referred to as `jpath` in original jsonnet implementation.76	library_paths: Vec<PathBuf>,77}78impl FileImportResolver {79	pub fn new(library_paths: Vec<PathBuf>) -> Self {80		Self { library_paths }81	}82	/// Dynamically add new jpath, used by bindings83	pub fn add_jpath(&mut self, path: PathBuf) {84		self.library_paths.push(path);85	}86}8788/// Create `SourcePath` from path, handling directories/Fifo files (on unix)/etc89fn check_path(path: &Path) -> Result<Option<SourcePath>> {90	let meta = match fs::metadata(path) {91		Ok(v) => v,92		Err(e) if e.kind() == ErrorKind::NotFound => {93			return Ok(None);94		}95		Err(e) => bail!(ImportIo(e.to_string())),96	};97	let ty = meta.file_type();98	if ty.is_file() {99		return Ok(Some(SourcePath::new(SourceFile::new(100			path.canonicalize().map_err(|e| ImportIo(e.to_string()))?,101		))));102	}103	let ty = meta.file_type();104	#[cfg(unix)]105	{106		use std::os::unix::fs::FileTypeExt;107		if ty.is_fifo() {108			let file = fs::read(path).map_err(|e| ImportIo(format!("FIFO read failed: {e}")))?;109			return Ok(Some(SourcePath::new(SourceFifo(110				format!("{}", path.display()),111				IBytes::from(file.as_slice()),112			))));113		}114	}115	// Block device/some other magic thing.116	Err(RuntimeError("special file can't be imported".into()).into())117}118119impl ImportResolver for FileImportResolver {120	fn resolve_from(&self, from: &SourcePath, path: &str) -> Result<SourcePath> {121		let mut direct = if let Some(f) = from.downcast_ref::<SourceFile>() {122			let mut o = f.path().to_owned();123			o.pop();124			o125		} else if let Some(d) = from.downcast_ref::<SourceDirectory>() {126			d.path().to_owned()127		} else if from.is_default() {128			current_dir().map_err(|e| ImportIo(e.to_string()))?129		} else {130			unreachable!("resolver can't return this path")131		};132133		direct.push(path);134		if let Some(direct) = check_path(&direct)? {135			return Ok(direct);136		}137		for library_path in &self.library_paths {138			let mut cloned = library_path.clone();139			cloned.push(path);140			if let Some(cloned) = check_path(&cloned)? {141				return Ok(cloned);142			}143		}144		bail!(ImportFileNotFound(from.clone(), path.to_owned()))145	}146	fn resolve(&self, path: &Path) -> Result<SourcePath> {147		let Some(source) = check_path(path)? else {148			bail!(AbsoluteImportFileNotFound(path.to_owned()))149		};150		Ok(source)151	}152153	fn load_file_contents(&self, id: &SourcePath) -> Result<Vec<u8>> {154		let path = if let Some(f) = id.downcast_ref::<SourceFile>() {155			f.path()156		} else if id.downcast_ref::<SourceDirectory>().is_some() {157			bail!(ImportIsADirectory(id.clone()))158		} else if let Some(f) = id.downcast_ref::<SourceFifo>() {159			return Ok(f.1.to_vec());160		} else {161			unreachable!("other types are not supported in resolve");162		};163		let mut file = File::open(path).map_err(|_e| ResolvedFileNotFound(id.clone()))?;164		let mut out = Vec::new();165		file.read_to_end(&mut out)166			.map_err(|e| ImportIo(e.to_string()))?;167		Ok(out)168	}169170	fn resolve_from_default(&self, path: &str) -> Result<SourcePath> {171		self.resolve_from(&SourcePath::default(), path)172	}173174	fn as_any(&self) -> &dyn Any {175		self176	}177178	fn as_any_mut(&mut self) -> &mut dyn Any {179		self180	}181}