From 7a7673ea9cb5b98dc3a66ee5f40972559f11d7d0 Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Sun, 22 Sep 2024 16:11:49 -0700 Subject: [PATCH] Allow negative indexes in DecodePath --- decoder.go | 20 ++++++++++++++------ reader_test.go | 30 ++++++++++++++++++++++++++---- result.go | 4 +++- 3 files changed, 43 insertions(+), 11 deletions(-) diff --git a/decoder.go b/decoder.go index 5864873..aae8a1b 100644 --- a/decoder.go +++ b/decoder.go @@ -147,13 +147,21 @@ PATH: // XXX - use type names in errors. return fmt.Errorf("expected a slice for %d but found %d", v, typeNum) } - if size < uint(v) { - // Slice is smaller than index, not found - return nil + var i uint + if v < 0 { + if size < uint(-v) { + // Slice is smaller than negative index, not found + return nil + } + i = size - uint(-v) + } else { + if size <= uint(v) { + // Slice is smaller than index, not found + return nil + } + i = uint(v) } - // TODO: support negative indexes? Seems useful for subdivisions in - // particular. - offset, err = d.nextValueOffset(offset, uint(v)) + offset, err = d.nextValueOffset(offset, i) if err != nil { return err } diff --git a/reader_test.go b/reader_test.go index 1908635..496437a 100644 --- a/reader_test.go +++ b/reader_test.go @@ -336,11 +336,33 @@ func TestDecodePath(t *testing.T) { require.NoError(t, result.DecodePath(&u, "array", 0)) assert.Equal(t, uint(1), u) - require.NoError(t, result.DecodePath(&u, "array", 2)) - assert.Equal(t, uint(3), u) + var u2 uint + require.NoError(t, result.DecodePath(&u2, "array", 2)) + assert.Equal(t, uint(3), u2) - require.NoError(t, result.DecodePath(&u, "map", "mapX", "arrayX", 1)) - assert.Equal(t, uint(8), u) + // This is past the end of the array + var u3 uint + require.NoError(t, result.DecodePath(&u3, "array", 3)) + assert.Equal(t, uint(0), u3) + + // Negative offsets + + var n1 uint + require.NoError(t, result.DecodePath(&n1, "array", -1)) + assert.Equal(t, uint(3), n1) + + var n2 uint + require.NoError(t, result.DecodePath(&n2, "array", -3)) + assert.Equal(t, uint(1), n2) + + var u4 uint + require.NoError(t, result.DecodePath(&u4, "map", "mapX", "arrayX", 1)) + assert.Equal(t, uint(8), u4) + + // Does key not exist + var ne uint + require.NoError(t, result.DecodePath(&ne, "does-not-exist", 1)) + assert.Equal(t, uint(0), ne) } type TestInterface interface { diff --git a/result.go b/result.go index e50bb80..7562b2b 100644 --- a/result.go +++ b/result.go @@ -62,7 +62,9 @@ func (r Result) Decode(v any) error { // value. // // For maps, string path elements are used as keys. -// For arrays, int path elements are used as indices. +// For arrays, int path elements are used as indices. A negative offset will +// return values from the end of the array, e.g., -1 will return the last +// element. // // If the path is empty, the entire data structure is decoded into v. //