difftreelog
feat importbin
in: master
9 files changed
bindings/jsonnet/src/import.rsdiffbeforeafterboth--- a/bindings/jsonnet/src/import.rs
+++ b/bindings/jsonnet/src/import.rs
@@ -2,7 +2,7 @@
use jrsonnet_evaluator::{
error::{Error::*, Result},
- throw, EvaluationState, IStr, ImportResolver,
+ throw, EvaluationState, ImportResolver,
};
use std::{
any::Any,
@@ -29,8 +29,7 @@
pub struct CallbackImportResolver {
cb: JsonnetImportCallback,
ctx: *mut c_void,
-
- out: RefCell<HashMap<PathBuf, IStr>>,
+ out: RefCell<HashMap<PathBuf, Vec<u8>>>,
}
impl ImportResolver for CallbackImportResolver {
fn resolve_file(&self, from: &Path, path: &Path) -> Result<Rc<Path>> {
@@ -75,9 +74,10 @@
Ok(found_here_buf.into())
}
- fn load_file_contents(&self, resolved: &Path) -> Result<IStr> {
+ fn load_file_contents(&self, resolved: &Path) -> Result<Vec<u8>> {
Ok(self.out.borrow().get(resolved).unwrap().clone())
}
+
unsafe fn as_any(&self) -> &dyn Any {
self
}
@@ -124,12 +124,12 @@
throw!(ImportFileNotFound(from.to_owned(), path.to_owned()))
}
}
- fn load_file_contents(&self, id: &Path) -> Result<IStr> {
+ fn load_file_contents(&self, id: &Path) -> Result<Vec<u8>> {
let mut file = File::open(id).map_err(|_e| ResolvedFileNotFound(id.to_owned()))?;
- let mut out = String::new();
- file.read_to_string(&mut out)
- .map_err(|_e| ImportBadFileUtf8(id.to_owned()))?;
- Ok(out.into())
+ let mut out = Vec::new();
+ file.read_to_end(&mut out)
+ .map_err(|e| ImportIo(e.to_string()))?;
+ Ok(out)
}
unsafe fn as_any(&self) -> &dyn Any {
self
crates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/error.rs
+++ b/crates/jrsonnet-evaluator/src/error.rs
@@ -82,6 +82,8 @@
ResolvedFileNotFound(PathBuf),
#[error("imported file is not valid utf-8: {0:?}")]
ImportBadFileUtf8(PathBuf),
+ #[error("import io error: {0}")]
+ ImportIo(String),
#[error("tried to import {1} from {0}, but imports is not supported")]
ImportNotSupported(PathBuf, PathBuf),
#[error(
crates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/evaluate/mod.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/mod.rs
@@ -700,5 +700,12 @@
import_location.pop();
Val::Str(with_state(|s| s.import_file_str(&import_location, path))?)
}
+ ImportBin(path) => {
+ let tmp = loc.clone().0;
+ let mut import_location = tmp.to_path_buf();
+ import_location.pop();
+ let bytes = with_state(|s| s.import_file_bin(&import_location, path))?;
+ Val::Arr(ArrValue::Bytes(bytes))
+ }
})
}
crates/jrsonnet-evaluator/src/import.rsdiffbeforeafterboth1use crate::{2 error::{Error::*, Result},3 throw,4};5use fs::File;6use jrsonnet_interner::IStr;7use std::fs;8use std::io::Read;9use std::{10 any::Any,11 cell::RefCell,12 collections::HashMap,13 path::{Path, PathBuf},14 rc::Rc,15};1617/// Implements file resolution logic for `import` and `importStr`18pub trait ImportResolver {19 /// Resolves real file path, e.g. `(/home/user/manifests, b.libjsonnet)` can correspond20 /// both to `/home/user/manifests/b.libjsonnet` and to `/home/user/${vendor}/b.libjsonnet`21 /// where `${vendor}` is a library path.22 fn resolve_file(&self, from: &Path, path: &Path) -> Result<Rc<Path>>;2324 /// Reads file from filesystem, should be used only with path received from `resolve_file`25 fn load_file_contents(&self, resolved: &Path) -> Result<IStr>;2627 /// # Safety28 ///29 /// For use only in bindings, should not be used elsewhere.30 /// Implementations which are not intended to be used in bindings31 /// should panic on call to this method.32 unsafe fn as_any(&self) -> &dyn Any;33}3435/// Dummy resolver, can't resolve/load any file36pub struct DummyImportResolver;37impl ImportResolver for DummyImportResolver {38 fn resolve_file(&self, from: &Path, path: &Path) -> Result<Rc<Path>> {39 throw!(ImportNotSupported(from.into(), path.into()))40 }4142 fn load_file_contents(&self, _resolved: &Path) -> Result<IStr> {43 // Can be only caused by library direct consumer, not by supplied jsonnet44 panic!("dummy resolver can't load any file")45 }4647 unsafe fn as_any(&self) -> &dyn Any {48 panic!("`as_any($self)` is not supported by dummy resolver")49 }50}51#[allow(clippy::use_self)]52impl Default for Box<dyn ImportResolver> {53 fn default() -> Self {54 Box::new(DummyImportResolver)55 }56}5758/// File resolver, can load file from both FS and library paths59#[derive(Default)]60pub struct FileImportResolver {61 /// Library directories to search for file.62 /// Referred to as `jpath` in original jsonnet implementation.63 pub library_paths: Vec<PathBuf>,64}65impl ImportResolver for FileImportResolver {66 fn resolve_file(&self, from: &Path, path: &Path) -> Result<Rc<Path>> {67 let mut direct = from.to_path_buf();68 direct.push(path);69 if direct.exists() {70 Ok(direct.into())71 } else {72 for library_path in self.library_paths.iter() {73 let mut cloned = library_path.clone();74 cloned.push(path);75 if cloned.exists() {76 return Ok(cloned.into());77 }78 }79 throw!(ImportFileNotFound(from.to_owned(), path.to_owned()))80 }81 }82 fn load_file_contents(&self, id: &Path) -> Result<IStr> {83 let mut file = File::open(id).map_err(|_e| ResolvedFileNotFound(id.to_owned()))?;84 let mut out = String::new();85 file.read_to_string(&mut out)86 .map_err(|_e| ImportBadFileUtf8(id.to_owned()))?;87 Ok(out.into())88 }89 unsafe fn as_any(&self) -> &dyn Any {90 panic!("this resolver can't be used as any")91 }92}9394type ResolutionData = (PathBuf, PathBuf);9596/// Caches results of the underlying resolver97pub struct CachingImportResolver {98 resolution_cache: RefCell<HashMap<ResolutionData, Result<Rc<Path>>>>,99 loading_cache: RefCell<HashMap<PathBuf, Result<IStr>>>,100 inner: Box<dyn ImportResolver>,101}102impl ImportResolver for CachingImportResolver {103 fn resolve_file(&self, from: &Path, path: &Path) -> Result<Rc<Path>> {104 self.resolution_cache105 .borrow_mut()106 .entry((from.to_owned(), path.to_owned()))107 .or_insert_with(|| self.inner.resolve_file(from, path))108 .clone()109 }110111 fn load_file_contents(&self, resolved: &Path) -> Result<IStr> {112 self.loading_cache113 .borrow_mut()114 .entry(resolved.to_owned())115 .or_insert_with(|| self.inner.load_file_contents(resolved))116 .clone()117 }118 unsafe fn as_any(&self) -> &dyn Any {119 panic!("this resolver can't be used as any")120 }121}crates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/lib.rs
+++ b/crates/jrsonnet-evaluator/src/lib.rs
@@ -119,8 +119,9 @@
breakpoints: Breakpoints,
/// Contains file source codes and evaluation results for imports and pretty-printed stacktraces
- files: HashMap<Rc<Path>, FileData>,
- str_files: HashMap<Rc<Path>, IStr>,
+ files: GcHashMap<Rc<Path>, FileData>,
+ str_files: GcHashMap<Rc<Path>, IStr>,
+ bin_files: GcHashMap<Rc<Path>, Rc<[u8]>>,
}
pub struct FileData {
@@ -276,18 +277,26 @@
return self.evaluate_loaded_file_raw(&file_path);
}
}
- let contents = self.load_file_contents(&file_path)?;
+ let contents = self.load_file_str(&file_path)?;
self.add_file(file_path.clone(), contents)?;
self.evaluate_loaded_file_raw(&file_path)
}
pub(crate) fn import_file_str(&self, from: &Path, path: &Path) -> Result<IStr> {
let path = self.resolve_file(from, path)?;
if !self.data().str_files.contains_key(&path) {
- let file_str = self.load_file_contents(&path)?;
+ let file_str = self.load_file_str(&path)?;
self.data_mut().str_files.insert(path.clone(), file_str);
}
Ok(self.data().str_files.get(&path).cloned().unwrap())
}
+ pub(crate) fn import_file_bin(&self, from: &Path, path: &Path) -> Result<Rc<[u8]>> {
+ let path = self.resolve_file(from, path)?;
+ if !self.data().bin_files.contains_key(&path) {
+ let file_bin = self.load_file_bin(&path)?;
+ self.data_mut().bin_files.insert(path.clone(), file_bin);
+ }
+ Ok(self.data().bin_files.get(&path).cloned().unwrap())
+ }
fn evaluate_loaded_file_raw(&self, name: &Path) -> Result<Val> {
let expr: LocExpr = {
@@ -603,8 +612,11 @@
pub fn resolve_file(&self, from: &Path, path: &Path) -> Result<Rc<Path>> {
self.settings().import_resolver.resolve_file(from, path)
}
- pub fn load_file_contents(&self, path: &Path) -> Result<IStr> {
- self.settings().import_resolver.load_file_contents(path)
+ pub fn load_file_str(&self, path: &Path) -> Result<IStr> {
+ self.settings().import_resolver.load_file_str(path)
+ }
+ pub fn load_file_bin(&self, path: &Path) -> Result<Rc<[u8]>> {
+ self.settings().import_resolver.load_file_bin(path)
}
pub fn import_resolver(&self) -> Ref<dyn ImportResolver> {
@@ -661,7 +673,6 @@
EvaluationState,
};
use gcmodule::{Cc, Trace};
- use jrsonnet_interner::IStr;
use jrsonnet_parser::*;
use std::{
path::{Path, PathBuf},
@@ -1165,19 +1176,23 @@
Ok(())
}
- struct TestImportResolver(IStr);
+ struct TestImportResolver(Vec<u8>);
impl crate::import::ImportResolver for TestImportResolver {
fn resolve_file(&self, _: &Path, _: &Path) -> crate::error::Result<Rc<Path>> {
Ok(PathBuf::from("/test").into())
}
- fn load_file_contents(&self, _: &Path) -> crate::error::Result<IStr> {
+ fn load_file_contents(&self, _: &Path) -> crate::error::Result<Vec<u8>> {
Ok(self.0.clone())
}
unsafe fn as_any(&self) -> &dyn std::any::Any {
panic!()
}
+
+ fn load_file_bin(&self, _resolved: &Path) -> crate::error::Result<Rc<[u8]>> {
+ panic!()
+ }
}
#[test]
crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -186,6 +186,7 @@
#[derive(Debug, Clone, Trace)]
#[force_tracking]
pub enum ArrValue {
+ Bytes(#[skip_trace] Rc<[u8]>),
Lazy(Cc<Vec<LazyVal>>),
Eager(Cc<Vec<Val>>),
Extended(Box<(Self, Self)>),
@@ -197,6 +198,7 @@
pub fn len(&self) -> usize {
match self {
+ Self::Bytes(i) => i.len(),
Self::Lazy(l) => l.len(),
Self::Eager(e) => e.len(),
Self::Extended(v) => v.0.len() + v.1.len(),
@@ -209,6 +211,9 @@
pub fn get(&self, index: usize) -> Result<Option<Val>> {
match self {
+ Self::Bytes(i) => i
+ .get(index)
+ .map_or(Ok(None), |v| Ok(Some(Val::Num(*v as f64)))),
Self::Lazy(vec) => {
if let Some(v) = vec.get(index) {
Ok(Some(v.evaluate()?))
@@ -230,6 +235,9 @@
pub fn get_lazy(&self, index: usize) -> Option<LazyVal> {
match self {
+ Self::Bytes(i) => i
+ .get(index)
+ .map(|b| LazyVal::new_resolved(Val::Num(*b as f64))),
Self::Lazy(vec) => vec.get(index).cloned(),
Self::Eager(vec) => vec.get(index).cloned().map(LazyVal::new_resolved),
Self::Extended(v) => {
@@ -245,6 +253,13 @@
pub fn evaluated(&self) -> Result<Cc<Vec<Val>>> {
Ok(match self {
+ Self::Bytes(i) => {
+ let mut out = Vec::with_capacity(i.len());
+ for v in i.iter() {
+ out.push(Val::Num(*v as f64));
+ }
+ Cc::new(out)
+ }
Self::Lazy(vec) => {
let mut out = Vec::with_capacity(vec.len());
for item in vec.iter() {
@@ -265,6 +280,7 @@
pub fn iter(&self) -> impl DoubleEndedIterator<Item = Result<Val>> + '_ {
(0..self.len()).map(move |idx| match self {
+ Self::Bytes(b) => Ok(Val::Num(b[idx] as f64)),
Self::Lazy(l) => l[idx].evaluate(),
Self::Eager(e) => Ok(e[idx].clone()),
Self::Extended(_) => self.get(idx).map(|e| e.unwrap()),
@@ -273,6 +289,7 @@
pub fn iter_lazy(&self) -> impl DoubleEndedIterator<Item = LazyVal> + '_ {
(0..self.len()).map(move |idx| match self {
+ Self::Bytes(b) => LazyVal::new_resolved(Val::Num(b[idx] as f64)),
Self::Lazy(l) => l[idx].clone(),
Self::Eager(e) => LazyVal::new_resolved(e[idx].clone()),
Self::Extended(_) => self.get_lazy(idx).unwrap(),
@@ -281,6 +298,11 @@
pub fn reversed(self) -> Self {
match self {
+ Self::Bytes(b) => {
+ let mut out = b.to_vec();
+ out.reverse();
+ Self::Bytes(out.into())
+ }
Self::Lazy(vec) => {
let mut out = (&vec as &Vec<_>).clone();
out.reverse();
crates/jrsonnet-interner/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-interner/src/lib.rs
+++ b/crates/jrsonnet-interner/src/lib.rs
@@ -4,10 +4,12 @@
use std::{
borrow::Cow,
cell::RefCell,
+ convert::TryFrom,
fmt::{self, Display},
hash::{BuildHasherDefault, Hash, Hasher},
ops::Deref,
rc::Rc,
+ str::Utf8Error,
};
#[derive(Clone, PartialOrd, Ord, Eq)]
@@ -85,6 +87,15 @@
}
}
+impl TryFrom<&[u8]> for IStr {
+ type Error = Utf8Error;
+
+ fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
+ let str = std::str::from_utf8(value)?;
+ Ok(str.into())
+ }
+}
+
impl From<String> for IStr {
fn from(str: String) -> Self {
(&str as &str).into()
crates/jrsonnet-parser/src/expr.rsdiffbeforeafterboth--- a/crates/jrsonnet-parser/src/expr.rs
+++ b/crates/jrsonnet-parser/src/expr.rs
@@ -306,6 +306,8 @@
Import(PathBuf),
/// importStr "file.txt"
ImportStr(PathBuf),
+ /// importBin "file.txt"
+ ImportBin(PathBuf),
/// error "I'm broken"
ErrorStmt(LocExpr),
/// a(b, c)
crates/jrsonnet-parser/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-parser/src/lib.rs
+++ b/crates/jrsonnet-parser/src/lib.rs
@@ -53,7 +53,7 @@
rule number() -> f64 = quiet!{a:$(uint_str() ("." uint_str())? (['e'|'E'] (s:['+'|'-'])? uint_str())?) {? a.parse().map_err(|_| "<number>") }} / expected!("<number>")
/// Reserved word followed by any non-alphanumberic
- rule reserved() = ("assert" / "else" / "error" / "false" / "for" / "function" / "if" / "import" / "importstr" / "in" / "local" / "null" / "tailstrict" / "then" / "self" / "super" / "true") end_of_ident()
+ rule reserved() = ("assert" / "else" / "error" / "false" / "for" / "function" / "if" / "import" / "importstr" / "importbin" / "in" / "local" / "null" / "tailstrict" / "then" / "self" / "super" / "true") end_of_ident()
rule id() = quiet!{ !reserved() alpha() (alpha() / digit())*} / expected!("<identifier>")
rule keyword(id: &'static str) -> ()
@@ -218,6 +218,7 @@
/ array_comp_expr(s)
/ keyword("importstr") _ path:string() {Expr::ImportStr(PathBuf::from(path))}
+ / keyword("importbin") _ path:string() {Expr::ImportBin(PathBuf::from(path))}
/ keyword("import") _ path:string() {Expr::Import(PathBuf::from(path))}
/ var_expr(s)