git.delta.rocks / jrsonnet / refs/commits / 74199ce77317

difftreelog

Merge remote-tracking branch 'origin/feature/importbin' into gcmodule

Yaroslav Bolyukin2022-04-20parents: #4f4be44 #be790e9.patch.diff
in: master

11 files changed

modifiedbindings/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
modifiedcrates/jrsonnet-evaluator/src/builtin/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/builtin/mod.rs
+++ b/crates/jrsonnet-evaluator/src/builtin/mod.rs
@@ -1,5 +1,5 @@
 use crate::function::{CallLocation, StaticBuiltin};
-use crate::typed::{Any, PositiveF64, VecVal, M1};
+use crate::typed::{Any, Bytes, PositiveF64, VecVal, M1};
 use crate::{
 	builtin::manifest::{manifest_yaml_ex, ManifestYamlOptions},
 	equals,
@@ -447,17 +447,15 @@
 }
 
 #[jrsonnet_macros::builtin]
-fn builtin_encode_utf8(str: IStr) -> Result<VecVal> {
-	Ok(VecVal(
-		str.bytes()
-			.map(|b| Val::Num(b as f64))
-			.collect::<Vec<Val>>(),
-	))
+fn builtin_encode_utf8(str: IStr) -> Result<Bytes> {
+	Ok(Bytes(str.bytes().map(|b| b).collect::<Vec<u8>>().into()))
 }
 
 #[jrsonnet_macros::builtin]
-fn builtin_decode_utf8(arr: Vec<u8>) -> Result<String> {
-	Ok(String::from_utf8(arr).map_err(|_| RuntimeError("bad utf8".into()))?)
+fn builtin_decode_utf8(arr: Bytes) -> Result<IStr> {
+	Ok(std::str::from_utf8(&arr.0)
+		.map_err(|_| RuntimeError("bad utf8".into()))?
+		.into())
 }
 
 #[jrsonnet_macros::builtin]
@@ -483,17 +481,21 @@
 }
 
 #[jrsonnet_macros::builtin]
-fn builtin_base64(input: Either![Vec<u8>, IStr]) -> Result<String> {
+fn builtin_base64(input: Either![Bytes, IStr]) -> Result<String> {
 	use Either2::*;
 	Ok(match input {
-		A(a) => base64::encode(a),
+		A(a) => base64::encode(a.0),
 		B(l) => base64::encode(l.bytes().collect::<Vec<_>>()),
 	})
 }
 
 #[jrsonnet_macros::builtin]
-fn builtin_base64_decode_bytes(input: IStr) -> Result<Vec<u8>> {
-	Ok(base64::decode(&input.as_bytes()).map_err(|_| RuntimeError("bad base64".into()))?)
+fn builtin_base64_decode_bytes(input: IStr) -> Result<Bytes> {
+	Ok(Bytes(
+		base64::decode(&input.as_bytes())
+			.map_err(|_| RuntimeError("bad base64".into()))?
+			.into(),
+	))
 }
 
 #[jrsonnet_macros::builtin]
modifiedcrates/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(
modifiedcrates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/evaluate/mod.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/mod.rs
@@ -701,5 +701,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))
+		}
 	})
 }
modifiedcrates/jrsonnet-evaluator/src/import.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/import.rs
+++ b/crates/jrsonnet-evaluator/src/import.rs
@@ -5,14 +5,12 @@
 use fs::File;
 use jrsonnet_interner::IStr;
 use std::fs;
-use std::io::Read;
 use std::{
 	any::Any,
-	cell::RefCell,
-	collections::HashMap,
 	path::{Path, PathBuf},
 	rc::Rc,
 };
+use std::{convert::TryFrom, io::Read};
 
 /// Implements file resolution logic for `import` and `importStr`
 pub trait ImportResolver {
@@ -21,9 +19,19 @@
 	/// where `${vendor}` is a library path.
 	fn resolve_file(&self, from: &Path, path: &Path) -> Result<Rc<Path>>;
 
+	fn load_file_contents(&self, resolved: &Path) -> Result<Vec<u8>>;
+
 	/// Reads file from filesystem, should be used only with path received from `resolve_file`
-	fn load_file_contents(&self, resolved: &Path) -> Result<IStr>;
+	fn load_file_str(&self, resolved: &Path) -> Result<IStr> {
+		Ok(IStr::try_from(&self.load_file_contents(resolved)? as &[u8])
+			.map_err(|_| ImportBadFileUtf8(resolved.to_path_buf()))?)
+	}
 
+	/// Reads file from filesystem, should be used only with path received from `resolve_file`
+	fn load_file_bin(&self, resolved: &Path) -> Result<Rc<[u8]>> {
+		Ok(self.load_file_contents(resolved)?.into())
+	}
+
 	/// # Safety
 	///
 	/// For use only in bindings, should not be used elsewhere.
@@ -39,8 +47,7 @@
 		throw!(ImportNotSupported(from.into(), path.into()))
 	}
 
-	fn load_file_contents(&self, _resolved: &Path) -> Result<IStr> {
-		// Can be only caused by library direct consumer, not by supplied jsonnet
+	fn load_file_contents(&self, _resolved: &Path) -> Result<Vec<u8>> {
 		panic!("dummy resolver can't load any file")
 	}
 
@@ -79,41 +86,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())
-	}
-	unsafe fn as_any(&self) -> &dyn Any {
-		panic!("this resolver can't be used as any")
-	}
-}
-
-type ResolutionData = (PathBuf, PathBuf);
-
-/// Caches results of the underlying resolver
-pub struct CachingImportResolver {
-	resolution_cache: RefCell<HashMap<ResolutionData, Result<Rc<Path>>>>,
-	loading_cache: RefCell<HashMap<PathBuf, Result<IStr>>>,
-	inner: Box<dyn ImportResolver>,
-}
-impl ImportResolver for CachingImportResolver {
-	fn resolve_file(&self, from: &Path, path: &Path) -> Result<Rc<Path>> {
-		self.resolution_cache
-			.borrow_mut()
-			.entry((from.to_owned(), path.to_owned()))
-			.or_insert_with(|| self.inner.resolve_file(from, path))
-			.clone()
-	}
-
-	fn load_file_contents(&self, resolved: &Path) -> Result<IStr> {
-		self.loading_cache
-			.borrow_mut()
-			.entry(resolved.to_owned())
-			.or_insert_with(|| self.inner.load_file_contents(resolved))
-			.clone()
+		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 {
 		panic!("this resolver can't be used as any")
modifiedcrates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/lib.rs
+++ b/crates/jrsonnet-evaluator/src/lib.rs
@@ -118,8 +118,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 {
@@ -280,18 +281,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 = {
@@ -607,8 +616,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> {
@@ -687,7 +699,6 @@
 		primitive_equals, EvaluationState,
 	};
 	use gcmodule::{Cc, Trace};
-	use jrsonnet_interner::IStr;
 	use jrsonnet_parser::*;
 	use std::{
 		path::{Path, PathBuf},
@@ -1204,19 +1215,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]
modifiedcrates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth
before · crates/jrsonnet-evaluator/src/typed/conversions.rs
1use std::convert::{TryFrom, TryInto};23use gcmodule::Cc;4use jrsonnet_interner::IStr;5pub use jrsonnet_macros::Typed;6use jrsonnet_types::{ComplexValType, ValType};78use crate::{9	error::{Error::*, LocError, Result},10	throw,11	typed::CheckType,12	ArrValue, FuncDesc, FuncVal, IndexableVal, ObjValue, ObjValueBuilder, Val,13};1415pub trait TypedObj: Typed {16	fn serialize(self, out: &mut ObjValueBuilder) -> Result<()>;17	fn parse(obj: &ObjValue) -> Result<Self>;18	fn into_object(self) -> Result<ObjValue> {19		let mut builder = ObjValueBuilder::new();20		self.serialize(&mut builder)?;21		Ok(builder.build())22	}23}2425pub trait Typed: TryFrom<Val, Error = LocError> + TryInto<Val, Error = LocError> {26	const TYPE: &'static ComplexValType;27}2829macro_rules! impl_int {30	($($ty:ty)*) => {$(31		impl Typed for $ty {32			const TYPE: &'static ComplexValType =33				&ComplexValType::BoundedNumber(Some(Self::MIN as f64), Some(Self::MAX as f64));34		}35		impl TryFrom<Val> for $ty {36			type Error = LocError;3738			fn try_from(value: Val) -> Result<Self> {39				<Self as Typed>::TYPE.check(&value)?;40				match value {41					Val::Num(n) => {42						if n.trunc() != n {43							throw!(RuntimeError(44								format!(45									"cannot convert number with fractional part to {}",46									stringify!($ty)47								)48								.into()49							))50						}51						Ok(n as Self)52					}53					_ => unreachable!(),54				}55			}56		}57		impl TryFrom<$ty> for Val {58			type Error = LocError;5960			fn try_from(value: $ty) -> Result<Self> {61				Ok(Self::Num(value as f64))62			}63		}64	)*};65}6667impl_int!(i8 u8 i16 u16 i32 u32);6869impl Typed for f64 {70	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Num);71}72impl TryFrom<Val> for f64 {73	type Error = LocError;7475	fn try_from(value: Val) -> Result<Self> {76		<Self as Typed>::TYPE.check(&value)?;77		match value {78			Val::Num(n) => Ok(n),79			_ => unreachable!(),80		}81	}82}83impl TryFrom<f64> for Val {84	type Error = LocError;8586	fn try_from(value: f64) -> Result<Self> {87		Ok(Self::Num(value))88	}89}9091pub struct PositiveF64(pub f64);92impl Typed for PositiveF64 {93	const TYPE: &'static ComplexValType = &ComplexValType::BoundedNumber(Some(0.0), None);94}95impl TryFrom<Val> for PositiveF64 {96	type Error = LocError;9798	fn try_from(value: Val) -> Result<Self> {99		<Self as Typed>::TYPE.check(&value)?;100		match value {101			Val::Num(n) => Ok(Self(n)),102			_ => unreachable!(),103		}104	}105}106impl TryFrom<PositiveF64> for Val {107	type Error = LocError;108109	fn try_from(value: PositiveF64) -> Result<Self> {110		Ok(Self::Num(value.0))111	}112}113114impl Typed for usize {115	// It is possible to store 54 bits of precision in f64, but leaving u32::MAX here for compatibility116	const TYPE: &'static ComplexValType =117		&ComplexValType::BoundedNumber(Some(0.0), Some(4294967295.0));118}119impl TryFrom<Val> for usize {120	type Error = LocError;121122	fn try_from(value: Val) -> Result<Self> {123		<Self as Typed>::TYPE.check(&value)?;124		match value {125			Val::Num(n) => {126				if n.trunc() != n {127					throw!(RuntimeError(128						"cannot convert number with fractional part to usize".into()129					))130				}131				Ok(n as Self)132			}133			_ => unreachable!(),134		}135	}136}137impl TryFrom<usize> for Val {138	type Error = LocError;139140	fn try_from(value: usize) -> Result<Self> {141		if value > u32::MAX as usize {142			throw!(RuntimeError("number is too large".into()))143		}144		Ok(Self::Num(value as f64))145	}146}147148impl Typed for IStr {149	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Str);150}151impl TryFrom<Val> for IStr {152	type Error = LocError;153154	fn try_from(value: Val) -> Result<Self> {155		<Self as Typed>::TYPE.check(&value)?;156		match value {157			Val::Str(s) => Ok(s),158			_ => unreachable!(),159		}160	}161}162impl TryFrom<IStr> for Val {163	type Error = LocError;164165	fn try_from(value: IStr) -> Result<Self> {166		Ok(Self::Str(value))167	}168}169170impl Typed for String {171	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Str);172}173impl TryFrom<Val> for String {174	type Error = LocError;175176	fn try_from(value: Val) -> Result<Self> {177		<Self as Typed>::TYPE.check(&value)?;178		match value {179			Val::Str(s) => Ok(s.to_string()),180			_ => unreachable!(),181		}182	}183}184impl TryFrom<String> for Val {185	type Error = LocError;186187	fn try_from(value: String) -> Result<Self> {188		Ok(Self::Str(value.into()))189	}190}191192impl Typed for char {193	const TYPE: &'static ComplexValType = &ComplexValType::Char;194}195impl TryFrom<Val> for char {196	type Error = LocError;197198	fn try_from(value: Val) -> Result<Self> {199		<Self as Typed>::TYPE.check(&value)?;200		match value {201			Val::Str(s) => Ok(s.chars().next().unwrap()),202			_ => unreachable!(),203		}204	}205}206impl TryFrom<char> for Val {207	type Error = LocError;208209	fn try_from(value: char) -> Result<Self> {210		Ok(Self::Str(value.to_string().into()))211	}212}213214impl<T> Typed for Vec<T>215where216	T: Typed,217	T: TryFrom<Val, Error = LocError>,218	T: TryInto<Val, Error = LocError>,219{220	const TYPE: &'static ComplexValType = &ComplexValType::ArrayRef(T::TYPE);221}222impl<T> TryFrom<Val> for Vec<T>223where224	T: Typed,225	T: TryFrom<Val, Error = LocError>,226	T: TryInto<Val, Error = LocError>,227{228	type Error = LocError;229230	fn try_from(value: Val) -> Result<Self> {231		<Self as Typed>::TYPE.check(&value)?;232		match value {233			Val::Arr(a) => {234				let mut o = Self::with_capacity(a.len());235				for i in a.iter() {236					o.push(T::try_from(i?)?);237				}238				Ok(o)239			}240			_ => unreachable!(),241		}242	}243}244impl<T> TryFrom<Vec<T>> for Val245where246	T: Typed,247	T: TryFrom<Self, Error = LocError>,248	T: TryInto<Self, Error = LocError>,249{250	type Error = LocError;251252	fn try_from(value: Vec<T>) -> Result<Self> {253		let mut o = Vec::with_capacity(value.len());254		for i in value {255			o.push(i.try_into()?);256		}257		Ok(Self::Arr(o.into()))258	}259}260261/// To be used in Vec<Any>262/// Regular Val can't be used here, because it has wrong TryFrom::Error type263#[derive(Clone)]264pub struct Any(pub Val);265266impl Typed for Any {267	const TYPE: &'static ComplexValType = &ComplexValType::Any;268}269impl TryFrom<Val> for Any {270	type Error = LocError;271272	fn try_from(value: Val) -> Result<Self> {273		Ok(Self(value))274	}275}276impl TryFrom<Any> for Val {277	type Error = LocError;278279	fn try_from(value: Any) -> Result<Self> {280		Ok(value.0)281	}282}283284/// Specialization, provides faster TryFrom<VecVal> for Val285pub struct VecVal(pub Vec<Val>);286287impl Typed for VecVal {288	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Arr);289}290impl TryFrom<Val> for VecVal {291	type Error = LocError;292293	fn try_from(value: Val) -> Result<Self> {294		<Self as Typed>::TYPE.check(&value)?;295		match value {296			Val::Arr(a) => Ok(Self(a.evaluated()?.to_vec())),297			_ => unreachable!(),298		}299	}300}301impl TryFrom<VecVal> for Val {302	type Error = LocError;303304	fn try_from(value: VecVal) -> Result<Self> {305		Ok(Self::Arr(value.0.into()))306	}307}308309pub struct M1;310impl Typed for M1 {311	const TYPE: &'static ComplexValType = &ComplexValType::BoundedNumber(Some(-1.0), Some(-1.0));312}313impl TryFrom<Val> for M1 {314	type Error = LocError;315316	fn try_from(value: Val) -> Result<Self> {317		<Self as Typed>::TYPE.check(&value)?;318		Ok(Self)319	}320}321impl TryFrom<M1> for Val {322	type Error = LocError;323324	fn try_from(_: M1) -> Result<Self> {325		Ok(Self::Num(-1.0))326	}327}328329macro_rules! decl_either {330	($($name: ident, $($id: ident)*);*) => {$(331		pub enum $name<$($id),*> {332			$($id($id)),*333		}334		impl<$($id),*> Typed for $name<$($id),*>335		where336			$($id: Typed,)*337		{338			const TYPE: &'static ComplexValType = &ComplexValType::UnionRef(&[$($id::TYPE),*]);339		}340		impl<$($id),*> TryFrom<Val> for $name<$($id),*>341		where342			$($id: Typed,)*343		{344			type Error = LocError;345346			fn try_from(value: Val) -> Result<Self> {347				$(348					if $id::TYPE.check(&value).is_ok() {349						$id::try_from(value).map(Self::$id)350					} else351				)* {352					<Self as Typed>::TYPE.check(&value)?;353					unreachable!()354				}355			}356		}357		impl<$($id),*> TryFrom<$name<$($id),*>> for Val358		where359			$($id: Typed,)*360		{361			type Error = LocError;362			fn try_from(value: $name<$($id),*>) -> Result<Self> {363				match value {$(364					$name::$id(v) => v.try_into()365				),*}366			}367		}368	)*}369}370decl_either!(371	Either1, A;372	Either2, A B;373	Either3, A B C;374	Either4, A B C D;375	Either5, A B C D E;376	Either6, A B C D E F;377	Either7, A B C D E F G378);379#[macro_export]380macro_rules! Either {381	($a:ty) => {Either1<$a>};382	($a:ty, $b:ty) => {Either2<$a, $b>};383	($a:ty, $b:ty, $c:ty) => {Either3<$a, $b, $c>};384	($a:ty, $b:ty, $c:ty, $d:ty) => {Either4<$a, $b, $c, $d>};385	($a:ty, $b:ty, $c:ty, $d:ty, $e:ty) => {Either5<$a, $b, $c, $d, $e>};386	($a:ty, $b:ty, $c:ty, $d:ty, $e:ty, $f:ty) => {Either6<$a, $b, $c, $d, $e, $f>};387	($a:ty, $b:ty, $c:ty, $d:ty, $e:ty, $f:ty, $g:ty) => {Either7<$a, $b, $c, $d, $e, $f, $g>};388}389390pub type MyType = Either![u32, f64, String];391392impl Typed for ArrValue {393	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Arr);394}395impl TryFrom<Val> for ArrValue {396	type Error = LocError;397398	fn try_from(value: Val) -> Result<Self> {399		<Self as Typed>::TYPE.check(&value)?;400		match value {401			Val::Arr(a) => Ok(a),402			_ => unreachable!(),403		}404	}405}406impl TryFrom<ArrValue> for Val {407	type Error = LocError;408409	fn try_from(value: ArrValue) -> Result<Self> {410		Ok(Self::Arr(value))411	}412}413414impl Typed for FuncVal {415	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);416}417impl TryFrom<Val> for FuncVal {418	type Error = LocError;419420	fn try_from(value: Val) -> Result<Self> {421		<Self as Typed>::TYPE.check(&value)?;422		match value {423			Val::Func(a) => Ok(a),424			_ => unreachable!(),425		}426	}427}428impl TryFrom<FuncVal> for Val {429	type Error = LocError;430431	fn try_from(value: FuncVal) -> Result<Self> {432		Ok(Self::Func(value))433	}434}435436impl Typed for Cc<FuncDesc> {437	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);438}439impl TryFrom<Val> for Cc<FuncDesc> {440	type Error = LocError;441442	fn try_from(value: Val) -> Result<Self, Self::Error> {443		<Self as Typed>::TYPE.check(&value)?;444		match value {445			Val::Func(FuncVal::Normal(desc)) => Ok(desc),446			Val::Func(_) => throw!(RuntimeError("expected normal function, not builtin".into())),447			_ => unreachable!(),448		}449	}450}451impl TryFrom<Cc<FuncDesc>> for Val {452	type Error = LocError;453454	fn try_from(value: Cc<FuncDesc>) -> Result<Self, Self::Error> {455		Ok(Self::Func(FuncVal::Normal(value)))456	}457}458459impl Typed for ObjValue {460	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Obj);461}462impl TryFrom<Val> for ObjValue {463	type Error = LocError;464465	fn try_from(value: Val) -> Result<Self> {466		<Self as Typed>::TYPE.check(&value)?;467		match value {468			Val::Obj(a) => Ok(a),469			_ => unreachable!(),470		}471	}472}473impl TryFrom<ObjValue> for Val {474	type Error = LocError;475476	fn try_from(value: ObjValue) -> Result<Self> {477		Ok(Self::Obj(value))478	}479}480481impl Typed for bool {482	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Bool);483}484impl TryFrom<Val> for bool {485	type Error = LocError;486487	fn try_from(value: Val) -> Result<Self> {488		<Self as Typed>::TYPE.check(&value)?;489		match value {490			Val::Bool(a) => Ok(a),491			_ => unreachable!(),492		}493	}494}495impl TryFrom<bool> for Val {496	type Error = LocError;497498	fn try_from(value: bool) -> Result<Self> {499		Ok(Self::Bool(value))500	}501}502503impl Typed for IndexableVal {504	const TYPE: &'static ComplexValType = &ComplexValType::UnionRef(&[505		&ComplexValType::Simple(ValType::Arr),506		&ComplexValType::Simple(ValType::Str),507	]);508}509impl TryFrom<Val> for IndexableVal {510	type Error = LocError;511512	fn try_from(value: Val) -> Result<Self> {513		<Self as Typed>::TYPE.check(&value)?;514		value.into_indexable()515	}516}517impl TryFrom<IndexableVal> for Val {518	type Error = LocError;519520	fn try_from(value: IndexableVal) -> Result<Self> {521		match value {522			IndexableVal::Str(s) => Ok(Self::Str(s)),523			IndexableVal::Arr(a) => Ok(Self::Arr(a)),524		}525	}526}527528pub struct Null;529impl Typed for Null {530	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Null);531}532impl TryFrom<Val> for Null {533	type Error = LocError;534535	fn try_from(value: Val) -> Result<Self> {536		<Self as Typed>::TYPE.check(&value)?;537		Ok(Self)538	}539}540impl TryFrom<Null> for Val {541	type Error = LocError;542543	fn try_from(_: Null) -> Result<Self> {544		Ok(Self::Null)545	}546}
after · crates/jrsonnet-evaluator/src/typed/conversions.rs
1use std::{2	convert::{TryFrom, TryInto},3	rc::Rc,4};56use gcmodule::Cc;7use jrsonnet_interner::IStr;8pub use jrsonnet_macros::Typed;9use jrsonnet_types::{ComplexValType, ValType};1011use crate::{12	error::{Error::*, LocError, Result},13	throw,14	typed::CheckType,15	ArrValue, FuncDesc, FuncVal, IndexableVal, ObjValue, ObjValueBuilder, Val,16};1718pub trait TypedObj: Typed {19	fn serialize(self, out: &mut ObjValueBuilder) -> Result<()>;20	fn parse(obj: &ObjValue) -> Result<Self>;21	fn into_object(self) -> Result<ObjValue> {22		let mut builder = ObjValueBuilder::new();23		self.serialize(&mut builder)?;24		Ok(builder.build())25	}26}2728pub trait Typed: TryFrom<Val, Error = LocError> + TryInto<Val, Error = LocError> {29	const TYPE: &'static ComplexValType;30}3132macro_rules! impl_int {33	($($ty:ty)*) => {$(34		impl Typed for $ty {35			const TYPE: &'static ComplexValType =36				&ComplexValType::BoundedNumber(Some(Self::MIN as f64), Some(Self::MAX as f64));37		}38		impl TryFrom<Val> for $ty {39			type Error = LocError;4041			fn try_from(value: Val) -> Result<Self> {42				<Self as Typed>::TYPE.check(&value)?;43				match value {44					Val::Num(n) => {45						if n.trunc() != n {46							throw!(RuntimeError(47								format!(48									"cannot convert number with fractional part to {}",49									stringify!($ty)50								)51								.into()52							))53						}54						Ok(n as Self)55					}56					_ => unreachable!(),57				}58			}59		}60		impl TryFrom<$ty> for Val {61			type Error = LocError;6263			fn try_from(value: $ty) -> Result<Self> {64				Ok(Self::Num(value as f64))65			}66		}67	)*};68}6970impl_int!(i8 u8 i16 u16 i32 u32);7172impl Typed for f64 {73	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Num);74}75impl TryFrom<Val> for f64 {76	type Error = LocError;7778	fn try_from(value: Val) -> Result<Self> {79		<Self as Typed>::TYPE.check(&value)?;80		match value {81			Val::Num(n) => Ok(n),82			_ => unreachable!(),83		}84	}85}86impl TryFrom<f64> for Val {87	type Error = LocError;8889	fn try_from(value: f64) -> Result<Self> {90		Ok(Self::Num(value))91	}92}9394pub struct PositiveF64(pub f64);95impl Typed for PositiveF64 {96	const TYPE: &'static ComplexValType = &ComplexValType::BoundedNumber(Some(0.0), None);97}98impl TryFrom<Val> for PositiveF64 {99	type Error = LocError;100101	fn try_from(value: Val) -> Result<Self> {102		<Self as Typed>::TYPE.check(&value)?;103		match value {104			Val::Num(n) => Ok(Self(n)),105			_ => unreachable!(),106		}107	}108}109impl TryFrom<PositiveF64> for Val {110	type Error = LocError;111112	fn try_from(value: PositiveF64) -> Result<Self> {113		Ok(Self::Num(value.0))114	}115}116117impl Typed for usize {118	// It is possible to store 54 bits of precision in f64, but leaving u32::MAX here for compatibility119	const TYPE: &'static ComplexValType =120		&ComplexValType::BoundedNumber(Some(0.0), Some(4294967295.0));121}122impl TryFrom<Val> for usize {123	type Error = LocError;124125	fn try_from(value: Val) -> Result<Self> {126		<Self as Typed>::TYPE.check(&value)?;127		match value {128			Val::Num(n) => {129				if n.trunc() != n {130					throw!(RuntimeError(131						"cannot convert number with fractional part to usize".into()132					))133				}134				Ok(n as Self)135			}136			_ => unreachable!(),137		}138	}139}140impl TryFrom<usize> for Val {141	type Error = LocError;142143	fn try_from(value: usize) -> Result<Self> {144		if value > u32::MAX as usize {145			throw!(RuntimeError("number is too large".into()))146		}147		Ok(Self::Num(value as f64))148	}149}150151impl Typed for IStr {152	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Str);153}154impl TryFrom<Val> for IStr {155	type Error = LocError;156157	fn try_from(value: Val) -> Result<Self> {158		<Self as Typed>::TYPE.check(&value)?;159		match value {160			Val::Str(s) => Ok(s),161			_ => unreachable!(),162		}163	}164}165impl TryFrom<IStr> for Val {166	type Error = LocError;167168	fn try_from(value: IStr) -> Result<Self> {169		Ok(Self::Str(value))170	}171}172173impl Typed for String {174	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Str);175}176impl TryFrom<Val> for String {177	type Error = LocError;178179	fn try_from(value: Val) -> Result<Self> {180		<Self as Typed>::TYPE.check(&value)?;181		match value {182			Val::Str(s) => Ok(s.to_string()),183			_ => unreachable!(),184		}185	}186}187impl TryFrom<String> for Val {188	type Error = LocError;189190	fn try_from(value: String) -> Result<Self> {191		Ok(Self::Str(value.into()))192	}193}194195impl Typed for char {196	const TYPE: &'static ComplexValType = &ComplexValType::Char;197}198impl TryFrom<Val> for char {199	type Error = LocError;200201	fn try_from(value: Val) -> Result<Self> {202		<Self as Typed>::TYPE.check(&value)?;203		match value {204			Val::Str(s) => Ok(s.chars().next().unwrap()),205			_ => unreachable!(),206		}207	}208}209impl TryFrom<char> for Val {210	type Error = LocError;211212	fn try_from(value: char) -> Result<Self> {213		Ok(Self::Str(value.to_string().into()))214	}215}216217impl<T> Typed for Vec<T>218where219	T: Typed,220	T: TryFrom<Val, Error = LocError>,221	T: TryInto<Val, Error = LocError>,222{223	const TYPE: &'static ComplexValType = &ComplexValType::ArrayRef(T::TYPE);224}225impl<T> TryFrom<Val> for Vec<T>226where227	T: Typed,228	T: TryFrom<Val, Error = LocError>,229	T: TryInto<Val, Error = LocError>,230{231	type Error = LocError;232233	fn try_from(value: Val) -> Result<Self> {234		<Self as Typed>::TYPE.check(&value)?;235		match value {236			Val::Arr(a) => {237				let mut o = Self::with_capacity(a.len());238				for i in a.iter() {239					o.push(T::try_from(i?)?);240				}241				Ok(o)242			}243			_ => unreachable!(),244		}245	}246}247impl<T> TryFrom<Vec<T>> for Val248where249	T: Typed,250	T: TryFrom<Self, Error = LocError>,251	T: TryInto<Self, Error = LocError>,252{253	type Error = LocError;254255	fn try_from(value: Vec<T>) -> Result<Self> {256		let mut o = Vec::with_capacity(value.len());257		for i in value {258			o.push(i.try_into()?);259		}260		Ok(Self::Arr(o.into()))261	}262}263264/// To be used in Vec<Any>265/// Regular Val can't be used here, because it has wrong TryFrom::Error type266#[derive(Clone)]267pub struct Any(pub Val);268269impl Typed for Any {270	const TYPE: &'static ComplexValType = &ComplexValType::Any;271}272impl TryFrom<Val> for Any {273	type Error = LocError;274275	fn try_from(value: Val) -> Result<Self> {276		Ok(Self(value))277	}278}279impl TryFrom<Any> for Val {280	type Error = LocError;281282	fn try_from(value: Any) -> Result<Self> {283		Ok(value.0)284	}285}286287/// Specialization, provides faster TryFrom<VecVal> for Val288pub struct VecVal(pub Vec<Val>);289290impl Typed for VecVal {291	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Arr);292}293impl TryFrom<Val> for VecVal {294	type Error = LocError;295296	fn try_from(value: Val) -> Result<Self> {297		<Self as Typed>::TYPE.check(&value)?;298		match value {299			Val::Arr(a) => Ok(Self(a.evaluated()?.to_vec())),300			_ => unreachable!(),301		}302	}303}304impl TryFrom<VecVal> for Val {305	type Error = LocError;306307	fn try_from(value: VecVal) -> Result<Self> {308		Ok(Self::Arr(value.0.into()))309	}310}311312/// Specialization313pub struct Bytes(pub Rc<[u8]>);314315impl Typed for Bytes {316	const TYPE: &'static ComplexValType =317		&ComplexValType::ArrayRef(&ComplexValType::BoundedNumber(Some(0.0), Some(255.0)));318}319impl TryFrom<Val> for Bytes {320	type Error = LocError;321322	fn try_from(value: Val) -> Result<Self> {323		match value {324			Val::Arr(ArrValue::Bytes(bytes)) => Ok(Self(bytes)),325			_ => {326				<Self as Typed>::TYPE.check(&value)?;327				match value {328					Val::Arr(a) => {329						let mut out = Vec::with_capacity(a.len());330						for e in a.iter() {331							let r = e?;332							out.push(u8::try_from(r)?);333						}334						Ok(Self(out.into()))335					}336					_ => unreachable!(),337				}338			}339		}340	}341}342impl TryFrom<Bytes> for Val {343	type Error = LocError;344345	fn try_from(value: Bytes) -> Result<Self> {346		Ok(Val::Arr(ArrValue::Bytes(value.0)))347	}348}349350pub struct M1;351impl Typed for M1 {352	const TYPE: &'static ComplexValType = &ComplexValType::BoundedNumber(Some(-1.0), Some(-1.0));353}354impl TryFrom<Val> for M1 {355	type Error = LocError;356357	fn try_from(value: Val) -> Result<Self> {358		<Self as Typed>::TYPE.check(&value)?;359		Ok(Self)360	}361}362impl TryFrom<M1> for Val {363	type Error = LocError;364365	fn try_from(_: M1) -> Result<Self> {366		Ok(Self::Num(-1.0))367	}368}369370macro_rules! decl_either {371	($($name: ident, $($id: ident)*);*) => {$(372		pub enum $name<$($id),*> {373			$($id($id)),*374		}375		impl<$($id),*> Typed for $name<$($id),*>376		where377			$($id: Typed,)*378		{379			const TYPE: &'static ComplexValType = &ComplexValType::UnionRef(&[$($id::TYPE),*]);380		}381		impl<$($id),*> TryFrom<Val> for $name<$($id),*>382		where383			$($id: Typed,)*384		{385			type Error = LocError;386387			fn try_from(value: Val) -> Result<Self> {388				$(389					if $id::TYPE.check(&value).is_ok() {390						$id::try_from(value).map(Self::$id)391					} else392				)* {393					<Self as Typed>::TYPE.check(&value)?;394					unreachable!()395				}396			}397		}398		impl<$($id),*> TryFrom<$name<$($id),*>> for Val399		where400			$($id: Typed,)*401		{402			type Error = LocError;403			fn try_from(value: $name<$($id),*>) -> Result<Self> {404				match value {$(405					$name::$id(v) => v.try_into()406				),*}407			}408		}409	)*}410}411decl_either!(412	Either1, A;413	Either2, A B;414	Either3, A B C;415	Either4, A B C D;416	Either5, A B C D E;417	Either6, A B C D E F;418	Either7, A B C D E F G419);420#[macro_export]421macro_rules! Either {422	($a:ty) => {Either1<$a>};423	($a:ty, $b:ty) => {Either2<$a, $b>};424	($a:ty, $b:ty, $c:ty) => {Either3<$a, $b, $c>};425	($a:ty, $b:ty, $c:ty, $d:ty) => {Either4<$a, $b, $c, $d>};426	($a:ty, $b:ty, $c:ty, $d:ty, $e:ty) => {Either5<$a, $b, $c, $d, $e>};427	($a:ty, $b:ty, $c:ty, $d:ty, $e:ty, $f:ty) => {Either6<$a, $b, $c, $d, $e, $f>};428	($a:ty, $b:ty, $c:ty, $d:ty, $e:ty, $f:ty, $g:ty) => {Either7<$a, $b, $c, $d, $e, $f, $g>};429}430431pub type MyType = Either![u32, f64, String];432433impl Typed for ArrValue {434	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Arr);435}436impl TryFrom<Val> for ArrValue {437	type Error = LocError;438439	fn try_from(value: Val) -> Result<Self> {440		<Self as Typed>::TYPE.check(&value)?;441		match value {442			Val::Arr(a) => Ok(a),443			_ => unreachable!(),444		}445	}446}447impl TryFrom<ArrValue> for Val {448	type Error = LocError;449450	fn try_from(value: ArrValue) -> Result<Self> {451		Ok(Self::Arr(value))452	}453}454455impl Typed for FuncVal {456	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);457}458impl TryFrom<Val> for FuncVal {459	type Error = LocError;460461	fn try_from(value: Val) -> Result<Self> {462		<Self as Typed>::TYPE.check(&value)?;463		match value {464			Val::Func(a) => Ok(a),465			_ => unreachable!(),466		}467	}468}469impl TryFrom<FuncVal> for Val {470	type Error = LocError;471472	fn try_from(value: FuncVal) -> Result<Self> {473		Ok(Self::Func(value))474	}475}476477impl Typed for Cc<FuncDesc> {478	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);479}480impl TryFrom<Val> for Cc<FuncDesc> {481	type Error = LocError;482483	fn try_from(value: Val) -> Result<Self, Self::Error> {484		<Self as Typed>::TYPE.check(&value)?;485		match value {486			Val::Func(FuncVal::Normal(desc)) => Ok(desc),487			Val::Func(_) => throw!(RuntimeError("expected normal function, not builtin".into())),488			_ => unreachable!(),489		}490	}491}492impl TryFrom<Cc<FuncDesc>> for Val {493	type Error = LocError;494495	fn try_from(value: Cc<FuncDesc>) -> Result<Self, Self::Error> {496		Ok(Self::Func(FuncVal::Normal(value)))497	}498}499500impl Typed for ObjValue {501	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Obj);502}503impl TryFrom<Val> for ObjValue {504	type Error = LocError;505506	fn try_from(value: Val) -> Result<Self> {507		<Self as Typed>::TYPE.check(&value)?;508		match value {509			Val::Obj(a) => Ok(a),510			_ => unreachable!(),511		}512	}513}514impl TryFrom<ObjValue> for Val {515	type Error = LocError;516517	fn try_from(value: ObjValue) -> Result<Self> {518		Ok(Self::Obj(value))519	}520}521522impl Typed for bool {523	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Bool);524}525impl TryFrom<Val> for bool {526	type Error = LocError;527528	fn try_from(value: Val) -> Result<Self> {529		<Self as Typed>::TYPE.check(&value)?;530		match value {531			Val::Bool(a) => Ok(a),532			_ => unreachable!(),533		}534	}535}536impl TryFrom<bool> for Val {537	type Error = LocError;538539	fn try_from(value: bool) -> Result<Self> {540		Ok(Self::Bool(value))541	}542}543544impl Typed for IndexableVal {545	const TYPE: &'static ComplexValType = &ComplexValType::UnionRef(&[546		&ComplexValType::Simple(ValType::Arr),547		&ComplexValType::Simple(ValType::Str),548	]);549}550impl TryFrom<Val> for IndexableVal {551	type Error = LocError;552553	fn try_from(value: Val) -> Result<Self> {554		<Self as Typed>::TYPE.check(&value)?;555		value.into_indexable()556	}557}558impl TryFrom<IndexableVal> for Val {559	type Error = LocError;560561	fn try_from(value: IndexableVal) -> Result<Self> {562		match value {563			IndexableVal::Str(s) => Ok(Self::Str(s)),564			IndexableVal::Arr(a) => Ok(Self::Arr(a)),565		}566	}567}568569pub struct Null;570impl Typed for Null {571	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Null);572}573impl TryFrom<Val> for Null {574	type Error = LocError;575576	fn try_from(value: Val) -> Result<Self> {577		<Self as Typed>::TYPE.check(&value)?;578		Ok(Self)579	}580}581impl TryFrom<Null> for Val {582	type Error = LocError;583584	fn try_from(_: Null) -> Result<Self> {585		Ok(Self::Null)586	}587}
modifiedcrates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -174,6 +174,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)>),
@@ -185,6 +186,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(),
@@ -197,6 +199,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()?))
@@ -218,6 +223,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) => {
@@ -233,6 +241,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() {
@@ -253,6 +268,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()),
@@ -261,6 +277,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(),
@@ -269,6 +286,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();
modifiedcrates/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()
modifiedcrates/jrsonnet-parser/src/expr.rsdiffbeforeafterboth
--- a/crates/jrsonnet-parser/src/expr.rs
+++ b/crates/jrsonnet-parser/src/expr.rs
@@ -285,6 +285,8 @@
 	Import(PathBuf),
 	/// importStr "file.txt"
 	ImportStr(PathBuf),
+	/// importBin "file.txt"
+	ImportBin(PathBuf),
 	/// error "I'm broken"
 	ErrorStmt(LocExpr),
 	/// a(b, c)
modifiedcrates/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)