From b2fe3e67385d77a72d7ef9f7acb39d7d6e58f6fc Mon Sep 17 00:00:00 2001 From: Mike Farah Date: Tue, 14 May 2019 11:20:41 +1000 Subject: [PATCH] fixing delete splat --- commands_test.go | 32 +++++++++++++++++ data_navigator.go | 78 ++++++++++++++++++++++++++++-------------- data_navigator_test.go | 14 ++++---- yq.go | 2 +- 4 files changed, 92 insertions(+), 34 deletions(-) diff --git a/commands_test.go b/commands_test.go index 686e20fa..e01fb700 100644 --- a/commands_test.go +++ b/commands_test.go @@ -874,6 +874,9 @@ b: hi: c: things d: something else + hello: + c: things2 + d: something else2 there: c: more things d: more something else @@ -891,12 +894,41 @@ b: b: hi: d: something else + hello: + d: something else2 there: d: more something else ` assertResult(t, expectedOutput, result.Output) } +func TestDeleteSplatArrayYaml(t *testing.T) { + content := `a: 2 +b: + hi: + - thing: item1 + name: fred + - thing: item2 + name: sam +` + filename := writeTempYamlFile(content) + defer removeTempYamlFile(filename) + + cmd := getRootCommand() + result := runCmd(cmd, fmt.Sprintf("delete -v %s b.hi[*].thing", filename)) + if result.Error != nil { + t.Error(result.Error) + } + + expectedOutput := `a: 2 +b: + hi: + - name: fred + - name: sam +` + assertResult(t, expectedOutput, result.Output) +} + func TestDeleteSplatPrefixYaml(t *testing.T) { content := `a: 2 b: diff --git a/data_navigator.go b/data_navigator.go index 22227c66..c44a2b07 100644 --- a/data_navigator.go +++ b/data_navigator.go @@ -232,13 +232,13 @@ func calculateValue(value interface{}, tail []string) (interface{}, error) { return value, nil } -func deleteMap(context interface{}, paths []string) yaml.MapSlice { +func deleteMap(context interface{}, paths []string) (yaml.MapSlice, error) { log.Debugf("deleteMap for %v for %v\n", paths, context) mapSlice := getMapSlice(context) if len(paths) == 0 { - return mapSlice + return mapSlice, nil } var index int @@ -246,21 +246,29 @@ func deleteMap(context interface{}, paths []string) yaml.MapSlice { for index, child = range mapSlice { if matchesKey(paths[0], child.Key) { log.Debugf("\tMatched [%v] with [%v] at index %v", paths[0], child.Key, index) - mapSlice = deleteEntryInMap(mapSlice, child, index, paths) + var badDelete error + mapSlice, badDelete = deleteEntryInMap(mapSlice, child, index, paths) + if badDelete != nil { + return nil, badDelete + } } } - return mapSlice + return mapSlice, nil } -func deleteEntryInMap(original yaml.MapSlice, child yaml.MapItem, index int, paths []string) yaml.MapSlice { +func deleteEntryInMap(original yaml.MapSlice, child yaml.MapItem, index int, paths []string) (yaml.MapSlice, error) { remainingPaths := paths[1:] var newSlice yaml.MapSlice if len(remainingPaths) > 0 { newChild := yaml.MapItem{Key: child.Key} - newChild.Value = deleteChildValue(child.Value, remainingPaths) + var errorDeleting error + newChild.Value, errorDeleting = deleteChildValue(child.Value, remainingPaths) + if errorDeleting != nil { + return nil, errorDeleting + } newSlice = make(yaml.MapSlice, len(original)) for i := range original { @@ -277,26 +285,38 @@ func deleteEntryInMap(original yaml.MapSlice, child yaml.MapItem, index int, pat } log.Debugf("\tReturning original %v\n", original) - return newSlice + return newSlice, nil } -func deleteArray(context interface{}, paths []string, index int64) interface{} { - log.Debugf("deleteArray for %v for %v\n", paths, context) - - array, ok := getArray(context) - if !ok { - // did not get an array - return context +func deleteArraySplat(array []interface{}, tail []string) (interface{}, error) { + log.Debugf("deleteArraySplat for %v for %v\n", tail, array) + var newArray = make([]interface{}, len(array)) + for index, value := range array { + val, err := deleteChildValue(value, tail) + if err != nil { + return nil, err + } + newArray[index] = val } + return newArray, nil +} + +func deleteArray(array []interface{}, paths []string, index int64) (interface{}, error) { + log.Debugf("deleteArray for %v for %v\n", paths, array) if index >= int64(len(array)) { - return array + return array, nil } remainingPaths := paths[1:] if len(remainingPaths) > 0 { // Recurse into the array element at index - array[index] = deleteMap(array[index], remainingPaths) + var errorDeleting error + array[index], errorDeleting = deleteMap(array[index], remainingPaths) + if errorDeleting != nil { + return nil, errorDeleting + } + } else { // Delete the array element at index array = append(array[:index], array[index+1:]...) @@ -304,19 +324,25 @@ func deleteArray(context interface{}, paths []string, index int64) interface{} { } log.Debugf("\tReturning array: %v\n", array) - return array + return array, nil } -func deleteChildValue(child interface{}, remainingPaths []string) interface{} { +func deleteChildValue(child interface{}, remainingPaths []string) (interface{}, error) { log.Debugf("deleteChildValue for %v for %v\n", remainingPaths, child) - - idx, nextIndexErr := strconv.ParseInt(remainingPaths[0], 10, 64) - if nextIndexErr != nil { - // must be a map - log.Debugf("\tdetected a map, invoking deleteMap\n") + var head = remainingPaths[0] + var tail = remainingPaths[1:] + switch child := child.(type) { + case yaml.MapSlice: return deleteMap(child, remainingPaths) + case []interface{}: + if head == "*" { + return deleteArraySplat(child, tail) + } + index, err := strconv.ParseInt(head, 10, 64) + if err != nil { + return nil, fmt.Errorf("error accessing array: %v", err) + } + return deleteArray(child, remainingPaths, index) } - - log.Debugf("\tdetected an array, so traversing element with index %d\n", idx) - return deleteArray(child, remainingPaths, idx) + return child, nil } diff --git a/data_navigator_test.go b/data_navigator_test.go index 873ff3ed..03e50a33 100644 --- a/data_navigator_test.go +++ b/data_navigator_test.go @@ -330,7 +330,7 @@ b: 456 b: 456 `) - result := deleteMap(data, []string{"a"}) + result, _ := deleteMap(data, []string{"a"}) assertResult(t, fmt.Sprintf("%v", expected), fmt.Sprintf("%v", result)) } @@ -339,7 +339,7 @@ func TestDelete_index_to_string(t *testing.T) { var data = parseData(` a: mystring `) - result := deleteMap(data, []string{"a", "0"}) + result, _ := deleteMap(data, []string{"a", "0"}) assertResult(t, fmt.Sprintf("%v", data), fmt.Sprintf("%v", result)) } @@ -350,7 +350,7 @@ a: [3, 4] var expected = parseData(` a: [3] `) - result := deleteMap(data, []string{"a", "1"}) + result, _ := deleteMap(data, []string{"a", "1"}) assertResult(t, fmt.Sprintf("%v", expected), fmt.Sprintf("%v", result)) } @@ -358,7 +358,7 @@ func TestDelete_list_index_beyond_bounds(t *testing.T) { var data = parseData(` a: [3, 4] `) - result := deleteMap(data, []string{"a", "5"}) + result, _ := deleteMap(data, []string{"a", "5"}) assertResult(t, fmt.Sprintf("%v", data), fmt.Sprintf("%v", result)) } @@ -366,7 +366,7 @@ func TestDelete_list_index_out_of_bounds_by_1(t *testing.T) { var data = parseData(` a: [3, 4] `) - result := deleteMap(data, []string{"a", "2"}) + result, _ := deleteMap(data, []string{"a", "2"}) assertResult(t, fmt.Sprintf("%v", data), fmt.Sprintf("%v", result)) } @@ -376,7 +376,7 @@ a: [3, 4] b: - name: test `) - result := deleteMap(data, []string{}) + result, _ := deleteMap(data, []string{}) assertResult(t, fmt.Sprintf("%v", data), fmt.Sprintf("%v", result)) } @@ -394,6 +394,6 @@ b: - name: john value: test `) - result := deleteMap(data, []string{"b", "0", "name"}) + result, _ := deleteMap(data, []string{"b", "0", "name"}) assertResult(t, fmt.Sprintf("%v", expected), fmt.Sprintf("%v", result)) } diff --git a/yq.go b/yq.go index deb18913..e28c7ead 100644 --- a/yq.go +++ b/yq.go @@ -503,7 +503,7 @@ func deleteProperty(cmd *cobra.Command, args []string) error { var updateData = func(dataBucket interface{}, currentIndex int) (interface{}, error) { if updateAll || currentIndex == docIndexInt { log.Debugf("Deleting path in doc %v", currentIndex) - return deleteChildValue(dataBucket, paths), nil + return deleteChildValue(dataBucket, paths) } return dataBucket, nil }