mirror of
https://github.com/mikefarah/yq.git
synced 2024-11-13 22:38:04 +00:00
Split marshal package from yqlib, implement interfaces
This commit is contained in:
parent
26a09e6ec0
commit
4b3fbb878f
@ -1,4 +1,4 @@
|
|||||||
package yqlib
|
package marshal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@ -8,21 +8,31 @@ import (
|
|||||||
yaml "github.com/mikefarah/yaml/v2"
|
yaml "github.com/mikefarah/yaml/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func JsonToString(context interface{}) (string, error) {
|
type JsonConverter interface {
|
||||||
out, err := json.Marshal(toJSON(context))
|
JsonToString(context interface{}) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type jsonConverter struct {}
|
||||||
|
|
||||||
|
func NewJsonConverter() JsonConverter {
|
||||||
|
return &jsonConverter{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *jsonConverter) JsonToString(context interface{}) (string, error) {
|
||||||
|
out, err := json.Marshal(j.toJSON(context))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("error printing yaml as json: %v", err)
|
return "", fmt.Errorf("error printing yaml as json: %v", err)
|
||||||
}
|
}
|
||||||
return string(out), nil
|
return string(out), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func toJSON(context interface{}) interface{} {
|
func (j *jsonConverter) toJSON(context interface{}) interface{} {
|
||||||
switch context := context.(type) {
|
switch context := context.(type) {
|
||||||
case []interface{}:
|
case []interface{}:
|
||||||
oldArray := context
|
oldArray := context
|
||||||
newArray := make([]interface{}, len(oldArray))
|
newArray := make([]interface{}, len(oldArray))
|
||||||
for index, value := range oldArray {
|
for index, value := range oldArray {
|
||||||
newArray[index] = toJSON(value)
|
newArray[index] = j.toJSON(value)
|
||||||
}
|
}
|
||||||
return newArray
|
return newArray
|
||||||
case yaml.MapSlice:
|
case yaml.MapSlice:
|
||||||
@ -30,11 +40,11 @@ func toJSON(context interface{}) interface{} {
|
|||||||
newMap := make(map[string]interface{})
|
newMap := make(map[string]interface{})
|
||||||
for _, entry := range oldMap {
|
for _, entry := range oldMap {
|
||||||
if str, ok := entry.Key.(string); ok {
|
if str, ok := entry.Key.(string); ok {
|
||||||
newMap[str] = toJSON(entry.Value)
|
newMap[str] = j.toJSON(entry.Value)
|
||||||
} else if i, ok := entry.Key.(int); ok {
|
} else if i, ok := entry.Key.(int); ok {
|
||||||
newMap[strconv.Itoa(i)] = toJSON(entry.Value)
|
newMap[strconv.Itoa(i)] = j.toJSON(entry.Value)
|
||||||
} else if b, ok := entry.Key.(bool); ok {
|
} else if b, ok := entry.Key.(bool); ok {
|
||||||
newMap[strconv.FormatBool(b)] = toJSON(entry.Value)
|
newMap[strconv.FormatBool(b)] = j.toJSON(entry.Value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return newMap
|
return newMap
|
@ -1,4 +1,4 @@
|
|||||||
package yqlib
|
package marshal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
@ -11,7 +11,7 @@ func TestJsonToString(t *testing.T) {
|
|||||||
b:
|
b:
|
||||||
c: 2
|
c: 2
|
||||||
`)
|
`)
|
||||||
got, _ := JsonToString(data)
|
got, _ := NewJsonConverter().JsonToString(data)
|
||||||
test.AssertResult(t, "{\"b\":{\"c\":2}}", got)
|
test.AssertResult(t, "{\"b\":{\"c\":2}}", got)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ func TestJsonToString_withIntKey(t *testing.T) {
|
|||||||
b:
|
b:
|
||||||
2: c
|
2: c
|
||||||
`)
|
`)
|
||||||
got, _ := JsonToString(data)
|
got, _ := NewJsonConverter().JsonToString(data)
|
||||||
test.AssertResult(t, `{"b":{"2":"c"}}`, got)
|
test.AssertResult(t, `{"b":{"2":"c"}}`, got)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,7 +31,7 @@ func TestJsonToString_withBoolKey(t *testing.T) {
|
|||||||
b:
|
b:
|
||||||
false: c
|
false: c
|
||||||
`)
|
`)
|
||||||
got, _ := JsonToString(data)
|
got, _ := NewJsonConverter().JsonToString(data)
|
||||||
test.AssertResult(t, `{"b":{"false":"c"}}`, got)
|
test.AssertResult(t, `{"b":{"false":"c"}}`, got)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,6 +42,6 @@ b:
|
|||||||
- item: one
|
- item: one
|
||||||
- item: two
|
- item: two
|
||||||
`)
|
`)
|
||||||
got, _ := JsonToString(data)
|
got, _ := NewJsonConverter().JsonToString(data)
|
||||||
test.AssertResult(t, "{\"b\":[{\"item\":\"one\"},{\"item\":\"two\"}]}", got)
|
test.AssertResult(t, "{\"b\":[{\"item\":\"one\"},{\"item\":\"two\"}]}", got)
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package yqlib
|
package marshal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
yaml "github.com/mikefarah/yaml/v2"
|
yaml "github.com/mikefarah/yaml/v2"
|
||||||
@ -6,16 +6,26 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func YamlToString(context interface{}, trimOutput bool) (string, error) {
|
type YamlConverter interface {
|
||||||
|
YamlToString(context interface{}, trimOutput bool) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type yamlConverter struct {}
|
||||||
|
|
||||||
|
func NewYamlConverter() YamlConverter {
|
||||||
|
return &yamlConverter{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (y *yamlConverter) YamlToString(context interface{}, trimOutput bool) (string, error) {
|
||||||
switch context := context.(type) {
|
switch context := context.(type) {
|
||||||
case string:
|
case string:
|
||||||
return context, nil
|
return context, nil
|
||||||
default:
|
default:
|
||||||
return marshalContext(context, trimOutput)
|
return y.marshalContext(context, trimOutput)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func marshalContext(context interface{}, trimOutput bool) (string, error) {
|
func (y *yamlConverter) marshalContext(context interface{}, trimOutput bool) (string, error) {
|
||||||
out, err := yaml.Marshal(context)
|
out, err := yaml.Marshal(context)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
@ -7,9 +7,96 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
yaml "github.com/mikefarah/yaml/v2"
|
yaml "github.com/mikefarah/yaml/v2"
|
||||||
|
logging "gopkg.in/op/go-logging.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func matchesKey(key string, actual interface{}) bool {
|
type DataNavigator interface {
|
||||||
|
ReadChildValue(child interface{}, remainingPaths []string) (interface{}, error)
|
||||||
|
UpdatedChildValue(child interface{}, remainingPaths []string, value interface{}) interface{}
|
||||||
|
DeleteChildValue(child interface{}, remainingPaths []string) (interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type navigator struct {
|
||||||
|
log *logging.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDataNavigator(l *logging.Logger) DataNavigator {
|
||||||
|
return &navigator {
|
||||||
|
log: l,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *navigator) ReadChildValue(child interface{}, remainingPaths []string) (interface{}, error) {
|
||||||
|
if len(remainingPaths) == 0 {
|
||||||
|
return child, nil
|
||||||
|
}
|
||||||
|
return n.recurse(child, remainingPaths[0], remainingPaths[1:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *navigator) UpdatedChildValue(child interface{}, remainingPaths []string, value interface{}) interface{} {
|
||||||
|
if len(remainingPaths) == 0 {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
n.log.Debugf("UpdatedChildValue for child %v with path %v to set value %v", child, remainingPaths, value)
|
||||||
|
n.log.Debugf("type of child is %v", reflect.TypeOf(child))
|
||||||
|
|
||||||
|
switch child := child.(type) {
|
||||||
|
case nil:
|
||||||
|
if remainingPaths[0] == "+" || remainingPaths[0] == "*" {
|
||||||
|
return n.writeArray(child, remainingPaths, value)
|
||||||
|
}
|
||||||
|
case []interface{}:
|
||||||
|
_, nextIndexErr := strconv.ParseInt(remainingPaths[0], 10, 64)
|
||||||
|
arrayCommand := nextIndexErr == nil || remainingPaths[0] == "+" || remainingPaths[0] == "*"
|
||||||
|
if arrayCommand {
|
||||||
|
return n.writeArray(child, remainingPaths, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n.writeMap(child, remainingPaths, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *navigator) DeleteChildValue(child interface{}, remainingPaths []string) (interface{}, error) {
|
||||||
|
n.log.Debugf("DeleteChildValue for %v for %v\n", remainingPaths, child)
|
||||||
|
if len(remainingPaths) == 0 {
|
||||||
|
return child, nil
|
||||||
|
}
|
||||||
|
var head = remainingPaths[0]
|
||||||
|
var tail = remainingPaths[1:]
|
||||||
|
switch child := child.(type) {
|
||||||
|
case yaml.MapSlice:
|
||||||
|
return n.deleteMap(child, remainingPaths)
|
||||||
|
case []interface{}:
|
||||||
|
if head == "*" {
|
||||||
|
return n.deleteArraySplat(child, tail)
|
||||||
|
}
|
||||||
|
index, err := strconv.ParseInt(head, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error accessing array: %v", err)
|
||||||
|
}
|
||||||
|
return n.deleteArray(child, remainingPaths, index)
|
||||||
|
}
|
||||||
|
return child, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *navigator) recurse(value interface{}, head string, tail []string) (interface{}, error) {
|
||||||
|
switch value := value.(type) {
|
||||||
|
case []interface{}:
|
||||||
|
if head == "*" {
|
||||||
|
return n.readArraySplat(value, tail)
|
||||||
|
}
|
||||||
|
index, err := strconv.ParseInt(head, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error accessing array: %v", err)
|
||||||
|
}
|
||||||
|
return n.readArray(value, index, tail)
|
||||||
|
case yaml.MapSlice:
|
||||||
|
return n.readMap(value, head, tail)
|
||||||
|
default:
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *navigator) matchesKey(key string, actual interface{}) bool {
|
||||||
var actualString = fmt.Sprintf("%v", actual)
|
var actualString = fmt.Sprintf("%v", actual)
|
||||||
var prefixMatch = strings.TrimSuffix(key, "*")
|
var prefixMatch = strings.TrimSuffix(key, "*")
|
||||||
if prefixMatch != key {
|
if prefixMatch != key {
|
||||||
@ -18,18 +105,18 @@ func matchesKey(key string, actual interface{}) bool {
|
|||||||
return actualString == key
|
return actualString == key
|
||||||
}
|
}
|
||||||
|
|
||||||
func entriesInSlice(context yaml.MapSlice, key string) []*yaml.MapItem {
|
func (n *navigator) entriesInSlice(context yaml.MapSlice, key string) []*yaml.MapItem {
|
||||||
var matches = make([]*yaml.MapItem, 0)
|
var matches = make([]*yaml.MapItem, 0)
|
||||||
for idx := range context {
|
for idx := range context {
|
||||||
var entry = &context[idx]
|
var entry = &context[idx]
|
||||||
if matchesKey(key, entry.Key) {
|
if n.matchesKey(key, entry.Key) {
|
||||||
matches = append(matches, entry)
|
matches = append(matches, entry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return matches
|
return matches
|
||||||
}
|
}
|
||||||
|
|
||||||
func getMapSlice(context interface{}) yaml.MapSlice {
|
func (n *navigator) getMapSlice(context interface{}) yaml.MapSlice {
|
||||||
var mapSlice yaml.MapSlice
|
var mapSlice yaml.MapSlice
|
||||||
switch context := context.(type) {
|
switch context := context.(type) {
|
||||||
case yaml.MapSlice:
|
case yaml.MapSlice:
|
||||||
@ -40,7 +127,7 @@ func getMapSlice(context interface{}) yaml.MapSlice {
|
|||||||
return mapSlice
|
return mapSlice
|
||||||
}
|
}
|
||||||
|
|
||||||
func getArray(context interface{}) (array []interface{}, ok bool) {
|
func (n *navigator) getArray(context interface{}) (array []interface{}, ok bool) {
|
||||||
switch context := context.(type) {
|
switch context := context.(type) {
|
||||||
case []interface{}:
|
case []interface{}:
|
||||||
array = context
|
array = context
|
||||||
@ -52,68 +139,46 @@ func getArray(context interface{}) (array []interface{}, ok bool) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeMap(context interface{}, paths []string, value interface{}) interface{} {
|
func (n *navigator) writeMap(context interface{}, paths []string, value interface{}) interface{} {
|
||||||
log.Debugf("writeMap with path %v for %v to set value %v\n", paths, context, value)
|
n.log.Debugf("writeMap with path %v for %v to set value %v\n", paths, context, value)
|
||||||
|
|
||||||
mapSlice := getMapSlice(context)
|
mapSlice := n.getMapSlice(context)
|
||||||
|
|
||||||
if len(paths) == 0 {
|
if len(paths) == 0 {
|
||||||
return context
|
return context
|
||||||
}
|
}
|
||||||
|
|
||||||
children := entriesInSlice(mapSlice, paths[0])
|
children := n.entriesInSlice(mapSlice, paths[0])
|
||||||
|
|
||||||
if len(children) == 0 && paths[0] == "*" {
|
if len(children) == 0 && paths[0] == "*" {
|
||||||
log.Debugf("\tNo matches, return map as is")
|
n.log.Debugf("\tNo matches, return map as is")
|
||||||
return context
|
return context
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(children) == 0 {
|
if len(children) == 0 {
|
||||||
newChild := yaml.MapItem{Key: paths[0]}
|
newChild := yaml.MapItem{Key: paths[0]}
|
||||||
mapSlice = append(mapSlice, newChild)
|
mapSlice = append(mapSlice, newChild)
|
||||||
children = entriesInSlice(mapSlice, paths[0])
|
children = n.entriesInSlice(mapSlice, paths[0])
|
||||||
log.Debugf("\tAppended child at %v for mapSlice %v\n", paths[0], mapSlice)
|
n.log.Debugf("\tAppended child at %v for mapSlice %v\n", paths[0], mapSlice)
|
||||||
}
|
}
|
||||||
|
|
||||||
remainingPaths := paths[1:]
|
remainingPaths := paths[1:]
|
||||||
for _, child := range children {
|
for _, child := range children {
|
||||||
child.Value = UpdatedChildValue(child.Value, remainingPaths, value)
|
child.Value = n.UpdatedChildValue(child.Value, remainingPaths, value)
|
||||||
}
|
}
|
||||||
log.Debugf("\tReturning mapSlice %v\n", mapSlice)
|
n.log.Debugf("\tReturning mapSlice %v\n", mapSlice)
|
||||||
return mapSlice
|
return mapSlice
|
||||||
}
|
}
|
||||||
|
|
||||||
func UpdatedChildValue(child interface{}, remainingPaths []string, value interface{}) interface{} {
|
func (n *navigator) writeArray(context interface{}, paths []string, value interface{}) []interface{} {
|
||||||
if len(remainingPaths) == 0 {
|
n.log.Debugf("writeArray with path %v for %v to set value %v\n", paths, context, value)
|
||||||
return value
|
array, _ := n.getArray(context)
|
||||||
}
|
|
||||||
log.Debugf("UpdatedChildValue for child %v with path %v to set value %v", child, remainingPaths, value)
|
|
||||||
log.Debugf("type of child is %v", reflect.TypeOf(child))
|
|
||||||
|
|
||||||
switch child := child.(type) {
|
|
||||||
case nil:
|
|
||||||
if remainingPaths[0] == "+" || remainingPaths[0] == "*" {
|
|
||||||
return writeArray(child, remainingPaths, value)
|
|
||||||
}
|
|
||||||
case []interface{}:
|
|
||||||
_, nextIndexErr := strconv.ParseInt(remainingPaths[0], 10, 64)
|
|
||||||
arrayCommand := nextIndexErr == nil || remainingPaths[0] == "+" || remainingPaths[0] == "*"
|
|
||||||
if arrayCommand {
|
|
||||||
return writeArray(child, remainingPaths, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return writeMap(child, remainingPaths, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeArray(context interface{}, paths []string, value interface{}) []interface{} {
|
|
||||||
log.Debugf("writeArray with path %v for %v to set value %v\n", paths, context, value)
|
|
||||||
array, _ := getArray(context)
|
|
||||||
|
|
||||||
if len(paths) == 0 {
|
if len(paths) == 0 {
|
||||||
return array
|
return array
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("\tarray %v\n", array)
|
n.log.Debugf("\tarray %v\n", array)
|
||||||
|
|
||||||
rawIndex := paths[0]
|
rawIndex := paths[0]
|
||||||
remainingPaths := paths[1:]
|
remainingPaths := paths[1:]
|
||||||
@ -123,7 +188,7 @@ func writeArray(context interface{}, paths []string, value interface{}) []interf
|
|||||||
index = int64(len(array))
|
index = int64(len(array))
|
||||||
} else if rawIndex == "*" {
|
} else if rawIndex == "*" {
|
||||||
for index, oldChild := range array {
|
for index, oldChild := range array {
|
||||||
array[index] = UpdatedChildValue(oldChild, remainingPaths, value)
|
array[index] = n.UpdatedChildValue(oldChild, remainingPaths, value)
|
||||||
}
|
}
|
||||||
return array
|
return array
|
||||||
} else {
|
} else {
|
||||||
@ -137,31 +202,31 @@ func writeArray(context interface{}, paths []string, value interface{}) []interf
|
|||||||
}
|
}
|
||||||
currentChild := array[index]
|
currentChild := array[index]
|
||||||
|
|
||||||
log.Debugf("\tcurrentChild %v\n", currentChild)
|
n.log.Debugf("\tcurrentChild %v\n", currentChild)
|
||||||
|
|
||||||
array[index] = UpdatedChildValue(currentChild, remainingPaths, value)
|
array[index] = n.UpdatedChildValue(currentChild, remainingPaths, value)
|
||||||
log.Debugf("\tReturning array %v\n", array)
|
n.log.Debugf("\tReturning array %v\n", array)
|
||||||
return array
|
return array
|
||||||
}
|
}
|
||||||
|
|
||||||
func readMap(context yaml.MapSlice, head string, tail []string) (interface{}, error) {
|
func (n *navigator) readMap(context yaml.MapSlice, head string, tail []string) (interface{}, error) {
|
||||||
log.Debugf("readingMap %v with key %v\n", context, head)
|
n.log.Debugf("readingMap %v with key %v\n", context, head)
|
||||||
if head == "*" {
|
if head == "*" {
|
||||||
return readMapSplat(context, tail)
|
return n.readMapSplat(context, tail)
|
||||||
}
|
}
|
||||||
|
|
||||||
entries := entriesInSlice(context, head)
|
entries := n.entriesInSlice(context, head)
|
||||||
if len(entries) == 1 {
|
if len(entries) == 1 {
|
||||||
return calculateValue(entries[0].Value, tail)
|
return n.calculateValue(entries[0].Value, tail)
|
||||||
} else if len(entries) == 0 {
|
} else if len(entries) == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
var errInIdx error
|
var errInIdx error
|
||||||
values := make([]interface{}, len(entries))
|
values := make([]interface{}, len(entries))
|
||||||
for idx, entry := range entries {
|
for idx, entry := range entries {
|
||||||
values[idx], errInIdx = calculateValue(entry.Value, tail)
|
values[idx], errInIdx = n.calculateValue(entry.Value, tail)
|
||||||
if errInIdx != nil {
|
if errInIdx != nil {
|
||||||
log.Errorf("Error updating index %v in %v", idx, context)
|
n.log.Errorf("Error updating index %v in %v", idx, context)
|
||||||
return nil, errInIdx
|
return nil, errInIdx
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,12 +234,12 @@ func readMap(context yaml.MapSlice, head string, tail []string) (interface{}, er
|
|||||||
return values, nil
|
return values, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func readMapSplat(context yaml.MapSlice, tail []string) (interface{}, error) {
|
func (n *navigator) readMapSplat(context yaml.MapSlice, tail []string) (interface{}, error) {
|
||||||
var newArray = make([]interface{}, len(context))
|
var newArray = make([]interface{}, len(context))
|
||||||
var i = 0
|
var i = 0
|
||||||
for _, entry := range context {
|
for _, entry := range context {
|
||||||
if len(tail) > 0 {
|
if len(tail) > 0 {
|
||||||
val, err := Recurse(entry.Value, tail[0], tail[1:])
|
val, err := n.recurse(entry.Value, tail[0], tail[1:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -187,37 +252,19 @@ func readMapSplat(context yaml.MapSlice, tail []string) (interface{}, error) {
|
|||||||
return newArray, nil
|
return newArray, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Recurse(value interface{}, head string, tail []string) (interface{}, error) {
|
func (n *navigator) readArray(array []interface{}, head int64, tail []string) (interface{}, error) {
|
||||||
switch value := value.(type) {
|
|
||||||
case []interface{}:
|
|
||||||
if head == "*" {
|
|
||||||
return readArraySplat(value, tail)
|
|
||||||
}
|
|
||||||
index, err := strconv.ParseInt(head, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error accessing array: %v", err)
|
|
||||||
}
|
|
||||||
return readArray(value, index, tail)
|
|
||||||
case yaml.MapSlice:
|
|
||||||
return readMap(value, head, tail)
|
|
||||||
default:
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func readArray(array []interface{}, head int64, tail []string) (interface{}, error) {
|
|
||||||
if head >= int64(len(array)) {
|
if head >= int64(len(array)) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
value := array[head]
|
value := array[head]
|
||||||
return calculateValue(value, tail)
|
return n.calculateValue(value, tail)
|
||||||
}
|
}
|
||||||
|
|
||||||
func readArraySplat(array []interface{}, tail []string) (interface{}, error) {
|
func (n *navigator) readArraySplat(array []interface{}, tail []string) (interface{}, error) {
|
||||||
var newArray = make([]interface{}, len(array))
|
var newArray = make([]interface{}, len(array))
|
||||||
for index, value := range array {
|
for index, value := range array {
|
||||||
val, err := calculateValue(value, tail)
|
val, err := n.calculateValue(value, tail)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -226,17 +273,17 @@ func readArraySplat(array []interface{}, tail []string) (interface{}, error) {
|
|||||||
return newArray, nil
|
return newArray, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func calculateValue(value interface{}, tail []string) (interface{}, error) {
|
func (n *navigator) calculateValue(value interface{}, tail []string) (interface{}, error) {
|
||||||
if len(tail) > 0 {
|
if len(tail) > 0 {
|
||||||
return Recurse(value, tail[0], tail[1:])
|
return n.recurse(value, tail[0], tail[1:])
|
||||||
}
|
}
|
||||||
return value, nil
|
return value, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteMap(context interface{}, paths []string) (yaml.MapSlice, error) {
|
func (n *navigator) deleteMap(context interface{}, paths []string) (yaml.MapSlice, error) {
|
||||||
log.Debugf("deleteMap for %v for %v\n", paths, context)
|
n.log.Debugf("deleteMap for %v for %v\n", paths, context)
|
||||||
|
|
||||||
mapSlice := getMapSlice(context)
|
mapSlice := n.getMapSlice(context)
|
||||||
|
|
||||||
if len(paths) == 0 {
|
if len(paths) == 0 {
|
||||||
return mapSlice, nil
|
return mapSlice, nil
|
||||||
@ -245,10 +292,10 @@ func deleteMap(context interface{}, paths []string) (yaml.MapSlice, error) {
|
|||||||
var index int
|
var index int
|
||||||
var child yaml.MapItem
|
var child yaml.MapItem
|
||||||
for index, child = range mapSlice {
|
for index, child = range mapSlice {
|
||||||
if matchesKey(paths[0], child.Key) {
|
if n.matchesKey(paths[0], child.Key) {
|
||||||
log.Debugf("\tMatched [%v] with [%v] at index %v", paths[0], child.Key, index)
|
n.log.Debugf("\tMatched [%v] with [%v] at index %v", paths[0], child.Key, index)
|
||||||
var badDelete error
|
var badDelete error
|
||||||
mapSlice, badDelete = deleteEntryInMap(mapSlice, child, index, paths)
|
mapSlice, badDelete = n.deleteEntryInMap(mapSlice, child, index, paths)
|
||||||
if badDelete != nil {
|
if badDelete != nil {
|
||||||
return nil, badDelete
|
return nil, badDelete
|
||||||
}
|
}
|
||||||
@ -259,14 +306,14 @@ func deleteMap(context interface{}, paths []string) (yaml.MapSlice, error) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteEntryInMap(original yaml.MapSlice, child yaml.MapItem, index int, paths []string) (yaml.MapSlice, error) {
|
func (n *navigator) deleteEntryInMap(original yaml.MapSlice, child yaml.MapItem, index int, paths []string) (yaml.MapSlice, error) {
|
||||||
remainingPaths := paths[1:]
|
remainingPaths := paths[1:]
|
||||||
|
|
||||||
var newSlice yaml.MapSlice
|
var newSlice yaml.MapSlice
|
||||||
if len(remainingPaths) > 0 {
|
if len(remainingPaths) > 0 {
|
||||||
newChild := yaml.MapItem{Key: child.Key}
|
newChild := yaml.MapItem{Key: child.Key}
|
||||||
var errorDeleting error
|
var errorDeleting error
|
||||||
newChild.Value, errorDeleting = DeleteChildValue(child.Value, remainingPaths)
|
newChild.Value, errorDeleting = n.DeleteChildValue(child.Value, remainingPaths)
|
||||||
if errorDeleting != nil {
|
if errorDeleting != nil {
|
||||||
return nil, errorDeleting
|
return nil, errorDeleting
|
||||||
}
|
}
|
||||||
@ -282,18 +329,18 @@ func deleteEntryInMap(original yaml.MapSlice, child yaml.MapItem, index int, pat
|
|||||||
} else {
|
} else {
|
||||||
// Delete item from slice at index
|
// Delete item from slice at index
|
||||||
newSlice = append(original[:index], original[index+1:]...)
|
newSlice = append(original[:index], original[index+1:]...)
|
||||||
log.Debugf("\tDeleted item index %d from original", index)
|
n.log.Debugf("\tDeleted item index %d from original", index)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("\tReturning original %v\n", original)
|
n.log.Debugf("\tReturning original %v\n", original)
|
||||||
return newSlice, nil
|
return newSlice, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteArraySplat(array []interface{}, tail []string) (interface{}, error) {
|
func (n *navigator) deleteArraySplat(array []interface{}, tail []string) (interface{}, error) {
|
||||||
log.Debugf("deleteArraySplat for %v for %v\n", tail, array)
|
n.log.Debugf("deleteArraySplat for %v for %v\n", tail, array)
|
||||||
var newArray = make([]interface{}, len(array))
|
var newArray = make([]interface{}, len(array))
|
||||||
for index, value := range array {
|
for index, value := range array {
|
||||||
val, err := DeleteChildValue(value, tail)
|
val, err := n.DeleteChildValue(value, tail)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -302,8 +349,8 @@ func deleteArraySplat(array []interface{}, tail []string) (interface{}, error) {
|
|||||||
return newArray, nil
|
return newArray, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteArray(array []interface{}, paths []string, index int64) (interface{}, error) {
|
func (n *navigator) deleteArray(array []interface{}, paths []string, index int64) (interface{}, error) {
|
||||||
log.Debugf("deleteArray for %v for %v\n", paths, array)
|
n.log.Debugf("deleteArray for %v for %v\n", paths, array)
|
||||||
|
|
||||||
if index >= int64(len(array)) {
|
if index >= int64(len(array)) {
|
||||||
return array, nil
|
return array, nil
|
||||||
@ -311,9 +358,9 @@ func deleteArray(array []interface{}, paths []string, index int64) (interface{},
|
|||||||
|
|
||||||
remainingPaths := paths[1:]
|
remainingPaths := paths[1:]
|
||||||
if len(remainingPaths) > 0 {
|
if len(remainingPaths) > 0 {
|
||||||
// Recurse into the array element at index
|
// recurse into the array element at index
|
||||||
var errorDeleting error
|
var errorDeleting error
|
||||||
array[index], errorDeleting = deleteMap(array[index], remainingPaths)
|
array[index], errorDeleting = n.deleteMap(array[index], remainingPaths)
|
||||||
if errorDeleting != nil {
|
if errorDeleting != nil {
|
||||||
return nil, errorDeleting
|
return nil, errorDeleting
|
||||||
}
|
}
|
||||||
@ -321,29 +368,9 @@ func deleteArray(array []interface{}, paths []string, index int64) (interface{},
|
|||||||
} else {
|
} else {
|
||||||
// Delete the array element at index
|
// Delete the array element at index
|
||||||
array = append(array[:index], array[index+1:]...)
|
array = append(array[:index], array[index+1:]...)
|
||||||
log.Debugf("\tDeleted item index %d from array, leaving %v", index, array)
|
n.log.Debugf("\tDeleted item index %d from array, leaving %v", index, array)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("\tReturning array: %v\n", array)
|
n.log.Debugf("\tReturning array: %v\n", array)
|
||||||
return array, nil
|
return array, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeleteChildValue(child interface{}, remainingPaths []string) (interface{}, error) {
|
|
||||||
log.Debugf("DeleteChildValue for %v for %v\n", remainingPaths, child)
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
return child, nil
|
|
||||||
}
|
|
||||||
|
@ -5,53 +5,58 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
"github.com/mikefarah/yq/test"
|
"github.com/mikefarah/yq/test"
|
||||||
|
logging "gopkg.in/op/go-logging.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestReadMap_simple(t *testing.T) {
|
func TestDataNavigator(t *testing.T) {
|
||||||
var data = test.ParseData(`
|
var log = logging.MustGetLogger("yq")
|
||||||
|
subject := NewDataNavigator(log)
|
||||||
|
|
||||||
|
t.Run("TestReadMap_simple", func(t *testing.T) {
|
||||||
|
var data = test.ParseData(`
|
||||||
---
|
---
|
||||||
b:
|
b:
|
||||||
c: 2
|
c: 2
|
||||||
`)
|
`)
|
||||||
got, _ := readMap(data, "b", []string{"c"})
|
got, _ := subject.ReadChildValue(data, []string{"b", "c"})
|
||||||
test.AssertResult(t, 2, got)
|
test.AssertResult(t, 2, got)
|
||||||
}
|
})
|
||||||
|
|
||||||
func TestReadMap_numberKey(t *testing.T) {
|
t.Run("TestReadMap_numberKey", func(t *testing.T) {
|
||||||
var data = test.ParseData(`
|
var data = test.ParseData(`
|
||||||
---
|
---
|
||||||
200: things
|
200: things
|
||||||
`)
|
`)
|
||||||
got, _ := readMap(data, "200", []string{})
|
got, _ := subject.ReadChildValue(data, []string{"200"})
|
||||||
test.AssertResult(t, "things", got)
|
test.AssertResult(t, "things", got)
|
||||||
}
|
})
|
||||||
|
|
||||||
func TestReadMap_splat(t *testing.T) {
|
t.Run("TestReadMap_splat", func(t *testing.T) {
|
||||||
var data = test.ParseData(`
|
var data = test.ParseData(`
|
||||||
---
|
---
|
||||||
mapSplat:
|
mapSplat:
|
||||||
item1: things
|
item1: things
|
||||||
item2: whatever
|
item2: whatever
|
||||||
otherThing: cat
|
otherThing: cat
|
||||||
`)
|
`)
|
||||||
res, _ := readMap(data, "mapSplat", []string{"*"})
|
res, _ := subject.ReadChildValue(data, []string{"mapSplat", "*"})
|
||||||
test.AssertResult(t, "[things whatever cat]", fmt.Sprintf("%v", res))
|
test.AssertResult(t, "[things whatever cat]", fmt.Sprintf("%v", res))
|
||||||
}
|
})
|
||||||
|
|
||||||
func TestReadMap_prefixSplat(t *testing.T) {
|
t.Run("TestReadMap_prefixSplat", func(t *testing.T) {
|
||||||
var data = test.ParseData(`
|
var data = test.ParseData(`
|
||||||
---
|
---
|
||||||
mapSplat:
|
mapSplat:
|
||||||
item1: things
|
item1: things
|
||||||
item2: whatever
|
item2: whatever
|
||||||
otherThing: cat
|
otherThing: cat
|
||||||
`)
|
`)
|
||||||
res, _ := readMap(data, "mapSplat", []string{"item*"})
|
res, _ := subject.ReadChildValue(data, []string{"mapSplat", "item*"})
|
||||||
test.AssertResult(t, "[things whatever]", fmt.Sprintf("%v", res))
|
test.AssertResult(t, "[things whatever]", fmt.Sprintf("%v", res))
|
||||||
}
|
})
|
||||||
|
|
||||||
func TestReadMap_deep_splat(t *testing.T) {
|
t.Run("TestReadMap_deep_splat", func(t *testing.T) {
|
||||||
var data = test.ParseData(`
|
var data = test.ParseData(`
|
||||||
---
|
---
|
||||||
mapSplatDeep:
|
mapSplatDeep:
|
||||||
item1:
|
item1:
|
||||||
@ -60,62 +65,62 @@ mapSplatDeep:
|
|||||||
cats: apples
|
cats: apples
|
||||||
`)
|
`)
|
||||||
|
|
||||||
res, _ := readMap(data, "mapSplatDeep", []string{"*", "cats"})
|
res, _ := subject.ReadChildValue(data, []string{"mapSplatDeep", "*", "cats"})
|
||||||
result := res.([]interface{})
|
result := res.([]interface{})
|
||||||
var actual = []string{result[0].(string), result[1].(string)}
|
var actual = []string{result[0].(string), result[1].(string)}
|
||||||
sort.Strings(actual)
|
sort.Strings(actual)
|
||||||
test.AssertResult(t, "[apples bananas]", fmt.Sprintf("%v", actual))
|
test.AssertResult(t, "[apples bananas]", fmt.Sprintf("%v", actual))
|
||||||
}
|
})
|
||||||
|
|
||||||
func TestReadMap_key_doesnt_exist(t *testing.T) {
|
t.Run("TestReadMap_key_doesnt_exist", func(t *testing.T) {
|
||||||
var data = test.ParseData(`
|
var data = test.ParseData(`
|
||||||
---
|
---
|
||||||
b:
|
b:
|
||||||
c: 2
|
c: 2
|
||||||
`)
|
`)
|
||||||
got, _ := readMap(data, "b.x.f", []string{"c"})
|
got, _ := subject.ReadChildValue(data, []string{"b", "x", "f", "c"})
|
||||||
test.AssertResult(t, nil, got)
|
test.AssertResult(t, nil, got)
|
||||||
}
|
})
|
||||||
|
|
||||||
func TestReadMap_recurse_against_string(t *testing.T) {
|
t.Run("TestReadMap_recurse_against_string", func(t *testing.T) {
|
||||||
var data = test.ParseData(`
|
var data = test.ParseData(`
|
||||||
---
|
---
|
||||||
a: cat
|
a: cat
|
||||||
`)
|
`)
|
||||||
got, _ := readMap(data, "a", []string{"b"})
|
got, _ := subject.ReadChildValue(data, []string{"a", "b"})
|
||||||
test.AssertResult(t, nil, got)
|
test.AssertResult(t, nil, got)
|
||||||
}
|
})
|
||||||
|
|
||||||
func TestReadMap_with_array(t *testing.T) {
|
t.Run("TestReadMap_with_array", func(t *testing.T) {
|
||||||
var data = test.ParseData(`
|
var data = test.ParseData(`
|
||||||
---
|
---
|
||||||
b:
|
b:
|
||||||
d:
|
d:
|
||||||
- 3
|
- 3
|
||||||
- 4
|
- 4
|
||||||
`)
|
`)
|
||||||
got, _ := readMap(data, "b", []string{"d", "1"})
|
got, _ := subject.ReadChildValue(data, []string{"b", "d", "1"})
|
||||||
test.AssertResult(t, 4, got)
|
test.AssertResult(t, 4, got)
|
||||||
}
|
})
|
||||||
|
|
||||||
func TestReadMap_with_array_and_bad_index(t *testing.T) {
|
t.Run("TestReadMap_with_array_and_bad_index", func(t *testing.T) {
|
||||||
var data = test.ParseData(`
|
var data = test.ParseData(`
|
||||||
---
|
---
|
||||||
b:
|
b:
|
||||||
d:
|
d:
|
||||||
- 3
|
- 3
|
||||||
- 4
|
- 4
|
||||||
`)
|
`)
|
||||||
_, err := readMap(data, "b", []string{"d", "x"})
|
_, err := subject.ReadChildValue(data, []string{"b", "d", "x"})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected error due to invalid path")
|
t.Fatal("Expected error due to invalid path")
|
||||||
}
|
}
|
||||||
expectedOutput := `error accessing array: strconv.ParseInt: parsing "x": invalid syntax`
|
expectedOutput := `error accessing array: strconv.ParseInt: parsing "x": invalid syntax`
|
||||||
test.AssertResult(t, expectedOutput, err.Error())
|
test.AssertResult(t, expectedOutput, err.Error())
|
||||||
}
|
})
|
||||||
|
|
||||||
func TestReadMap_with_mapsplat_array_and_bad_index(t *testing.T) {
|
t.Run("TestReadMap_with_mapsplat_array_and_bad_index", func(t *testing.T) {
|
||||||
var data = test.ParseData(`
|
var data = test.ParseData(`
|
||||||
---
|
---
|
||||||
b:
|
b:
|
||||||
d:
|
d:
|
||||||
@ -126,16 +131,16 @@ b:
|
|||||||
- 1
|
- 1
|
||||||
- 2
|
- 2
|
||||||
`)
|
`)
|
||||||
_, err := readMap(data, "b", []string{"d", "*", "x"})
|
_, err := subject.ReadChildValue(data, []string{"b", "d", "*", "x"})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected error due to invalid path")
|
t.Fatal("Expected error due to invalid path")
|
||||||
}
|
}
|
||||||
expectedOutput := `error accessing array: strconv.ParseInt: parsing "x": invalid syntax`
|
expectedOutput := `error accessing array: strconv.ParseInt: parsing "x": invalid syntax`
|
||||||
test.AssertResult(t, expectedOutput, err.Error())
|
test.AssertResult(t, expectedOutput, err.Error())
|
||||||
}
|
})
|
||||||
|
|
||||||
func TestReadMap_with_arraysplat_map_array_and_bad_index(t *testing.T) {
|
t.Run("TestReadMap_with_arraysplat_map_array_and_bad_index", func(t *testing.T) {
|
||||||
var data = test.ParseData(`
|
var data = test.ParseData(`
|
||||||
---
|
---
|
||||||
b:
|
b:
|
||||||
d:
|
d:
|
||||||
@ -146,40 +151,40 @@ b:
|
|||||||
- sam
|
- sam
|
||||||
- bo
|
- bo
|
||||||
`)
|
`)
|
||||||
_, err := readMap(data, "b", []string{"d", "*", "names", "x"})
|
_, err := subject.ReadChildValue(data, []string{"b", "d", "*", "names", "x"})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected error due to invalid path")
|
t.Fatal("Expected error due to invalid path")
|
||||||
}
|
}
|
||||||
expectedOutput := `error accessing array: strconv.ParseInt: parsing "x": invalid syntax`
|
expectedOutput := `error accessing array: strconv.ParseInt: parsing "x": invalid syntax`
|
||||||
test.AssertResult(t, expectedOutput, err.Error())
|
test.AssertResult(t, expectedOutput, err.Error())
|
||||||
}
|
})
|
||||||
|
|
||||||
func TestReadMap_with_array_out_of_bounds(t *testing.T) {
|
t.Run("TestReadMap_with_array_out_of_bounds", func(t *testing.T) {
|
||||||
var data = test.ParseData(`
|
var data = test.ParseData(`
|
||||||
---
|
---
|
||||||
b:
|
b:
|
||||||
d:
|
d:
|
||||||
- 3
|
- 3
|
||||||
- 4
|
- 4
|
||||||
`)
|
`)
|
||||||
got, _ := readMap(data, "b", []string{"d", "3"})
|
got, _ := subject.ReadChildValue(data, []string{"b", "d", "3"})
|
||||||
test.AssertResult(t, nil, got)
|
test.AssertResult(t, nil, got)
|
||||||
}
|
})
|
||||||
|
|
||||||
func TestReadMap_with_array_out_of_bounds_by_1(t *testing.T) {
|
t.Run("TestReadMap_with_array_out_of_bounds_by_1", func(t *testing.T) {
|
||||||
var data = test.ParseData(`
|
var data = test.ParseData(`
|
||||||
---
|
---
|
||||||
b:
|
b:
|
||||||
d:
|
d:
|
||||||
- 3
|
- 3
|
||||||
- 4
|
- 4
|
||||||
`)
|
`)
|
||||||
got, _ := readMap(data, "b", []string{"d", "2"})
|
got, _ := subject.ReadChildValue(data, []string{"b", "d", "2"})
|
||||||
test.AssertResult(t, nil, got)
|
test.AssertResult(t, nil, got)
|
||||||
}
|
})
|
||||||
|
|
||||||
func TestReadMap_with_array_splat(t *testing.T) {
|
t.Run("TestReadMap_with_array_splat", func(t *testing.T) {
|
||||||
var data = test.ParseData(`
|
var data = test.ParseData(`
|
||||||
e:
|
e:
|
||||||
-
|
-
|
||||||
name: Fred
|
name: Fred
|
||||||
@ -188,213 +193,204 @@ e:
|
|||||||
name: Sam
|
name: Sam
|
||||||
thing: dog
|
thing: dog
|
||||||
`)
|
`)
|
||||||
got, _ := readMap(data, "e", []string{"*", "name"})
|
got, _ := subject.ReadChildValue(data, []string{"e", "*", "name"})
|
||||||
test.AssertResult(t, "[Fred Sam]", fmt.Sprintf("%v", got))
|
test.AssertResult(t, "[Fred Sam]", fmt.Sprintf("%v", got))
|
||||||
}
|
})
|
||||||
|
|
||||||
func TestWrite_really_simple(t *testing.T) {
|
t.Run("TestWrite_really_simple", func(t *testing.T) {
|
||||||
var data = test.ParseData(`
|
var data = test.ParseData(`
|
||||||
b: 2
|
b: 2
|
||||||
`)
|
`)
|
||||||
|
|
||||||
updated := writeMap(data, []string{"b"}, "4")
|
updated := subject.UpdatedChildValue(data, []string{"b"}, "4")
|
||||||
test.AssertResult(t, "[{b 4}]", fmt.Sprintf("%v", updated))
|
test.AssertResult(t, "[{b 4}]", fmt.Sprintf("%v", updated))
|
||||||
}
|
})
|
||||||
|
|
||||||
func TestWrite_simple(t *testing.T) {
|
t.Run("TestWrite_simple", func(t *testing.T) {
|
||||||
var data = test.ParseData(`
|
var data = test.ParseData(`
|
||||||
b:
|
b:
|
||||||
c: 2
|
c: 2
|
||||||
`)
|
`)
|
||||||
|
|
||||||
updated := writeMap(data, []string{"b", "c"}, "4")
|
updated := subject.UpdatedChildValue(data, []string{"b", "c"}, "4")
|
||||||
test.AssertResult(t, "[{b [{c 4}]}]", fmt.Sprintf("%v", updated))
|
test.AssertResult(t, "[{b [{c 4}]}]", fmt.Sprintf("%v", updated))
|
||||||
}
|
})
|
||||||
|
|
||||||
func TestWrite_new(t *testing.T) {
|
t.Run("TestWrite_new", func(t *testing.T) {
|
||||||
var data = test.ParseData(`
|
var data = test.ParseData(`
|
||||||
b:
|
b:
|
||||||
c: 2
|
c: 2
|
||||||
`)
|
`)
|
||||||
|
|
||||||
updated := writeMap(data, []string{"b", "d"}, "4")
|
updated := subject.UpdatedChildValue(data, []string{"b", "d"}, "4")
|
||||||
test.AssertResult(t, "[{b [{c 2} {d 4}]}]", fmt.Sprintf("%v", updated))
|
test.AssertResult(t, "[{b [{c 2} {d 4}]}]", fmt.Sprintf("%v", updated))
|
||||||
}
|
})
|
||||||
|
|
||||||
func TestWrite_new_deep(t *testing.T) {
|
t.Run("TestWrite_new_deep", func(t *testing.T) {
|
||||||
var data = test.ParseData(`
|
var data = test.ParseData(`
|
||||||
b:
|
b:
|
||||||
c: 2
|
c: 2
|
||||||
`)
|
`)
|
||||||
|
|
||||||
updated := writeMap(data, []string{"b", "d", "f"}, "4")
|
updated := subject.UpdatedChildValue(data, []string{"b", "d", "f"}, "4")
|
||||||
test.AssertResult(t, "[{b [{c 2} {d [{f 4}]}]}]", fmt.Sprintf("%v", updated))
|
test.AssertResult(t, "[{b [{c 2} {d [{f 4}]}]}]", fmt.Sprintf("%v", updated))
|
||||||
}
|
})
|
||||||
|
|
||||||
func TestWrite_array(t *testing.T) {
|
t.Run("TestWrite_array", func(t *testing.T) {
|
||||||
var data = test.ParseData(`
|
var data = test.ParseData(`
|
||||||
b:
|
b:
|
||||||
- aa
|
- aa
|
||||||
`)
|
`)
|
||||||
|
|
||||||
updated := writeMap(data, []string{"b", "0"}, "bb")
|
updated := subject.UpdatedChildValue(data, []string{"b", "0"}, "bb")
|
||||||
|
|
||||||
test.AssertResult(t, "[{b [bb]}]", fmt.Sprintf("%v", updated))
|
test.AssertResult(t, "[{b [bb]}]", fmt.Sprintf("%v", updated))
|
||||||
}
|
})
|
||||||
|
|
||||||
func TestWrite_new_array(t *testing.T) {
|
t.Run("TestWrite_new_array", func(t *testing.T) {
|
||||||
var data = test.ParseData(`
|
var data = test.ParseData(`
|
||||||
b:
|
b:
|
||||||
c: 2
|
c: 2
|
||||||
`)
|
`)
|
||||||
|
|
||||||
updated := writeMap(data, []string{"b", "0"}, "4")
|
updated := subject.UpdatedChildValue(data, []string{"b", "0"}, "4")
|
||||||
test.AssertResult(t, "[{b [{c 2} {0 4}]}]", fmt.Sprintf("%v", updated))
|
test.AssertResult(t, "[{b [{c 2} {0 4}]}]", fmt.Sprintf("%v", updated))
|
||||||
}
|
})
|
||||||
|
|
||||||
func TestWrite_new_array_deep(t *testing.T) {
|
t.Run("TestWrite_new_array_deep", func(t *testing.T) {
|
||||||
var data = test.ParseData(`
|
var data = test.ParseData(`
|
||||||
a: apple
|
a: apple
|
||||||
`)
|
`)
|
||||||
|
|
||||||
var expected = `a: apple
|
updated := subject.UpdatedChildValue(data, []string{"b", "+", "c"}, "4")
|
||||||
b:
|
test.AssertResult(t, "[{a apple} {b [[{c 4}]]}]", fmt.Sprintf("%v", updated))
|
||||||
- c: "4"`
|
})
|
||||||
|
|
||||||
updated := writeMap(data, []string{"b", "+", "c"}, "4")
|
t.Run("TestWrite_new_map_array_deep", func(t *testing.T) {
|
||||||
got, _ := YamlToString(updated, true)
|
var data = test.ParseData(`
|
||||||
test.AssertResult(t, expected, got)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWrite_new_map_array_deep(t *testing.T) {
|
|
||||||
var data = test.ParseData(`
|
|
||||||
b:
|
b:
|
||||||
c: 2
|
c: 2
|
||||||
`)
|
`)
|
||||||
var expected = `b:
|
|
||||||
c: 2
|
|
||||||
d:
|
|
||||||
- "4"`
|
|
||||||
|
|
||||||
updated := writeMap(data, []string{"b", "d", "+"}, "4")
|
updated := subject.UpdatedChildValue(data, []string{"b", "d", "+"}, "4")
|
||||||
got, _ := YamlToString(updated, true)
|
test.AssertResult(t, "[{b [{c 2} {d [4]}]}]", fmt.Sprintf("%v", updated))
|
||||||
test.AssertResult(t, expected, got)
|
})
|
||||||
}
|
|
||||||
|
|
||||||
func TestWrite_add_to_array(t *testing.T) {
|
t.Run("TestWrite_add_to_array", func(t *testing.T) {
|
||||||
var data = test.ParseData(`
|
var data = test.ParseData(`
|
||||||
b:
|
b:
|
||||||
- aa
|
- aa
|
||||||
`)
|
`)
|
||||||
|
|
||||||
var expected = `b:
|
updated := subject.UpdatedChildValue(data, []string{"b", "1"}, "bb")
|
||||||
- aa
|
test.AssertResult(t, "[{b [aa bb]}]", fmt.Sprintf("%v", updated))
|
||||||
- bb`
|
})
|
||||||
|
|
||||||
updated := writeMap(data, []string{"b", "1"}, "bb")
|
t.Run("TestWrite_with_no_tail", func(t *testing.T) {
|
||||||
got, _ := YamlToString(updated, true)
|
var data = test.ParseData(`
|
||||||
test.AssertResult(t, expected, got)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWrite_with_no_tail(t *testing.T) {
|
|
||||||
var data = test.ParseData(`
|
|
||||||
b:
|
b:
|
||||||
c: 2
|
c: 2
|
||||||
`)
|
`)
|
||||||
updated := writeMap(data, []string{"b"}, "4")
|
updated := subject.UpdatedChildValue(data, []string{"b"}, "4")
|
||||||
|
|
||||||
test.AssertResult(t, "[{b 4}]", fmt.Sprintf("%v", updated))
|
test.AssertResult(t, "[{b 4}]", fmt.Sprintf("%v", updated))
|
||||||
}
|
})
|
||||||
|
|
||||||
func TestWriteMap_no_paths(t *testing.T) {
|
t.Run("TestWriteMap_no_paths", func(t *testing.T) {
|
||||||
var data = test.ParseData(`
|
var data = test.ParseData(`
|
||||||
b: 5
|
b: 5
|
||||||
`)
|
`)
|
||||||
|
var new = test.ParseData(`
|
||||||
|
c: 4
|
||||||
|
`)
|
||||||
|
result := subject.UpdatedChildValue(data, []string{}, new)
|
||||||
|
test.AssertResult(t, fmt.Sprintf("%v", new), fmt.Sprintf("%v", result))
|
||||||
|
})
|
||||||
|
|
||||||
result := writeMap(data, []string{}, 4)
|
t.Run("TestWriteArray_no_paths", func(t *testing.T) {
|
||||||
test.AssertResult(t, fmt.Sprintf("%v", data), fmt.Sprintf("%v", result))
|
var data = make([]interface{}, 1)
|
||||||
}
|
data[0] = "mike"
|
||||||
|
var new = test.ParseData(`
|
||||||
|
c: 4
|
||||||
|
`)
|
||||||
|
result := subject.UpdatedChildValue(data, []string{}, new)
|
||||||
|
test.AssertResult(t, fmt.Sprintf("%v", new), fmt.Sprintf("%v", result))
|
||||||
|
})
|
||||||
|
|
||||||
func TestWriteArray_no_paths(t *testing.T) {
|
t.Run("TestDelete_MapItem", func(t *testing.T) {
|
||||||
var data = make([]interface{}, 1)
|
var data = test.ParseData(`
|
||||||
data[0] = "mike"
|
|
||||||
result := writeArray(data, []string{}, 4)
|
|
||||||
test.AssertResult(t, fmt.Sprintf("%v", data), fmt.Sprintf("%v", result))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDelete_MapItem(t *testing.T) {
|
|
||||||
var data = test.ParseData(`
|
|
||||||
a: 123
|
a: 123
|
||||||
b: 456
|
b: 456
|
||||||
`)
|
`)
|
||||||
var expected = test.ParseData(`
|
var expected = test.ParseData(`
|
||||||
b: 456
|
b: 456
|
||||||
`)
|
`)
|
||||||
|
|
||||||
result, _ := deleteMap(data, []string{"a"})
|
result, _ := subject.DeleteChildValue(data, []string{"a"})
|
||||||
test.AssertResult(t, fmt.Sprintf("%v", expected), fmt.Sprintf("%v", result))
|
test.AssertResult(t, fmt.Sprintf("%v", expected), fmt.Sprintf("%v", result))
|
||||||
}
|
})
|
||||||
|
|
||||||
// Ensure deleting an index into a string does nothing
|
// Ensure deleting an index into a string does nothing
|
||||||
func TestDelete_index_to_string(t *testing.T) {
|
t.Run("TestDelete_index_to_string", func(t *testing.T) {
|
||||||
var data = test.ParseData(`
|
var data = test.ParseData(`
|
||||||
a: mystring
|
a: mystring
|
||||||
`)
|
`)
|
||||||
result, _ := deleteMap(data, []string{"a", "0"})
|
result, _ := subject.DeleteChildValue(data, []string{"a", "0"})
|
||||||
test.AssertResult(t, fmt.Sprintf("%v", data), fmt.Sprintf("%v", result))
|
test.AssertResult(t, fmt.Sprintf("%v", data), fmt.Sprintf("%v", result))
|
||||||
}
|
})
|
||||||
|
|
||||||
func TestDelete_list_index(t *testing.T) {
|
t.Run("TestDelete_list_index", func(t *testing.T) {
|
||||||
var data = test.ParseData(`
|
var data = test.ParseData(`
|
||||||
a: [3, 4]
|
a: [3, 4]
|
||||||
`)
|
`)
|
||||||
var expected = test.ParseData(`
|
var expected = test.ParseData(`
|
||||||
a: [3]
|
a: [3]
|
||||||
`)
|
`)
|
||||||
result, _ := deleteMap(data, []string{"a", "1"})
|
result, _ := subject.DeleteChildValue(data, []string{"a", "1"})
|
||||||
test.AssertResult(t, fmt.Sprintf("%v", expected), fmt.Sprintf("%v", result))
|
test.AssertResult(t, fmt.Sprintf("%v", expected), fmt.Sprintf("%v", result))
|
||||||
}
|
})
|
||||||
|
|
||||||
func TestDelete_list_index_beyond_bounds(t *testing.T) {
|
t.Run("TestDelete_list_index_beyond_bounds", func(t *testing.T) {
|
||||||
var data = test.ParseData(`
|
var data = test.ParseData(`
|
||||||
a: [3, 4]
|
a: [3, 4]
|
||||||
`)
|
`)
|
||||||
result, _ := deleteMap(data, []string{"a", "5"})
|
result, _ := subject.DeleteChildValue(data, []string{"a", "5"})
|
||||||
test.AssertResult(t, fmt.Sprintf("%v", data), fmt.Sprintf("%v", result))
|
test.AssertResult(t, fmt.Sprintf("%v", data), fmt.Sprintf("%v", result))
|
||||||
}
|
})
|
||||||
|
|
||||||
func TestDelete_list_index_out_of_bounds_by_1(t *testing.T) {
|
t.Run("TestDelete_list_index_out_of_bounds_by_1", func(t *testing.T) {
|
||||||
var data = test.ParseData(`
|
var data = test.ParseData(`
|
||||||
a: [3, 4]
|
a: [3, 4]
|
||||||
`)
|
`)
|
||||||
result, _ := deleteMap(data, []string{"a", "2"})
|
result, _ := subject.DeleteChildValue(data, []string{"a", "2"})
|
||||||
test.AssertResult(t, fmt.Sprintf("%v", data), fmt.Sprintf("%v", result))
|
test.AssertResult(t, fmt.Sprintf("%v", data), fmt.Sprintf("%v", result))
|
||||||
}
|
})
|
||||||
|
|
||||||
func TestDelete_no_paths(t *testing.T) {
|
t.Run("TestDelete_no_paths", func(t *testing.T) {
|
||||||
var data = test.ParseData(`
|
var data = test.ParseData(`
|
||||||
a: [3, 4]
|
a: [3, 4]
|
||||||
b:
|
b:
|
||||||
- name: test
|
- name: test
|
||||||
`)
|
`)
|
||||||
result, _ := deleteMap(data, []string{})
|
result, _ := subject.DeleteChildValue(data, []string{})
|
||||||
test.AssertResult(t, fmt.Sprintf("%v", data), fmt.Sprintf("%v", result))
|
test.AssertResult(t, fmt.Sprintf("%v", data), fmt.Sprintf("%v", result))
|
||||||
}
|
})
|
||||||
|
|
||||||
func TestDelete_array_map_item(t *testing.T) {
|
t.Run("TestDelete_array_map_item", func(t *testing.T) {
|
||||||
var data = test.ParseData(`
|
var data = test.ParseData(`
|
||||||
b:
|
b:
|
||||||
- name: fred
|
- name: fred
|
||||||
value: blah
|
value: blah
|
||||||
- name: john
|
- name: john
|
||||||
value: test
|
value: test
|
||||||
`)
|
`)
|
||||||
var expected = test.ParseData(`
|
var expected = test.ParseData(`
|
||||||
b:
|
b:
|
||||||
- value: blah
|
- value: blah
|
||||||
- name: john
|
- name: john
|
||||||
value: test
|
value: test
|
||||||
`)
|
`)
|
||||||
result, _ := deleteMap(data, []string{"b", "0", "name"})
|
result, _ := subject.DeleteChildValue(data, []string{"b", "0", "name"})
|
||||||
test.AssertResult(t, fmt.Sprintf("%v", expected), fmt.Sprintf("%v", result))
|
test.AssertResult(t, fmt.Sprintf("%v", expected), fmt.Sprintf("%v", result))
|
||||||
}
|
})
|
||||||
|
}
|
@ -5,24 +5,38 @@ import (
|
|||||||
logging "gopkg.in/op/go-logging.v1"
|
logging "gopkg.in/op/go-logging.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var log = logging.MustGetLogger("yq")
|
type YqLib interface {
|
||||||
|
ReadPath(dataBucket interface{}, path string) (interface{}, error)
|
||||||
func SetLogger(l *logging.Logger) {
|
WritePath(dataBucket interface{}, path string, value interface{}) (interface{})
|
||||||
log = l
|
PrefixPath(dataBucket interface{}, prefix string) (interface{})
|
||||||
|
DeletePath(dataBucket interface{}, path string) (interface{}, error)
|
||||||
|
Merge(dst interface{}, src interface{}, overwrite bool, append bool) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReadPath(dataBucket interface{}, path string) (interface{}, error) {
|
type lib struct {
|
||||||
var paths = ParsePath(path)
|
navigator DataNavigator
|
||||||
return Recurse(dataBucket, paths[0], paths[1:])
|
parser PathParser
|
||||||
}
|
}
|
||||||
|
|
||||||
func WritePath(dataBucket interface{}, path string, value interface{}) (interface{}) {
|
func NewYqLib(l *logging.Logger) YqLib {
|
||||||
var paths = ParsePath(path)
|
return &lib {
|
||||||
return UpdatedChildValue(dataBucket, paths, value)
|
navigator: NewDataNavigator(l),
|
||||||
|
parser: NewPathParser(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func PrefixPath(dataBucket interface{}, prefix string) (interface{}) {
|
func (l *lib) ReadPath(dataBucket interface{}, path string) (interface{}, error) {
|
||||||
var paths = ParsePath(prefix)
|
var paths = l.parser.ParsePath(path)
|
||||||
|
return l.navigator.ReadChildValue(dataBucket, paths)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *lib) WritePath(dataBucket interface{}, path string, value interface{}) (interface{}) {
|
||||||
|
var paths = l.parser.ParsePath(path)
|
||||||
|
return l.navigator.UpdatedChildValue(dataBucket, paths, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *lib) PrefixPath(dataBucket interface{}, prefix string) (interface{}) {
|
||||||
|
var paths = l.parser.ParsePath(prefix)
|
||||||
|
|
||||||
// Inverse order
|
// Inverse order
|
||||||
for i := len(paths)/2 - 1; i >= 0; i-- {
|
for i := len(paths)/2 - 1; i >= 0; i-- {
|
||||||
@ -33,18 +47,18 @@ func PrefixPath(dataBucket interface{}, prefix string) (interface{}) {
|
|||||||
var mapDataBucket = dataBucket
|
var mapDataBucket = dataBucket
|
||||||
for _, key := range paths {
|
for _, key := range paths {
|
||||||
singlePath := []string{key}
|
singlePath := []string{key}
|
||||||
mapDataBucket = UpdatedChildValue(nil, singlePath, mapDataBucket)
|
mapDataBucket = l.navigator.UpdatedChildValue(nil, singlePath, mapDataBucket)
|
||||||
}
|
}
|
||||||
|
|
||||||
return mapDataBucket
|
return mapDataBucket
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeletePath(dataBucket interface{}, path string) (interface{}, error) {
|
func (l *lib) DeletePath(dataBucket interface{}, path string) (interface{}, error) {
|
||||||
var paths = ParsePath(path)
|
var paths = l.parser.ParsePath(path)
|
||||||
return DeleteChildValue(dataBucket, paths)
|
return l.navigator.DeleteChildValue(dataBucket, paths)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Merge(dst interface{}, src interface{}, overwrite bool, append bool) error {
|
func (l *lib) Merge(dst interface{}, src interface{}, overwrite bool, append bool) error {
|
||||||
if overwrite {
|
if overwrite {
|
||||||
return mergo.Merge(dst, src, mergo.WithOverride)
|
return mergo.Merge(dst, src, mergo.WithOverride)
|
||||||
} else if append {
|
} else if append {
|
||||||
|
@ -4,145 +4,153 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
"github.com/mikefarah/yq/test"
|
"github.com/mikefarah/yq/test"
|
||||||
|
logging "gopkg.in/op/go-logging.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestReadPath(t *testing.T) {
|
func TestLib(t *testing.T) {
|
||||||
var data = test.ParseData(`
|
|
||||||
|
var log = logging.MustGetLogger("yq")
|
||||||
|
subject := NewYqLib(log)
|
||||||
|
|
||||||
|
t.Run("TestReadPath", func(t *testing.T) {
|
||||||
|
var data = test.ParseData(`
|
||||||
---
|
---
|
||||||
b:
|
b:
|
||||||
2: c
|
2: c
|
||||||
`)
|
`)
|
||||||
|
|
||||||
got, _ := ReadPath(data, "b.2")
|
got, _ := subject.ReadPath(data, "b.2")
|
||||||
test.AssertResult(t, `c`, got)
|
test.AssertResult(t, `c`, got)
|
||||||
}
|
})
|
||||||
|
|
||||||
func TestReadPath_WithError(t *testing.T) {
|
t.Run("TestReadPath_WithError", func(t *testing.T) {
|
||||||
var data = test.ParseData(`
|
var data = test.ParseData(`
|
||||||
---
|
---
|
||||||
b:
|
b:
|
||||||
- c
|
- c
|
||||||
`)
|
`)
|
||||||
|
|
||||||
_, err := ReadPath(data, "b.[a]")
|
_, err := subject.ReadPath(data, "b.[a]")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected error due to invalid path")
|
t.Fatal("Expected error due to invalid path")
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
func TestWritePath(t *testing.T) {
|
t.Run("TestWritePath", func(t *testing.T) {
|
||||||
var data = test.ParseData(`
|
var data = test.ParseData(`
|
||||||
---
|
---
|
||||||
b:
|
b:
|
||||||
2: c
|
2: c
|
||||||
`)
|
`)
|
||||||
|
|
||||||
got := WritePath(data, "b.3", "a")
|
got := subject.WritePath(data, "b.3", "a")
|
||||||
test.AssertResult(t, `[{b [{2 c} {3 a}]}]`, fmt.Sprintf("%v", got))
|
test.AssertResult(t, `[{b [{2 c} {3 a}]}]`, fmt.Sprintf("%v", got))
|
||||||
}
|
})
|
||||||
|
|
||||||
func TestPrefixPath(t *testing.T) {
|
t.Run("TestPrefixPath", func(t *testing.T) {
|
||||||
var data = test.ParseData(`
|
var data = test.ParseData(`
|
||||||
---
|
---
|
||||||
b:
|
b:
|
||||||
2: c
|
2: c
|
||||||
`)
|
`)
|
||||||
|
|
||||||
got := PrefixPath(data, "d")
|
got := subject.PrefixPath(data, "d")
|
||||||
test.AssertResult(t, `[{d [{b [{2 c}]}]}]`, fmt.Sprintf("%v", got))
|
test.AssertResult(t, `[{d [{b [{2 c}]}]}]`, fmt.Sprintf("%v", got))
|
||||||
}
|
})
|
||||||
|
|
||||||
func TestDeletePath(t *testing.T) {
|
t.Run("TestDeletePath", func(t *testing.T) {
|
||||||
var data = test.ParseData(`
|
var data = test.ParseData(`
|
||||||
---
|
---
|
||||||
b:
|
b:
|
||||||
2: c
|
2: c
|
||||||
3: a
|
3: a
|
||||||
`)
|
`)
|
||||||
|
|
||||||
got, _ := DeletePath(data, "b.2")
|
got, _ := subject.DeletePath(data, "b.2")
|
||||||
test.AssertResult(t, `[{b [{3 a}]}]`, fmt.Sprintf("%v", got))
|
test.AssertResult(t, `[{b [{3 a}]}]`, fmt.Sprintf("%v", got))
|
||||||
}
|
})
|
||||||
|
|
||||||
func TestDeletePath_WithError(t *testing.T) {
|
t.Run("TestDeletePath_WithError", func(t *testing.T) {
|
||||||
var data = test.ParseData(`
|
var data = test.ParseData(`
|
||||||
---
|
---
|
||||||
b:
|
b:
|
||||||
- c
|
- c
|
||||||
`)
|
`)
|
||||||
|
|
||||||
_, err := DeletePath(data, "b.[a]")
|
_, err := subject.DeletePath(data, "b.[a]")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected error due to invalid path")
|
t.Fatal("Expected error due to invalid path")
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
func TestMerge(t *testing.T) {
|
t.Run("TestMerge", func(t *testing.T) {
|
||||||
var dst = test.ParseData(`
|
var dst = test.ParseData(`
|
||||||
---
|
---
|
||||||
a: b
|
a: b
|
||||||
c: d
|
c: d
|
||||||
`)
|
`)
|
||||||
var src = test.ParseData(`
|
var src = test.ParseData(`
|
||||||
---
|
---
|
||||||
a: 1
|
a: 1
|
||||||
b: 2
|
b: 2
|
||||||
`)
|
`)
|
||||||
|
|
||||||
var mergedData = make(map[interface{}]interface{})
|
var mergedData = make(map[interface{}]interface{})
|
||||||
mergedData["root"] = dst
|
mergedData["root"] = dst
|
||||||
var mapDataBucket = make(map[interface{}]interface{})
|
var mapDataBucket = make(map[interface{}]interface{})
|
||||||
mapDataBucket["root"] = src
|
mapDataBucket["root"] = src
|
||||||
|
|
||||||
Merge(&mergedData, mapDataBucket, false, false)
|
subject.Merge(&mergedData, mapDataBucket, false, false)
|
||||||
test.AssertResult(t, `[{a b} {c d}]`, fmt.Sprintf("%v", mergedData["root"]))
|
test.AssertResult(t, `[{a b} {c d}]`, fmt.Sprintf("%v", mergedData["root"]))
|
||||||
}
|
})
|
||||||
|
|
||||||
func TestMerge_WithOverwrite(t *testing.T) {
|
t.Run("TestMerge_WithOverwrite", func(t *testing.T) {
|
||||||
var dst = test.ParseData(`
|
var dst = test.ParseData(`
|
||||||
---
|
---
|
||||||
a: b
|
a: b
|
||||||
c: d
|
c: d
|
||||||
`)
|
`)
|
||||||
var src = test.ParseData(`
|
var src = test.ParseData(`
|
||||||
---
|
---
|
||||||
a: 1
|
a: 1
|
||||||
b: 2
|
b: 2
|
||||||
`)
|
`)
|
||||||
|
|
||||||
var mergedData = make(map[interface{}]interface{})
|
var mergedData = make(map[interface{}]interface{})
|
||||||
mergedData["root"] = dst
|
mergedData["root"] = dst
|
||||||
var mapDataBucket = make(map[interface{}]interface{})
|
var mapDataBucket = make(map[interface{}]interface{})
|
||||||
mapDataBucket["root"] = src
|
mapDataBucket["root"] = src
|
||||||
|
|
||||||
Merge(&mergedData, mapDataBucket, true, false)
|
subject.Merge(&mergedData, mapDataBucket, true, false)
|
||||||
test.AssertResult(t, `[{a 1} {b 2}]`, fmt.Sprintf("%v", mergedData["root"]))
|
test.AssertResult(t, `[{a 1} {b 2}]`, fmt.Sprintf("%v", mergedData["root"]))
|
||||||
}
|
})
|
||||||
|
|
||||||
func TestMerge_WithAppend(t *testing.T) {
|
t.Run("TestMerge_WithAppend", func(t *testing.T) {
|
||||||
var dst = test.ParseData(`
|
var dst = test.ParseData(`
|
||||||
---
|
---
|
||||||
a: b
|
a: b
|
||||||
c: d
|
c: d
|
||||||
`)
|
`)
|
||||||
var src = test.ParseData(`
|
var src = test.ParseData(`
|
||||||
---
|
---
|
||||||
a: 1
|
a: 1
|
||||||
b: 2
|
b: 2
|
||||||
`)
|
`)
|
||||||
|
|
||||||
|
var mergedData = make(map[interface{}]interface{})
|
||||||
|
mergedData["root"] = dst
|
||||||
|
var mapDataBucket = make(map[interface{}]interface{})
|
||||||
|
mapDataBucket["root"] = src
|
||||||
|
|
||||||
|
subject.Merge(&mergedData, mapDataBucket, false, true)
|
||||||
|
test.AssertResult(t, `[{a b} {c d} {a 1} {b 2}]`, fmt.Sprintf("%v", mergedData["root"]))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("TestMerge_WithError", func(t *testing.T) {
|
||||||
|
err := subject.Merge(nil, nil, false, false)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Expected error due to nil")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
var mergedData = make(map[interface{}]interface{})
|
|
||||||
mergedData["root"] = dst
|
|
||||||
var mapDataBucket = make(map[interface{}]interface{})
|
|
||||||
mapDataBucket["root"] = src
|
|
||||||
|
|
||||||
Merge(&mergedData, mapDataBucket, false, true)
|
|
||||||
test.AssertResult(t, `[{a b} {c d} {a 1} {b 2}]`, fmt.Sprintf("%v", mergedData["root"]))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMerge_WithError(t *testing.T) {
|
|
||||||
err := Merge(nil, nil, false, false)
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("Expected error due to nil")
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,35 +1,45 @@
|
|||||||
package yqlib
|
package yqlib
|
||||||
|
|
||||||
func ParsePath(path string) []string {
|
type PathParser interface {
|
||||||
return parsePathAccum([]string{}, path)
|
ParsePath(path string) []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func parsePathAccum(paths []string, remaining string) []string {
|
type parser struct {}
|
||||||
head, tail := nextYamlPath(remaining)
|
|
||||||
|
func NewPathParser() PathParser {
|
||||||
|
return &parser{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) ParsePath(path string) []string {
|
||||||
|
return p.parsePathAccum([]string{}, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) parsePathAccum(paths []string, remaining string) []string {
|
||||||
|
head, tail := p.nextYamlPath(remaining)
|
||||||
if tail == "" {
|
if tail == "" {
|
||||||
return append(paths, head)
|
return append(paths, head)
|
||||||
}
|
}
|
||||||
return parsePathAccum(append(paths, head), tail)
|
return p.parsePathAccum(append(paths, head), tail)
|
||||||
}
|
}
|
||||||
|
|
||||||
func nextYamlPath(path string) (pathElement string, remaining string) {
|
func (p *parser) nextYamlPath(path string) (pathElement string, remaining string) {
|
||||||
switch path[0] {
|
switch path[0] {
|
||||||
case '[':
|
case '[':
|
||||||
// e.g [0].blah.cat -> we need to return "0" and "blah.cat"
|
// e.g [0].blah.cat -> we need to return "0" and "blah.cat"
|
||||||
return search(path[1:], []uint8{']'}, true)
|
return p.search(path[1:], []uint8{']'}, true)
|
||||||
case '"':
|
case '"':
|
||||||
// e.g "a.b".blah.cat -> we need to return "a.b" and "blah.cat"
|
// e.g "a.b".blah.cat -> we need to return "a.b" and "blah.cat"
|
||||||
return search(path[1:], []uint8{'"'}, true)
|
return p.search(path[1:], []uint8{'"'}, true)
|
||||||
default:
|
default:
|
||||||
// e.g "a.blah.cat" -> return "a" and "blah.cat"
|
// e.g "a.blah.cat" -> return "a" and "blah.cat"
|
||||||
return search(path[0:], []uint8{'.', '['}, false)
|
return p.search(path[0:], []uint8{'.', '['}, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func search(path string, matchingChars []uint8, skipNext bool) (pathElement string, remaining string) {
|
func (p *parser) search(path string, matchingChars []uint8, skipNext bool) (pathElement string, remaining string) {
|
||||||
for i := 0; i < len(path); i++ {
|
for i := 0; i < len(path); i++ {
|
||||||
var char = path[i]
|
var char = path[i]
|
||||||
if contains(matchingChars, char) {
|
if p.contains(matchingChars, char) {
|
||||||
var remainingStart = i + 1
|
var remainingStart = i + 1
|
||||||
if skipNext {
|
if skipNext {
|
||||||
remainingStart = remainingStart + 1
|
remainingStart = remainingStart + 1
|
||||||
@ -45,7 +55,7 @@ func search(path string, matchingChars []uint8, skipNext bool) (pathElement stri
|
|||||||
return path, ""
|
return path, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func contains(matchingChars []uint8, candidate uint8) bool {
|
func (p *parser) contains(matchingChars []uint8, candidate uint8) bool {
|
||||||
for _, a := range matchingChars {
|
for _, a := range matchingChars {
|
||||||
if a == candidate {
|
if a == candidate {
|
||||||
return true
|
return true
|
||||||
|
@ -12,33 +12,17 @@ var parsePathsTests = []struct {
|
|||||||
{"a.b", []string{"a", "b"}},
|
{"a.b", []string{"a", "b"}},
|
||||||
{"a.b[0]", []string{"a", "b", "0"}},
|
{"a.b[0]", []string{"a", "b", "0"}},
|
||||||
{"a.b.d[+]", []string{"a", "b", "d", "+"}},
|
{"a.b.d[+]", []string{"a", "b", "d", "+"}},
|
||||||
|
{"a", []string{"a"}},
|
||||||
|
{"a.b.c", []string{"a", "b", "c"}},
|
||||||
|
{"\"a.b\".c", []string{"a.b", "c"}},
|
||||||
|
{"a.\"b.c\".d", []string{"a", "b.c", "d"}},
|
||||||
|
{"[1].a.d", []string{"1", "a", "d"}},
|
||||||
|
{"a[0].c", []string{"a", "0", "c"}},
|
||||||
|
{"[0]", []string{"0"}},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParsePath(t *testing.T) {
|
func TestParsePath(t *testing.T) {
|
||||||
for _, tt := range parsePathsTests {
|
for _, tt := range parsePathsTests {
|
||||||
test.AssertResultComplex(t, tt.expectedPaths, ParsePath(tt.path))
|
test.AssertResultComplex(t, tt.expectedPaths, NewPathParser().ParsePath(tt.path))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var nextYamlPathTests = []struct {
|
|
||||||
path string
|
|
||||||
expectedElement string
|
|
||||||
expectedRemaining string
|
|
||||||
}{
|
|
||||||
{"a.b", "a", "b"},
|
|
||||||
{"a", "a", ""},
|
|
||||||
{"a.b.c", "a", "b.c"},
|
|
||||||
{"\"a.b\".c", "a.b", "c"},
|
|
||||||
{"a.\"b.c\".d", "a", "\"b.c\".d"},
|
|
||||||
{"[1].a.d", "1", "a.d"},
|
|
||||||
{"a[0].c", "a", "[0].c"},
|
|
||||||
{"[0]", "0", ""},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNextYamlPath(t *testing.T) {
|
|
||||||
for _, tt := range nextYamlPathTests {
|
|
||||||
var element, remaining = nextYamlPath(tt.path)
|
|
||||||
test.AssertResultWithContext(t, tt.expectedElement, element, tt)
|
|
||||||
test.AssertResultWithContext(t, tt.expectedRemaining, remaining, tt)
|
|
||||||
}
|
|
||||||
}
|
|
23
yq.go
23
yq.go
@ -10,6 +10,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"github.com/mikefarah/yq/pkg/yqlib"
|
"github.com/mikefarah/yq/pkg/yqlib"
|
||||||
|
"github.com/mikefarah/yq/pkg/marshal"
|
||||||
|
|
||||||
errors "github.com/pkg/errors"
|
errors "github.com/pkg/errors"
|
||||||
|
|
||||||
@ -29,6 +30,9 @@ var verbose = false
|
|||||||
var version = false
|
var version = false
|
||||||
var docIndex = "0"
|
var docIndex = "0"
|
||||||
var log = logging.MustGetLogger("yq")
|
var log = logging.MustGetLogger("yq")
|
||||||
|
var lib = yqlib.NewYqLib(log)
|
||||||
|
var jsonConverter = marshal.NewJsonConverter()
|
||||||
|
var yamlConverter = marshal.NewYamlConverter()
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
cmd := newCommandCLI()
|
cmd := newCommandCLI()
|
||||||
@ -39,7 +43,6 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func newCommandCLI() *cobra.Command {
|
func newCommandCLI() *cobra.Command {
|
||||||
yqlib.SetLogger(log)
|
|
||||||
yaml.DefaultMapType = reflect.TypeOf(yaml.MapSlice{})
|
yaml.DefaultMapType = reflect.TypeOf(yaml.MapSlice{})
|
||||||
var rootCmd = &cobra.Command{
|
var rootCmd = &cobra.Command{
|
||||||
Use: "yq",
|
Use: "yq",
|
||||||
@ -277,7 +280,7 @@ func readProperty(cmd *cobra.Command, args []string) error {
|
|||||||
log.Debugf("%v", dataBucket)
|
log.Debugf("%v", dataBucket)
|
||||||
mappedDocs = append(mappedDocs, dataBucket)
|
mappedDocs = append(mappedDocs, dataBucket)
|
||||||
} else {
|
} else {
|
||||||
mappedDoc, errorParsing := yqlib.ReadPath(dataBucket, path)
|
mappedDoc, errorParsing := lib.ReadPath(dataBucket, path)
|
||||||
log.Debugf("%v", mappedDoc)
|
log.Debugf("%v", mappedDoc)
|
||||||
if errorParsing != nil {
|
if errorParsing != nil {
|
||||||
return errors.Wrapf(errorParsing, "Error reading path in document index %v", currentIndex)
|
return errors.Wrapf(errorParsing, "Error reading path in document index %v", currentIndex)
|
||||||
@ -338,7 +341,7 @@ func newYaml(args []string) (interface{}, error) {
|
|||||||
path := entry.Key.(string)
|
path := entry.Key.(string)
|
||||||
value := entry.Value
|
value := entry.Value
|
||||||
log.Debugf("setting %v to %v", path, value)
|
log.Debugf("setting %v to %v", path, value)
|
||||||
dataBucket = yqlib.WritePath(dataBucket, path, value)
|
dataBucket = lib.WritePath(dataBucket, path, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
return dataBucket, nil
|
return dataBucket, nil
|
||||||
@ -414,7 +417,7 @@ func writeProperty(cmd *cobra.Command, args []string) error {
|
|||||||
path := entry.Key.(string)
|
path := entry.Key.(string)
|
||||||
value := entry.Value
|
value := entry.Value
|
||||||
log.Debugf("setting %v to %v", path, value)
|
log.Debugf("setting %v to %v", path, value)
|
||||||
dataBucket = yqlib.WritePath(dataBucket, path, value)
|
dataBucket = lib.WritePath(dataBucket, path, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return dataBucket, nil
|
return dataBucket, nil
|
||||||
@ -437,7 +440,7 @@ func prefixProperty(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
if updateAll || currentIndex == docIndexInt {
|
if updateAll || currentIndex == docIndexInt {
|
||||||
log.Debugf("Prefixing %v to doc %v", prefixPath, currentIndex)
|
log.Debugf("Prefixing %v to doc %v", prefixPath, currentIndex)
|
||||||
var mapDataBucket = yqlib.PrefixPath(dataBucket, prefixPath)
|
var mapDataBucket = lib.PrefixPath(dataBucket, prefixPath)
|
||||||
return mapDataBucket, nil
|
return mapDataBucket, nil
|
||||||
}
|
}
|
||||||
return dataBucket, nil
|
return dataBucket, nil
|
||||||
@ -491,7 +494,7 @@ func deleteProperty(cmd *cobra.Command, args []string) error {
|
|||||||
var updateData = func(dataBucket interface{}, currentIndex int) (interface{}, error) {
|
var updateData = func(dataBucket interface{}, currentIndex int) (interface{}, error) {
|
||||||
if updateAll || currentIndex == docIndexInt {
|
if updateAll || currentIndex == docIndexInt {
|
||||||
log.Debugf("Deleting path in doc %v", currentIndex)
|
log.Debugf("Deleting path in doc %v", currentIndex)
|
||||||
return yqlib.DeletePath(dataBucket, deletePath)
|
return lib.DeletePath(dataBucket, deletePath)
|
||||||
}
|
}
|
||||||
return dataBucket, nil
|
return dataBucket, nil
|
||||||
}
|
}
|
||||||
@ -518,7 +521,7 @@ func mergeProperties(cmd *cobra.Command, args []string) error {
|
|||||||
// map
|
// map
|
||||||
var mapDataBucket = make(map[interface{}]interface{})
|
var mapDataBucket = make(map[interface{}]interface{})
|
||||||
mapDataBucket["root"] = dataBucket
|
mapDataBucket["root"] = dataBucket
|
||||||
if err := yqlib.Merge(&mergedData, mapDataBucket, overwriteFlag, appendFlag); err != nil {
|
if err := lib.Merge(&mergedData, mapDataBucket, overwriteFlag, appendFlag); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, f := range filesToMerge {
|
for _, f := range filesToMerge {
|
||||||
@ -530,7 +533,7 @@ func mergeProperties(cmd *cobra.Command, args []string) error {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
mapDataBucket["root"] = fileToMerge
|
mapDataBucket["root"] = fileToMerge
|
||||||
if err := yqlib.Merge(&mergedData, mapDataBucket, overwriteFlag, appendFlag); err != nil {
|
if err := lib.Merge(&mergedData, mapDataBucket, overwriteFlag, appendFlag); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -580,9 +583,9 @@ func parseValue(argument string) interface{} {
|
|||||||
|
|
||||||
func toString(context interface{}) (string, error) {
|
func toString(context interface{}) (string, error) {
|
||||||
if outputToJSON {
|
if outputToJSON {
|
||||||
return yqlib.JsonToString(context)
|
return jsonConverter.JsonToString(context)
|
||||||
}
|
}
|
||||||
return yqlib.YamlToString(context, trimOutput)
|
return yamlConverter.YamlToString(context, trimOutput)
|
||||||
}
|
}
|
||||||
|
|
||||||
func safelyRenameFile(from string, to string) {
|
func safelyRenameFile(from string, to string) {
|
||||||
|
@ -5,7 +5,7 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
"github.com/mikefarah/yq/test"
|
"github.com/mikefarah/yq/test"
|
||||||
"github.com/mikefarah/yq/pkg/yqlib"
|
"github.com/mikefarah/yq/pkg/marshal"
|
||||||
)
|
)
|
||||||
|
|
||||||
var parseValueTests = []struct {
|
var parseValueTests = []struct {
|
||||||
@ -30,7 +30,7 @@ func TestMultilineString(t *testing.T) {
|
|||||||
testString := `
|
testString := `
|
||||||
abcd
|
abcd
|
||||||
efg`
|
efg`
|
||||||
formattedResult, _ := yqlib.YamlToString(testString, false)
|
formattedResult, _ := marshal.NewYamlConverter().YamlToString(testString, false)
|
||||||
test.AssertResult(t, testString, formattedResult)
|
test.AssertResult(t, testString, formattedResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ func TestNewYaml_WithScript(t *testing.T) {
|
|||||||
e:
|
e:
|
||||||
- name: Mike Farah`
|
- name: Mike Farah`
|
||||||
result, _ := newYaml([]string{""})
|
result, _ := newYaml([]string{""})
|
||||||
actualResult, _ := yqlib.YamlToString(result, true)
|
actualResult, _ := marshal.NewYamlConverter().YamlToString(result, true)
|
||||||
test.AssertResult(t, expectedResult, actualResult)
|
test.AssertResult(t, expectedResult, actualResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user