From a375bccefb1cea8bc5eeb74a2f837b4016f9002b Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Wed, 14 Jun 2023 20:53:49 +0200 Subject: [PATCH] feat: allow negative indexes in std.slice Upstream issue: https://github.com/google/jsonnet/pull/1093 --- crates/jrsonnet-evaluator/src/val.rs | 37 +++++++++++++++++++++++----- crates/jrsonnet-stdlib/src/arrays.rs | 4 +-- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/crates/jrsonnet-evaluator/src/val.rs b/crates/jrsonnet-evaluator/src/val.rs index ae08fac8..a0fb967d 100644 --- a/crates/jrsonnet-evaluator/src/val.rs +++ b/crates/jrsonnet-evaluator/src/val.rs @@ -164,14 +164,34 @@ impl IndexableVal { /// For arrays, nothing will be copied on this call, instead [`ArrValue::Slice`] view will be returned. pub fn slice( self, - index: Option>, - end: Option>, + index: Option, + end: Option, step: Option>, ) -> Result { 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, 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 { @@ -188,8 +208,13 @@ impl IndexableVal { )) } 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, 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 { diff --git a/crates/jrsonnet-stdlib/src/arrays.rs b/crates/jrsonnet-stdlib/src/arrays.rs index 12f84675..27e78de9 100644 --- a/crates/jrsonnet-stdlib/src/arrays.rs +++ b/crates/jrsonnet-stdlib/src/arrays.rs @@ -37,8 +37,8 @@ pub fn builtin_repeat(what: Either![IStr, ArrValue], count: usize) -> Result>, - end: Option>, + index: Option, + end: Option, step: Option>, ) -> Result { indexable.slice(index, end, step).map(Val::from)