From 44ee869e472f74159bd643c6680c4f8e898e76f5 Mon Sep 17 00:00:00 2001 From: Mike Farah Date: Mon, 27 Feb 2017 09:01:52 +1100 Subject: [PATCH] Maintain order --- data_navigator.go | 43 +++++++++++++++++++++++++++++------------- data_navigator_test.go | 21 ++++++++++++++++----- json_converter.go | 9 +++++---- order.yaml | 2 ++ order.yml | 6 ++++++ utils_test.go | 4 ++-- yaml.go | 5 +++-- yaml_test.go | 16 +++++++++++++++- 8 files changed, 79 insertions(+), 27 deletions(-) create mode 100644 order.yaml create mode 100644 order.yml diff --git a/data_navigator.go b/data_navigator.go index 9f74d6d9..17f19bbe 100644 --- a/data_navigator.go +++ b/data_navigator.go @@ -2,21 +2,34 @@ package main import ( // "fmt" + "github.com/mikefarah/yaml/Godeps/_workspace/src/gopkg.in/yaml.v2" "strconv" ) -func write(context map[interface{}]interface{}, head string, tail []string, value interface{}) { +func entryInSlice(context yaml.MapSlice, key interface{}) *yaml.MapItem { + for idx := range context { + var entry = &context[idx] + if entry.Key == key { + return entry + } + } + return nil +} + +func write(context yaml.MapSlice, head string, tail []string, value interface{}) { if len(tail) == 0 { - context[head] = value + var entry = entryInSlice(context, head) + entry.Value = value } else { // e.g. if updating a.b.c, we need to get the 'b', this could be a map or an array var parent = readMap(context, head, tail[0:len(tail)-1]) switch parent.(type) { - case map[interface{}]interface{}: - toUpdate := parent.(map[interface{}]interface{}) + case yaml.MapSlice: + toUpdate := parent.(yaml.MapSlice) // b is a map, update the key 'c' to the supplied value key := (tail[len(tail)-1]) - toUpdate[key] = value + toUpdateEntry := entryInSlice(toUpdate, key) + toUpdateEntry.Value = value case []interface{}: toUpdate := parent.([]interface{}) // b is an array, update it at index 'c' to the supplied value @@ -31,22 +44,26 @@ func write(context map[interface{}]interface{}, head string, tail []string, valu } } -func readMap(context map[interface{}]interface{}, head string, tail []string) interface{} { +func readMap(context yaml.MapSlice, head string, tail []string) interface{} { if head == "*" { return readMapSplat(context, tail) } - value := context[head] + entry := entryInSlice(context, head) + var value interface{} + if entry != nil { + value = entry.Value + } return calculateValue(value, tail) } -func readMapSplat(context map[interface{}]interface{}, tail []string) interface{} { +func readMapSplat(context yaml.MapSlice, tail []string) interface{} { var newArray = make([]interface{}, len(context)) var i = 0 - for _, value := range context { + for _, entry := range context { if len(tail) > 0 { - newArray[i] = recurse(value, tail[0], tail[1:len(tail)]) + newArray[i] = recurse(entry.Value, tail[0], tail[1:len(tail)]) } else { - newArray[i] = value + newArray[i] = entry.Value } i++ } @@ -64,8 +81,8 @@ func recurse(value interface{}, head string, tail []string) interface{} { die("Error accessing array: %v", err) } return readArray(value.([]interface{}), index, tail) - case map[interface{}]interface{}: - return readMap(value.(map[interface{}]interface{}), head, tail) + case yaml.MapSlice: + return readMap(value.(yaml.MapSlice), head, tail) default: return nil } diff --git a/data_navigator_test.go b/data_navigator_test.go index 114d72de..d6ce05d4 100644 --- a/data_navigator_test.go +++ b/data_navigator_test.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "github.com/mikefarah/yaml/Godeps/_workspace/src/gopkg.in/yaml.v2" "sort" "testing" ) @@ -107,6 +108,16 @@ e: assertResult(t, "[Fred Sam]", fmt.Sprintf("%v", readMap(data, "e", []string{"*", "name"}))) } +func TestWrite_really_simple(t *testing.T) { + var data = parseData(` + b: 2 +`) + + write(data, "b", []string{}, "4") + b := entryInSlice(data, "b").Value + assertResult(t, "4", b) +} + func TestWrite_simple(t *testing.T) { var data = parseData(` b: @@ -114,9 +125,9 @@ b: `) write(data, "b", []string{"c"}, "4") - - b := data["b"].(map[interface{}]interface{}) - assertResult(t, "4", b["c"].(string)) + b := entryInSlice(data, "b").Value.(yaml.MapSlice) + c := entryInSlice(b, "c").Value + assertResult(t, "4", c) } func TestWrite_array(t *testing.T) { @@ -127,7 +138,7 @@ b: write(data, "b", []string{"0"}, "bb") - b := data["b"].([]interface{}) + b := entryInSlice(data, "b").Value.([]interface{}) assertResult(t, "bb", b[0].(string)) } @@ -138,6 +149,6 @@ b: `) write(data, "b", []string{}, "4") - b := data["b"] + b := entryInSlice(data, "b").Value assertResult(t, "4", fmt.Sprintf("%v", b)) } diff --git a/json_converter.go b/json_converter.go index d9b9e1fd..893ca798 100644 --- a/json_converter.go +++ b/json_converter.go @@ -2,6 +2,7 @@ package main import ( "encoding/json" + "github.com/mikefarah/yaml/Godeps/_workspace/src/gopkg.in/yaml.v2" ) func fromJSONBytes(jsonBytes []byte, parsedData *map[interface{}]interface{}) { @@ -55,11 +56,11 @@ func toJSON(context interface{}) interface{} { newArray[index] = toJSON(value) } return newArray - case map[interface{}]interface{}: - oldMap := context.(map[interface{}]interface{}) + case yaml.MapSlice: + oldMap := context.(yaml.MapSlice) newMap := make(map[string]interface{}) - for key, value := range oldMap { - newMap[key.(string)] = toJSON(value) + for _, entry := range oldMap { + newMap[entry.Key.(string)] = toJSON(entry.Value) } return newMap default: diff --git a/order.yaml b/order.yaml new file mode 100644 index 00000000..b3d896bf --- /dev/null +++ b/order.yaml @@ -0,0 +1,2 @@ +version: 3 +application: MyApp \ No newline at end of file diff --git a/order.yml b/order.yml new file mode 100644 index 00000000..2dc6204b --- /dev/null +++ b/order.yml @@ -0,0 +1,6 @@ +version: '2' +services: + test: + image: ubuntu:14.04 + stdin_open: true + tty: true \ No newline at end of file diff --git a/utils_test.go b/utils_test.go index 3c7a75d1..948c8adc 100644 --- a/utils_test.go +++ b/utils_test.go @@ -7,8 +7,8 @@ import ( "testing" ) -func parseData(rawData string) map[interface{}]interface{} { - var parsedData map[interface{}]interface{} +func parseData(rawData string) yaml.MapSlice { + var parsedData yaml.MapSlice err := yaml.Unmarshal([]byte(rawData), &parsedData) if err != nil { fmt.Println("Error parsing yaml: %v", err) diff --git a/yaml.go b/yaml.go index 33085ffd..825e8793 100644 --- a/yaml.go +++ b/yaml.go @@ -81,7 +81,7 @@ func readProperty(cmd *cobra.Command, args []string) { } func read(args []string) interface{} { - var parsedData map[interface{}]interface{} + var parsedData yaml.MapSlice readData(args[0], &parsedData, inputJSON) @@ -114,13 +114,14 @@ func updateYaml(args []string) interface{} { writeCommands[args[1]] = parseValue(args[2]) } - var parsedData map[interface{}]interface{} + var parsedData yaml.MapSlice readData(args[0], &parsedData, inputJSON) for path, value := range writeCommands { var paths = parsePath(path) write(parsedData, paths[0], paths[1:len(paths)], value) } + return parsedData } diff --git a/yaml_test.go b/yaml_test.go index 9365040f..0cd22e35 100644 --- a/yaml_test.go +++ b/yaml_test.go @@ -1,6 +1,7 @@ package main import ( + "fmt" "testing" ) @@ -26,8 +27,21 @@ func TestRead(t *testing.T) { assertResult(t, 2, result) } +func TestOrder(t *testing.T) { + result := read([]string{"order.yaml"}) + formattedResult := yamlToString(result) + assertResult(t, + `version: 3 +application: MyApp`, + formattedResult) +} + func TestUpdateYaml(t *testing.T) { - updateYaml([]string{"sample.yaml", "b.c", "3"}) + result := updateYaml([]string{"sample.yaml", "b.c", "3"}) + formattedResult := fmt.Sprintf("%v", result) + assertResult(t, + "[{a Easy! as one two three} {b [{c 3} {d [3 4]} {e [[{name fred} {value 3}] [{name sam} {value 4}]]}]}]", + formattedResult) } func TestUpdateYaml_WithScript(t *testing.T) {