1use std::{2 any::Any,3 cell::RefCell,4 env::current_dir,5 fs,6 io::{ErrorKind, Read},7 path::{Path, PathBuf},8};910use fs::File;11use jrsonnet_gcmodule::Trace;12use jrsonnet_interner::IBytes;13use jrsonnet_parser::{SourceDirectory, SourceFile, SourcePath, SourceFifo};1415use crate::{16 bail,17 error::{ErrorKind::*, Result},18};192021pub trait ImportResolver: Trace {22 23 24 25 26 27 28 fn resolve_from(&self, from: &SourcePath, path: &str) -> Result<SourcePath> {29 bail!(ImportNotSupported(from.clone(), path.into()))30 }31 fn resolve_from_default(&self, path: &str) -> Result<SourcePath> {32 self.resolve_from(&SourcePath::default(), path)33 }34 35 fn resolve(&self, path: &Path) -> Result<SourcePath> {36 bail!(AbsoluteImportNotSupported(path.to_owned()))37 }3839 40 41 42 fn load_file_contents(&self, resolved: &SourcePath) -> Result<Vec<u8>>;4344 45 fn as_any(&self) -> &dyn Any;46}474849#[derive(Trace)]50pub struct DummyImportResolver;51impl ImportResolver for DummyImportResolver {52 fn load_file_contents(&self, _resolved: &SourcePath) -> Result<Vec<u8>> {53 panic!("dummy resolver can't load any file")54 }5556 fn as_any(&self) -> &dyn Any {57 self58 }59}60#[allow(clippy::use_self)]61impl Default for Box<dyn ImportResolver> {62 fn default() -> Self {63 Box::new(DummyImportResolver)64 }65}666768#[derive(Default, Trace)]69pub struct FileImportResolver {70 71 72 library_paths: RefCell<Vec<PathBuf>>,73}74impl FileImportResolver {75 pub fn new(jpath: Vec<PathBuf>) -> Self {76 Self {77 library_paths: RefCell::new(jpath),78 }79 }80 81 pub fn add_jpath(&self, path: PathBuf) {82 self.library_paths.borrow_mut().push(path);83 }84}858687fn check_path(path: &Path) -> Result<Option<SourcePath>> {88 let meta = match fs::metadata(path) {89 Ok(v) => v,90 Err(e) if e.kind() == ErrorKind::NotFound => {91 return Ok(None);92 }93 Err(e) => bail!(ImportIo(e.to_string())),94 };95 let ty = meta.file_type();96 if ty.is_file() {97 return Ok(Some(SourcePath::new(SourceFile::new(98 path.canonicalize().map_err(|e| ImportIo(e.to_string()))?,99 ))));100 }101 let ty = meta.file_type();102 #[cfg(unix)]103 {104 use std::os::unix::fs::FileTypeExt;105 if ty.is_fifo() {106 let file = fs::read(path).map_err(|e| ImportIo(format!("FIFO read failed: {e}")))?;107 return Ok(Some(SourcePath::new(SourceFifo(108 format!("{}", path.display()),109 IBytes::from(file.as_slice()),110 ))));111 }112 }113 114 Err(RuntimeError("special file can't be imported".into()).into())115}116117impl ImportResolver for FileImportResolver {118 fn resolve_from(&self, from: &SourcePath, path: &str) -> Result<SourcePath> {119 let mut direct = if let Some(f) = from.downcast_ref::<SourceFile>() {120 let mut o = f.path().to_owned();121 o.pop();122 o123 } else if let Some(d) = from.downcast_ref::<SourceDirectory>() {124 d.path().to_owned()125 } else if from.is_default() {126 current_dir().map_err(|e| ImportIo(e.to_string()))?127 } else {128 unreachable!("resolver can't return this path")129 };130131 direct.push(path);132 if let Some(direct) = check_path(&direct)? {133 return Ok(direct);134 }135 for library_path in self.library_paths.borrow().iter() {136 let mut cloned = library_path.clone();137 cloned.push(path);138 if let Some(cloned) = check_path(&cloned)? {139 return Ok(cloned);140 }141 }142 bail!(ImportFileNotFound(from.clone(), path.to_owned()))143 }144 fn resolve(&self, path: &Path) -> Result<SourcePath> {145 let Some(source) = check_path(path)? else {146 bail!(AbsoluteImportFileNotFound(path.to_owned()))147 };148 Ok(source)149 }150151 fn load_file_contents(&self, id: &SourcePath) -> Result<Vec<u8>> {152 let path = if let Some(f) = id.downcast_ref::<SourceFile>() {153 f.path()154 } else if id.downcast_ref::<SourceDirectory>().is_some() {155 bail!(ImportIsADirectory(id.clone()))156 } else if let Some(f) = id.downcast_ref::<SourceFifo>() {157 return Ok(f.1.to_vec());158 } else {159 unreachable!("other types are not supported in resolve");160 };161 let mut file = File::open(path).map_err(|_e| ResolvedFileNotFound(id.clone()))?;162 let mut out = Vec::new();163 file.read_to_end(&mut out)164 .map_err(|e| ImportIo(e.to_string()))?;165 Ok(out)166 }167168 fn as_any(&self) -> &dyn Any {169 self170 }171172 fn resolve_from_default(&self, path: &str) -> Result<SourcePath> {173 self.resolve_from(&SourcePath::default(), path)174 }175}