Maintain order

This commit is contained in:
Mike Farah 2017-02-27 09:01:52 +11:00
parent 5d2d37a5f8
commit 44ee869e47
8 changed files with 79 additions and 27 deletions

View File

@ -2,21 +2,34 @@ package main
import ( import (
// "fmt" // "fmt"
"github.com/mikefarah/yaml/Godeps/_workspace/src/gopkg.in/yaml.v2"
"strconv" "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 { if len(tail) == 0 {
context[head] = value var entry = entryInSlice(context, head)
entry.Value = value
} else { } else {
// e.g. if updating a.b.c, we need to get the 'b', this could be a map or an array // 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]) var parent = readMap(context, head, tail[0:len(tail)-1])
switch parent.(type) { switch parent.(type) {
case map[interface{}]interface{}: case yaml.MapSlice:
toUpdate := parent.(map[interface{}]interface{}) toUpdate := parent.(yaml.MapSlice)
// b is a map, update the key 'c' to the supplied value // b is a map, update the key 'c' to the supplied value
key := (tail[len(tail)-1]) key := (tail[len(tail)-1])
toUpdate[key] = value toUpdateEntry := entryInSlice(toUpdate, key)
toUpdateEntry.Value = value
case []interface{}: case []interface{}:
toUpdate := parent.([]interface{}) toUpdate := parent.([]interface{})
// b is an array, update it at index 'c' to the supplied value // 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 == "*" { if head == "*" {
return readMapSplat(context, tail) return readMapSplat(context, tail)
} }
value := context[head] entry := entryInSlice(context, head)
var value interface{}
if entry != nil {
value = entry.Value
}
return calculateValue(value, tail) 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 newArray = make([]interface{}, len(context))
var i = 0 var i = 0
for _, value := range context { for _, entry := range context {
if len(tail) > 0 { 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 { } else {
newArray[i] = value newArray[i] = entry.Value
} }
i++ i++
} }
@ -64,8 +81,8 @@ func recurse(value interface{}, head string, tail []string) interface{} {
die("Error accessing array: %v", err) die("Error accessing array: %v", err)
} }
return readArray(value.([]interface{}), index, tail) return readArray(value.([]interface{}), index, tail)
case map[interface{}]interface{}: case yaml.MapSlice:
return readMap(value.(map[interface{}]interface{}), head, tail) return readMap(value.(yaml.MapSlice), head, tail)
default: default:
return nil return nil
} }

View File

@ -2,6 +2,7 @@ package main
import ( import (
"fmt" "fmt"
"github.com/mikefarah/yaml/Godeps/_workspace/src/gopkg.in/yaml.v2"
"sort" "sort"
"testing" "testing"
) )
@ -107,6 +108,16 @@ e:
assertResult(t, "[Fred Sam]", fmt.Sprintf("%v", readMap(data, "e", []string{"*", "name"}))) 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) { func TestWrite_simple(t *testing.T) {
var data = parseData(` var data = parseData(`
b: b:
@ -114,9 +125,9 @@ b:
`) `)
write(data, "b", []string{"c"}, "4") write(data, "b", []string{"c"}, "4")
b := entryInSlice(data, "b").Value.(yaml.MapSlice)
b := data["b"].(map[interface{}]interface{}) c := entryInSlice(b, "c").Value
assertResult(t, "4", b["c"].(string)) assertResult(t, "4", c)
} }
func TestWrite_array(t *testing.T) { func TestWrite_array(t *testing.T) {
@ -127,7 +138,7 @@ b:
write(data, "b", []string{"0"}, "bb") write(data, "b", []string{"0"}, "bb")
b := data["b"].([]interface{}) b := entryInSlice(data, "b").Value.([]interface{})
assertResult(t, "bb", b[0].(string)) assertResult(t, "bb", b[0].(string))
} }
@ -138,6 +149,6 @@ b:
`) `)
write(data, "b", []string{}, "4") write(data, "b", []string{}, "4")
b := data["b"] b := entryInSlice(data, "b").Value
assertResult(t, "4", fmt.Sprintf("%v", b)) assertResult(t, "4", fmt.Sprintf("%v", b))
} }

View File

@ -2,6 +2,7 @@ package main
import ( import (
"encoding/json" "encoding/json"
"github.com/mikefarah/yaml/Godeps/_workspace/src/gopkg.in/yaml.v2"
) )
func fromJSONBytes(jsonBytes []byte, parsedData *map[interface{}]interface{}) { func fromJSONBytes(jsonBytes []byte, parsedData *map[interface{}]interface{}) {
@ -55,11 +56,11 @@ func toJSON(context interface{}) interface{} {
newArray[index] = toJSON(value) newArray[index] = toJSON(value)
} }
return newArray return newArray
case map[interface{}]interface{}: case yaml.MapSlice:
oldMap := context.(map[interface{}]interface{}) oldMap := context.(yaml.MapSlice)
newMap := make(map[string]interface{}) newMap := make(map[string]interface{})
for key, value := range oldMap { for _, entry := range oldMap {
newMap[key.(string)] = toJSON(value) newMap[entry.Key.(string)] = toJSON(entry.Value)
} }
return newMap return newMap
default: default:

2
order.yaml Normal file
View File

@ -0,0 +1,2 @@
version: 3
application: MyApp

6
order.yml Normal file
View File

@ -0,0 +1,6 @@
version: '2'
services:
test:
image: ubuntu:14.04
stdin_open: true
tty: true

View File

@ -7,8 +7,8 @@ import (
"testing" "testing"
) )
func parseData(rawData string) map[interface{}]interface{} { func parseData(rawData string) yaml.MapSlice {
var parsedData map[interface{}]interface{} var parsedData yaml.MapSlice
err := yaml.Unmarshal([]byte(rawData), &parsedData) err := yaml.Unmarshal([]byte(rawData), &parsedData)
if err != nil { if err != nil {
fmt.Println("Error parsing yaml: %v", err) fmt.Println("Error parsing yaml: %v", err)

View File

@ -81,7 +81,7 @@ func readProperty(cmd *cobra.Command, args []string) {
} }
func read(args []string) interface{} { func read(args []string) interface{} {
var parsedData map[interface{}]interface{} var parsedData yaml.MapSlice
readData(args[0], &parsedData, inputJSON) readData(args[0], &parsedData, inputJSON)
@ -114,13 +114,14 @@ func updateYaml(args []string) interface{} {
writeCommands[args[1]] = parseValue(args[2]) writeCommands[args[1]] = parseValue(args[2])
} }
var parsedData map[interface{}]interface{} var parsedData yaml.MapSlice
readData(args[0], &parsedData, inputJSON) readData(args[0], &parsedData, inputJSON)
for path, value := range writeCommands { for path, value := range writeCommands {
var paths = parsePath(path) var paths = parsePath(path)
write(parsedData, paths[0], paths[1:len(paths)], value) write(parsedData, paths[0], paths[1:len(paths)], value)
} }
return parsedData return parsedData
} }

View File

@ -1,6 +1,7 @@
package main package main
import ( import (
"fmt"
"testing" "testing"
) )
@ -26,8 +27,21 @@ func TestRead(t *testing.T) {
assertResult(t, 2, result) 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) { 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) { func TestUpdateYaml_WithScript(t *testing.T) {