diff --git a/README.md b/README.md index 3f926e9..84fe347 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,7 @@ Available operations: | del(path) | delete a key | | merge(path, value) | merge json value into the path. Only JSON values are allowed | | pop(path) | remove last element from an array | +| push(path, value) | add new element to an array | ## Examples: diff --git a/pkg/operations/parse.go b/pkg/operations/parse.go index 0fd9863..f9c9e34 100644 --- a/pkg/operations/parse.go +++ b/pkg/operations/parse.go @@ -35,6 +35,7 @@ var operations = map[string]Operation{ "del": Delete, "merge": Merge, "pop": Pop, + "push": Push, } //nolint:govet diff --git a/pkg/operations/push.go b/pkg/operations/push.go new file mode 100644 index 0000000..b800493 --- /dev/null +++ b/pkg/operations/push.go @@ -0,0 +1,34 @@ +package operations + +import ( + "github.com/can3p/sackmesser/pkg/traverse/types" + "github.com/pkg/errors" +) + +func Push(root types.Node, path []types.PathElement, args ...any) error { + if len(args) != 1 { + return errors.Errorf("set operation expects one argument") + } + + value := args[0] + node, lastChunk, err := traverseButOne(root, path) + + if err != nil { + return err + } + + val, err := node.GetField(lastChunk) + + if err != nil { + return err + } + + typed, ok := val.([]any) + if !ok { + return types.ErrWrongVisit + } + + typed = append(typed, value) + + return node.SetField(lastChunk, typed) +} diff --git a/pkg/operations/push_test.go b/pkg/operations/push_test.go new file mode 100644 index 0000000..fabe4ef --- /dev/null +++ b/pkg/operations/push_test.go @@ -0,0 +1,52 @@ +package operations + +import ( + "testing" + + "github.com/alecthomas/assert/v2" + "github.com/can3p/sackmesser/pkg/traverse/simplejson" + "github.com/can3p/sackmesser/pkg/traverse/types" +) + +func TestPushOperation(t *testing.T) { + jstr := `{ "abc": [ 1, 2, 3 ] }` + + examples := []struct { + description string + path []types.PathElement + expected string + arg any + isErr bool + }{ + { + description: "push array item", + path: testPath("abc"), + arg: true, + expected: `{ "abc": [ 1, 2, 3, true ]}`, + }, + { + description: "push into scalar", + path: testPath("abc", 0), + arg: true, + isErr: true, + }, + } + + for idx, ex := range examples { + node := simplejson.MustParse([]byte(jstr)) + + err := Push(node, ex.path, ex.arg) + + if ex.isErr { + assert.Error(t, err, "[Ex %d - %s]", idx+1, ex.description) + continue + } else { + assert.NoError(t, err, "[Ex %d - %s]", idx+1, ex.description) + } + + expected := simplejson.MustParse([]byte(ex.expected)) + + assert.Equal(t, expected.Value(), node.Value(), "[Ex %d - %s]", idx+1, ex.description) + } + +}