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_parser::{SourceDirectory, SourceFile, SourcePath};1314use crate::{15 error::{16 Error::{self, *},17 Result,18 },19 throw,20};212223pub trait ImportResolver: Trace {24 25 26 27 28 29 30 fn resolve_from(&self, from: &SourcePath, path: &str) -> Result<SourcePath> {31 throw!(ImportNotSupported(from.clone(), path.into()))32 }33 fn resolve_from_default(&self, path: &str) -> Result<SourcePath> {34 self.resolve_from(&SourcePath::default(), path)35 }36 37 fn resolve(&self, path: &Path) -> Result<SourcePath> {38 throw!(AbsoluteImportNotSupported(path.to_owned()))39 }4041 42 43 44 fn load_file_contents(&self, resolved: &SourcePath) -> Result<Vec<u8>>;4546 47 fn as_any(&self) -> &dyn Any;48}495051#[derive(Trace)]52pub struct DummyImportResolver;53impl ImportResolver for DummyImportResolver {54 fn load_file_contents(&self, _resolved: &SourcePath) -> Result<Vec<u8>> {55 panic!("dummy resolver can't load any file")56 }5758 fn as_any(&self) -> &dyn Any {59 self60 }61}62#[allow(clippy::use_self)]63impl Default for Box<dyn ImportResolver> {64 fn default() -> Self {65 Box::new(DummyImportResolver)66 }67}686970#[derive(Default, Trace)]71pub struct FileImportResolver {72 73 74 library_paths: RefCell<Vec<PathBuf>>,75}76impl FileImportResolver {77 pub fn new(jpath: Vec<PathBuf>) -> Self {78 Self {79 library_paths: RefCell::new(jpath),80 }81 }82 83 pub fn add_jpath(&self, path: PathBuf) {84 self.library_paths.borrow_mut().push(path);85 }86}8788impl ImportResolver for FileImportResolver {89 fn resolve_from(&self, from: &SourcePath, path: &str) -> Result<SourcePath> {90 let mut direct = if let Some(f) = from.downcast_ref::<SourceFile>() {91 let mut o = f.path().to_owned();92 o.pop();93 o94 } else if let Some(d) = from.downcast_ref::<SourceDirectory>() {95 d.path().to_owned()96 } else if from.is_default() {97 current_dir().map_err(|e| Error::ImportIo(e.to_string()))?98 } else {99 unreachable!("resolver can't return this path")100 };101 direct.push(path);102 if direct.is_file() {103 Ok(SourcePath::new(SourceFile::new(104 direct.canonicalize().map_err(|e| ImportIo(e.to_string()))?,105 )))106 } else {107 for library_path in self.library_paths.borrow().iter() {108 let mut cloned = library_path.clone();109 cloned.push(path);110 if cloned.exists() {111 return Ok(SourcePath::new(SourceFile::new(112 cloned.canonicalize().map_err(|e| ImportIo(e.to_string()))?,113 )));114 }115 }116 throw!(ImportFileNotFound(from.clone(), path.to_owned()))117 }118 }119 fn resolve(&self, path: &Path) -> Result<SourcePath> {120 let meta = match fs::metadata(path) {121 Ok(v) => v,122 Err(e) if e.kind() == ErrorKind::NotFound => {123 throw!(AbsoluteImportFileNotFound(path.to_owned()))124 }125 Err(e) => throw!(Error::ImportIo(e.to_string())),126 };127 if meta.is_file() {128 Ok(SourcePath::new(SourceFile::new(129 path.canonicalize().map_err(|e| ImportIo(e.to_string()))?,130 )))131 } else if meta.is_dir() {132 Ok(SourcePath::new(SourceDirectory::new(133 path.canonicalize().map_err(|e| ImportIo(e.to_string()))?,134 )))135 } else {136 unreachable!("this can't be a symlink")137 }138 }139140 fn load_file_contents(&self, id: &SourcePath) -> Result<Vec<u8>> {141 let path = if let Some(f) = id.downcast_ref::<SourceFile>() {142 f.path()143 } else if id.downcast_ref::<SourceDirectory>().is_some() || id.is_default() {144 throw!(Error::ImportIsADirectory(id.clone()))145 } else {146 unreachable!("other types are not supported in resolve");147 };148 let mut file = File::open(path).map_err(|_e| ResolvedFileNotFound(id.clone()))?;149 let mut out = Vec::new();150 file.read_to_end(&mut out)151 .map_err(|e| ImportIo(e.to_string()))?;152 Ok(out)153 }154155 fn as_any(&self) -> &dyn Any {156 self157 }158159 fn resolve_from_default(&self, path: &str) -> Result<SourcePath> {160 self.resolve_from(&SourcePath::default(), path)161 }162}