difftreelog
fix ignore jpath when resolving filename passed to jrsonnet
in: master
4 files changed
cmds/jrsonnet/src/main.rsdiffbeforeafterboth--- a/cmds/jrsonnet/src/main.rs
+++ b/cmds/jrsonnet/src/main.rs
@@ -11,6 +11,7 @@
error::{Error as JrError, ErrorKind},
ResultExt, State, Val,
};
+use jrsonnet_parser::{SourceDefaultIgnoreJpath, SourcePath};
#[cfg(feature = "mimalloc")]
#[global_allocator]
@@ -182,7 +183,7 @@
let input_str = std::str::from_utf8(&input)?;
s.evaluate_snippet("<stdin>".to_owned(), input_str)?
} else {
- s.import(input.as_str())?
+ s.import_from(&SourcePath::new(SourceDefaultIgnoreJpath), input.as_str())?
};
let tla = opts.tla.tla_opts()?;
crates/jrsonnet-evaluator/src/import.rsdiffbeforeafterboth1use std::{2 any::Any,3 borrow::Cow,4 env::current_dir,5 fmt, fs,6 io::{ErrorKind, Read},7 path::{Path, PathBuf},8};910use fs::File;11use jrsonnet_gcmodule::Acyclic;12use jrsonnet_interner::IBytes;13use jrsonnet_parser::{IStr, SourceDirectory, SourceFifo, SourceFile, SourcePath};1415use crate::{16 bail,17 error::{ErrorKind::*, Result},18};19#[derive(Clone, Debug, Acyclic, Eq, Hash, PartialEq)]20pub enum ResolvePathOwned {21 Str(String),22 Path(PathBuf),23}24impl fmt::Display for ResolvePathOwned {25 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {26 match self {27 ResolvePathOwned::Str(s) => write!(f, "{s}"),28 ResolvePathOwned::Path(p) => write!(f, "{}", p.display()),29 }30 }31}32#[derive(Clone, Copy)]33pub enum ResolvePath<'s> {34 Str(&'s str),35 Path(&'s Path),36}37impl ResolvePath<'_> {38 pub fn to_owned(self) -> ResolvePathOwned {39 match self {40 ResolvePath::Str(s) => ResolvePathOwned::Str(s.to_owned()),41 ResolvePath::Path(p) => ResolvePathOwned::Path(p.to_owned()),42 }43 }44}45impl AsRef<Path> for ResolvePath<'_> {46 fn as_ref(&self) -> &Path {47 match self {48 ResolvePath::Str(s) => s.as_ref(),49 ResolvePath::Path(p) => p,50 }51 }52}53pub trait AsPathLike {54 fn as_path(&self) -> ResolvePath<'_>;55}56impl<T> AsPathLike for &T57where58 T: AsPathLike + ?Sized,59{60 fn as_path(&self) -> ResolvePath<'_> {61 (*self).as_path()62 }63}64impl AsPathLike for str {65 fn as_path(&self) -> ResolvePath<'_> {66 ResolvePath::Str(self)67 }68}69impl AsPathLike for IStr {70 fn as_path(&self) -> ResolvePath<'_> {71 ResolvePath::Str(self)72 }73}74impl AsPathLike for Cow<'_, Path> {75 fn as_path(&self) -> ResolvePath<'_> {76 ResolvePath::Path(self.as_ref())77 }78}79impl AsPathLike for Path {80 fn as_path(&self) -> ResolvePath<'_> {81 ResolvePath::Path(self)82 }83}84impl AsPathLike for ResolvePathOwned {85 fn as_path(&self) -> ResolvePath<'_> {86 match self {87 ResolvePathOwned::Str(s) => ResolvePath::Str(s),88 ResolvePathOwned::Path(path_buf) => ResolvePath::Path(path_buf),89 }90 }91}9293/// Implements file resolution logic for `import` and `importStr`94pub trait ImportResolver: Acyclic + Any {95 /// Resolves file path, e.g. `(/home/user/manifests, b.libjsonnet)` can correspond96 /// both to `/home/user/manifests/b.libjsonnet` and to `/home/user/${vendor}/b.libjsonnet`97 /// where `${vendor}` is a library path.98 ///99 /// `from` should only be returned from [`ImportResolver::resolve`], or from other defined file, any other value100 /// may result in panic101 fn resolve_from(&self, from: &SourcePath, path: &dyn AsPathLike) -> Result<SourcePath> {102 bail!(ImportNotSupported(from.clone(), path.as_path().to_owned()))103 }104 fn resolve_from_default(&self, path: &dyn AsPathLike) -> Result<SourcePath> {105 self.resolve_from(&SourcePath::default(), path)106 }107108 /// Load resolved file109 /// This should only be called with value returned from [`ImportResolver::resolve_file`]/[`ImportResolver::resolve`],110 /// this cannot be resolved using associated type, as evaluator uses object instead of generic for [`ImportResolver`]111 fn load_file_contents(&self, resolved: &SourcePath) -> Result<Vec<u8>>;112}113114/// Dummy resolver, can't resolve/load any file115#[derive(Acyclic)]116pub struct DummyImportResolver;117impl ImportResolver for DummyImportResolver {118 fn load_file_contents(&self, _resolved: &SourcePath) -> Result<Vec<u8>> {119 panic!("dummy resolver can't load any file")120 }121}122#[allow(clippy::use_self)]123impl Default for Box<dyn ImportResolver> {124 fn default() -> Self {125 Box::new(DummyImportResolver)126 }127}128129/// File resolver, can load file from both FS and library paths130#[derive(Default, Acyclic)]131pub struct FileImportResolver {132 /// Library directories to search for file.133 /// Referred to as `jpath` in original jsonnet implementation.134 library_paths: Vec<PathBuf>,135}136impl FileImportResolver {137 pub fn new(library_paths: Vec<PathBuf>) -> Self {138 Self { library_paths }139 }140 /// Dynamically add new jpath, used by bindings141 pub fn add_jpath(&mut self, path: PathBuf) {142 self.library_paths.push(path);143 }144}145146/// Create `SourcePath` from path, handling directories/Fifo files (on unix)/etc147fn check_path(path: &Path) -> Result<Option<SourcePath>> {148 let meta = match fs::metadata(path) {149 Ok(v) => v,150 Err(e) if e.kind() == ErrorKind::NotFound => {151 return Ok(None);152 }153 Err(e) => bail!(ImportIo(e.to_string())),154 };155 let ty = meta.file_type();156 if ty.is_file() {157 return Ok(Some(SourcePath::new(SourceFile::new(158 path.canonicalize().map_err(|e| ImportIo(e.to_string()))?,159 ))));160 }161 let ty = meta.file_type();162 #[cfg(unix)]163 {164 use std::os::unix::fs::FileTypeExt;165 if ty.is_fifo() {166 let file = fs::read(path).map_err(|e| ImportIo(format!("FIFO read failed: {e}")))?;167 return Ok(Some(SourcePath::new(SourceFifo(168 format!("{}", path.display()),169 IBytes::from(file.as_slice()),170 ))));171 }172 }173 // Block device/some other magic thing.174 Err(RuntimeError("special file can't be imported".into()).into())175}176177impl ImportResolver for FileImportResolver {178 fn resolve_from(&self, from: &SourcePath, path: &dyn AsPathLike) -> Result<SourcePath> {179 let path = path.as_path();180 let mut direct = if let Some(f) = from.downcast_ref::<SourceFile>() {181 let mut o = f.path().to_owned();182 o.pop();183 o184 } else if let Some(d) = from.downcast_ref::<SourceDirectory>() {185 d.path().to_owned()186 } else if from.is_default() {187 current_dir().map_err(|e| ImportIo(e.to_string()))?188 } else {189 unreachable!("resolver can't return this path")190 };191192 direct.push(path);193 if let Some(direct) = check_path(&direct)? {194 return Ok(direct);195 }196 for library_path in &self.library_paths {197 let mut cloned = library_path.clone();198 cloned.push(path);199 if let Some(cloned) = check_path(&cloned)? {200 return Ok(cloned);201 }202 }203 bail!(ImportFileNotFound(from.clone(), path.to_owned()))204 }205206 fn load_file_contents(&self, id: &SourcePath) -> Result<Vec<u8>> {207 let path = if let Some(f) = id.downcast_ref::<SourceFile>() {208 f.path()209 } else if id.downcast_ref::<SourceDirectory>().is_some() {210 bail!(ImportIsADirectory(id.clone()))211 } else if let Some(f) = id.downcast_ref::<SourceFifo>() {212 return Ok(f.1.to_vec());213 } else {214 unreachable!("other types are not supported in resolve");215 };216 let mut file = File::open(path).map_err(|_e| ResolvedFileNotFound(id.clone()))?;217 let mut out = Vec::new();218 file.read_to_end(&mut out)219 .map_err(|e| ImportIo(e.to_string()))?;220 Ok(out)221 }222223 fn resolve_from_default(&self, path: &dyn AsPathLike) -> Result<SourcePath> {224 self.resolve_from(&SourcePath::default(), path)225 }226}1use std::{2 any::Any,3 borrow::Cow,4 env::current_dir,5 fmt, fs,6 io::{ErrorKind, Read},7 path::{Path, PathBuf},8};910use fs::File;11use jrsonnet_gcmodule::Acyclic;12use jrsonnet_interner::IBytes;13use jrsonnet_parser::{14 IStr, SourceDefaultIgnoreJpath, SourceDirectory, SourceFifo, SourceFile, SourcePath,15};1617use crate::{18 bail,19 error::{ErrorKind::*, Result},20};21#[derive(Clone, Debug, Acyclic, Eq, Hash, PartialEq)]22pub enum ResolvePathOwned {23 Str(String),24 Path(PathBuf),25}26impl fmt::Display for ResolvePathOwned {27 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {28 match self {29 ResolvePathOwned::Str(s) => write!(f, "{s}"),30 ResolvePathOwned::Path(p) => write!(f, "{}", p.display()),31 }32 }33}34#[derive(Clone, Copy)]35pub enum ResolvePath<'s> {36 Str(&'s str),37 Path(&'s Path),38}39impl ResolvePath<'_> {40 pub fn to_owned(self) -> ResolvePathOwned {41 match self {42 ResolvePath::Str(s) => ResolvePathOwned::Str(s.to_owned()),43 ResolvePath::Path(p) => ResolvePathOwned::Path(p.to_owned()),44 }45 }46}47impl AsRef<Path> for ResolvePath<'_> {48 fn as_ref(&self) -> &Path {49 match self {50 ResolvePath::Str(s) => s.as_ref(),51 ResolvePath::Path(p) => p,52 }53 }54}55pub trait AsPathLike {56 fn as_path(&self) -> ResolvePath<'_>;57}58impl<T> AsPathLike for &T59where60 T: AsPathLike + ?Sized,61{62 fn as_path(&self) -> ResolvePath<'_> {63 (*self).as_path()64 }65}66impl AsPathLike for str {67 fn as_path(&self) -> ResolvePath<'_> {68 ResolvePath::Str(self)69 }70}71impl AsPathLike for IStr {72 fn as_path(&self) -> ResolvePath<'_> {73 ResolvePath::Str(self)74 }75}76impl AsPathLike for Cow<'_, Path> {77 fn as_path(&self) -> ResolvePath<'_> {78 ResolvePath::Path(self.as_ref())79 }80}81impl AsPathLike for Path {82 fn as_path(&self) -> ResolvePath<'_> {83 ResolvePath::Path(self)84 }85}86impl AsPathLike for ResolvePathOwned {87 fn as_path(&self) -> ResolvePath<'_> {88 match self {89 ResolvePathOwned::Str(s) => ResolvePath::Str(s),90 ResolvePathOwned::Path(path_buf) => ResolvePath::Path(path_buf),91 }92 }93}9495/// Implements file resolution logic for `import` and `importStr`96pub trait ImportResolver: Acyclic + Any {97 /// Resolves file path, e.g. `(/home/user/manifests, b.libjsonnet)` can correspond98 /// both to `/home/user/manifests/b.libjsonnet` and to `/home/user/${vendor}/b.libjsonnet`99 /// where `${vendor}` is a library path.100 ///101 /// `from` should only be returned from [`ImportResolver::resolve`], or from other defined file, any other value102 /// may result in panic103 fn resolve_from(&self, from: &SourcePath, path: &dyn AsPathLike) -> Result<SourcePath> {104 bail!(ImportNotSupported(from.clone(), path.as_path().to_owned()))105 }106 fn resolve_from_default(&self, path: &dyn AsPathLike) -> Result<SourcePath> {107 self.resolve_from(&SourcePath::default(), path)108 }109110 /// Load resolved file111 /// This should only be called with value returned from [`ImportResolver::resolve_file`]/[`ImportResolver::resolve`],112 /// this cannot be resolved using associated type, as evaluator uses object instead of generic for [`ImportResolver`]113 fn load_file_contents(&self, resolved: &SourcePath) -> Result<Vec<u8>>;114}115116/// Dummy resolver, can't resolve/load any file117#[derive(Acyclic)]118pub struct DummyImportResolver;119impl ImportResolver for DummyImportResolver {120 fn load_file_contents(&self, _resolved: &SourcePath) -> Result<Vec<u8>> {121 panic!("dummy resolver can't load any file")122 }123}124#[allow(clippy::use_self)]125impl Default for Box<dyn ImportResolver> {126 fn default() -> Self {127 Box::new(DummyImportResolver)128 }129}130131/// File resolver, can load file from both FS and library paths132#[derive(Default, Acyclic)]133pub struct FileImportResolver {134 /// Library directories to search for file.135 /// Referred to as `jpath` in original jsonnet implementation.136 library_paths: Vec<PathBuf>,137}138impl FileImportResolver {139 pub fn new(library_paths: Vec<PathBuf>) -> Self {140 Self { library_paths }141 }142 /// Dynamically add new jpath, used by bindings143 pub fn add_jpath(&mut self, path: PathBuf) {144 self.library_paths.push(path);145 }146}147148/// Create `SourcePath` from path, handling directories/Fifo files (on unix)/etc149fn check_path(path: &Path) -> Result<Option<SourcePath>> {150 let meta = match fs::metadata(path) {151 Ok(v) => v,152 Err(e) if e.kind() == ErrorKind::NotFound => {153 return Ok(None);154 }155 Err(e) => bail!(ImportIo(e.to_string())),156 };157 let ty = meta.file_type();158 if ty.is_file() {159 return Ok(Some(SourcePath::new(SourceFile::new(160 path.canonicalize().map_err(|e| ImportIo(e.to_string()))?,161 ))));162 }163 let ty = meta.file_type();164 #[cfg(unix)]165 {166 use std::os::unix::fs::FileTypeExt;167 if ty.is_fifo() {168 let file = fs::read(path).map_err(|e| ImportIo(format!("FIFO read failed: {e}")))?;169 return Ok(Some(SourcePath::new(SourceFifo(170 format!("{}", path.display()),171 IBytes::from(file.as_slice()),172 ))));173 }174 }175 // Block device/some other magic thing.176 Err(RuntimeError("special file can't be imported".into()).into())177}178179impl ImportResolver for FileImportResolver {180 fn resolve_from(&self, from: &SourcePath, path: &dyn AsPathLike) -> Result<SourcePath> {181 let path = path.as_path();182 let mut direct = if let Some(f) = from.downcast_ref::<SourceFile>() {183 let mut o = f.path().to_owned();184 o.pop();185 o186 } else if let Some(d) = from.downcast_ref::<SourceDirectory>() {187 d.path().to_owned()188 } else if from.downcast_ref::<SourceDefaultIgnoreJpath>().is_some() {189 let mut direct = current_dir().map_err(|e| ImportIo(e.to_string()))?;190 direct.push(path);191 if let Some(direct) = check_path(&direct)? {192 return Ok(direct);193 }194 bail!(ImportFileNotFound(from.clone(), path.to_owned()))195 } else if from.is_default() {196 current_dir().map_err(|e| ImportIo(e.to_string()))?197 } else {198 unreachable!("resolver can't return this path")199 };200201 direct.push(path);202 if let Some(direct) = check_path(&direct)? {203 return Ok(direct);204 }205 for library_path in &self.library_paths {206 let mut cloned = library_path.clone();207 cloned.push(path);208 if let Some(cloned) = check_path(&cloned)? {209 return Ok(cloned);210 }211 }212 bail!(ImportFileNotFound(from.clone(), path.to_owned()))213 }214215 fn load_file_contents(&self, id: &SourcePath) -> Result<Vec<u8>> {216 let path = if let Some(f) = id.downcast_ref::<SourceFile>() {217 f.path()218 } else if id.downcast_ref::<SourceDirectory>().is_some() {219 bail!(ImportIsADirectory(id.clone()))220 } else if let Some(f) = id.downcast_ref::<SourceFifo>() {221 return Ok(f.1.to_vec());222 } else {223 unreachable!("other types are not supported in resolve");224 };225 let mut file = File::open(path).map_err(|_e| ResolvedFileNotFound(id.clone()))?;226 let mut out = Vec::new();227 file.read_to_end(&mut out)228 .map_err(|e| ImportIo(e.to_string()))?;229 Ok(out)230 }231232 fn resolve_from_default(&self, path: &dyn AsPathLike) -> Result<SourcePath> {233 self.resolve_from(&SourcePath::default(), path)234 }235}crates/jrsonnet-parser/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-parser/src/lib.rs
+++ b/crates/jrsonnet-parser/src/lib.rs
@@ -12,7 +12,8 @@
mod unescape;
pub use location::CodeLocation;
pub use source::{
- Source, SourceDirectory, SourceFifo, SourceFile, SourcePath, SourcePathT, SourceVirtual,
+ Source, SourceDefaultIgnoreJpath, SourceDirectory, SourceFifo, SourceFile, SourcePath,
+ SourcePathT, SourceVirtual,
};
pub struct ParserSettings {
crates/jrsonnet-parser/src/source.rsdiffbeforeafterboth--- a/crates/jrsonnet-parser/src/source.rs
+++ b/crates/jrsonnet-parser/src/source.rs
@@ -134,6 +134,23 @@
any_ext_impl!(SourcePathT);
}
+#[derive(Acyclic, Hash, PartialEq, Eq, Debug)]
+pub struct SourceDefaultIgnoreJpath;
+impl Display for SourceDefaultIgnoreJpath {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "<default (ignoring jpath)>")
+ }
+}
+impl SourcePathT for SourceDefaultIgnoreJpath {
+ fn is_default(&self) -> bool {
+ true
+ }
+ fn path(&self) -> Option<&Path> {
+ None
+ }
+ any_ext_impl!(SourcePathT);
+}
+
/// Represents path to the file on the disk
/// Directories shouldn't be put here, as resolution for files differs from resolution for directories:
///