Skip to content

Commit d91bf0e

Browse files
committed
feat: allow negative indexes in std.slice
Upstream issue: google/jsonnet#1093
1 parent 7f29d2d commit d91bf0e

File tree

2 files changed

+33
-8
lines changed

2 files changed

+33
-8
lines changed

crates/jrsonnet-evaluator/src/val.rs

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -234,14 +234,34 @@ impl IndexableVal {
234234
/// For arrays, nothing will be copied on this call, instead [`ArrValue::Slice`] view will be returned.
235235
pub fn slice(
236236
self,
237-
index: Option<BoundedUsize<0, { i32::MAX as usize }>>,
238-
end: Option<BoundedUsize<0, { i32::MAX as usize }>>,
237+
index: Option<i32>,
238+
end: Option<i32>,
239239
step: Option<BoundedUsize<1, { i32::MAX as usize }>>,
240240
) -> Result<Self> {
241241
match &self {
242242
IndexableVal::Str(s) => {
243-
let index = index.as_deref().copied().unwrap_or(0);
244-
let end = end.as_deref().copied().unwrap_or(usize::MAX);
243+
let mut computed_len = None;
244+
let mut get_len = || {
245+
computed_len.map_or_else(
246+
|| {
247+
let len = s.chars().count();
248+
let _ = computed_len.insert(len);
249+
len
250+
},
251+
|len| len,
252+
)
253+
};
254+
let mut get_idx = |pos: Option<i32>, default| {
255+
match pos {
256+
Some(v) if v < 0 => get_len().saturating_sub((-v) as usize),
257+
// No need to clamp, as iterator interface is used
258+
Some(v) => v as usize,
259+
None => default,
260+
}
261+
};
262+
263+
let index = get_idx(index, 0);
264+
let end = get_idx(end, usize::MAX);
245265
let step = step.as_deref().copied().unwrap_or(1);
246266

247267
if index >= end {
@@ -258,8 +278,13 @@ impl IndexableVal {
258278
))
259279
}
260280
IndexableVal::Arr(arr) => {
261-
let index = index.as_deref().copied().unwrap_or(0);
262-
let end = end.as_deref().copied().unwrap_or(usize::MAX).min(arr.len());
281+
let get_idx = |pos: Option<i32>, len: usize, default| match pos {
282+
Some(v) if v < 0 => len.saturating_sub((-v) as usize),
283+
Some(v) => (v as usize).min(len),
284+
None => default,
285+
};
286+
let index = get_idx(index, arr.len(), 0);
287+
let end = get_idx(end, arr.len(), arr.len());
263288
let step = step.as_deref().copied().unwrap_or(1);
264289

265290
if index >= end {

crates/jrsonnet-stdlib/src/arrays.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ pub fn builtin_repeat(what: Either![IStr, ArrValue], count: usize) -> Result<Val
4747
#[builtin]
4848
pub fn builtin_slice(
4949
indexable: IndexableVal,
50-
index: Option<BoundedUsize<0, { i32::MAX as usize }>>,
51-
end: Option<BoundedUsize<0, { i32::MAX as usize }>>,
50+
index: Option<i32>,
51+
end: Option<i32>,
5252
step: Option<BoundedUsize<1, { i32::MAX as usize }>>,
5353
) -> Result<Val> {
5454
indexable.slice(index, end, step).map(Val::from)

0 commit comments

Comments
 (0)