difftreelog
feat library paths
in: master
5 files changed
cmds/jrsonnet/src/main.rsdiffbeforeafterboth--- a/cmds/jrsonnet/src/main.rs
+++ b/cmds/jrsonnet/src/main.rs
@@ -99,6 +99,9 @@
)]
max_trace: usize,
+ #[clap(long, short = "J", about = "Library search dir")]
+ jpath: Vec<PathBuf>,
+
#[clap(
long,
default_value = "3",
@@ -112,10 +115,15 @@
fn main() {
let opts: Opts = Opts::parse();
- let evaluator = jsonnet_evaluator::EvaluationState::new(EvaluationSettings {
- import_resolver: Box::new(|path| String::from_utf8(std::fs::read(path).unwrap()).unwrap()),
- ..Default::default()
- });
+ let evaluator = jsonnet_evaluator::EvaluationState::new(
+ EvaluationSettings {
+ max_stack_trace_size: opts.max_trace,
+ max_stack_frames: opts.max_stack,
+ },
+ Box::new(jsonnet_evaluator::FileImportResolver {
+ library_paths: opts.jpath.clone(),
+ }),
+ );
if !opts.no_stdlib {
evaluator.with_stdlib();
}
crates/jsonnet-evaluator/src/error.rsdiffbeforeafterboth--- a/crates/jsonnet-evaluator/src/error.rs
+++ b/crates/jsonnet-evaluator/src/error.rs
@@ -1,5 +1,6 @@
use crate::ValType;
use jsonnet_parser::LocExpr;
+use std::path::PathBuf;
#[derive(Debug)]
pub enum Error {
@@ -24,6 +25,12 @@
StandaloneSuper,
+ ImportFileNotFound(PathBuf, PathBuf),
+ ResolvedFileNotFound(PathBuf),
+ ImportBadFileUtf8(PathBuf),
+ ImportNotSupported(PathBuf, PathBuf),
+ ImportSyntaxError(jsonnet_parser::ParseError),
+
RuntimeError(String),
StackOverflow,
FractionalIndex,
crates/jsonnet-evaluator/src/evaluate.rsdiffbeforeafterboth--- a/crates/jsonnet-evaluator/src/evaluate.rs
+++ b/crates/jsonnet-evaluator/src/evaluate.rs
@@ -785,24 +785,22 @@
}
}
Import(path) => {
- let mut lib_path = loc
+ let mut import_location = loc
.clone()
.expect("imports can't be used without loc_data")
.0
.clone();
- lib_path.pop();
- lib_path.push(path);
- with_state(|s| s.import_file(&lib_path))?
+ import_location.pop();
+ with_state(|s| s.import_file(&import_location, path))?
}
ImportStr(path) => {
- let mut file_path = loc
+ let mut import_location = loc
.clone()
.expect("imports can't be used without loc_data")
.0
.clone();
- file_path.pop();
- file_path.push(path);
- Val::Str(with_state(|s| s.import_file_str(&file_path))?)
+ import_location.pop();
+ Val::Str(with_state(|s| s.import_file_str(&import_location, path))?)
}
Literal(LiteralType::Super) => return create_error(crate::error::Error::StandaloneSuper),
})
crates/jsonnet-evaluator/src/import.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/jsonnet-evaluator/src/import.rs
@@ -0,0 +1,85 @@
+use crate::create_error;
+use crate::error::{Error, Result};
+use fs::File;
+use std::fs;
+use std::io::Read;
+use std::{cell::RefCell, collections::HashMap, path::PathBuf};
+
+pub trait ImportResolver {
+ fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<PathBuf>;
+ fn load_file_contents(&self, resolved: &PathBuf) -> Result<String>;
+}
+
+pub struct DummyImportResolver;
+impl ImportResolver for DummyImportResolver {
+ fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<PathBuf> {
+ create_error(Error::ImportNotSupported(from.clone(), path.clone()))
+ }
+ fn load_file_contents(&self, _resolved: &PathBuf) -> Result<String> {
+ // Can be only caused by library direct consumer, not by supplied jsonnet
+ panic!("dummy resolver can't load any file")
+ }
+}
+impl Default for Box<dyn ImportResolver> {
+ fn default() -> Self {
+ Box::new(DummyImportResolver)
+ }
+}
+
+pub struct FileImportResolver {
+ pub library_paths: Vec<PathBuf>,
+}
+impl ImportResolver for FileImportResolver {
+ fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<PathBuf> {
+ let mut new_path = from.clone();
+ new_path.push(path);
+ if new_path.exists() {
+ Ok(new_path)
+ } else {
+ for library_path in self.library_paths.iter() {
+ let mut cloned = library_path.clone();
+ cloned.push(path);
+ if cloned.exists() {
+ return Ok(cloned);
+ }
+ }
+ create_error(Error::ImportFileNotFound(from.clone(), path.clone()))
+ }
+ }
+ fn load_file_contents(&self, id: &PathBuf) -> Result<String> {
+ let mut file = File::open(id).map_err(|_e| {
+ create_error::<()>(Error::ResolvedFileNotFound(id.clone()))
+ .err()
+ .unwrap()
+ })?;
+ let mut out = String::new();
+ file.read_to_string(&mut out).map_err(|_e| {
+ create_error::<()>(Error::ImportBadFileUtf8(id.clone()))
+ .err()
+ .unwrap()
+ })?;
+ Ok(out)
+ }
+}
+
+pub struct CachingImportResolver {
+ resolution_cache: RefCell<HashMap<(PathBuf, PathBuf), Result<PathBuf>>>,
+ loading_cache: RefCell<HashMap<PathBuf, Result<String>>>,
+ inner: Box<dyn ImportResolver>,
+}
+impl ImportResolver for CachingImportResolver {
+ fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<PathBuf> {
+ self.resolution_cache
+ .borrow_mut()
+ .entry((from.clone(), path.clone()))
+ .or_insert_with(|| self.inner.resolve_file(from, path))
+ .clone()
+ }
+ fn load_file_contents(&self, resolved: &PathBuf) -> Result<String> {
+ self.loading_cache
+ .borrow_mut()
+ .entry(resolved.clone())
+ .or_insert_with(|| self.inner.load_file_contents(resolved))
+ .clone()
+ }
+}
crates/jsonnet-evaluator/src/lib.rsdiffbeforeafterboth8mod error;8mod error;9mod evaluate;9mod evaluate;10mod function;10mod function;11mod import;11mod map;12mod map;12mod obj;13mod obj;13mod val;14mod val;17pub use error::*;18pub use error::*;18pub use evaluate::*;19pub use evaluate::*;19pub use function::parse_function_call;20pub use function::parse_function_call;21pub use import::*;20use jsonnet_parser::*;22use jsonnet_parser::*;21pub use obj::*;23pub use obj::*;22use std::{cell::RefCell, collections::HashMap, fmt::Debug, path::PathBuf, rc::Rc};24use std::{cell::RefCell, collections::HashMap, fmt::Debug, path::PathBuf, rc::Rc};46pub struct EvaluationSettings {48pub struct EvaluationSettings {47 pub max_stack_frames: usize,49 pub max_stack_frames: usize,48 pub max_stack_trace_size: usize,50 pub max_stack_trace_size: usize,49 pub import_resolver: Box<dyn Fn(&PathBuf) -> String>,50}51}51impl Default for EvaluationSettings {52impl Default for EvaluationSettings {52 fn default() -> Self {53 fn default() -> Self {53 EvaluationSettings {54 EvaluationSettings {54 max_stack_frames: 200,55 max_stack_frames: 200,55 max_stack_trace_size: 20,56 max_stack_trace_size: 20,56 import_resolver: Box::new(|path| {57 panic!("default EvaluationSettings have no support for import resolution, can't import {:?}", path)58 }),59 }57 }60 }58 }61}59}75 ext_vars: RefCell<HashMap<String, Val>>,73 ext_vars: RefCell<HashMap<String, Val>>,767477 settings: EvaluationSettings,75 settings: EvaluationSettings,76 import_resolver: Box<dyn ImportResolver>,78}77}797880thread_local! {79thread_local! {101#[derive(Default, Clone)]100#[derive(Default, Clone)]102pub struct EvaluationState(Rc<EvaluationStateInternals>);101pub struct EvaluationState(Rc<EvaluationStateInternals>);103impl EvaluationState {102impl EvaluationState {104 pub fn new(settings: EvaluationSettings) -> Self {103 pub fn new(settings: EvaluationSettings, import_resolver: Box<dyn ImportResolver>) -> Self {105 EvaluationState(Rc::new(EvaluationStateInternals {104 EvaluationState(Rc::new(EvaluationStateInternals {106 settings,105 settings,106 import_resolver,107 ..Default::default()107 ..Default::default()108 }))108 }))109 }109 }171 }171 }172 Ok(value)172 Ok(value)173 }173 }174 pub(crate) fn import_file(&self, path: &PathBuf) -> Result<Val> {174 pub(crate) fn import_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Val> {175 let file_path = self.0.import_resolver.resolve_file(from, path)?;176 {175 if !self.0.files.borrow().contains_key(path) {177 let files = self.0.files.borrow();178 if files.contains_key(&file_path) {179 return self.evaluate_file(&file_path);180 }181 }176 let file_str = (self.0.settings.import_resolver)(path);182 let contents = self.0.import_resolver.load_file_contents(&file_path)?;177 self.add_file(path.clone(), file_str).unwrap();183 self.add_file(file_path.clone(), contents).map_err(|e| {178 }184 create_error::<()>(Error::ImportSyntaxError(e))185 .err()186 .unwrap()187 })?;179 self.evaluate_file_in_current_state(path)188 self.evaluate_file(&file_path)180 }189 }181 pub(crate) fn import_file_str(&self, path: &PathBuf) -> Result<String> {190 pub(crate) fn import_file_str(&self, from: &PathBuf, path: &PathBuf) -> Result<String> {191 let path = self.0.import_resolver.resolve_file(from, path)?;182 if !self.0.str_files.borrow().contains_key(path) {192 if !self.0.str_files.borrow().contains_key(&path) {183 let file_str = (self.0.settings.import_resolver)(path);193 let file_str = self.0.import_resolver.load_file_contents(&path)?;184 self.0.str_files.borrow_mut().insert(path.clone(), file_str);194 self.0.str_files.borrow_mut().insert(path.clone(), file_str);185 }195 }186 Ok(self.0.str_files.borrow().get(path).cloned().unwrap())196 Ok(self.0.str_files.borrow().get(&path).cloned().unwrap())187 }197 }188198189 pub fn parse_evaluate_raw(&self, code: &str) -> Result<Val> {199 pub fn parse_evaluate_raw(&self, code: &str) -> Result<Val> {