From 8ca85b1c64ace86962e205a788c221ee6999a962 Mon Sep 17 00:00:00 2001 From: Mike Farah Date: Fri, 15 Jun 2018 16:40:52 +1000 Subject: [PATCH] Simplified merge command --- commands_test.go | 4 +-- data_navigator.go | 18 ----------- merge_test.go | 30 ------------------ yq.go | 78 +++++++++++++++++++++-------------------------- 4 files changed, 37 insertions(+), 93 deletions(-) delete mode 100644 merge_test.go diff --git a/commands_test.go b/commands_test.go index 68b05e4c..c0cf43e3 100644 --- a/commands_test.go +++ b/commands_test.go @@ -601,7 +601,7 @@ func TestMergeCmd_ErrorUnreadableFile(t *testing.T) { if result.Error == nil { t.Error("Expected command to fail due to unknown file") } - expectedOutput := `open fake-unknown: no such file or directory` + expectedOutput := `Error updating document at index 0: open fake-unknown: no such file or directory` assertResult(t, expectedOutput, result.Error.Error()) } @@ -637,5 +637,5 @@ b: - 2 c: test: 1` - assertResult(t, expectedOutput, gotOutput) + assertResult(t, expectedOutput, strings.Trim(gotOutput, "\n ")) } diff --git a/data_navigator.go b/data_navigator.go index 1961aca5..44460353 100644 --- a/data_navigator.go +++ b/data_navigator.go @@ -2,7 +2,6 @@ package main import ( "fmt" - "sort" "strconv" "gopkg.in/yaml.v2" @@ -194,23 +193,6 @@ func calculateValue(value interface{}, tail []string) (interface{}, error) { return value, nil } -func mapToMapSlice(data map[interface{}]interface{}) yaml.MapSlice { - var mapSlice yaml.MapSlice - - for k, v := range data { - if mv, ok := v.(map[interface{}]interface{}); ok { - v = mapToMapSlice(mv) - } - item := yaml.MapItem{Key: k, Value: v} - mapSlice = append(mapSlice, item) - } - - // because the parsing of the yaml was done via a map the order will be inconsistent - // apply order to allow a consistent output - sort.SliceStable(mapSlice, func(i, j int) bool { return mapSlice[i].Key.(string) < mapSlice[j].Key.(string) }) - return mapSlice -} - func deleteMap(context interface{}, paths []string) yaml.MapSlice { log.Debugf("deleteMap for %v for %v\n", paths, context) diff --git a/merge_test.go b/merge_test.go deleted file mode 100644 index 4509a8aa..00000000 --- a/merge_test.go +++ /dev/null @@ -1,30 +0,0 @@ -package main - -import ( - "testing" - - yaml "gopkg.in/yaml.v2" -) - -func TestMerge(t *testing.T) { - result, _ := mergeYaml([]string{"examples/data1.yaml", "examples/data2.yaml", "examples/data3.yaml"}) - expected := yaml.MapSlice{ - yaml.MapItem{Key: "a", Value: "simple"}, - yaml.MapItem{Key: "b", Value: []interface{}{1, 2}}, - yaml.MapItem{Key: "c", Value: yaml.MapSlice{yaml.MapItem{Key: "other", Value: true}, yaml.MapItem{Key: "test", Value: 1}}}, - yaml.MapItem{Key: "d", Value: false}, - } - assertResultComplex(t, expected, result) -} - -func TestMergeWithOverwrite(t *testing.T) { - overwriteFlag = true - result, _ := mergeYaml([]string{"examples/data1.yaml", "examples/data2.yaml", "examples/data3.yaml"}) - expected := yaml.MapSlice{ - yaml.MapItem{Key: "a", Value: "other"}, - yaml.MapItem{Key: "b", Value: []interface{}{2, 3, 4}}, - yaml.MapItem{Key: "c", Value: yaml.MapSlice{yaml.MapItem{Key: "other", Value: true}, yaml.MapItem{Key: "test", Value: 2}}}, - yaml.MapItem{Key: "d", Value: false}, - } - assertResultComplex(t, expected, result) -} diff --git a/yq.go b/yq.go index d60e3243..359b576c 100644 --- a/yq.go +++ b/yq.go @@ -275,13 +275,14 @@ func newYaml(args []string) (interface{}, error) { return dataBucket, nil } -type updateDataFn func(dataBucket interface{}, currentIndex int) interface{} +type updateDataFn func(dataBucket interface{}, currentIndex int) (interface{}, error) func mapYamlDecoder(updateData updateDataFn, encoder *yaml.Encoder) yamlDecoderFn { return func(decoder *yaml.Decoder) error { var dataBucket interface{} var errorReading error var errorWriting error + var errorUpdating error var currentIndex = 0 for { @@ -296,7 +297,10 @@ func mapYamlDecoder(updateData updateDataFn, encoder *yaml.Encoder) yamlDecoderF } else if errorReading != nil { return errors.Wrapf(errorReading, "Error reading document at index %v, %v", currentIndex, errorReading) } - dataBucket = updateData(dataBucket, currentIndex) + dataBucket, errorUpdating = updateData(dataBucket, currentIndex) + if errorUpdating != nil { + return errors.Wrapf(errorUpdating, "Error updating document at index %v", currentIndex) + } errorWriting = encoder.Encode(dataBucket) @@ -313,7 +317,7 @@ func writeProperty(cmd *cobra.Command, args []string) error { if writeCommandsError != nil { return writeCommandsError } - var updateData = func(dataBucket interface{}, currentIndex int) interface{} { + var updateData = func(dataBucket interface{}, currentIndex int) (interface{}, error) { if currentIndex == docIndex { log.Debugf("Updating doc %v", currentIndex) for _, entry := range writeCommands { @@ -324,7 +328,7 @@ func writeProperty(cmd *cobra.Command, args []string) error { dataBucket = updatedChildValue(dataBucket, paths, value) } } - return dataBucket + return dataBucket, nil } return readAndUpdate(cmd.OutOrStdout(), args[0], updateData) } @@ -354,34 +358,18 @@ func readAndUpdate(stdOut io.Writer, inputFile string, updateData updateDataFn) return readStream(inputFile, mapYamlDecoder(updateData, encoder)) } -func write(cmd *cobra.Command, filename string, updatedData interface{}) error { - if writeInplace { - dataStr, err := yamlToString(updatedData) - if err != nil { - return err - } - return ioutil.WriteFile(filename, []byte(dataStr), 0644) - } - dataStr, err := toString(updatedData) - if err != nil { - return err - } - cmd.Println(dataStr) - return nil -} - func deleteProperty(cmd *cobra.Command, args []string) error { if len(args) < 2 { return errors.New("Must provide ") } var deletePath = args[1] var paths = parsePath(deletePath) - var updateData = func(dataBucket interface{}, currentIndex int) interface{} { + var updateData = func(dataBucket interface{}, currentIndex int) (interface{}, error) { if currentIndex == docIndex { - log.Debugf("Updating doc %v", currentIndex) - return deleteChildValue(dataBucket, paths) + log.Debugf("Deleting path in doc %v", currentIndex) + return deleteChildValue(dataBucket, paths), nil } - return dataBucket + return dataBucket, nil } return readAndUpdate(cmd.OutOrStdout(), args[0], updateData) @@ -391,28 +379,32 @@ func mergeProperties(cmd *cobra.Command, args []string) error { if len(args) < 2 { return errors.New("Must provide at least 2 yaml files") } + var input = args[0] + var filesToMerge = args[1:] - updatedData, err := mergeYaml(args) - if err != nil { - return err - } - return write(cmd, args[0], updatedData) -} - -func mergeYaml(args []string) (interface{}, error) { - var updatedData map[interface{}]interface{} - - for _, f := range args { - var parsedData map[interface{}]interface{} - if err := readData(f, 0, &parsedData); err != nil { - return nil, err - } - if err := merge(&updatedData, parsedData, overwriteFlag); err != nil { - return nil, err + var updateData = func(dataBucket interface{}, currentIndex int) (interface{}, error) { + if currentIndex == docIndex { + log.Debugf("Merging doc %v", currentIndex) + var mergedData map[interface{}]interface{} + if err := merge(&mergedData, dataBucket, overwriteFlag); err != nil { + return nil, err + } + for _, f := range filesToMerge { + var fileToMerge interface{} + if err := readData(f, 0, &fileToMerge); err != nil { + return nil, err + } + if err := merge(&mergedData, fileToMerge, overwriteFlag); err != nil { + return nil, err + } + } + return mergedData, nil } + return dataBucket, nil } - - return mapToMapSlice(updatedData), nil + yaml.DefaultMapType = reflect.TypeOf(map[interface{}]interface{}{}) + defer func() { yaml.DefaultMapType = reflect.TypeOf(yaml.MapSlice{}) }() + return readAndUpdate(cmd.OutOrStdout(), input, updateData) } func readWriteCommands(args []string, expectedArgs int, badArgsMessage string) (yaml.MapSlice, error) {