diff --git a/CHANGELOG.md b/CHANGELOG.md index 683111e..41b89ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm The structure and content of this file follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## [1.22.1] - 2024-06-23 +### Added +- Added the missing support of Keyed and Indexed in jp.Modify. + ## [1.22.0] - 2024-04-22 ### Added - Added support for C style comment /* */ in the SEN parser. diff --git a/cmd/oj/main.go b/cmd/oj/main.go index fcd6a40..1ff7d45 100644 --- a/cmd/oj/main.go +++ b/cmd/oj/main.go @@ -720,8 +720,8 @@ should be in $.asm. An example of a plan in SEN format is (the first asm is optional): [ asm - [set $.asm {good: bye}] // set output to {good: bad} - [set $.asm.hello world] // output is now {good: bad, hello: world} + [set $.asm {good: bye}] // set output to {good: bye} + [set $.asm.hello world] // output is now {good: bye, hello: world} ] The functions available are: diff --git a/jp/modify.go b/jp/modify.go index 9e9f96b..e80ea2b 100644 --- a/jp/modify.go +++ b/jp/modify.go @@ -131,19 +131,21 @@ done: } } } else { - switch v.(type) { - case map[string]any, []any, gen.Object, gen.Array: - stack = append(stack, v) - default: - kind := reflect.Invalid - if rt := reflect.TypeOf(v); rt != nil { - kind = rt.Kind() - } - switch kind { - case reflect.Ptr, reflect.Slice, reflect.Struct, reflect.Array, reflect.Map: - stack = append(stack, v) + stack = stackAddValue(stack, v) + } + } + case Keyed: + if v, has = tv.ValueForKey(key); has { + if int(fi) == len(wx)-1 { // last one + if nv, changed := modifier(v); changed { + tv.SetValueForKey(key, nv) + if one && changed { + break done } } + + } else { + stack = stackAddValue(stack, v) } } case gen.Object: @@ -172,19 +174,7 @@ done: } } } else { - switch v.(type) { - case map[string]any, []any, gen.Object, gen.Array: - stack = append(stack, v) - default: - kind := reflect.Invalid - if rt := reflect.TypeOf(v); rt != nil { - kind = rt.Kind() - } - switch kind { - case reflect.Ptr, reflect.Slice, reflect.Struct, reflect.Array, reflect.Map: - stack = append(stack, v) - } - } + stack = stackAddValue(stack, v) } } } @@ -204,20 +194,25 @@ done: } } } else { - v = tv[i] - switch v.(type) { - case map[string]any, []any, gen.Object, gen.Array: - stack = append(stack, v) - default: - kind := reflect.Invalid - if rt := reflect.TypeOf(v); rt != nil { - kind = rt.Kind() - } - switch kind { - case reflect.Ptr, reflect.Slice, reflect.Struct, reflect.Array, reflect.Map: - stack = append(stack, v) + stack = stackAddValue(stack, tv[i]) + } + } + case Indexed: + size := tv.Size() + if i < 0 { + i = size + i + } + if 0 <= i && i < size { + v = tv.ValueAtIndex(i) + if int(fi) == len(wx)-1 { // last one + if nv, changed := modifier(v); changed { + tv.SetValueAtIndex(i, nv) + if one && changed { + break done } } + } else { + stack = stackAddValue(stack, v) } } case gen.Array: @@ -251,19 +246,7 @@ done: } } } else { - switch v.(type) { - case map[string]any, []any, gen.Object, gen.Array: - stack = append(stack, v) - default: - kind := reflect.Invalid - if rt := reflect.TypeOf(v); rt != nil { - kind = rt.Kind() - } - switch kind { - case reflect.Ptr, reflect.Slice, reflect.Struct, reflect.Array, reflect.Map: - stack = append(stack, v) - } - } + stack = stackAddValue(stack, v) } } } @@ -282,19 +265,7 @@ done: } } else { for _, v = range tv { - switch v.(type) { - case map[string]any, []any, gen.Object, gen.Array: - stack = append(stack, v) - default: - kind := reflect.Invalid - if rt := reflect.TypeOf(v); rt != nil { - kind = rt.Kind() - } - switch kind { - case reflect.Ptr, reflect.Slice, reflect.Struct, reflect.Array, reflect.Map: - stack = append(stack, v) - } - } + stack = stackAddValue(stack, v) } } case []any: @@ -309,20 +280,41 @@ done: } } else { for _, v = range tv { - switch v.(type) { - case map[string]any, []any, gen.Object, gen.Array: - stack = append(stack, v) - default: - kind := reflect.Invalid - if rt := reflect.TypeOf(v); rt != nil { - kind = rt.Kind() + stack = stackAddValue(stack, v) + } + } + case Keyed: + if int(fi) == len(wx)-1 { // last one + for _, k := range tv.Keys() { + v, _ = tv.ValueForKey(k) + if nv, changed := modifier(v); changed { + tv.SetValueForKey(k, nv) + if one && changed { + break done } - switch kind { - case reflect.Ptr, reflect.Slice, reflect.Struct, reflect.Array, reflect.Map: - stack = append(stack, v) + } + } + } else { + for _, k := range tv.Keys() { + v, _ = tv.ValueForKey(k) + stack = stackAddValue(stack, v) + } + } + case Indexed: + size := tv.Size() + if int(fi) == len(wx)-1 { // last one + for i := 0; i < size; i++ { + if nv, changed := modifier(tv.ValueAtIndex(i)); changed { + tv.SetValueAtIndex(i, nv) + if one && changed { + break done } } } + } else { + for i := 0; i < size; i++ { + stack = stackAddValue(stack, tv.ValueAtIndex(i)) + } } case gen.Object: var k string @@ -393,19 +385,7 @@ done: } } else { for _, v := range wx.reflectGetWild(tv) { - switch v.(type) { - case map[string]any, []any, gen.Object, gen.Array: - stack = append(stack, v) - default: - kind := reflect.Invalid - if rt := reflect.TypeOf(v); rt != nil { - kind = rt.Kind() - } - switch kind { - case reflect.Ptr, reflect.Slice, reflect.Struct, reflect.Array, reflect.Map: - stack = append(stack, v) - } - } + stack = stackAddValue(stack, v) } } } @@ -425,19 +405,20 @@ done: } } } else { - switch v.(type) { - case map[string]any, []any, gen.Object, gen.Array: - stack = append(stack, v) - default: - kind := reflect.Invalid - if rt := reflect.TypeOf(v); rt != nil { - kind = rt.Kind() - } - switch kind { - case reflect.Ptr, reflect.Slice, reflect.Struct, reflect.Array, reflect.Map: - stack = append(stack, v) + stack = stackAddValue(stack, v) + } + } + case Keyed: + if v, has = tv.ValueForKey(tu); has { + if int(fi) == len(wx)-1 { // last one + if nv, changed := modifier(v); changed { + tv.SetValueForKey(tu, nv) + if one && changed { + break done } } + } else { + stack = stackAddValue(stack, v) } } case gen.Object: @@ -467,19 +448,7 @@ done: } } } else { - switch v.(type) { - case map[string]any, []any, gen.Object, gen.Array: - stack = append(stack, v) - default: - kind := reflect.Invalid - if rt := reflect.TypeOf(v); rt != nil { - kind = rt.Kind() - } - switch kind { - case reflect.Ptr, reflect.Slice, reflect.Struct, reflect.Array, reflect.Map: - stack = append(stack, v) - } - } + stack = stackAddValue(stack, v) } } } @@ -500,19 +469,25 @@ done: } } } else { - switch v.(type) { - case map[string]any, []any, gen.Object, gen.Array: - stack = append(stack, v) - default: - kind := reflect.Invalid - if rt := reflect.TypeOf(v); rt != nil { - kind = rt.Kind() - } - switch kind { - case reflect.Ptr, reflect.Slice, reflect.Struct, reflect.Array, reflect.Map: - stack = append(stack, v) + stack = stackAddValue(stack, v) + } + } + case Indexed: + size := tv.Size() + if i < 0 { + i = size + i + } + if 0 <= i && i < size { + v = tv.ValueAtIndex(i) + if int(fi) == len(wx)-1 { // last one + if nv, changed := modifier(v); changed { + tv.SetValueAtIndex(i, nv) + if one && changed { + break done } } + } else { + stack = stackAddValue(stack, v) } } case gen.Array: @@ -556,19 +531,7 @@ done: } else { var has bool if v, has = wx.reflectGetNth(tv, i); has { - switch v.(type) { - case map[string]any, []any, gen.Object, gen.Array: - stack = append(stack, v) - default: - kind := reflect.Invalid - if rt := reflect.TypeOf(v); rt != nil { - kind = rt.Kind() - } - switch kind { - case reflect.Ptr, reflect.Slice, reflect.Struct, reflect.Array, reflect.Map: - stack = append(stack, v) - } - } + stack = stackAddValue(stack, v) } } } @@ -613,7 +576,7 @@ done: } } else { switch v.(type) { - case map[string]any, []any, gen.Object, gen.Array: + case map[string]any, []any, gen.Object, gen.Array, Keyed, Indexed: stack = append(stack, v) } } @@ -630,7 +593,56 @@ done: } } else { switch v.(type) { - case map[string]any, []any, gen.Object, gen.Array: + case map[string]any, []any, gen.Object, gen.Array, Keyed, Indexed: + stack = append(stack, v) + } + } + } + } + case Indexed: + size := tv.Size() + if start < 0 { + start = size + start + } + if end < 0 { + end = size + end + } + if size <= end { + end = size - 1 + } + if start < 0 || end < 0 || size <= start || step == 0 { + continue + } + if 0 < step { + for i := start; i <= end; i += step { + v = tv.ValueAtIndex(i) + if int(fi) == len(wx)-1 { // last one + if nv, changed := modifier(v); changed { + tv.SetValueAtIndex(i, nv) + if one && changed { + break done + } + } + } else { + switch v.(type) { + case map[string]any, []any, gen.Object, gen.Array, Keyed, Indexed: + stack = append(stack, v) + } + } + } + } else { + for i := start; end <= i; i += step { + v = tv.ValueAtIndex(i) + if int(fi) == len(wx)-1 { // last one + if nv, changed := modifier(v); changed { + tv.SetValueAtIndex(i, nv) + if one && changed { + break done + } + } + } else { + switch v.(type) { + case map[string]any, []any, gen.Object, gen.Array, Keyed, Indexed: stack = append(stack, v) } } @@ -724,20 +736,8 @@ done: } } } else { - for _, v := range wx.reflectGetSlice(tv, start, end, step) { - switch v.(type) { - case map[string]any, []any, gen.Object, gen.Array: - stack = append(stack, v) - default: - kind := reflect.Invalid - if rt := reflect.TypeOf(v); rt != nil { - kind = rt.Kind() - } - switch kind { - case reflect.Ptr, reflect.Slice, reflect.Struct, reflect.Array, reflect.Map: - stack = append(stack, v) - } - } + for _, v = range wx.reflectGetSlice(tv, start, end, step) { + stack = stackAddValue(stack, v) } } } @@ -755,6 +755,19 @@ done: } } } + case Indexed: + size := tv.Size() + for i := 0; i < size; i++ { + v = tv.ValueAtIndex(i) + if tf.Match(v) { + if nv, changed := modifier(v); changed { + tv.SetValueAtIndex(i, nv) + if one && changed { + break done + } + } + } + } case gen.Array: for i, vv := range tv { if tf.Match(vv) { @@ -816,45 +829,30 @@ done: stack[len(stack)-1] = prev stack = append(stack, di|descentFlag) for _, v = range tv { - switch v.(type) { - case nil, gen.Bool, gen.Int, gen.Float, gen.String, - bool, string, float64, float32, int, uint, int8, int16, int32, int64, uint8, uint16, uint32, uint64: - case map[string]any, []any, gen.Object, gen.Array: - stack = append(stack, v) - stack = append(stack, fi|descentChildFlag) - default: - kind := reflect.Invalid - if rt := reflect.TypeOf(v); rt != nil { - kind = rt.Kind() - } - switch kind { - case reflect.Ptr, reflect.Slice, reflect.Struct, reflect.Array, reflect.Map: - stack = append(stack, v) - } - } + stack = descentAddValue(stack, v, fi) } case []any: // Put prev back and slide fi. stack[len(stack)-1] = prev stack = append(stack, di|descentFlag) for i := len(tv) - 1; 0 <= i; i-- { - v = tv[i] - switch v.(type) { - case nil, gen.Bool, gen.Int, gen.Float, gen.String, - bool, string, float64, float32, int, uint, int8, int16, int32, int64, uint8, uint16, uint32, uint64: - case map[string]any, []any, gen.Object, gen.Array: - stack = append(stack, v) - stack = append(stack, fi|descentChildFlag) - default: - kind := reflect.Invalid - if rt := reflect.TypeOf(v); rt != nil { - kind = rt.Kind() - } - switch kind { - case reflect.Ptr, reflect.Slice, reflect.Struct, reflect.Array, reflect.Map: - stack = append(stack, v) - } - } + stack = descentAddValue(stack, tv[i], fi) + } + case Keyed: + // Put prev back and slide fi. + stack[len(stack)-1] = prev + stack = append(stack, di|descentFlag) + for _, k := range tv.Keys() { + v, _ = tv.ValueForKey(k) + stack = descentAddValue(stack, v, fi) + } + case Indexed: + // Put prev back and slide fi. + stack[len(stack)-1] = prev + stack = append(stack, di|descentFlag) + size := tv.Size() + for i := size - 1; 0 <= i; i-- { + stack = descentAddValue(stack, tv.ValueAtIndex(i), fi) } case gen.Object: // Put prev back and slide fi. @@ -862,7 +860,7 @@ done: stack = append(stack, di|descentFlag) for _, v = range tv { switch v.(type) { - case map[string]any, []any, gen.Object, gen.Array: + case map[string]any, []any, gen.Object, gen.Array, Keyed, Indexed: stack = append(stack, v) stack = append(stack, fi|descentChildFlag) } @@ -874,7 +872,7 @@ done: for i := len(tv) - 1; 0 <= i; i-- { v = tv[i] switch v.(type) { - case map[string]any, []any, gen.Object, gen.Array: + case map[string]any, []any, gen.Object, gen.Array, Keyed, Indexed: stack = append(stack, v) stack = append(stack, fi|descentChildFlag) } @@ -915,3 +913,41 @@ done: } return wrap[0] } + +func stackAddValue(stack []any, v any) []any { + switch v.(type) { + case map[string]any, []any, gen.Object, gen.Array, Keyed, Indexed: + stack = append(stack, v) + default: + kind := reflect.Invalid + if rt := reflect.TypeOf(v); rt != nil { + kind = rt.Kind() + } + switch kind { + case reflect.Ptr, reflect.Slice, reflect.Struct, reflect.Array, reflect.Map: + stack = append(stack, v) + } + } + return stack +} + +func descentAddValue(stack []any, v any, fi fragIndex) []any { + switch v.(type) { + case nil, gen.Bool, gen.Int, gen.Float, gen.String, + bool, string, float64, float32, + int, uint, int8, int16, int32, int64, uint8, uint16, uint32, uint64: + case map[string]any, []any, gen.Object, gen.Array, Keyed, Indexed: + stack = append(stack, v) + stack = append(stack, fi|descentChildFlag) + default: + kind := reflect.Invalid + if rt := reflect.TypeOf(v); rt != nil { + kind = rt.Kind() + } + switch kind { + case reflect.Ptr, reflect.Slice, reflect.Struct, reflect.Array, reflect.Map: + stack = append(stack, v) + } + } + return stack +} diff --git a/jp/modify_test.go b/jp/modify_test.go index 233740c..83b5f8f 100644 --- a/jp/modify_test.go +++ b/jp/modify_test.go @@ -124,3 +124,204 @@ func TestExprModifyDescent(t *testing.T) { tt.Equal(t, "{a: {key: 4}}", string(pw.Encode(result))) tt.Equal(t, "{a: {key: 4}}", string(pw.Encode(data))) } + +func keyedData() any { + return &keyed{ + ordered: ordered{ + entries: []*entry{ + {key: "a", value: 1}, + {key: "b", value: 2}, + { + key: "c", + value: &keyed{ + ordered: ordered{ + entries: []*entry{ + {key: "c1", value: 11}, + {key: "c2", value: 12}, + {key: "c3", value: 13}, + }, + }, + }, + }, + }, + }, + } +} + +func TestExprModifyKeyedChild(t *testing.T) { + data := keyedData() + x := jp.MustParseString("b") + result, err := x.Modify(data, func(element any) (any, bool) { + if num, ok := element.(int); ok { + element = num + 1 + } + return element, true + }) + tt.Nil(t, err) + tt.Equal(t, 3, jp.C("b").First(result)) + + x = jp.C("c").C("c2") + result = x.MustModifyOne(data, func(element any) (any, bool) { + if num, ok := element.(int); ok { + element = num + 5 + } + return element, true + }) + tt.Equal(t, 17, x.First(result)) +} + +func TestExprModifyKeyedWild(t *testing.T) { + data := keyedData() + x := jp.W().W() + result := x.MustModifyOne(data, func(element any) (any, bool) { + if num, ok := element.(int); ok { + element = num + 5 + } + return element, true + }) + tt.Equal(t, 16, jp.C("c").C("c1").First(result)) +} + +func TestExprModifyKeyedUnion(t *testing.T) { + data := keyedData() + x := jp.U(1, "c").U(2, "c1") + result := x.MustModifyOne(data, func(element any) (any, bool) { + if num, ok := element.(int); ok { + element = num + 5 + } + return element, true + }) + tt.Equal(t, 16, jp.C("c").C("c1").First(result)) +} + +func TestExprModifyKeyedDescent(t *testing.T) { + data := keyedData() + x := jp.D().C("c1") + result := x.MustModifyOne(data, func(element any) (any, bool) { + if num, ok := element.(int); ok { + element = num + 5 + } + return element, true + }) + tt.Equal(t, 16, jp.C("c").C("c1").First(result)) +} + +func indexedData() any { + return &indexed{ + ordered: ordered{ + entries: []*entry{ + {value: 1}, + {value: 2}, + {value: &indexed{ + ordered: ordered{ + entries: []*entry{ + {value: 11}, + {value: 12}, + {value: 13}, + }, + }, + }, + }, + }, + }, + } +} + +func TestExprModifyIndexedNth(t *testing.T) { + data := indexedData() + x := jp.N(1) + result, err := x.Modify(data, func(element any) (any, bool) { + if num, ok := element.(int); ok { + element = num + 1 + } + return element, true + }) + tt.Nil(t, err) + tt.Equal(t, 3, x.First(result)) + + x = jp.N(-1).N(0) + result = x.MustModifyOne(data, func(element any) (any, bool) { + if num, ok := element.(int); ok { + element = num + 5 + } + return element, true + }) + tt.Equal(t, 16, x.First(result)) +} + +func TestExprModifyIndexedWild(t *testing.T) { + data := indexedData() + x := jp.W().W() + result := x.MustModifyOne(data, func(element any) (any, bool) { + if num, ok := element.(int); ok { + element = num + 5 + } + return element, true + }) + tt.Equal(t, 16, jp.N(2).N(0).First(result)) +} + +func TestExprModifyIndexedUnion(t *testing.T) { + data := indexedData() + x := jp.U("a", -1).U("b", 0) + result := x.MustModifyOne(data, func(element any) (any, bool) { + if num, ok := element.(int); ok { + element = num + 5 + } + return element, true + }) + tt.Equal(t, 16, jp.N(2).N(0).First(result)) +} + +func TestExprModifyIndexedSlice(t *testing.T) { + data := indexedData() + x := jp.S(-2, -1).S(0, 4) + result := x.MustModifyOne(data, func(element any) (any, bool) { + if num, ok := element.(int); ok { + element = num + 5 + } + return element, true + }) + tt.Equal(t, 16, jp.N(2).N(0).First(result)) + + x = jp.S(2, 0, -1).S(2, 0, -1) + result = x.MustModifyOne(data, func(element any) (any, bool) { + if num, ok := element.(int); ok { + element = num + 5 + } + return element, true + }) + tt.Equal(t, 18, jp.N(2).N(2).First(result)) + + x = jp.S(-5, 2).S(2, 0) + var changed bool + _ = x.MustModifyOne(data, func(element any) (any, bool) { + changed = true + return element, true + }) + tt.Equal(t, false, changed) +} + +func TestExprModifyIndexedFilter(t *testing.T) { + data := indexedData() + x := jp.MustParseString("*[?@ == 11]") + result := x.MustModifyOne(data, func(element any) (any, bool) { + if num, ok := element.(int); ok { + element = num + 5 + } + return element, true + }) + tt.Equal(t, 16, jp.N(2).N(0).First(result)) +} + +func TestExprModifyIndexedDescent(t *testing.T) { + data := indexedData() + x := jp.D().N(0) + result := x.MustModifyOne(data, func(element any) (any, bool) { + if num, ok := element.(int); ok { + element = num + 5 + } + return element, true + }) + tt.Equal(t, 16, jp.N(2).N(0).First(result)) +} diff --git a/notes b/notes index 87a548c..4019627 100644 --- a/notes +++ b/notes @@ -23,10 +23,6 @@ - errors don't panic but close off current (if whole) and then back to discover maps - implement on all parsers - -- jp.GetPaths() and jp.FirstPath() - discussion #59 - - returns paths that match or maybe pairs of path and value - - embedded struct pointer encoding issue - json.Unmarshaler - use alt.Recompose and convert simple to bytes and pass to unmarshaller