difftreelog
feat importbin
in: master
9 files changed
bindings/jsonnet/src/import.rsdiffbeforeafterboth223use jrsonnet_evaluator::{3use jrsonnet_evaluator::{4 error::{Error::*, Result},4 error::{Error::*, Result},5 throw, EvaluationState, IStr, ImportResolver,5 throw, EvaluationState, ImportResolver,6};6};7use std::{7use std::{8 any::Any,8 any::Any,30 cb: JsonnetImportCallback,30 cb: JsonnetImportCallback,31 ctx: *mut c_void,31 ctx: *mut c_void,3233 out: RefCell<HashMap<PathBuf, IStr>>,32 out: RefCell<HashMap<PathBuf, Vec<u8>>>,34}33}35impl ImportResolver for CallbackImportResolver {34impl ImportResolver for CallbackImportResolver {36 fn resolve_file(&self, from: &Path, path: &Path) -> Result<Rc<Path>> {35 fn resolve_file(&self, from: &Path, path: &Path) -> Result<Rc<Path>> {757476 Ok(found_here_buf.into())75 Ok(found_here_buf.into())77 }76 }78 fn load_file_contents(&self, resolved: &Path) -> Result<IStr> {77 fn load_file_contents(&self, resolved: &Path) -> Result<Vec<u8>> {79 Ok(self.out.borrow().get(resolved).unwrap().clone())78 Ok(self.out.borrow().get(resolved).unwrap().clone())80 }79 }80124 throw!(ImportFileNotFound(from.to_owned(), path.to_owned()))124 throw!(ImportFileNotFound(from.to_owned(), path.to_owned()))125 }125 }126 }126 }127 fn load_file_contents(&self, id: &Path) -> Result<IStr> {127 fn load_file_contents(&self, id: &Path) -> Result<Vec<u8>> {128 let mut file = File::open(id).map_err(|_e| ResolvedFileNotFound(id.to_owned()))?;128 let mut file = File::open(id).map_err(|_e| ResolvedFileNotFound(id.to_owned()))?;129 let mut out = String::new();129 let mut out = Vec::new();130 file.read_to_string(&mut out)130 file.read_to_end(&mut out)131 .map_err(|_e| ImportBadFileUtf8(id.to_owned()))?;131 .map_err(|e| ImportIo(e.to_string()))?;132 Ok(out.into())132 Ok(out)133 }133 }134 unsafe fn as_any(&self) -> &dyn Any {134 unsafe fn as_any(&self) -> &dyn Any {135 self135 selfcrates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth82 ResolvedFileNotFound(PathBuf),82 ResolvedFileNotFound(PathBuf),83 #[error("imported file is not valid utf-8: {0:?}")]83 #[error("imported file is not valid utf-8: {0:?}")]84 ImportBadFileUtf8(PathBuf),84 ImportBadFileUtf8(PathBuf),85 #[error("import io error: {0}")]86 ImportIo(String),85 #[error("tried to import {1} from {0}, but imports is not supported")]87 #[error("tried to import {1} from {0}, but imports is not supported")]86 ImportNotSupported(PathBuf, PathBuf),88 ImportNotSupported(PathBuf, PathBuf),87 #[error(89 #[error(crates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth700 import_location.pop();700 import_location.pop();701 Val::Str(with_state(|s| s.import_file_str(&import_location, path))?)701 Val::Str(with_state(|s| s.import_file_str(&import_location, path))?)702 }702 }703 ImportBin(path) => {704 let tmp = loc.clone().0;705 let mut import_location = tmp.to_path_buf();706 import_location.pop();707 let bytes = with_state(|s| s.import_file_bin(&import_location, path))?;708 Val::Arr(ArrValue::Bytes(bytes))709 }703 })710 })704}711}705712crates/jrsonnet-evaluator/src/import.rsdiffbeforeafterboth5use fs::File;5use fs::File;6use jrsonnet_interner::IStr;6use jrsonnet_interner::IStr;7use std::fs;7use std::fs;8use std::io::Read;9use std::{8use std::{10 any::Any,9 any::Any,11 cell::RefCell,12 collections::HashMap,13 path::{Path, PathBuf},10 path::{Path, PathBuf},14 rc::Rc,11 rc::Rc,15};12};13use std::{convert::TryFrom, io::Read};161417/// Implements file resolution logic for `import` and `importStr`15/// Implements file resolution logic for `import` and `importStr`18pub trait ImportResolver {16pub trait ImportResolver {21 /// where `${vendor}` is a library path.19 /// where `${vendor}` is a library path.22 fn resolve_file(&self, from: &Path, path: &Path) -> Result<Rc<Path>>;20 fn resolve_file(&self, from: &Path, path: &Path) -> Result<Rc<Path>>;2122 fn load_file_contents(&self, resolved: &Path) -> Result<Vec<u8>>;232324 /// Reads file from filesystem, should be used only with path received from `resolve_file`24 /// Reads file from filesystem, should be used only with path received from `resolve_file`25 fn load_file_contents(&self, resolved: &Path) -> Result<IStr>;25 fn load_file_str(&self, resolved: &Path) -> Result<IStr> {26 Ok(IStr::try_from(&self.load_file_contents(resolved)? as &[u8])27 .map_err(|_| ImportBadFileUtf8(resolved.to_path_buf()))?)28 }2930 /// Reads file from filesystem, should be used only with path received from `resolve_file`31 fn load_file_bin(&self, resolved: &Path) -> Result<Rc<[u8]>> {32 Ok(self.load_file_contents(resolved)?.into())33 }263427 /// # Safety35 /// # Safety28 ///36 ///39 throw!(ImportNotSupported(from.into(), path.into()))47 throw!(ImportNotSupported(from.into(), path.into()))40 }48 }414942 fn load_file_contents(&self, _resolved: &Path) -> Result<IStr> {50 fn load_file_contents(&self, _resolved: &Path) -> Result<Vec<u8>> {43 // Can be only caused by library direct consumer, not by supplied jsonnet44 panic!("dummy resolver can't load any file")51 panic!("dummy resolver can't load any file")45 }52 }465379 throw!(ImportFileNotFound(from.to_owned(), path.to_owned()))86 throw!(ImportFileNotFound(from.to_owned(), path.to_owned()))80 }87 }81 }88 }82 fn load_file_contents(&self, id: &Path) -> Result<IStr> {89 fn load_file_contents(&self, id: &Path) -> Result<Vec<u8>> {83 let mut file = File::open(id).map_err(|_e| ResolvedFileNotFound(id.to_owned()))?;90 let mut file = File::open(id).map_err(|_e| ResolvedFileNotFound(id.to_owned()))?;84 let mut out = String::new();91 let mut out = Vec::new();85 file.read_to_string(&mut out)92 file.read_to_end(&mut out)86 .map_err(|_e| ImportBadFileUtf8(id.to_owned()))?;93 .map_err(|e| ImportIo(e.to_string()))?;87 Ok(out.into())94 Ok(out)88 }95 }89 unsafe fn as_any(&self) -> &dyn Any {96 unsafe fn as_any(&self) -> &dyn Any {90 panic!("this resolver can't be used as any")97 panic!("this resolver can't be used as any")91 }98 }92}99}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}122100crates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth119119120 breakpoints: Breakpoints,120 breakpoints: Breakpoints,121 /// Contains file source codes and evaluation results for imports and pretty-printed stacktraces121 /// Contains file source codes and evaluation results for imports and pretty-printed stacktraces122 files: HashMap<Rc<Path>, FileData>,122 files: GcHashMap<Rc<Path>, FileData>,123 str_files: HashMap<Rc<Path>, IStr>,123 str_files: GcHashMap<Rc<Path>, IStr>,124 bin_files: GcHashMap<Rc<Path>, Rc<[u8]>>,124}125}125126126pub struct FileData {127pub struct FileData {276 return self.evaluate_loaded_file_raw(&file_path);277 return self.evaluate_loaded_file_raw(&file_path);277 }278 }278 }279 }279 let contents = self.load_file_contents(&file_path)?;280 let contents = self.load_file_str(&file_path)?;280 self.add_file(file_path.clone(), contents)?;281 self.add_file(file_path.clone(), contents)?;281 self.evaluate_loaded_file_raw(&file_path)282 self.evaluate_loaded_file_raw(&file_path)282 }283 }283 pub(crate) fn import_file_str(&self, from: &Path, path: &Path) -> Result<IStr> {284 pub(crate) fn import_file_str(&self, from: &Path, path: &Path) -> Result<IStr> {284 let path = self.resolve_file(from, path)?;285 let path = self.resolve_file(from, path)?;285 if !self.data().str_files.contains_key(&path) {286 if !self.data().str_files.contains_key(&path) {286 let file_str = self.load_file_contents(&path)?;287 let file_str = self.load_file_str(&path)?;287 self.data_mut().str_files.insert(path.clone(), file_str);288 self.data_mut().str_files.insert(path.clone(), file_str);288 }289 }289 Ok(self.data().str_files.get(&path).cloned().unwrap())290 Ok(self.data().str_files.get(&path).cloned().unwrap())290 }291 }292 pub(crate) fn import_file_bin(&self, from: &Path, path: &Path) -> Result<Rc<[u8]>> {293 let path = self.resolve_file(from, path)?;294 if !self.data().bin_files.contains_key(&path) {295 let file_bin = self.load_file_bin(&path)?;296 self.data_mut().bin_files.insert(path.clone(), file_bin);297 }298 Ok(self.data().bin_files.get(&path).cloned().unwrap())299 }291300292 fn evaluate_loaded_file_raw(&self, name: &Path) -> Result<Val> {301 fn evaluate_loaded_file_raw(&self, name: &Path) -> Result<Val> {293 let expr: LocExpr = {302 let expr: LocExpr = {603 pub fn resolve_file(&self, from: &Path, path: &Path) -> Result<Rc<Path>> {612 pub fn resolve_file(&self, from: &Path, path: &Path) -> Result<Rc<Path>> {604 self.settings().import_resolver.resolve_file(from, path)613 self.settings().import_resolver.resolve_file(from, path)605 }614 }606 pub fn load_file_contents(&self, path: &Path) -> Result<IStr> {615 pub fn load_file_str(&self, path: &Path) -> Result<IStr> {607 self.settings().import_resolver.load_file_contents(path)616 self.settings().import_resolver.load_file_str(path)608 }617 }618 pub fn load_file_bin(&self, path: &Path) -> Result<Rc<[u8]>> {619 self.settings().import_resolver.load_file_bin(path)620 }609621610 pub fn import_resolver(&self) -> Ref<dyn ImportResolver> {622 pub fn import_resolver(&self) -> Ref<dyn ImportResolver> {611 Ref::map(self.settings(), |s| &*s.import_resolver)623 Ref::map(self.settings(), |s| &*s.import_resolver)661 EvaluationState,673 EvaluationState,662 };674 };663 use gcmodule::{Cc, Trace};675 use gcmodule::{Cc, Trace};664 use jrsonnet_interner::IStr;665 use jrsonnet_parser::*;676 use jrsonnet_parser::*;666 use std::{677 use std::{667 path::{Path, PathBuf},678 path::{Path, PathBuf},1165 Ok(())1176 Ok(())1166 }1177 }116711781168 struct TestImportResolver(IStr);1179 struct TestImportResolver(Vec<u8>);1169 impl crate::import::ImportResolver for TestImportResolver {1180 impl crate::import::ImportResolver for TestImportResolver {1170 fn resolve_file(&self, _: &Path, _: &Path) -> crate::error::Result<Rc<Path>> {1181 fn resolve_file(&self, _: &Path, _: &Path) -> crate::error::Result<Rc<Path>> {1171 Ok(PathBuf::from("/test").into())1182 Ok(PathBuf::from("/test").into())1172 }1183 }117311841174 fn load_file_contents(&self, _: &Path) -> crate::error::Result<IStr> {1185 fn load_file_contents(&self, _: &Path) -> crate::error::Result<Vec<u8>> {1175 Ok(self.0.clone())1186 Ok(self.0.clone())1176 }1187 }117711881178 unsafe fn as_any(&self) -> &dyn std::any::Any {1189 unsafe fn as_any(&self) -> &dyn std::any::Any {1179 panic!()1190 panic!()1180 }1191 }11921193 fn load_file_bin(&self, _resolved: &Path) -> crate::error::Result<Rc<[u8]>> {1194 panic!()1195 }1181 }1196 }118211971183 #[test]1198 #[test]crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth186#[derive(Debug, Clone, Trace)]186#[derive(Debug, Clone, Trace)]187#[force_tracking]187#[force_tracking]188pub enum ArrValue {188pub enum ArrValue {189 Bytes(#[skip_trace] Rc<[u8]>),189 Lazy(Cc<Vec<LazyVal>>),190 Lazy(Cc<Vec<LazyVal>>),190 Eager(Cc<Vec<Val>>),191 Eager(Cc<Vec<Val>>),191 Extended(Box<(Self, Self)>),192 Extended(Box<(Self, Self)>),197198198 pub fn len(&self) -> usize {199 pub fn len(&self) -> usize {199 match self {200 match self {201 Self::Bytes(i) => i.len(),200 Self::Lazy(l) => l.len(),202 Self::Lazy(l) => l.len(),201 Self::Eager(e) => e.len(),203 Self::Eager(e) => e.len(),202 Self::Extended(v) => v.0.len() + v.1.len(),204 Self::Extended(v) => v.0.len() + v.1.len(),209211210 pub fn get(&self, index: usize) -> Result<Option<Val>> {212 pub fn get(&self, index: usize) -> Result<Option<Val>> {211 match self {213 match self {214 Self::Bytes(i) => i215 .get(index)216 .map_or(Ok(None), |v| Ok(Some(Val::Num(*v as f64)))),212 Self::Lazy(vec) => {217 Self::Lazy(vec) => {213 if let Some(v) = vec.get(index) {218 if let Some(v) = vec.get(index) {214 Ok(Some(v.evaluate()?))219 Ok(Some(v.evaluate()?))230235231 pub fn get_lazy(&self, index: usize) -> Option<LazyVal> {236 pub fn get_lazy(&self, index: usize) -> Option<LazyVal> {232 match self {237 match self {238 Self::Bytes(i) => i239 .get(index)240 .map(|b| LazyVal::new_resolved(Val::Num(*b as f64))),233 Self::Lazy(vec) => vec.get(index).cloned(),241 Self::Lazy(vec) => vec.get(index).cloned(),234 Self::Eager(vec) => vec.get(index).cloned().map(LazyVal::new_resolved),242 Self::Eager(vec) => vec.get(index).cloned().map(LazyVal::new_resolved),235 Self::Extended(v) => {243 Self::Extended(v) => {245253246 pub fn evaluated(&self) -> Result<Cc<Vec<Val>>> {254 pub fn evaluated(&self) -> Result<Cc<Vec<Val>>> {247 Ok(match self {255 Ok(match self {256 Self::Bytes(i) => {257 let mut out = Vec::with_capacity(i.len());258 for v in i.iter() {259 out.push(Val::Num(*v as f64));260 }261 Cc::new(out)262 }248 Self::Lazy(vec) => {263 Self::Lazy(vec) => {249 let mut out = Vec::with_capacity(vec.len());264 let mut out = Vec::with_capacity(vec.len());250 for item in vec.iter() {265 for item in vec.iter() {265280266 pub fn iter(&self) -> impl DoubleEndedIterator<Item = Result<Val>> + '_ {281 pub fn iter(&self) -> impl DoubleEndedIterator<Item = Result<Val>> + '_ {267 (0..self.len()).map(move |idx| match self {282 (0..self.len()).map(move |idx| match self {283 Self::Bytes(b) => Ok(Val::Num(b[idx] as f64)),268 Self::Lazy(l) => l[idx].evaluate(),284 Self::Lazy(l) => l[idx].evaluate(),269 Self::Eager(e) => Ok(e[idx].clone()),285 Self::Eager(e) => Ok(e[idx].clone()),270 Self::Extended(_) => self.get(idx).map(|e| e.unwrap()),286 Self::Extended(_) => self.get(idx).map(|e| e.unwrap()),273289274 pub fn iter_lazy(&self) -> impl DoubleEndedIterator<Item = LazyVal> + '_ {290 pub fn iter_lazy(&self) -> impl DoubleEndedIterator<Item = LazyVal> + '_ {275 (0..self.len()).map(move |idx| match self {291 (0..self.len()).map(move |idx| match self {292 Self::Bytes(b) => LazyVal::new_resolved(Val::Num(b[idx] as f64)),276 Self::Lazy(l) => l[idx].clone(),293 Self::Lazy(l) => l[idx].clone(),277 Self::Eager(e) => LazyVal::new_resolved(e[idx].clone()),294 Self::Eager(e) => LazyVal::new_resolved(e[idx].clone()),278 Self::Extended(_) => self.get_lazy(idx).unwrap(),295 Self::Extended(_) => self.get_lazy(idx).unwrap(),281298282 pub fn reversed(self) -> Self {299 pub fn reversed(self) -> Self {283 match self {300 match self {301 Self::Bytes(b) => {302 let mut out = b.to_vec();303 out.reverse();304 Self::Bytes(out.into())305 }284 Self::Lazy(vec) => {306 Self::Lazy(vec) => {285 let mut out = (&vec as &Vec<_>).clone();307 let mut out = (&vec as &Vec<_>).clone();286 out.reverse();308 out.reverse();crates/jrsonnet-interner/src/lib.rsdiffbeforeafterboth4use std::{4use std::{5 borrow::Cow,5 borrow::Cow,6 cell::RefCell,6 cell::RefCell,7 convert::TryFrom,7 fmt::{self, Display},8 fmt::{self, Display},8 hash::{BuildHasherDefault, Hash, Hasher},9 hash::{BuildHasherDefault, Hash, Hasher},9 ops::Deref,10 ops::Deref,10 rc::Rc,11 rc::Rc,12 str::Utf8Error,11};13};121413#[derive(Clone, PartialOrd, Ord, Eq)]15#[derive(Clone, PartialOrd, Ord, Eq)]85 }87 }86}88}8990impl TryFrom<&[u8]> for IStr {91 type Error = Utf8Error;9293 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {94 let str = std::str::from_utf8(value)?;95 Ok(str.into())96 }97}879888impl From<String> for IStr {99impl From<String> for IStr {89 fn from(str: String) -> Self {100 fn from(str: String) -> Self {crates/jrsonnet-parser/src/expr.rsdiffbeforeafterboth306 Import(PathBuf),306 Import(PathBuf),307 /// importStr "file.txt"307 /// importStr "file.txt"308 ImportStr(PathBuf),308 ImportStr(PathBuf),309 /// importBin "file.txt"310 ImportBin(PathBuf),309 /// error "I'm broken"311 /// error "I'm broken"310 ErrorStmt(LocExpr),312 ErrorStmt(LocExpr),311 /// a(b, c)313 /// a(b, c)crates/jrsonnet-parser/src/lib.rsdiffbeforeafterboth53 rule number() -> f64 = quiet!{a:$(uint_str() ("." uint_str())? (['e'|'E'] (s:['+'|'-'])? uint_str())?) {? a.parse().map_err(|_| "<number>") }} / expected!("<number>")53 rule number() -> f64 = quiet!{a:$(uint_str() ("." uint_str())? (['e'|'E'] (s:['+'|'-'])? uint_str())?) {? a.parse().map_err(|_| "<number>") }} / expected!("<number>")545455 /// Reserved word followed by any non-alphanumberic55 /// Reserved word followed by any non-alphanumberic56 rule reserved() = ("assert" / "else" / "error" / "false" / "for" / "function" / "if" / "import" / "importstr" / "in" / "local" / "null" / "tailstrict" / "then" / "self" / "super" / "true") end_of_ident()56 rule reserved() = ("assert" / "else" / "error" / "false" / "for" / "function" / "if" / "import" / "importstr" / "importbin" / "in" / "local" / "null" / "tailstrict" / "then" / "self" / "super" / "true") end_of_ident()57 rule id() = quiet!{ !reserved() alpha() (alpha() / digit())*} / expected!("<identifier>")57 rule id() = quiet!{ !reserved() alpha() (alpha() / digit())*} / expected!("<identifier>")585859 rule keyword(id: &'static str) -> ()59 rule keyword(id: &'static str) -> ()218 / array_comp_expr(s)218 / array_comp_expr(s)219219220 / keyword("importstr") _ path:string() {Expr::ImportStr(PathBuf::from(path))}220 / keyword("importstr") _ path:string() {Expr::ImportStr(PathBuf::from(path))}221 / keyword("importbin") _ path:string() {Expr::ImportBin(PathBuf::from(path))}221 / keyword("import") _ path:string() {Expr::Import(PathBuf::from(path))}222 / keyword("import") _ path:string() {Expr::Import(PathBuf::from(path))}222223223 / var_expr(s)224 / var_expr(s)