Simplified merge command

This commit is contained in:
Mike Farah 2018-06-15 16:40:52 +10:00
parent 08870f8ec9
commit 8ca85b1c64
4 changed files with 37 additions and 93 deletions

View File

@ -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 "))
}

View File

@ -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)

View File

@ -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)
}

78
yq.go
View File

@ -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 <filename> <path_to_delete>")
}
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) {