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::Acyclic;11use jrsonnet_interner::IBytes;12use jrsonnet_parser::{SourceDirectory, SourceFifo, SourceFile, SourcePath};1314use crate::{15 bail,16 error::{ErrorKind::*, Result},17};181920pub trait ImportResolver: Acyclic + Any {21 22 23 24 25 26 27 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 34 fn resolve(&self, path: &Path) -> Result<SourcePath> {35 bail!(AbsoluteImportNotSupported(path.to_owned()))36 }3738 39 40 41 fn load_file_contents(&self, resolved: &SourcePath) -> Result<Vec<u8>>;42}434445#[derive(Acyclic)]46pub struct DummyImportResolver;47impl ImportResolver for DummyImportResolver {48 fn load_file_contents(&self, _resolved: &SourcePath) -> Result<Vec<u8>> {49 panic!("dummy resolver can't load any file")50 }51}52#[allow(clippy::use_self)]53impl Default for Box<dyn ImportResolver> {54 fn default() -> Self {55 Box::new(DummyImportResolver)56 }57}585960#[derive(Default, Acyclic)]61pub struct FileImportResolver {62 63 64 library_paths: Vec<PathBuf>,65}66impl FileImportResolver {67 pub fn new(library_paths: Vec<PathBuf>) -> Self {68 Self { library_paths }69 }70 71 pub fn add_jpath(&mut self, path: PathBuf) {72 self.library_paths.push(path);73 }74}757677fn check_path(path: &Path) -> Result<Option<SourcePath>> {78 let meta = match fs::metadata(path) {79 Ok(v) => v,80 Err(e) if e.kind() == ErrorKind::NotFound => {81 return Ok(None);82 }83 Err(e) => bail!(ImportIo(e.to_string())),84 };85 let ty = meta.file_type();86 if ty.is_file() {87 return Ok(Some(SourcePath::new(SourceFile::new(88 path.canonicalize().map_err(|e| ImportIo(e.to_string()))?,89 ))));90 }91 let ty = meta.file_type();92 #[cfg(unix)]93 {94 use std::os::unix::fs::FileTypeExt;95 if ty.is_fifo() {96 let file = fs::read(path).map_err(|e| ImportIo(format!("FIFO read failed: {e}")))?;97 return Ok(Some(SourcePath::new(SourceFifo(98 format!("{}", path.display()),99 IBytes::from(file.as_slice()),100 ))));101 }102 }103 104 Err(RuntimeError("special file can't be imported".into()).into())105}106107impl ImportResolver for FileImportResolver {108 fn resolve_from(&self, from: &SourcePath, path: &str) -> Result<SourcePath> {109 let mut direct = if let Some(f) = from.downcast_ref::<SourceFile>() {110 let mut o = f.path().to_owned();111 o.pop();112 o113 } else if let Some(d) = from.downcast_ref::<SourceDirectory>() {114 d.path().to_owned()115 } else if from.is_default() {116 current_dir().map_err(|e| ImportIo(e.to_string()))?117 } else {118 unreachable!("resolver can't return this path")119 };120121 direct.push(path);122 if let Some(direct) = check_path(&direct)? {123 return Ok(direct);124 }125 for library_path in &self.library_paths {126 let mut cloned = library_path.clone();127 cloned.push(path);128 if let Some(cloned) = check_path(&cloned)? {129 return Ok(cloned);130 }131 }132 bail!(ImportFileNotFound(from.clone(), path.to_owned()))133 }134 fn resolve(&self, path: &Path) -> Result<SourcePath> {135 let Some(source) = check_path(path)? else {136 bail!(AbsoluteImportFileNotFound(path.to_owned()))137 };138 Ok(source)139 }140141 fn load_file_contents(&self, id: &SourcePath) -> Result<Vec<u8>> {142 let path = if let Some(f) = id.downcast_ref::<SourceFile>() {143 f.path()144 } else if id.downcast_ref::<SourceDirectory>().is_some() {145 bail!(ImportIsADirectory(id.clone()))146 } else if let Some(f) = id.downcast_ref::<SourceFifo>() {147 return Ok(f.1.to_vec());148 } else {149 unreachable!("other types are not supported in resolve");150 };151 let mut file = File::open(path).map_err(|_e| ResolvedFileNotFound(id.clone()))?;152 let mut out = Vec::new();153 file.read_to_end(&mut out)154 .map_err(|e| ImportIo(e.to_string()))?;155 Ok(out)156 }157158 fn resolve_from_default(&self, path: &str) -> Result<SourcePath> {159 self.resolve_from(&SourcePath::default(), path)160 }161}