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};181920pub trait ImportResolver: Trace {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>>;4243 44 45 fn as_any(&self) -> &dyn Any;46 fn as_any_mut(&mut self) -> &mut dyn Any;47}484950#[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}707172#[derive(Default, Trace)]73pub struct FileImportResolver {74 75 76 library_paths: Vec<PathBuf>,77}78impl FileImportResolver {79 pub fn new(library_paths: Vec<PathBuf>) -> Self {80 Self { library_paths }81 }82 83 pub fn add_jpath(&mut self, path: PathBuf) {84 self.library_paths.push(path);85 }86}878889fn 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 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}