git.delta.rocks / jrsonnet / refs/commits / d91bf0e3d51a

difftreelog

feat allow negative indexes in std.slice

Yaroslav Bolyukin2023-06-14parent: #7f29d2d.patch.diff
in: master
Upstream issue: https://github.com/google/jsonnet/pull/1093

2 files changed

modifiedcrates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -234,14 +234,34 @@
 	/// For arrays, nothing will be copied on this call, instead [`ArrValue::Slice`] view will be returned.
 	pub fn slice(
 		self,
-		index: Option<BoundedUsize<0, { i32::MAX as usize }>>,
-		end: Option<BoundedUsize<0, { i32::MAX as usize }>>,
+		index: Option<i32>,
+		end: Option<i32>,
 		step: Option<BoundedUsize<1, { i32::MAX as usize }>>,
 	) -> Result<Self> {
 		match &self {
 			IndexableVal::Str(s) => {
-				let index = index.as_deref().copied().unwrap_or(0);
-				let end = end.as_deref().copied().unwrap_or(usize::MAX);
+				let mut computed_len = None;
+				let mut get_len = || {
+					computed_len.map_or_else(
+						|| {
+							let len = s.chars().count();
+							let _ = computed_len.insert(len);
+							len
+						},
+						|len| len,
+					)
+				};
+				let mut get_idx = |pos: Option<i32>, default| {
+					match pos {
+						Some(v) if v < 0 => get_len().saturating_sub((-v) as usize),
+						// No need to clamp, as iterator interface is used
+						Some(v) => v as usize,
+						None => default,
+					}
+				};
+
+				let index = get_idx(index, 0);
+				let end = get_idx(end, usize::MAX);
 				let step = step.as_deref().copied().unwrap_or(1);
 
 				if index >= end {
@@ -258,8 +278,13 @@
 				))
 			}
 			IndexableVal::Arr(arr) => {
-				let index = index.as_deref().copied().unwrap_or(0);
-				let end = end.as_deref().copied().unwrap_or(usize::MAX).min(arr.len());
+				let get_idx = |pos: Option<i32>, len: usize, default| match pos {
+					Some(v) if v < 0 => len.saturating_sub((-v) as usize),
+					Some(v) => (v as usize).min(len),
+					None => default,
+				};
+				let index = get_idx(index, arr.len(), 0);
+				let end = get_idx(end, arr.len(), arr.len());
 				let step = step.as_deref().copied().unwrap_or(1);
 
 				if index >= end {
modifiedcrates/jrsonnet-stdlib/src/arrays.rsdiffbeforeafterboth
47#[builtin]47#[builtin]
48pub fn builtin_slice(48pub fn builtin_slice(
49 indexable: IndexableVal,49 indexable: IndexableVal,
50 index: Option<BoundedUsize<0, { i32::MAX as usize }>>,50 index: Option<i32>,
51 end: Option<BoundedUsize<0, { i32::MAX as usize }>>,51 end: Option<i32>,
52 step: Option<BoundedUsize<1, { i32::MAX as usize }>>,52 step: Option<BoundedUsize<1, { i32::MAX as usize }>>,
53) -> Result<Val> {53) -> Result<Val> {
54 indexable.slice(index, end, step).map(Val::from)54 indexable.slice(index, end, step).map(Val::from)