git.delta.rocks / jrsonnet / refs/commits / 4e0dbf427e61

difftreelog

style update rustfmt

Yaroslav Bolyukin2023-07-26parent: #f883d5e.patch.diff
in: master

9 files changed

modifiedcmds/jrsonnet/src/main.rsdiffbeforeafterboth
--- a/cmds/jrsonnet/src/main.rs
+++ b/cmds/jrsonnet/src/main.rs
@@ -208,7 +208,10 @@
 			create_dir_all(dir)?;
 		}
 		let Val::Obj(obj) = val else {
-			throw!("value should be object for --multi manifest, got {}", val.value_type())
+			throw!(
+				"value should be object for --multi manifest, got {}",
+				val.value_type()
+			)
 		};
 		for (field, data) in obj.iter(
 			#[cfg(feature = "exp-preserve-order")]
modifiedcrates/jrsonnet-evaluator/src/arr/spec.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/arr/spec.rs
+++ b/crates/jrsonnet-evaluator/src/arr/spec.rs
@@ -178,7 +178,9 @@
 			ArrayThunk::Waiting(..) => {}
 		};
 
-		let ArrayThunk::Waiting(expr) = replace(&mut self.0.cached.borrow_mut()[index], ArrayThunk::Pending) else {
+		let ArrayThunk::Waiting(expr) =
+			replace(&mut self.0.cached.borrow_mut()[index], ArrayThunk::Pending)
+		else {
 			unreachable!()
 		};
 
@@ -489,7 +491,9 @@
 			ArrayThunk::Waiting(..) => {}
 		};
 
-		let ArrayThunk::Waiting(_) = replace(&mut self.0.cached.borrow_mut()[index], ArrayThunk::Pending) else {
+		let ArrayThunk::Waiting(_) =
+			replace(&mut self.0.cached.borrow_mut()[index], ArrayThunk::Pending)
+		else {
 			unreachable!()
 		};
 
modifiedcrates/jrsonnet-evaluator/src/manifest.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/manifest.rs
+++ b/crates/jrsonnet-evaluator/src/manifest.rs
@@ -276,7 +276,10 @@
 impl ManifestFormat for StringFormat {
 	fn manifest_buf(&self, val: Val, out: &mut String) -> Result<()> {
 		let Val::Str(s) = val else {
-			throw!("output should be string for string manifest format, got {}", val.value_type())
+			throw!(
+				"output should be string for string manifest format, got {}",
+				val.value_type()
+			)
 		};
 		write!(out, "{s}").unwrap();
 		Ok(())
@@ -290,7 +293,10 @@
 impl<I: ManifestFormat> ManifestFormat for YamlStreamFormat<I> {
 	fn manifest_buf(&self, val: Val, out: &mut String) -> Result<()> {
 		let Val::Arr(arr) = val else {
-			throw!("output should be array for yaml stream format, got {}", val.value_type())
+			throw!(
+				"output should be array for yaml stream format, got {}",
+				val.value_type()
+			)
 		};
 		if !arr.is_empty() {
 			for v in arr.iter() {
modifiedcrates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth
before · crates/jrsonnet-evaluator/src/val.rs
1use std::{2	cell::RefCell,3	fmt::{self, Debug, Display},4	mem::replace,5	rc::Rc,6};78use jrsonnet_gcmodule::{Cc, Trace};9use jrsonnet_interner::IStr;10use jrsonnet_types::ValType;1112pub use crate::arr::ArrValue;13use crate::{14	error::{Error, ErrorKind::*},15	function::FuncVal,16	gc::{GcHashMap, TraceBox},17	manifest::{ManifestFormat, ToStringFormat},18	tb, throw,19	typed::BoundedUsize,20	ObjValue, Result, Unbound, WeakObjValue,21};2223pub trait ThunkValue: Trace {24	type Output;25	fn get(self: Box<Self>) -> Result<Self::Output>;26}2728#[derive(Trace)]29enum ThunkInner<T: Trace> {30	Computed(T),31	Errored(Error),32	Waiting(TraceBox<dyn ThunkValue<Output = T>>),33	Pending,34}3536/// Lazily evaluated value37#[allow(clippy::module_name_repetitions)]38#[derive(Clone, Trace)]39pub struct Thunk<T: Trace>(Cc<RefCell<ThunkInner<T>>>);4041impl<T: Trace> Thunk<T> {42	pub fn evaluated(val: T) -> Self {43		Self(Cc::new(RefCell::new(ThunkInner::Computed(val))))44	}45	pub fn new(f: impl ThunkValue<Output = T> + 'static) -> Self {46		Self(Cc::new(RefCell::new(ThunkInner::Waiting(tb!(f)))))47	}48	pub fn errored(e: Error) -> Self {49		Self(Cc::new(RefCell::new(ThunkInner::Errored(e))))50	}51}5253impl<T> Thunk<T>54where55	T: Clone + Trace,56{57	pub fn force(&self) -> Result<()> {58		self.evaluate()?;59		Ok(())60	}6162	/// Evaluate thunk, or return cached value63	///64	/// # Errors65	///66	/// - Lazy value evaluation returned error67	/// - This method was called during inner value evaluation68	pub fn evaluate(&self) -> Result<T> {69		match &*self.0.borrow() {70			ThunkInner::Computed(v) => return Ok(v.clone()),71			ThunkInner::Errored(e) => return Err(e.clone()),72			ThunkInner::Pending => return Err(InfiniteRecursionDetected.into()),73			ThunkInner::Waiting(..) => (),74		};75		let ThunkInner::Waiting(value) = replace(&mut *self.0.borrow_mut(), ThunkInner::Pending) else {76			unreachable!();77		};78		let new_value = match value.0.get() {79			Ok(v) => v,80			Err(e) => {81				*self.0.borrow_mut() = ThunkInner::Errored(e.clone());82				return Err(e);83			}84		};85		*self.0.borrow_mut() = ThunkInner::Computed(new_value.clone());86		Ok(new_value)87	}88}8990type CacheKey = (Option<WeakObjValue>, Option<WeakObjValue>);9192#[derive(Trace, Clone)]93pub struct CachedUnbound<I, T>94where95	I: Unbound<Bound = T>,96	T: Trace,97{98	cache: Cc<RefCell<GcHashMap<CacheKey, T>>>,99	value: I,100}101impl<I: Unbound<Bound = T>, T: Trace> CachedUnbound<I, T> {102	pub fn new(value: I) -> Self {103		Self {104			cache: Cc::new(RefCell::new(GcHashMap::new())),105			value,106		}107	}108}109impl<I: Unbound<Bound = T>, T: Clone + Trace> Unbound for CachedUnbound<I, T> {110	type Bound = T;111	fn bind(&self, sup: Option<ObjValue>, this: Option<ObjValue>) -> Result<T> {112		let cache_key = (113			sup.as_ref().map(|s| s.clone().downgrade()),114			this.as_ref().map(|t| t.clone().downgrade()),115		);116		{117			if let Some(t) = self.cache.borrow().get(&cache_key) {118				return Ok(t.clone());119			}120		}121		let bound = self.value.bind(sup, this)?;122123		{124			let mut cache = self.cache.borrow_mut();125			cache.insert(cache_key, bound.clone());126		}127128		Ok(bound)129	}130}131132impl<T: Debug + Trace> Debug for Thunk<T> {133	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {134		write!(f, "Lazy")135	}136}137impl<T: Trace> PartialEq for Thunk<T> {138	fn eq(&self, other: &Self) -> bool {139		Cc::ptr_eq(&self.0, &other.0)140	}141}142143/// Represents a Jsonnet value, which can be sliced or indexed (string or array).144#[allow(clippy::module_name_repetitions)]145pub enum IndexableVal {146	/// String.147	Str(IStr),148	/// Array.149	Arr(ArrValue),150}151impl IndexableVal {152	pub fn to_array(self) -> ArrValue {153		match self {154			IndexableVal::Str(s) => ArrValue::chars(s.chars()),155			IndexableVal::Arr(arr) => arr,156		}157	}158	/// Slice the value.159	///160	/// # Implementation161	///162	/// For strings, will create a copy of specified interval.163	///164	/// For arrays, nothing will be copied on this call, instead [`ArrValue::Slice`] view will be returned.165	pub fn slice(166		self,167		index: Option<BoundedUsize<0, { i32::MAX as usize }>>,168		end: Option<BoundedUsize<0, { i32::MAX as usize }>>,169		step: Option<BoundedUsize<1, { i32::MAX as usize }>>,170	) -> Result<Self> {171		match &self {172			IndexableVal::Str(s) => {173				let index = index.as_deref().copied().unwrap_or(0);174				let end = end.as_deref().copied().unwrap_or(usize::MAX);175				let step = step.as_deref().copied().unwrap_or(1);176177				if index >= end {178					return Ok(Self::Str("".into()));179				}180181				Ok(Self::Str(182					(s.chars()183						.skip(index)184						.take(end - index)185						.step_by(step)186						.collect::<String>())187					.into(),188				))189			}190			IndexableVal::Arr(arr) => {191				let index = index.as_deref().copied().unwrap_or(0);192				let end = end.as_deref().copied().unwrap_or(usize::MAX).min(arr.len());193				let step = step.as_deref().copied().unwrap_or(1);194195				if index >= end {196					return Ok(Self::Arr(ArrValue::empty()));197				}198199				Ok(Self::Arr(200					arr.clone()201						.slice(Some(index), Some(end), Some(step))202						.expect("arguments checked"),203				))204			}205		}206	}207}208209#[derive(Debug, Clone, Trace)]210pub enum StrValue {211	Flat(IStr),212	Tree(Rc<(StrValue, StrValue, usize)>),213}214impl StrValue {215	pub fn concat(a: StrValue, b: StrValue) -> Self {216		// TODO: benchmark for an optimal value, currently just a arbitrary choice217		const STRING_EXTEND_THRESHOLD: usize = 100;218219		if a.is_empty() {220			b221		} else if b.is_empty() {222			a223		} else if a.len() + b.len() < STRING_EXTEND_THRESHOLD {224			Self::Flat(format!("{a}{b}").into())225		} else {226			let len = a.len() + b.len();227			Self::Tree(Rc::new((a, b, len)))228		}229	}230	pub fn into_flat(self) -> IStr {231		#[cold]232		fn write_buf(s: &StrValue, out: &mut String) {233			match s {234				StrValue::Flat(f) => out.push_str(f),235				StrValue::Tree(t) => {236					write_buf(&t.0, out);237					write_buf(&t.1, out);238				}239			}240		}241		match self {242			StrValue::Flat(f) => f,243			StrValue::Tree(_) => {244				let mut buf = String::with_capacity(self.len());245				write_buf(&self, &mut buf);246				buf.into()247			}248		}249	}250	pub fn len(&self) -> usize {251		match self {252			StrValue::Flat(v) => v.len(),253			StrValue::Tree(t) => t.2,254		}255	}256	pub fn is_empty(&self) -> bool {257		match self {258			Self::Flat(v) => v.is_empty(),259			// Can't create non-flat empty string260			Self::Tree(_) => false,261		}262	}263}264impl From<&str> for StrValue {265	fn from(value: &str) -> Self {266		Self::Flat(value.into())267	}268}269impl From<String> for StrValue {270	fn from(value: String) -> Self {271		Self::Flat(value.into())272	}273}274impl Display for StrValue {275	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {276		match self {277			StrValue::Flat(v) => write!(f, "{v}"),278			StrValue::Tree(t) => {279				write!(f, "{}", t.0)?;280				write!(f, "{}", t.1)281			}282		}283	}284}285impl PartialEq for StrValue {286	fn eq(&self, other: &Self) -> bool {287		let a = self.clone().into_flat();288		let b = other.clone().into_flat();289		a == b290	}291}292impl Eq for StrValue {}293impl PartialOrd for StrValue {294	fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {295		let a = self.clone().into_flat();296		let b = other.clone().into_flat();297		Some(a.cmp(&b))298	}299}300impl Ord for StrValue {301	fn cmp(&self, other: &Self) -> std::cmp::Ordering {302		self.partial_cmp(other)303			.expect("partial_cmp always returns Some")304	}305}306307/// Represents any valid Jsonnet value.308#[derive(Debug, Clone, Trace)]309pub enum Val {310	/// Represents a Jsonnet boolean.311	Bool(bool),312	/// Represents a Jsonnet null value.313	Null,314	/// Represents a Jsonnet string.315	Str(StrValue),316	/// Represents a Jsonnet number.317	/// Should be finite, and not NaN318	/// This restriction isn't enforced by enum, as enum field can't be marked as private319	Num(f64),320	/// Experimental bigint321	#[cfg(feature = "exp-bigint")]322	BigInt(#[trace(skip)] Box<num_bigint::BigInt>),323	/// Represents a Jsonnet array.324	Arr(ArrValue),325	/// Represents a Jsonnet object.326	Obj(ObjValue),327	/// Represents a Jsonnet function.328	Func(FuncVal),329}330331#[cfg(target_pointer_width = "64")]332static_assertions::assert_eq_size!(Val, [u8; 24]);333334impl From<IndexableVal> for Val {335	fn from(v: IndexableVal) -> Self {336		match v {337			IndexableVal::Str(s) => Self::Str(StrValue::Flat(s)),338			IndexableVal::Arr(a) => Self::Arr(a),339		}340	}341}342343impl Val {344	pub const fn as_bool(&self) -> Option<bool> {345		match self {346			Self::Bool(v) => Some(*v),347			_ => None,348		}349	}350	pub const fn as_null(&self) -> Option<()> {351		match self {352			Self::Null => Some(()),353			_ => None,354		}355	}356	pub fn as_str(&self) -> Option<IStr> {357		match self {358			Self::Str(s) => Some(s.clone().into_flat()),359			_ => None,360		}361	}362	pub const fn as_num(&self) -> Option<f64> {363		match self {364			Self::Num(n) => Some(*n),365			_ => None,366		}367	}368	pub fn as_arr(&self) -> Option<ArrValue> {369		match self {370			Self::Arr(a) => Some(a.clone()),371			_ => None,372		}373	}374	pub fn as_obj(&self) -> Option<ObjValue> {375		match self {376			Self::Obj(o) => Some(o.clone()),377			_ => None,378		}379	}380	pub fn as_func(&self) -> Option<FuncVal> {381		match self {382			Self::Func(f) => Some(f.clone()),383			_ => None,384		}385	}386387	/// Creates `Val::Num` after checking for numeric overflow.388	/// As numbers are `f64`, we can just check for their finity.389	pub fn new_checked_num(num: f64) -> Result<Self> {390		if num.is_finite() {391			Ok(Self::Num(num))392		} else {393			throw!("overflow")394		}395	}396397	pub const fn value_type(&self) -> ValType {398		match self {399			Self::Str(..) => ValType::Str,400			Self::Num(..) => ValType::Num,401			#[cfg(feature = "exp-bigint")]402			Self::BigInt(..) => ValType::BigInt,403			Self::Arr(..) => ValType::Arr,404			Self::Obj(..) => ValType::Obj,405			Self::Bool(_) => ValType::Bool,406			Self::Null => ValType::Null,407			Self::Func(..) => ValType::Func,408		}409	}410411	pub fn manifest(&self, format: impl ManifestFormat) -> Result<String> {412		fn manifest_dyn(val: &Val, manifest: &dyn ManifestFormat) -> Result<String> {413			manifest.manifest(val.clone())414		}415		manifest_dyn(self, &format)416	}417418	pub fn to_string(&self) -> Result<IStr> {419		Ok(match self {420			Self::Bool(true) => "true".into(),421			Self::Bool(false) => "false".into(),422			Self::Null => "null".into(),423			Self::Str(s) => s.clone().into_flat(),424			_ => self.manifest(ToStringFormat).map(IStr::from)?,425		})426	}427428	pub fn into_indexable(self) -> Result<IndexableVal> {429		Ok(match self {430			Val::Str(s) => IndexableVal::Str(s.into_flat()),431			Val::Arr(arr) => IndexableVal::Arr(arr),432			_ => throw!(ValueIsNotIndexable(self.value_type())),433		})434	}435}436437const fn is_function_like(val: &Val) -> bool {438	matches!(val, Val::Func(_))439}440441/// Native implementation of `std.primitiveEquals`442pub fn primitive_equals(val_a: &Val, val_b: &Val) -> Result<bool> {443	Ok(match (val_a, val_b) {444		(Val::Bool(a), Val::Bool(b)) => a == b,445		(Val::Null, Val::Null) => true,446		(Val::Str(a), Val::Str(b)) => a == b,447		(Val::Num(a), Val::Num(b)) => (a - b).abs() <= f64::EPSILON,448		#[cfg(feature = "exp-bigint")]449		(Val::BigInt(a), Val::BigInt(b)) => a == b,450		(Val::Arr(_), Val::Arr(_)) => {451			throw!("primitiveEquals operates on primitive types, got array")452		}453		(Val::Obj(_), Val::Obj(_)) => {454			throw!("primitiveEquals operates on primitive types, got object")455		}456		(a, b) if is_function_like(a) && is_function_like(b) => {457			throw!("cannot test equality of functions")458		}459		(_, _) => false,460	})461}462463/// Native implementation of `std.equals`464pub fn equals(val_a: &Val, val_b: &Val) -> Result<bool> {465	if val_a.value_type() != val_b.value_type() {466		return Ok(false);467	}468	match (val_a, val_b) {469		(Val::Arr(a), Val::Arr(b)) => {470			if ArrValue::ptr_eq(a, b) {471				return Ok(true);472			}473			if a.len() != b.len() {474				return Ok(false);475			}476			for (a, b) in a.iter().zip(b.iter()) {477				if !equals(&a?, &b?)? {478					return Ok(false);479				}480			}481			Ok(true)482		}483		(Val::Obj(a), Val::Obj(b)) => {484			if ObjValue::ptr_eq(a, b) {485				return Ok(true);486			}487			let fields = a.fields(488				#[cfg(feature = "exp-preserve-order")]489				false,490			);491			if fields492				!= b.fields(493					#[cfg(feature = "exp-preserve-order")]494					false,495				) {496				return Ok(false);497			}498			for field in fields {499				if !equals(500					&a.get(field.clone())?.expect("field exists"),501					&b.get(field)?.expect("field exists"),502				)? {503					return Ok(false);504				}505			}506			Ok(true)507		}508		(a, b) => Ok(primitive_equals(a, b)?),509	}510}
after · crates/jrsonnet-evaluator/src/val.rs
1use std::{2	cell::RefCell,3	fmt::{self, Debug, Display},4	mem::replace,5	rc::Rc,6};78use jrsonnet_gcmodule::{Cc, Trace};9use jrsonnet_interner::IStr;10use jrsonnet_types::ValType;1112pub use crate::arr::ArrValue;13use crate::{14	error::{Error, ErrorKind::*},15	function::FuncVal,16	gc::{GcHashMap, TraceBox},17	manifest::{ManifestFormat, ToStringFormat},18	tb, throw,19	typed::BoundedUsize,20	ObjValue, Result, Unbound, WeakObjValue,21};2223pub trait ThunkValue: Trace {24	type Output;25	fn get(self: Box<Self>) -> Result<Self::Output>;26}2728#[derive(Trace)]29enum ThunkInner<T: Trace> {30	Computed(T),31	Errored(Error),32	Waiting(TraceBox<dyn ThunkValue<Output = T>>),33	Pending,34}3536/// Lazily evaluated value37#[allow(clippy::module_name_repetitions)]38#[derive(Clone, Trace)]39pub struct Thunk<T: Trace>(Cc<RefCell<ThunkInner<T>>>);4041impl<T: Trace> Thunk<T> {42	pub fn evaluated(val: T) -> Self {43		Self(Cc::new(RefCell::new(ThunkInner::Computed(val))))44	}45	pub fn new(f: impl ThunkValue<Output = T> + 'static) -> Self {46		Self(Cc::new(RefCell::new(ThunkInner::Waiting(tb!(f)))))47	}48	pub fn errored(e: Error) -> Self {49		Self(Cc::new(RefCell::new(ThunkInner::Errored(e))))50	}51}5253impl<T> Thunk<T>54where55	T: Clone + Trace,56{57	pub fn force(&self) -> Result<()> {58		self.evaluate()?;59		Ok(())60	}6162	/// Evaluate thunk, or return cached value63	///64	/// # Errors65	///66	/// - Lazy value evaluation returned error67	/// - This method was called during inner value evaluation68	pub fn evaluate(&self) -> Result<T> {69		match &*self.0.borrow() {70			ThunkInner::Computed(v) => return Ok(v.clone()),71			ThunkInner::Errored(e) => return Err(e.clone()),72			ThunkInner::Pending => return Err(InfiniteRecursionDetected.into()),73			ThunkInner::Waiting(..) => (),74		};75		let ThunkInner::Waiting(value) = replace(&mut *self.0.borrow_mut(), ThunkInner::Pending)76		else {77			unreachable!();78		};79		let new_value = match value.0.get() {80			Ok(v) => v,81			Err(e) => {82				*self.0.borrow_mut() = ThunkInner::Errored(e.clone());83				return Err(e);84			}85		};86		*self.0.borrow_mut() = ThunkInner::Computed(new_value.clone());87		Ok(new_value)88	}89}9091type CacheKey = (Option<WeakObjValue>, Option<WeakObjValue>);9293#[derive(Trace, Clone)]94pub struct CachedUnbound<I, T>95where96	I: Unbound<Bound = T>,97	T: Trace,98{99	cache: Cc<RefCell<GcHashMap<CacheKey, T>>>,100	value: I,101}102impl<I: Unbound<Bound = T>, T: Trace> CachedUnbound<I, T> {103	pub fn new(value: I) -> Self {104		Self {105			cache: Cc::new(RefCell::new(GcHashMap::new())),106			value,107		}108	}109}110impl<I: Unbound<Bound = T>, T: Clone + Trace> Unbound for CachedUnbound<I, T> {111	type Bound = T;112	fn bind(&self, sup: Option<ObjValue>, this: Option<ObjValue>) -> Result<T> {113		let cache_key = (114			sup.as_ref().map(|s| s.clone().downgrade()),115			this.as_ref().map(|t| t.clone().downgrade()),116		);117		{118			if let Some(t) = self.cache.borrow().get(&cache_key) {119				return Ok(t.clone());120			}121		}122		let bound = self.value.bind(sup, this)?;123124		{125			let mut cache = self.cache.borrow_mut();126			cache.insert(cache_key, bound.clone());127		}128129		Ok(bound)130	}131}132133impl<T: Debug + Trace> Debug for Thunk<T> {134	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {135		write!(f, "Lazy")136	}137}138impl<T: Trace> PartialEq for Thunk<T> {139	fn eq(&self, other: &Self) -> bool {140		Cc::ptr_eq(&self.0, &other.0)141	}142}143144/// Represents a Jsonnet value, which can be sliced or indexed (string or array).145#[allow(clippy::module_name_repetitions)]146pub enum IndexableVal {147	/// String.148	Str(IStr),149	/// Array.150	Arr(ArrValue),151}152impl IndexableVal {153	pub fn to_array(self) -> ArrValue {154		match self {155			IndexableVal::Str(s) => ArrValue::chars(s.chars()),156			IndexableVal::Arr(arr) => arr,157		}158	}159	/// Slice the value.160	///161	/// # Implementation162	///163	/// For strings, will create a copy of specified interval.164	///165	/// For arrays, nothing will be copied on this call, instead [`ArrValue::Slice`] view will be returned.166	pub fn slice(167		self,168		index: Option<BoundedUsize<0, { i32::MAX as usize }>>,169		end: Option<BoundedUsize<0, { i32::MAX as usize }>>,170		step: Option<BoundedUsize<1, { i32::MAX as usize }>>,171	) -> Result<Self> {172		match &self {173			IndexableVal::Str(s) => {174				let index = index.as_deref().copied().unwrap_or(0);175				let end = end.as_deref().copied().unwrap_or(usize::MAX);176				let step = step.as_deref().copied().unwrap_or(1);177178				if index >= end {179					return Ok(Self::Str("".into()));180				}181182				Ok(Self::Str(183					(s.chars()184						.skip(index)185						.take(end - index)186						.step_by(step)187						.collect::<String>())188					.into(),189				))190			}191			IndexableVal::Arr(arr) => {192				let index = index.as_deref().copied().unwrap_or(0);193				let end = end.as_deref().copied().unwrap_or(usize::MAX).min(arr.len());194				let step = step.as_deref().copied().unwrap_or(1);195196				if index >= end {197					return Ok(Self::Arr(ArrValue::empty()));198				}199200				Ok(Self::Arr(201					arr.clone()202						.slice(Some(index), Some(end), Some(step))203						.expect("arguments checked"),204				))205			}206		}207	}208}209210#[derive(Debug, Clone, Trace)]211pub enum StrValue {212	Flat(IStr),213	Tree(Rc<(StrValue, StrValue, usize)>),214}215impl StrValue {216	pub fn concat(a: StrValue, b: StrValue) -> Self {217		// TODO: benchmark for an optimal value, currently just a arbitrary choice218		const STRING_EXTEND_THRESHOLD: usize = 100;219220		if a.is_empty() {221			b222		} else if b.is_empty() {223			a224		} else if a.len() + b.len() < STRING_EXTEND_THRESHOLD {225			Self::Flat(format!("{a}{b}").into())226		} else {227			let len = a.len() + b.len();228			Self::Tree(Rc::new((a, b, len)))229		}230	}231	pub fn into_flat(self) -> IStr {232		#[cold]233		fn write_buf(s: &StrValue, out: &mut String) {234			match s {235				StrValue::Flat(f) => out.push_str(f),236				StrValue::Tree(t) => {237					write_buf(&t.0, out);238					write_buf(&t.1, out);239				}240			}241		}242		match self {243			StrValue::Flat(f) => f,244			StrValue::Tree(_) => {245				let mut buf = String::with_capacity(self.len());246				write_buf(&self, &mut buf);247				buf.into()248			}249		}250	}251	pub fn len(&self) -> usize {252		match self {253			StrValue::Flat(v) => v.len(),254			StrValue::Tree(t) => t.2,255		}256	}257	pub fn is_empty(&self) -> bool {258		match self {259			Self::Flat(v) => v.is_empty(),260			// Can't create non-flat empty string261			Self::Tree(_) => false,262		}263	}264}265impl From<&str> for StrValue {266	fn from(value: &str) -> Self {267		Self::Flat(value.into())268	}269}270impl From<String> for StrValue {271	fn from(value: String) -> Self {272		Self::Flat(value.into())273	}274}275impl Display for StrValue {276	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {277		match self {278			StrValue::Flat(v) => write!(f, "{v}"),279			StrValue::Tree(t) => {280				write!(f, "{}", t.0)?;281				write!(f, "{}", t.1)282			}283		}284	}285}286impl PartialEq for StrValue {287	fn eq(&self, other: &Self) -> bool {288		let a = self.clone().into_flat();289		let b = other.clone().into_flat();290		a == b291	}292}293impl Eq for StrValue {}294impl PartialOrd for StrValue {295	fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {296		let a = self.clone().into_flat();297		let b = other.clone().into_flat();298		Some(a.cmp(&b))299	}300}301impl Ord for StrValue {302	fn cmp(&self, other: &Self) -> std::cmp::Ordering {303		self.partial_cmp(other)304			.expect("partial_cmp always returns Some")305	}306}307308/// Represents any valid Jsonnet value.309#[derive(Debug, Clone, Trace)]310pub enum Val {311	/// Represents a Jsonnet boolean.312	Bool(bool),313	/// Represents a Jsonnet null value.314	Null,315	/// Represents a Jsonnet string.316	Str(StrValue),317	/// Represents a Jsonnet number.318	/// Should be finite, and not NaN319	/// This restriction isn't enforced by enum, as enum field can't be marked as private320	Num(f64),321	/// Experimental bigint322	#[cfg(feature = "exp-bigint")]323	BigInt(#[trace(skip)] Box<num_bigint::BigInt>),324	/// Represents a Jsonnet array.325	Arr(ArrValue),326	/// Represents a Jsonnet object.327	Obj(ObjValue),328	/// Represents a Jsonnet function.329	Func(FuncVal),330}331332#[cfg(target_pointer_width = "64")]333static_assertions::assert_eq_size!(Val, [u8; 24]);334335impl From<IndexableVal> for Val {336	fn from(v: IndexableVal) -> Self {337		match v {338			IndexableVal::Str(s) => Self::Str(StrValue::Flat(s)),339			IndexableVal::Arr(a) => Self::Arr(a),340		}341	}342}343344impl Val {345	pub const fn as_bool(&self) -> Option<bool> {346		match self {347			Self::Bool(v) => Some(*v),348			_ => None,349		}350	}351	pub const fn as_null(&self) -> Option<()> {352		match self {353			Self::Null => Some(()),354			_ => None,355		}356	}357	pub fn as_str(&self) -> Option<IStr> {358		match self {359			Self::Str(s) => Some(s.clone().into_flat()),360			_ => None,361		}362	}363	pub const fn as_num(&self) -> Option<f64> {364		match self {365			Self::Num(n) => Some(*n),366			_ => None,367		}368	}369	pub fn as_arr(&self) -> Option<ArrValue> {370		match self {371			Self::Arr(a) => Some(a.clone()),372			_ => None,373		}374	}375	pub fn as_obj(&self) -> Option<ObjValue> {376		match self {377			Self::Obj(o) => Some(o.clone()),378			_ => None,379		}380	}381	pub fn as_func(&self) -> Option<FuncVal> {382		match self {383			Self::Func(f) => Some(f.clone()),384			_ => None,385		}386	}387388	/// Creates `Val::Num` after checking for numeric overflow.389	/// As numbers are `f64`, we can just check for their finity.390	pub fn new_checked_num(num: f64) -> Result<Self> {391		if num.is_finite() {392			Ok(Self::Num(num))393		} else {394			throw!("overflow")395		}396	}397398	pub const fn value_type(&self) -> ValType {399		match self {400			Self::Str(..) => ValType::Str,401			Self::Num(..) => ValType::Num,402			#[cfg(feature = "exp-bigint")]403			Self::BigInt(..) => ValType::BigInt,404			Self::Arr(..) => ValType::Arr,405			Self::Obj(..) => ValType::Obj,406			Self::Bool(_) => ValType::Bool,407			Self::Null => ValType::Null,408			Self::Func(..) => ValType::Func,409		}410	}411412	pub fn manifest(&self, format: impl ManifestFormat) -> Result<String> {413		fn manifest_dyn(val: &Val, manifest: &dyn ManifestFormat) -> Result<String> {414			manifest.manifest(val.clone())415		}416		manifest_dyn(self, &format)417	}418419	pub fn to_string(&self) -> Result<IStr> {420		Ok(match self {421			Self::Bool(true) => "true".into(),422			Self::Bool(false) => "false".into(),423			Self::Null => "null".into(),424			Self::Str(s) => s.clone().into_flat(),425			_ => self.manifest(ToStringFormat).map(IStr::from)?,426		})427	}428429	pub fn into_indexable(self) -> Result<IndexableVal> {430		Ok(match self {431			Val::Str(s) => IndexableVal::Str(s.into_flat()),432			Val::Arr(arr) => IndexableVal::Arr(arr),433			_ => throw!(ValueIsNotIndexable(self.value_type())),434		})435	}436}437438const fn is_function_like(val: &Val) -> bool {439	matches!(val, Val::Func(_))440}441442/// Native implementation of `std.primitiveEquals`443pub fn primitive_equals(val_a: &Val, val_b: &Val) -> Result<bool> {444	Ok(match (val_a, val_b) {445		(Val::Bool(a), Val::Bool(b)) => a == b,446		(Val::Null, Val::Null) => true,447		(Val::Str(a), Val::Str(b)) => a == b,448		(Val::Num(a), Val::Num(b)) => (a - b).abs() <= f64::EPSILON,449		#[cfg(feature = "exp-bigint")]450		(Val::BigInt(a), Val::BigInt(b)) => a == b,451		(Val::Arr(_), Val::Arr(_)) => {452			throw!("primitiveEquals operates on primitive types, got array")453		}454		(Val::Obj(_), Val::Obj(_)) => {455			throw!("primitiveEquals operates on primitive types, got object")456		}457		(a, b) if is_function_like(a) && is_function_like(b) => {458			throw!("cannot test equality of functions")459		}460		(_, _) => false,461	})462}463464/// Native implementation of `std.equals`465pub fn equals(val_a: &Val, val_b: &Val) -> Result<bool> {466	if val_a.value_type() != val_b.value_type() {467		return Ok(false);468	}469	match (val_a, val_b) {470		(Val::Arr(a), Val::Arr(b)) => {471			if ArrValue::ptr_eq(a, b) {472				return Ok(true);473			}474			if a.len() != b.len() {475				return Ok(false);476			}477			for (a, b) in a.iter().zip(b.iter()) {478				if !equals(&a?, &b?)? {479					return Ok(false);480				}481			}482			Ok(true)483		}484		(Val::Obj(a), Val::Obj(b)) => {485			if ObjValue::ptr_eq(a, b) {486				return Ok(true);487			}488			let fields = a.fields(489				#[cfg(feature = "exp-preserve-order")]490				false,491			);492			if fields493				!= b.fields(494					#[cfg(feature = "exp-preserve-order")]495					false,496				) {497				return Ok(false);498			}499			for field in fields {500				if !equals(501					&a.get(field.clone())?.expect("field exists"),502					&b.get(field)?.expect("field exists"),503				)? {504					return Ok(false);505				}506			}507			Ok(true)508		}509		(a, b) => Ok(primitive_equals(a, b)?),510	}511}
modifiedcrates/jrsonnet-macros/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-macros/src/lib.rs
+++ b/crates/jrsonnet-macros/src/lib.rs
@@ -51,7 +51,7 @@
 
 fn extract_type_from_option(ty: &Type) -> Result<Option<&Type>> {
 	let Some(args) = type_is_path(ty, "Option") else {
-		return Ok(None)
+		return Ok(None);
 	};
 	// It should have only on angle-bracketed param ("<String>"):
 	let PathArguments::AngleBracketed(params) = args else {
@@ -63,7 +63,7 @@
 		return Err(Error::new(
 			generic_arg.span(),
 			"option generic should be a type",
-		))
+		));
 	};
 	Ok(Some(ty))
 }
@@ -210,7 +210,7 @@
 		return Err(Error::new(
 			fun.sig.span(),
 			"builtin should return something",
-		))
+		));
 	};
 
 	let name = fun.sig.ident.to_string();
modifiedcrates/jrsonnet-parser/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-parser/src/lib.rs
+++ b/crates/jrsonnet-parser/src/lib.rs
@@ -630,7 +630,7 @@
 					el!(
 						Apply(
 							el!(
-								Index{
+								Index {
 									indexable: el!(Var("std".into()), 1, 4),
 									index: el!(Str("deepJoin".into()), 5, 13),
 									#[cfg(feature = "exp-null-coaelse")]
modifiedcrates/jrsonnet-parser/src/source.rsdiffbeforeafterboth
--- a/crates/jrsonnet-parser/src/source.rs
+++ b/crates/jrsonnet-parser/src/source.rs
@@ -33,7 +33,7 @@
 		}
 		fn dyn_eq(&self, other: &dyn $T) -> bool {
 			let Some(other) = other.as_any().downcast_ref::<Self>() else {
-				return false
+				return false;
 			};
 			let this = <Self as $T>::as_any(self)
 				.downcast_ref::<Self>()
modifiedflake.lockdiffbeforeafterboth
--- a/flake.lock
+++ b/flake.lock
@@ -20,11 +20,11 @@
     },
     "nixpkgs": {
       "locked": {
-        "lastModified": 1689162265,
-        "narHash": "sha256-kdW79sfwX2TTX8yFBNUsEYOG+gQuAOHU+WcUtxMUnlc=",
+        "lastModified": 1690394427,
+        "narHash": "sha256-ZT1ABAZVdJycCJMUHu533dvcMuxqUGDnp6N2zLcFrv4=",
         "owner": "nixos",
         "repo": "nixpkgs",
-        "rev": "1941c7d8f1219c615a1d6dae826e0d6fab89acca",
+        "rev": "78df3591ec67310b8cc4b753e1496999da2678cf",
         "type": "github"
       },
       "original": {
@@ -50,11 +50,11 @@
         ]
       },
       "locked": {
-        "lastModified": 1689129196,
-        "narHash": "sha256-/z/Al4sFcIh5oPQWA9MclQmJR9g3RO8UDiHGaj/T9R8=",
+        "lastModified": 1690338181,
+        "narHash": "sha256-Sz2oQ9aNS3MVncnCMndr0302G26UrFUfPynoH2iLjsg=",
         "owner": "oxalica",
         "repo": "rust-overlay",
-        "rev": "db8d909c9526d4406579ee7343bf2d7de3d15eac",
+        "rev": "b7f0b7b58b3c6f14a1377ec31a3d78b23ab843ec",
         "type": "github"
       },
       "original": {
modifiedflake.nixdiffbeforeafterboth
--- a/flake.nix
+++ b/flake.nix
@@ -16,7 +16,7 @@
           inherit system;
           overlays = [ rust-overlay.overlays.default ];
         };
-        rust = ((pkgs.rustChannelOf { date = "2023-06-26"; channel = "nightly"; }).default.override {
+        rust = ((pkgs.rustChannelOf { date = "2023-07-23"; channel = "nightly"; }).default.override {
           extensions = [ "rust-src" "miri" "rust-analyzer" ];
         });
       in