mirror of
				https://github.com/taigrr/yq
				synced 2025-01-18 04:53:17 -08:00 
			
		
		
		
	better v3
This commit is contained in:
		
							parent
							
								
									5fc13bdccd
								
							
						
					
					
						commit
						aad15ccc6e
					
				| @ -1,2 +1,7 @@ | |||||||
| b.c: cat | - command: update | ||||||
|  |   path: b.c | ||||||
|  |   value: | ||||||
|  |     #great  | ||||||
|  |     things: frog # wow! | ||||||
|  | 
 | ||||||
| b.e[+].name: Mike Farah | b.e[+].name: Mike Farah | ||||||
|  | |||||||
							
								
								
									
										3
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								go.mod
									
									
									
									
									
								
							| @ -1,4 +1,4 @@ | |||||||
| module github.com/mikefarah/yq/v2 | module github.com/mikefarah/yq/v3 | ||||||
| 
 | 
 | ||||||
| require ( | require ( | ||||||
| 	github.com/mikefarah/yaml/v2 v2.4.0 | 	github.com/mikefarah/yaml/v2 v2.4.0 | ||||||
| @ -7,6 +7,7 @@ require ( | |||||||
| 	golang.org/x/tools v0.0.0-20191030203535-5e247c9ad0a0 // indirect | 	golang.org/x/tools v0.0.0-20191030203535-5e247c9ad0a0 // indirect | ||||||
| 	gopkg.in/imdario/mergo.v0 v0.3.7 | 	gopkg.in/imdario/mergo.v0 v0.3.7 | ||||||
| 	gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473 | 	gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473 | ||||||
|  | 	gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2 | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| go 1.13 | go 1.13 | ||||||
|  | |||||||
							
								
								
									
										5
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								go.sum
									
									
									
									
									
								
							| @ -10,8 +10,11 @@ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T | |||||||
| github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= | github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= | ||||||
| github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= | ||||||
| github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= | github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= | ||||||
|  | github.com/mikefarah/yaml v2.1.0+incompatible h1:nu2cqmzk4WlWJNgnevY88faMcdrDzYGcsUjYFxEpB7Y= | ||||||
| github.com/mikefarah/yaml/v2 v2.4.0 h1:eYqfooY0BnvKTJxr7+ABJs13n3dg9n347GScDaU2Lww= | github.com/mikefarah/yaml/v2 v2.4.0 h1:eYqfooY0BnvKTJxr7+ABJs13n3dg9n347GScDaU2Lww= | ||||||
| github.com/mikefarah/yaml/v2 v2.4.0/go.mod h1:ahVqZF4n1W4NqwvVnZzC4es67xsW9uR/RRf2RRxieJU= | github.com/mikefarah/yaml/v2 v2.4.0/go.mod h1:ahVqZF4n1W4NqwvVnZzC4es67xsW9uR/RRf2RRxieJU= | ||||||
|  | github.com/mikefarah/yq v2.4.0+incompatible h1:oBxbWy8R9hI3BIUUxEf0CzikWa2AgnGrGhvGQt5jgjk= | ||||||
|  | github.com/mikefarah/yq/v2 v2.4.1 h1:tajDonaFK6WqitSZExB6fKlWQy/yCkptqxh2AXEe3N4= | ||||||
| github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= | ||||||
| github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= | ||||||
| github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= | ||||||
| @ -48,3 +51,5 @@ gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473 h1:6D+BvnJ/j6e222UW | |||||||
| gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473/go.mod h1:N1eN2tsCx0Ydtgjl4cqmbRCsY4/+z4cYDeqwZTk6zog= | gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473/go.mod h1:N1eN2tsCx0Ydtgjl4cqmbRCsY4/+z4cYDeqwZTk6zog= | ||||||
| gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= | ||||||
| gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||||
|  | gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2 h1:XZx7nhd5GMaZpmDaEHFVafUZC7ya0fuo7cSJ3UCKYmM= | ||||||
|  | gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||||||
|  | |||||||
| @ -1,19 +1,20 @@ | |||||||
| package yqlib | package yqlib | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"fmt" |  | ||||||
| 	"reflect" |  | ||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"strings" |  | ||||||
| 
 | 
 | ||||||
| 	yaml "github.com/mikefarah/yaml/v2" |  | ||||||
| 	logging "gopkg.in/op/go-logging.v1" | 	logging "gopkg.in/op/go-logging.v1" | ||||||
|  | 	yaml "gopkg.in/yaml.v3" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | type WriteCommand struct { | ||||||
|  | 	// Command string TODO | ||||||
|  | 	Value yaml.Node | ||||||
|  | } | ||||||
|  | 
 | ||||||
| type DataNavigator interface { | type DataNavigator interface { | ||||||
| 	ReadChildValue(child interface{}, remainingPaths []string) (interface{}, error) | 	Get(rootNode *yaml.Node, remainingPath []string) (*yaml.Node, error) | ||||||
| 	UpdatedChildValue(child interface{}, remainingPaths []string, value interface{}) interface{} | 	Update(rootNode *yaml.Node, remainingPath []string, writeCommand WriteCommand) error | ||||||
| 	DeleteChildValue(child interface{}, remainingPaths []string) (interface{}, error) |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type navigator struct { | type navigator struct { | ||||||
| @ -26,351 +27,451 @@ func NewDataNavigator(l *logging.Logger) DataNavigator { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (n *navigator) ReadChildValue(child interface{}, remainingPaths []string) (interface{}, error) { | func (n *navigator) Get(value *yaml.Node, path []string) (*yaml.Node, error) { | ||||||
| 	if len(remainingPaths) == 0 { | 	realValue := value | ||||||
| 		return child, nil | 	if realValue.Kind == yaml.DocumentNode { | ||||||
|  | 		realValue = value.Content[0] | ||||||
| 	} | 	} | ||||||
| 	return n.recurse(child, remainingPaths[0], remainingPaths[1:]) | 	if len(path) > 0 { | ||||||
|  | 		n.log.Debug("diving into %v", path[0]) | ||||||
|  | 		return n.recurse(realValue, path[0], path[1:]) | ||||||
|  | 	} | ||||||
|  | 	return realValue, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (n *navigator) UpdatedChildValue(child interface{}, remainingPaths []string, value interface{}) interface{} { | func (n *navigator) guessKind(tail []string) yaml.Kind { | ||||||
| 	if len(remainingPaths) == 0 { | 	n.log.Debug("tail %v", tail) | ||||||
| 		return value | 	if len(tail) == 0 { | ||||||
|  | 		n.log.Debug("scalar") | ||||||
|  | 		return yaml.ScalarNode | ||||||
| 	} | 	} | ||||||
| 	n.log.Debugf("UpdatedChildValue for child %v with path %v to set value %v", child, remainingPaths, value) | 	var _, errorParsingInt = strconv.ParseInt(tail[0], 10, 64) | ||||||
| 	n.log.Debugf("type of child is %v", reflect.TypeOf(child)) | 	if tail[0] == "*" || tail[0] == "+" || errorParsingInt == nil { | ||||||
| 
 | 		return yaml.SequenceNode | ||||||
| 	switch child := child.(type) { |  | ||||||
| 	case nil: |  | ||||||
| 		if remainingPaths[0] == "+" || remainingPaths[0] == "*" { |  | ||||||
| 			return n.writeArray(child, remainingPaths, value) |  | ||||||
| 	} | 	} | ||||||
| 	case []interface{}: | 	return yaml.MappingNode | ||||||
| 		_, 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) { | func (n *navigator) getOrReplace(original *yaml.Node, expectedKind yaml.Kind) *yaml.Node { | ||||||
| 	n.log.Debugf("DeleteChildValue for %v for %v\n", remainingPaths, child) | 	// expected is a scalar when we reach the end of the path | ||||||
| 	if len(remainingPaths) == 0 { | 	// no need to clobber the original because: | ||||||
| 		return child, nil | 	// when reading, it should deal with the original kind | ||||||
|  | 	// when writing, it will clobber the kind anyway | ||||||
|  | 	if original.Kind != expectedKind && (expectedKind != yaml.ScalarNode) { | ||||||
|  | 		return &yaml.Node{Kind: expectedKind} | ||||||
| 	} | 	} | ||||||
| 	var head = remainingPaths[0] | 	return original | ||||||
| 	var tail = remainingPaths[1:] | } | ||||||
| 	switch child := child.(type) { | 
 | ||||||
| 	case yaml.MapSlice: | func (n *navigator) recurse(value *yaml.Node, head string, tail []string) (*yaml.Node, error) { | ||||||
| 		return n.deleteMap(child, remainingPaths) | 	switch value.Kind { | ||||||
| 	case []interface{}: | 	case yaml.MappingNode: | ||||||
|  | 		n.log.Debug("its a map with %v entries", len(value.Content)/2) | ||||||
|  | 		for index, content := range value.Content { | ||||||
|  | 			// value.Content is a concatenated array of key, value, | ||||||
|  | 			// so keys are in the even indexes, values in odd. | ||||||
|  | 			if index%2 == 1 || content.Value != head { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 			value.Content[index+1] = n.getOrReplace(value.Content[index+1], n.guessKind(tail)) | ||||||
|  | 			return n.Get(value.Content[index+1], tail) | ||||||
|  | 		} | ||||||
|  | 		value.Content = append(value.Content, &yaml.Node{Value: head, Kind: yaml.ScalarNode}) | ||||||
|  | 		mapEntryValue := yaml.Node{Kind: n.guessKind(tail)} | ||||||
|  | 		value.Content = append(value.Content, &mapEntryValue) | ||||||
|  | 		n.log.Debug("adding new node %v", value.Content) | ||||||
|  | 		return n.Get(&mapEntryValue, tail) | ||||||
|  | 	case yaml.SequenceNode: | ||||||
|  | 		n.log.Debug("its a sequence of %v things!", len(value.Content)) | ||||||
| 		if head == "*" { | 		if head == "*" { | ||||||
| 			return n.deleteArraySplat(child, tail) | 			var newNode = yaml.Node{Kind: yaml.SequenceNode, Style: value.Style} | ||||||
| 		} | 			newNode.Content = make([]*yaml.Node, len(value.Content)) | ||||||
| 		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) { | 			for index, value := range value.Content { | ||||||
| 	switch value := value.(type) { | 				value.Content[index] = n.getOrReplace(value.Content[index], n.guessKind(tail)) | ||||||
| 	case []interface{}: | 				var nestedValue, err = n.Get(value.Content[index], tail) | ||||||
| 		if head == "*" { |  | ||||||
| 			return n.readArraySplat(value, tail) |  | ||||||
| 		} |  | ||||||
| 		index, err := strconv.ParseInt(head, 10, 64) |  | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 			return nil, fmt.Errorf("error accessing array: %v", err) | 					return nil, err | ||||||
| 				} | 				} | ||||||
| 		return n.readArray(value, index, tail) | 				newNode.Content[index] = nestedValue | ||||||
| 	case yaml.MapSlice: | 			} | ||||||
| 		return n.readMap(value, head, tail) | 			return &newNode, nil | ||||||
|  | 		} else if head == "+" { | ||||||
|  | 
 | ||||||
|  | 			var newNode = yaml.Node{Kind: n.guessKind(tail)} | ||||||
|  | 			value.Content = append(value.Content, &newNode) | ||||||
|  | 			n.log.Debug("appending a new node, %v", value.Content) | ||||||
|  | 			return n.Get(&newNode, tail) | ||||||
|  | 		} | ||||||
|  | 		var index, err = strconv.ParseInt(head, 10, 64) // nolint | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 		if index >= int64(len(value.Content)) { | ||||||
|  | 			return nil, nil | ||||||
|  | 		} | ||||||
|  | 		value.Content[index] = n.getOrReplace(value.Content[index], n.guessKind(tail)) | ||||||
|  | 		return n.Get(value.Content[index], tail) | ||||||
| 	default: | 	default: | ||||||
| 		return nil, nil | 		return nil, nil | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (n *navigator) matchesKey(key string, actual interface{}) bool { | func (n *navigator) Update(dataBucket *yaml.Node, remainingPath []string, writeCommand WriteCommand) error { | ||||||
| 	var actualString = fmt.Sprintf("%v", actual) | 	nodeToUpdate, errorRecursing := n.Get(dataBucket, remainingPath) | ||||||
| 	var prefixMatch = strings.TrimSuffix(key, "*") | 	if errorRecursing != nil { | ||||||
| 	if prefixMatch != key { | 		return errorRecursing | ||||||
| 		return strings.HasPrefix(actualString, prefixMatch) |  | ||||||
| 	} | 	} | ||||||
| 	return actualString == key | 	// later, support ability to execute other commands | ||||||
|  | 
 | ||||||
|  | 	changesToApply := writeCommand.Value | ||||||
|  | 
 | ||||||
|  | 	nodeToUpdate.Value = changesToApply.Value | ||||||
|  | 	nodeToUpdate.Tag = changesToApply.Tag | ||||||
|  | 	nodeToUpdate.Kind = changesToApply.Kind | ||||||
|  | 	nodeToUpdate.Style = changesToApply.Style | ||||||
|  | 	nodeToUpdate.Content = changesToApply.Content | ||||||
|  | 	nodeToUpdate.HeadComment = changesToApply.HeadComment | ||||||
|  | 	nodeToUpdate.LineComment = changesToApply.LineComment | ||||||
|  | 	nodeToUpdate.FootComment = changesToApply.FootComment | ||||||
|  | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (n *navigator) entriesInSlice(context yaml.MapSlice, key string) []*yaml.MapItem { | // func matchesKey(key string, actual interface{}) bool { | ||||||
| 	var matches = make([]*yaml.MapItem, 0) | // 	var actualString = fmt.Sprintf("%v", actual) | ||||||
| 	for idx := range context { | // 	var prefixMatch = strings.TrimSuffix(key, "*") | ||||||
| 		var entry = &context[idx] | // 	if prefixMatch != key { | ||||||
| 		if n.matchesKey(key, entry.Key) { | // 		return strings.HasPrefix(actualString, prefixMatch) | ||||||
| 			matches = append(matches, entry) | // 	} | ||||||
| 		} | // 	return actualString == key | ||||||
| 	} | // } | ||||||
| 	return matches |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| func (n *navigator) getMapSlice(context interface{}) yaml.MapSlice { | // func entriesInSlice(context yaml.MapSlice, key string) []*yaml.MapItem { | ||||||
| 	var mapSlice yaml.MapSlice | // 	var matches = make([]*yaml.MapItem, 0) | ||||||
| 	switch context := context.(type) { | // 	for idx := range context { | ||||||
| 	case yaml.MapSlice: | // 		var entry = &context[idx] | ||||||
| 		mapSlice = context | // 		if matchesKey(key, entry.Key) { | ||||||
| 	default: | // 			matches = append(matches, entry) | ||||||
| 		mapSlice = make(yaml.MapSlice, 0) | // 		} | ||||||
| 	} | // 	} | ||||||
| 	return mapSlice | // 	return matches | ||||||
| } | // } | ||||||
| 
 | 
 | ||||||
| func (n *navigator) getArray(context interface{}) (array []interface{}, ok bool) { | // func getMapSlice(context interface{}) yaml.MapSlice { | ||||||
| 	switch context := context.(type) { | // 	var mapSlice yaml.MapSlice | ||||||
| 	case []interface{}: | // 	switch context := context.(type) { | ||||||
| 		array = context | // 	case yaml.MapSlice: | ||||||
| 		ok = true | // 		mapSlice = context | ||||||
| 	default: | // 	default: | ||||||
| 		array = make([]interface{}, 0) | // 		mapSlice = make(yaml.MapSlice, 0) | ||||||
| 		ok = false | // 	} | ||||||
| 	} | // 	return mapSlice | ||||||
| 	return | // } | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| func (n *navigator) writeMap(context interface{}, paths []string, value interface{}) interface{} { | // func getArray(context interface{}) (array []interface{}, ok bool) { | ||||||
| 	n.log.Debugf("writeMap with path %v for %v to set value %v\n", paths, context, value) | // 	switch context := context.(type) { | ||||||
|  | // 	case []interface{}: | ||||||
|  | // 		array = context | ||||||
|  | // 		ok = true | ||||||
|  | // 	default: | ||||||
|  | // 		array = make([]interface{}, 0) | ||||||
|  | // 		ok = false | ||||||
|  | // 	} | ||||||
|  | // 	return | ||||||
|  | // } | ||||||
| 
 | 
 | ||||||
| 	mapSlice := n.getMapSlice(context) | // func writeMap(context interface{}, paths []string, value interface{}) interface{} { | ||||||
|  | // 	log.Debugf("writeMap with path %v for %v to set value %v\n", paths, context, value) | ||||||
| 
 | 
 | ||||||
| 	if len(paths) == 0 { | // 	mapSlice := getMapSlice(context) | ||||||
| 		return context |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	children := n.entriesInSlice(mapSlice, paths[0]) | // 	if len(paths) == 0 { | ||||||
|  | // 		return context | ||||||
|  | // 	} | ||||||
| 
 | 
 | ||||||
| 	if len(children) == 0 && paths[0] == "*" { | // 	children := entriesInSlice(mapSlice, paths[0]) | ||||||
| 		n.log.Debugf("\tNo matches, return map as is") |  | ||||||
| 		return context |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	if len(children) == 0 { | // 	if len(children) == 0 && paths[0] == "*" { | ||||||
| 		newChild := yaml.MapItem{Key: paths[0]} | // 		log.Debugf("\tNo matches, return map as is") | ||||||
| 		mapSlice = append(mapSlice, newChild) | // 		return context | ||||||
| 		children = n.entriesInSlice(mapSlice, paths[0]) | // 	} | ||||||
| 		n.log.Debugf("\tAppended child at %v for mapSlice %v\n", paths[0], mapSlice) |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	remainingPaths := paths[1:] | // 	if len(children) == 0 { | ||||||
| 	for _, child := range children { | // 		newChild := yaml.MapItem{Key: paths[0]} | ||||||
| 		child.Value = n.UpdatedChildValue(child.Value, remainingPaths, value) | // 		mapSlice = append(mapSlice, newChild) | ||||||
| 	} | // 		children = entriesInSlice(mapSlice, paths[0]) | ||||||
| 	n.log.Debugf("\tReturning mapSlice %v\n", mapSlice) | // 		log.Debugf("\tAppended child at %v for mapSlice %v\n", paths[0], mapSlice) | ||||||
| 	return mapSlice | // 	} | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| func (n *navigator) writeArray(context interface{}, paths []string, value interface{}) []interface{} { | // 	remainingPaths := paths[1:] | ||||||
| 	n.log.Debugf("writeArray with path %v for %v to set value %v\n", paths, context, value) | // 	for _, child := range children { | ||||||
| 	array, _ := n.getArray(context) | // 		child.Value = updatedChildValue(child.Value, remainingPaths, value) | ||||||
|  | // 	} | ||||||
|  | // 	log.Debugf("\tReturning mapSlice %v\n", mapSlice) | ||||||
|  | // 	return mapSlice | ||||||
|  | // } | ||||||
| 
 | 
 | ||||||
| 	if len(paths) == 0 { | // func updatedChildValue(child interface{}, remainingPaths []string, value interface{}) interface{} { | ||||||
| 		return array | // 	if len(remainingPaths) == 0 { | ||||||
| 	} | // 		return value | ||||||
|  | // 	} | ||||||
|  | // 	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)) | ||||||
| 
 | 
 | ||||||
| 	n.log.Debugf("\tarray %v\n", array) | // 	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) | ||||||
|  | // } | ||||||
| 
 | 
 | ||||||
| 	rawIndex := paths[0] | // func writeArray(context interface{}, paths []string, value interface{}) []interface{} { | ||||||
| 	remainingPaths := paths[1:] | // 	log.Debugf("writeArray with path %v for %v to set value %v\n", paths, context, value) | ||||||
| 	var index int64 | // 	array, _ := getArray(context) | ||||||
| 	// the append array indicator |  | ||||||
| 	if rawIndex == "+" { |  | ||||||
| 		index = int64(len(array)) |  | ||||||
| 	} else if rawIndex == "*" { |  | ||||||
| 		for index, oldChild := range array { |  | ||||||
| 			array[index] = n.UpdatedChildValue(oldChild, remainingPaths, value) |  | ||||||
| 		} |  | ||||||
| 		return array |  | ||||||
| 	} else { |  | ||||||
| 		index, _ = strconv.ParseInt(rawIndex, 10, 64) // nolint |  | ||||||
| 		// writeArray is only called by UpdatedChildValue which handles parsing the |  | ||||||
| 		// index, as such this renders this dead code. |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	for index >= int64(len(array)) { | // 	if len(paths) == 0 { | ||||||
| 		array = append(array, nil) | // 		return array | ||||||
| 	} | // 	} | ||||||
| 	currentChild := array[index] |  | ||||||
| 
 | 
 | ||||||
| 	n.log.Debugf("\tcurrentChild %v\n", currentChild) | // 	log.Debugf("\tarray %v\n", array) | ||||||
| 
 | 
 | ||||||
| 	array[index] = n.UpdatedChildValue(currentChild, remainingPaths, value) | // 	rawIndex := paths[0] | ||||||
| 	n.log.Debugf("\tReturning array %v\n", array) | // 	remainingPaths := paths[1:] | ||||||
| 	return array | // 	var index int64 | ||||||
| } | // 	// the append array indicator | ||||||
|  | // 	if rawIndex == "+" { | ||||||
|  | // 		index = int64(len(array)) | ||||||
|  | // 	} else if rawIndex == "*" { | ||||||
|  | // 		for index, oldChild := range array { | ||||||
|  | // 			array[index] = updatedChildValue(oldChild, remainingPaths, value) | ||||||
|  | // 		} | ||||||
|  | // 		return array | ||||||
|  | // 	} else { | ||||||
|  | // 		index, _ = strconv.ParseInt(rawIndex, 10, 64) // nolint | ||||||
|  | // 		// writeArray is only called by updatedChildValue which handles parsing the | ||||||
|  | // 		// index, as such this renders this dead code. | ||||||
|  | // 	} | ||||||
| 
 | 
 | ||||||
| func (n *navigator) readMap(context yaml.MapSlice, head string, tail []string) (interface{}, error) { | // 	for index >= int64(len(array)) { | ||||||
| 	n.log.Debugf("readingMap %v with key %v\n", context, head) | // 		array = append(array, nil) | ||||||
| 	if head == "*" { | // 	} | ||||||
| 		return n.readMapSplat(context, tail) | // 	currentChild := array[index] | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	entries := n.entriesInSlice(context, head) | // 	log.Debugf("\tcurrentChild %v\n", currentChild) | ||||||
| 	if len(entries) == 1 { |  | ||||||
| 		return n.calculateValue(entries[0].Value, tail) |  | ||||||
| 	} else if len(entries) == 0 { |  | ||||||
| 		return nil, nil |  | ||||||
| 	} |  | ||||||
| 	var errInIdx error |  | ||||||
| 	values := make([]interface{}, len(entries)) |  | ||||||
| 	for idx, entry := range entries { |  | ||||||
| 		values[idx], errInIdx = n.calculateValue(entry.Value, tail) |  | ||||||
| 		if errInIdx != nil { |  | ||||||
| 			n.log.Errorf("Error updating index %v in %v", idx, context) |  | ||||||
| 			return nil, errInIdx |  | ||||||
| 		} |  | ||||||
| 
 | 
 | ||||||
| 	} | // 	array[index] = updatedChildValue(currentChild, remainingPaths, value) | ||||||
| 	return values, nil | // 	log.Debugf("\tReturning array %v\n", array) | ||||||
| } | // 	return array | ||||||
|  | // } | ||||||
| 
 | 
 | ||||||
| func (n *navigator) readMapSplat(context yaml.MapSlice, tail []string) (interface{}, error) { | // func readMap(context yaml.MapSlice, head string, tail []string) (interface{}, error) { | ||||||
| 	var newArray = make([]interface{}, len(context)) | // 	log.Debugf("readingMap %v with key %v\n", context, head) | ||||||
| 	var i = 0 | // 	if head == "*" { | ||||||
| 	for _, entry := range context { | // 		return readMapSplat(context, tail) | ||||||
| 		if len(tail) > 0 { | // 	} | ||||||
| 			val, err := n.recurse(entry.Value, tail[0], tail[1:]) |  | ||||||
| 			if err != nil { |  | ||||||
| 				return nil, err |  | ||||||
| 			} |  | ||||||
| 			newArray[i] = val |  | ||||||
| 		} else { |  | ||||||
| 			newArray[i] = entry.Value |  | ||||||
| 		} |  | ||||||
| 		i++ |  | ||||||
| 	} |  | ||||||
| 	return newArray, nil |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| func (n *navigator) readArray(array []interface{}, head int64, tail []string) (interface{}, error) { | // 	entries := entriesInSlice(context, head) | ||||||
| 	if head >= int64(len(array)) { | // 	if len(entries) == 1 { | ||||||
| 		return nil, nil | // 		return calculateValue(entries[0].Value, tail) | ||||||
| 	} | // 	} else if len(entries) == 0 { | ||||||
|  | // 		return nil, nil | ||||||
|  | // 	} | ||||||
|  | // 	var errInIdx error | ||||||
|  | // 	values := make([]interface{}, len(entries)) | ||||||
|  | // 	for idx, entry := range entries { | ||||||
|  | // 		values[idx], errInIdx = calculateValue(entry.Value, tail) | ||||||
|  | // 		if errInIdx != nil { | ||||||
|  | // 			log.Errorf("Error updating index %v in %v", idx, context) | ||||||
|  | // 			return nil, errInIdx | ||||||
|  | // 		} | ||||||
| 
 | 
 | ||||||
| 	value := array[head] | // 	} | ||||||
| 	return n.calculateValue(value, tail) | // 	return values, nil | ||||||
| } | // } | ||||||
| 
 | 
 | ||||||
| func (n *navigator) readArraySplat(array []interface{}, tail []string) (interface{}, error) { | // func readMapSplat(context yaml.MapSlice, tail []string) (interface{}, error) { | ||||||
| 	var newArray = make([]interface{}, len(array)) | // 	var newArray = make([]interface{}, len(context)) | ||||||
| 	for index, value := range array { | // 	var i = 0 | ||||||
| 		val, err := n.calculateValue(value, tail) | // 	for _, entry := range context { | ||||||
| 		if err != nil { | // 		if len(tail) > 0 { | ||||||
| 			return nil, err | // 			val, err := recurse(entry.Value, tail[0], tail[1:]) | ||||||
| 		} | // 			if err != nil { | ||||||
| 		newArray[index] = val | // 				return nil, err | ||||||
| 	} | // 			} | ||||||
| 	return newArray, nil | // 			newArray[i] = val | ||||||
| } | // 		} else { | ||||||
|  | // 			newArray[i] = entry.Value | ||||||
|  | // 		} | ||||||
|  | // 		i++ | ||||||
|  | // 	} | ||||||
|  | // 	return newArray, nil | ||||||
|  | // } | ||||||
| 
 | 
 | ||||||
| func (n *navigator) calculateValue(value interface{}, tail []string) (interface{}, error) { | // func recurse(value interface{}, head string, tail []string) (interface{}, error) { | ||||||
| 	if len(tail) > 0 { | // 	switch value := value.(type) { | ||||||
| 		return n.recurse(value, tail[0], tail[1:]) | // 	case []interface{}: | ||||||
| 	} | // 		if head == "*" { | ||||||
| 	return value, nil | // 			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 (n *navigator) deleteMap(context interface{}, paths []string) (yaml.MapSlice, error) { | // func readArray(array []interface{}, head int64, tail []string) (interface{}, error) { | ||||||
| 	n.log.Debugf("deleteMap for %v for %v\n", paths, context) | // 	if head >= int64(len(array)) { | ||||||
|  | // 		return nil, nil | ||||||
|  | // 	} | ||||||
| 
 | 
 | ||||||
| 	mapSlice := n.getMapSlice(context) | // 	value := array[head] | ||||||
|  | // 	return calculateValue(value, tail) | ||||||
|  | // } | ||||||
| 
 | 
 | ||||||
| 	if len(paths) == 0 { | // func readArraySplat(array []interface{}, tail []string) (interface{}, error) { | ||||||
| 		return mapSlice, nil | // 	var newArray = make([]interface{}, len(array)) | ||||||
| 	} | // 	for index, value := range array { | ||||||
|  | // 		val, err := calculateValue(value, tail) | ||||||
|  | // 		if err != nil { | ||||||
|  | // 			return nil, err | ||||||
|  | // 		} | ||||||
|  | // 		newArray[index] = val | ||||||
|  | // 	} | ||||||
|  | // 	return newArray, nil | ||||||
|  | // } | ||||||
| 
 | 
 | ||||||
| 	var index int | // func calculateValue(value interface{}, tail []string) (interface{}, error) { | ||||||
| 	var child yaml.MapItem | // 	if len(tail) > 0 { | ||||||
| 	for index, child = range mapSlice { | // 		return recurse(value, tail[0], tail[1:]) | ||||||
| 		if n.matchesKey(paths[0], child.Key) { | // 	} | ||||||
| 			n.log.Debugf("\tMatched [%v] with [%v] at index %v", paths[0], child.Key, index) | // 	return value, nil | ||||||
| 			var badDelete error | // } | ||||||
| 			mapSlice, badDelete = n.deleteEntryInMap(mapSlice, child, index, paths) |  | ||||||
| 			if badDelete != nil { |  | ||||||
| 				return nil, badDelete |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	return mapSlice, nil | // func deleteMap(context interface{}, paths []string) (yaml.MapSlice, error) { | ||||||
|  | // 	log.Debugf("deleteMap for %v for %v\n", paths, context) | ||||||
| 
 | 
 | ||||||
| } | // 	mapSlice := getMapSlice(context) | ||||||
| 
 | 
 | ||||||
| func (n *navigator) deleteEntryInMap(original yaml.MapSlice, child yaml.MapItem, index int, paths []string) (yaml.MapSlice, error) { | // 	if len(paths) == 0 { | ||||||
| 	remainingPaths := paths[1:] | // 		return mapSlice, nil | ||||||
|  | // 	} | ||||||
| 
 | 
 | ||||||
| 	var newSlice yaml.MapSlice | // 	var index int | ||||||
| 	if len(remainingPaths) > 0 { | // 	var child yaml.MapItem | ||||||
| 		newChild := yaml.MapItem{Key: child.Key} | // 	for index, child = range mapSlice { | ||||||
| 		var errorDeleting error | // 		if matchesKey(paths[0], child.Key) { | ||||||
| 		newChild.Value, errorDeleting = n.DeleteChildValue(child.Value, remainingPaths) | // 			log.Debugf("\tMatched [%v] with [%v] at index %v", paths[0], child.Key, index) | ||||||
| 		if errorDeleting != nil { | // 			var badDelete error | ||||||
| 			return nil, errorDeleting | // 			mapSlice, badDelete = deleteEntryInMap(mapSlice, child, index, paths) | ||||||
| 		} | // 			if badDelete != nil { | ||||||
|  | // 				return nil, badDelete | ||||||
|  | // 			} | ||||||
|  | // 		} | ||||||
|  | // 	} | ||||||
| 
 | 
 | ||||||
| 		newSlice = make(yaml.MapSlice, len(original)) | // 	return mapSlice, nil | ||||||
| 		for i := range original { |  | ||||||
| 			item := original[i] |  | ||||||
| 			if i == index { |  | ||||||
| 				item = newChild |  | ||||||
| 			} |  | ||||||
| 			newSlice[i] = item |  | ||||||
| 		} |  | ||||||
| 	} else { |  | ||||||
| 		// Delete item from slice at index |  | ||||||
| 		newSlice = append(original[:index], original[index+1:]...) |  | ||||||
| 		n.log.Debugf("\tDeleted item index %d from original", index) |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	n.log.Debugf("\tReturning original %v\n", original) | // } | ||||||
| 	return newSlice, nil |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| func (n *navigator) deleteArraySplat(array []interface{}, tail []string) (interface{}, error) { | // func deleteEntryInMap(original yaml.MapSlice, child yaml.MapItem, index int, paths []string) (yaml.MapSlice, error) { | ||||||
| 	n.log.Debugf("deleteArraySplat for %v for %v\n", tail, array) | // 	remainingPaths := paths[1:] | ||||||
| 	var newArray = make([]interface{}, len(array)) |  | ||||||
| 	for index, value := range array { |  | ||||||
| 		val, err := n.DeleteChildValue(value, tail) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return nil, err |  | ||||||
| 		} |  | ||||||
| 		newArray[index] = val |  | ||||||
| 	} |  | ||||||
| 	return newArray, nil |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| func (n *navigator) deleteArray(array []interface{}, paths []string, index int64) (interface{}, error) { | // 	var newSlice yaml.MapSlice | ||||||
| 	n.log.Debugf("deleteArray for %v for %v\n", paths, array) | // 	if len(remainingPaths) > 0 { | ||||||
|  | // 		newChild := yaml.MapItem{Key: child.Key} | ||||||
|  | // 		var errorDeleting error | ||||||
|  | // 		newChild.Value, errorDeleting = deleteChildValue(child.Value, remainingPaths) | ||||||
|  | // 		if errorDeleting != nil { | ||||||
|  | // 			return nil, errorDeleting | ||||||
|  | // 		} | ||||||
| 
 | 
 | ||||||
| 	if index >= int64(len(array)) { | // 		newSlice = make(yaml.MapSlice, len(original)) | ||||||
| 		return array, nil | // 		for i := range original { | ||||||
| 	} | // 			item := original[i] | ||||||
|  | // 			if i == index { | ||||||
|  | // 				item = newChild | ||||||
|  | // 			} | ||||||
|  | // 			newSlice[i] = item | ||||||
|  | // 		} | ||||||
|  | // 	} else { | ||||||
|  | // 		// Delete item from slice at index | ||||||
|  | // 		newSlice = append(original[:index], original[index+1:]...) | ||||||
|  | // 		log.Debugf("\tDeleted item index %d from original", index) | ||||||
|  | // 	} | ||||||
| 
 | 
 | ||||||
| 	remainingPaths := paths[1:] | // 	log.Debugf("\tReturning original %v\n", original) | ||||||
| 	if len(remainingPaths) > 0 { | // 	return newSlice, nil | ||||||
| 		// recurse into the array element at index | // } | ||||||
| 		var errorDeleting error |  | ||||||
| 		array[index], errorDeleting = n.deleteMap(array[index], remainingPaths) |  | ||||||
| 		if errorDeleting != nil { |  | ||||||
| 			return nil, errorDeleting |  | ||||||
| 		} |  | ||||||
| 
 | 
 | ||||||
| 	} else { | // func deleteArraySplat(array []interface{}, tail []string) (interface{}, error) { | ||||||
| 		// Delete the array element at index | // 	log.Debugf("deleteArraySplat for %v for %v\n", tail, array) | ||||||
| 		array = append(array[:index], array[index+1:]...) | // 	var newArray = make([]interface{}, len(array)) | ||||||
| 		n.log.Debugf("\tDeleted item index %d from array, leaving %v", index, array) | // 	for index, value := range array { | ||||||
| 	} | // 		val, err := deleteChildValue(value, tail) | ||||||
|  | // 		if err != nil { | ||||||
|  | // 			return nil, err | ||||||
|  | // 		} | ||||||
|  | // 		newArray[index] = val | ||||||
|  | // 	} | ||||||
|  | // 	return newArray, nil | ||||||
|  | // } | ||||||
| 
 | 
 | ||||||
| 	n.log.Debugf("\tReturning array: %v\n", array) | // func deleteArray(array []interface{}, paths []string, index int64) (interface{}, error) { | ||||||
| 	return array, nil | // 	log.Debugf("deleteArray for %v for %v\n", paths, array) | ||||||
| } | 
 | ||||||
|  | // 	if index >= int64(len(array)) { | ||||||
|  | // 		return array, nil | ||||||
|  | // 	} | ||||||
|  | 
 | ||||||
|  | // 	remainingPaths := paths[1:] | ||||||
|  | // 	if len(remainingPaths) > 0 { | ||||||
|  | // 		// Recurse into the array element at index | ||||||
|  | // 		var errorDeleting error | ||||||
|  | // 		array[index], errorDeleting = deleteMap(array[index], remainingPaths) | ||||||
|  | // 		if errorDeleting != nil { | ||||||
|  | // 			return nil, errorDeleting | ||||||
|  | // 		} | ||||||
|  | 
 | ||||||
|  | // 	} else { | ||||||
|  | // 		// Delete the array element at index | ||||||
|  | // 		array = append(array[:index], array[index+1:]...) | ||||||
|  | // 		log.Debugf("\tDeleted item index %d from array, leaving %v", index, array) | ||||||
|  | // 	} | ||||||
|  | 
 | ||||||
|  | // 	log.Debugf("\tReturning array: %v\n", array) | ||||||
|  | // 	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 | ||||||
|  | // } | ||||||
|  | |||||||
| @ -1,16 +1,13 @@ | |||||||
| package yqlib | package yqlib | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	mergo "gopkg.in/imdario/mergo.v0" |  | ||||||
| 	logging "gopkg.in/op/go-logging.v1" | 	logging "gopkg.in/op/go-logging.v1" | ||||||
|  | 	yaml "gopkg.in/yaml.v3" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type YqLib interface { | type YqLib interface { | ||||||
| 	ReadPath(dataBucket interface{}, path string) (interface{}, error) | 	Get(rootNode *yaml.Node, path string) (*yaml.Node, error) | ||||||
| 	WritePath(dataBucket interface{}, path string, value interface{}) interface{} | 	Update(rootNode *yaml.Node, path string, writeCommand WriteCommand) error | ||||||
| 	PrefixPath(dataBucket interface{}, prefix string) interface{} |  | ||||||
| 	DeletePath(dataBucket interface{}, path string) (interface{}, error) |  | ||||||
| 	Merge(dst interface{}, src interface{}, overwrite bool, append bool) error |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type lib struct { | type lib struct { | ||||||
| @ -25,44 +22,15 @@ func NewYqLib(l *logging.Logger) YqLib { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (l *lib) ReadPath(dataBucket interface{}, path string) (interface{}, error) { | func (l *lib) Get(rootNode *yaml.Node, path string) (*yaml.Node, error) { | ||||||
| 	var paths = l.parser.ParsePath(path) | 	if path == "" { | ||||||
| 	return l.navigator.ReadChildValue(dataBucket, paths) | 		return rootNode, nil | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 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 |  | ||||||
| 	for i := len(paths)/2 - 1; i >= 0; i-- { |  | ||||||
| 		opp := len(paths) - 1 - i |  | ||||||
| 		paths[i], paths[opp] = paths[opp], paths[i] |  | ||||||
| 	} | 	} | ||||||
| 
 |  | ||||||
| 	var mapDataBucket = dataBucket |  | ||||||
| 	for _, key := range paths { |  | ||||||
| 		singlePath := []string{key} |  | ||||||
| 		mapDataBucket = l.navigator.UpdatedChildValue(nil, singlePath, mapDataBucket) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return mapDataBucket |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (l *lib) DeletePath(dataBucket interface{}, path string) (interface{}, error) { |  | ||||||
| 	var paths = l.parser.ParsePath(path) | 	var paths = l.parser.ParsePath(path) | ||||||
| 	return l.navigator.DeleteChildValue(dataBucket, paths) | 	return l.navigator.Get(rootNode, paths) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (l *lib) Merge(dst interface{}, src interface{}, overwrite bool, append bool) error { | func (l *lib) Update(rootNode *yaml.Node, path string, writeCommand WriteCommand) error { | ||||||
| 	if overwrite { | 	var paths = l.parser.ParsePath(path) | ||||||
| 		return mergo.Merge(dst, src, mergo.WithOverride) | 	return l.navigator.Update(rootNode, paths, writeCommand) | ||||||
| 	} else if append { |  | ||||||
| 		return mergo.Merge(dst, src, mergo.WithAppendSlice) |  | ||||||
| 	} |  | ||||||
| 	return mergo.Merge(dst, src) |  | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										550
									
								
								yq.go
									
									
									
									
									
								
							
							
						
						
									
										550
									
								
								yq.go
									
									
									
									
									
								
							| @ -6,20 +6,19 @@ import ( | |||||||
| 	"io" | 	"io" | ||||||
| 	"io/ioutil" | 	"io/ioutil" | ||||||
| 	"os" | 	"os" | ||||||
| 	"reflect" |  | ||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"strings" |  | ||||||
| 
 | 
 | ||||||
| 	"github.com/mikefarah/yq/v2/pkg/marshal" | 	"github.com/mikefarah/yq/v3/pkg/marshal" | ||||||
| 	"github.com/mikefarah/yq/v2/pkg/yqlib" | 	"github.com/mikefarah/yq/v3/pkg/yqlib" | ||||||
| 
 | 
 | ||||||
| 	errors "github.com/pkg/errors" | 	errors "github.com/pkg/errors" | ||||||
| 
 | 
 | ||||||
| 	yaml "github.com/mikefarah/yaml/v2" |  | ||||||
| 	"github.com/spf13/cobra" | 	"github.com/spf13/cobra" | ||||||
| 	logging "gopkg.in/op/go-logging.v1" | 	logging "gopkg.in/op/go-logging.v1" | ||||||
|  | 	yaml "gopkg.in/yaml.v3" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | var rawOutput = false | ||||||
| var trimOutput = true | var trimOutput = true | ||||||
| var writeInplace = false | var writeInplace = false | ||||||
| var writeScript = "" | var writeScript = "" | ||||||
| @ -45,7 +44,6 @@ func main() { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func newCommandCLI() *cobra.Command { | func newCommandCLI() *cobra.Command { | ||||||
| 	yaml.DefaultMapType = reflect.TypeOf(yaml.MapSlice{}) |  | ||||||
| 	var rootCmd = &cobra.Command{ | 	var rootCmd = &cobra.Command{ | ||||||
| 		Use:   "yq", | 		Use:   "yq", | ||||||
| 		Short: "yq is a lightweight and portable command-line YAML processor.", | 		Short: "yq is a lightweight and portable command-line YAML processor.", | ||||||
| @ -83,10 +81,10 @@ func newCommandCLI() *cobra.Command { | |||||||
| 	rootCmd.AddCommand( | 	rootCmd.AddCommand( | ||||||
| 		createReadCmd(), | 		createReadCmd(), | ||||||
| 		createWriteCmd(), | 		createWriteCmd(), | ||||||
| 		createPrefixCmd(), | 		// createPrefixCmd(), | ||||||
| 		createDeleteCmd(), | 		// createDeleteCmd(), | ||||||
| 		createNewCmd(), | 		// createNewCmd(), | ||||||
| 		createMergeCmd(), | 		// createMergeCmd(), | ||||||
| 	) | 	) | ||||||
| 	rootCmd.SetOutput(os.Stdout) | 	rootCmd.SetOutput(os.Stdout) | ||||||
| 
 | 
 | ||||||
| @ -110,6 +108,7 @@ yq r -- things.yaml --key-starting-with-dashes | |||||||
| 		RunE: readProperty, | 		RunE: readProperty, | ||||||
| 	} | 	} | ||||||
| 	cmdRead.PersistentFlags().StringVarP(&docIndex, "doc", "d", "0", "process document index number (0 based, * for all documents)") | 	cmdRead.PersistentFlags().StringVarP(&docIndex, "doc", "d", "0", "process document index number (0 based, * for all documents)") | ||||||
|  | 	cmdRead.PersistentFlags().BoolVarP(&rawOutput, "raw", "r", false, "raw yaml output - prints out values instead of yaml") | ||||||
| 	cmdRead.PersistentFlags().BoolVarP(&outputToJSON, "tojson", "j", false, "output as json") | 	cmdRead.PersistentFlags().BoolVarP(&outputToJSON, "tojson", "j", false, "output as json") | ||||||
| 	return cmdRead | 	return cmdRead | ||||||
| } | } | ||||||
| @ -149,104 +148,104 @@ a.b.e: | |||||||
| 	return cmdWrite | 	return cmdWrite | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func createPrefixCmd() *cobra.Command { | // func createPrefixCmd() *cobra.Command { | ||||||
| 	var cmdWrite = &cobra.Command{ | // 	var cmdWrite = &cobra.Command{ | ||||||
| 		Use:     "prefix [yaml_file] [path]", | // 		Use:     "prefix [yaml_file] [path]", | ||||||
| 		Aliases: []string{"p"}, | // 		Aliases: []string{"p"}, | ||||||
| 		Short:   "yq p [--inplace/-i] [--doc/-d index] sample.yaml a.b.c", | // 		Short:   "yq p [--inplace/-i] [--doc/-d index] sample.yaml a.b.c", | ||||||
| 		Example: ` | // 		Example: ` | ||||||
| yq prefix things.yaml a.b.c | // yq prefix things.yaml a.b.c | ||||||
| yq prefix --inplace things.yaml a.b.c | // yq prefix --inplace things.yaml a.b.c | ||||||
| yq prefix --inplace -- things.yaml --key-starting-with-dash | // yq prefix --inplace -- things.yaml --key-starting-with-dash | ||||||
| yq p -i things.yaml a.b.c | // yq p -i things.yaml a.b.c | ||||||
| yq p --doc 2 things.yaml a.b.d | // yq p --doc 2 things.yaml a.b.d | ||||||
| yq p -d2 things.yaml a.b.d | // yq p -d2 things.yaml a.b.d | ||||||
|       `, | //       `, | ||||||
| 		Long: `Prefixes w.r.t to the yaml file at the given path. | // 		Long: `Prefixes w.r.t to the yaml file at the given path. | ||||||
| Outputs to STDOUT unless the inplace flag is used, in which case the file is updated instead. | // Outputs to STDOUT unless the inplace flag is used, in which case the file is updated instead. | ||||||
| `, | // `, | ||||||
| 		RunE: prefixProperty, | // 		RunE: prefixProperty, | ||||||
| 	} | // 	} | ||||||
| 	cmdWrite.PersistentFlags().BoolVarP(&writeInplace, "inplace", "i", false, "update the yaml file inplace") | // 	cmdWrite.PersistentFlags().BoolVarP(&writeInplace, "inplace", "i", false, "update the yaml file inplace") | ||||||
| 	cmdWrite.PersistentFlags().StringVarP(&docIndex, "doc", "d", "0", "process document index number (0 based, * for all documents)") | // 	cmdWrite.PersistentFlags().StringVarP(&docIndex, "doc", "d", "0", "process document index number (0 based, * for all documents)") | ||||||
| 	return cmdWrite | // 	return cmdWrite | ||||||
| } | // } | ||||||
| 
 | 
 | ||||||
| func createDeleteCmd() *cobra.Command { | // func createDeleteCmd() *cobra.Command { | ||||||
| 	var cmdDelete = &cobra.Command{ | // 	var cmdDelete = &cobra.Command{ | ||||||
| 		Use:     "delete [yaml_file] [path]", | // 		Use:     "delete [yaml_file] [path]", | ||||||
| 		Aliases: []string{"d"}, | // 		Aliases: []string{"d"}, | ||||||
| 		Short:   "yq d [--inplace/-i] [--doc/-d index] sample.yaml a.b.c", | // 		Short:   "yq d [--inplace/-i] [--doc/-d index] sample.yaml a.b.c", | ||||||
| 		Example: ` | // 		Example: ` | ||||||
| yq delete things.yaml a.b.c | // yq delete things.yaml a.b.c | ||||||
| yq delete --inplace things.yaml a.b.c | // yq delete --inplace things.yaml a.b.c | ||||||
| yq delete --inplace -- things.yaml --key-starting-with-dash | // yq delete --inplace -- things.yaml --key-starting-with-dash | ||||||
| yq d -i things.yaml a.b.c | // yq d -i things.yaml a.b.c | ||||||
| yq d things.yaml a.b.c | // yq d things.yaml a.b.c | ||||||
| 	`, | // 	`, | ||||||
| 		Long: `Deletes the given path from the YAML file. | // 		Long: `Deletes the given path from the YAML file. | ||||||
| Outputs to STDOUT unless the inplace flag is used, in which case the file is updated instead. | // Outputs to STDOUT unless the inplace flag is used, in which case the file is updated instead. | ||||||
| `, | // `, | ||||||
| 		RunE: deleteProperty, | // 		RunE: deleteProperty, | ||||||
| 	} | // 	} | ||||||
| 	cmdDelete.PersistentFlags().BoolVarP(&writeInplace, "inplace", "i", false, "update the yaml file inplace") | // 	cmdDelete.PersistentFlags().BoolVarP(&writeInplace, "inplace", "i", false, "update the yaml file inplace") | ||||||
| 	cmdDelete.PersistentFlags().StringVarP(&docIndex, "doc", "d", "0", "process document index number (0 based, * for all documents)") | // 	cmdDelete.PersistentFlags().StringVarP(&docIndex, "doc", "d", "0", "process document index number (0 based, * for all documents)") | ||||||
| 	return cmdDelete | // 	return cmdDelete | ||||||
| } | // } | ||||||
| 
 | 
 | ||||||
| func createNewCmd() *cobra.Command { | // func createNewCmd() *cobra.Command { | ||||||
| 	var cmdNew = &cobra.Command{ | // 	var cmdNew = &cobra.Command{ | ||||||
| 		Use:     "new [path] [value]", | // 		Use:     "new [path] [value]", | ||||||
| 		Aliases: []string{"n"}, | // 		Aliases: []string{"n"}, | ||||||
| 		Short:   "yq n [--script/-s script_file] a.b.c newValue", | // 		Short:   "yq n [--script/-s script_file] a.b.c newValue", | ||||||
| 		Example: ` | // 		Example: ` | ||||||
| yq new a.b.c cat | // yq new a.b.c cat | ||||||
| yq n a.b.c cat | // yq n a.b.c cat | ||||||
| yq n -- --key-starting-with-dash cat | // yq n -- --key-starting-with-dash cat | ||||||
| yq n --script create_script.yaml | // yq n --script create_script.yaml | ||||||
|       `, | //       `, | ||||||
| 		Long: `Creates a new yaml w.r.t the given path and value. | // 		Long: `Creates a new yaml w.r.t the given path and value. | ||||||
| Outputs to STDOUT | // Outputs to STDOUT | ||||||
| 
 | 
 | ||||||
| Create Scripts: | // Create Scripts: | ||||||
| Note that you can give a create script to perform more sophisticated yaml. This follows the same format as the update script. | // Note that you can give a create script to perform more sophisticated yaml. This follows the same format as the update script. | ||||||
| `, | // `, | ||||||
| 		RunE: newProperty, | // 		RunE: newProperty, | ||||||
| 	} | // 	} | ||||||
| 	cmdNew.PersistentFlags().StringVarP(&writeScript, "script", "s", "", "yaml script for updating yaml") | // 	cmdNew.PersistentFlags().StringVarP(&writeScript, "script", "s", "", "yaml script for updating yaml") | ||||||
| 	return cmdNew | // 	return cmdNew | ||||||
| } | // } | ||||||
| 
 | 
 | ||||||
| func createMergeCmd() *cobra.Command { | // func createMergeCmd() *cobra.Command { | ||||||
| 	var cmdMerge = &cobra.Command{ | // 	var cmdMerge = &cobra.Command{ | ||||||
| 		Use:     "merge [initial_yaml_file] [additional_yaml_file]...", | // 		Use:     "merge [initial_yaml_file] [additional_yaml_file]...", | ||||||
| 		Aliases: []string{"m"}, | // 		Aliases: []string{"m"}, | ||||||
| 		Short:   "yq m [--inplace/-i] [--doc/-d index] [--overwrite/-x] [--append/-a] sample.yaml sample2.yaml", | // 		Short:   "yq m [--inplace/-i] [--doc/-d index] [--overwrite/-x] [--append/-a] sample.yaml sample2.yaml", | ||||||
| 		Example: ` | // 		Example: ` | ||||||
| yq merge things.yaml other.yaml | // yq merge things.yaml other.yaml | ||||||
| yq merge --inplace things.yaml other.yaml | // yq merge --inplace things.yaml other.yaml | ||||||
| yq m -i things.yaml other.yaml | // yq m -i things.yaml other.yaml | ||||||
| yq m --overwrite things.yaml other.yaml | // yq m --overwrite things.yaml other.yaml | ||||||
| yq m -i -x things.yaml other.yaml | // yq m -i -x things.yaml other.yaml | ||||||
| yq m -i -a things.yaml other.yaml | // yq m -i -a things.yaml other.yaml | ||||||
|       `, | //       `, | ||||||
| 		Long: `Updates the yaml file by adding/updating the path(s) and value(s) from additional yaml file(s). | // 		Long: `Updates the yaml file by adding/updating the path(s) and value(s) from additional yaml file(s). | ||||||
| Outputs to STDOUT unless the inplace flag is used, in which case the file is updated instead. | // Outputs to STDOUT unless the inplace flag is used, in which case the file is updated instead. | ||||||
| 
 | 
 | ||||||
| If overwrite flag is set then existing values will be overwritten using the values from each additional yaml file. | // If overwrite flag is set then existing values will be overwritten using the values from each additional yaml file. | ||||||
| If append flag is set then existing arrays will be merged with the arrays from each additional yaml file. | // If append flag is set then existing arrays will be merged with the arrays from each additional yaml file. | ||||||
| 
 | 
 | ||||||
| Note that if you set both flags only overwrite will take effect. | // Note that if you set both flags only overwrite will take effect. | ||||||
| `, | // `, | ||||||
| 		RunE: mergeProperties, | // 		RunE: mergeProperties, | ||||||
| 	} | // 	} | ||||||
| 	cmdMerge.PersistentFlags().BoolVarP(&writeInplace, "inplace", "i", false, "update the yaml file inplace") | // 	cmdMerge.PersistentFlags().BoolVarP(&writeInplace, "inplace", "i", false, "update the yaml file inplace") | ||||||
| 	cmdMerge.PersistentFlags().BoolVarP(&overwriteFlag, "overwrite", "x", false, "update the yaml file by overwriting existing values") | // 	cmdMerge.PersistentFlags().BoolVarP(&overwriteFlag, "overwrite", "x", false, "update the yaml file by overwriting existing values") | ||||||
| 	cmdMerge.PersistentFlags().BoolVarP(&appendFlag, "append", "a", false, "update the yaml file by appending array values") | // 	cmdMerge.PersistentFlags().BoolVarP(&appendFlag, "append", "a", false, "update the yaml file by appending array values") | ||||||
| 	cmdMerge.PersistentFlags().BoolVarP(&allowEmptyFlag, "allow-empty", "e", false, "allow empty yaml files") | // 	cmdMerge.PersistentFlags().BoolVarP(&allowEmptyFlag, "allow-empty", "e", false, "allow empty yaml files") | ||||||
| 	cmdMerge.PersistentFlags().StringVarP(&docIndex, "doc", "d", "0", "process document index number (0 based, * for all documents)") | // 	cmdMerge.PersistentFlags().StringVarP(&docIndex, "doc", "d", "0", "process document index number (0 based, * for all documents)") | ||||||
| 	return cmdMerge | // 	return cmdMerge | ||||||
| } | // } | ||||||
| 
 | 
 | ||||||
| func readProperty(cmd *cobra.Command, args []string) error { | func readProperty(cmd *cobra.Command, args []string) error { | ||||||
| 	var path = "" | 	var path = "" | ||||||
| @ -261,8 +260,9 @@ func readProperty(cmd *cobra.Command, args []string) error { | |||||||
| 	if errorParsingDocIndex != nil { | 	if errorParsingDocIndex != nil { | ||||||
| 		return errorParsingDocIndex | 		return errorParsingDocIndex | ||||||
| 	} | 	} | ||||||
| 	var mappedDocs []interface{} | 
 | ||||||
| 	var dataBucket interface{} | 	var mappedDocs []*yaml.Node | ||||||
|  | 	var dataBucket yaml.Node | ||||||
| 	var currentIndex = 0 | 	var currentIndex = 0 | ||||||
| 	var errorReadingStream = readStream(args[0], func(decoder *yaml.Decoder) error { | 	var errorReadingStream = readStream(args[0], func(decoder *yaml.Decoder) error { | ||||||
| 		for { | 		for { | ||||||
| @ -274,22 +274,15 @@ func readProperty(cmd *cobra.Command, args []string) error { | |||||||
| 				} | 				} | ||||||
| 				return nil | 				return nil | ||||||
| 			} | 			} | ||||||
| 			log.Debugf("processing %v - requested index %v", currentIndex, docIndexInt) | 			log.Debugf("processing document %v - requested index %v", currentIndex, docIndexInt) | ||||||
| 			if updateAll || currentIndex == docIndexInt { | 			if updateAll || currentIndex == docIndexInt { | ||||||
| 				log.Debugf("reading %v in index %v", path, currentIndex) | 				log.Debugf("reading %v in document %v", path, currentIndex) | ||||||
| 				if path == "" { | 				mappedDoc, errorParsing := lib.Get(&dataBucket, path) | ||||||
| 					log.Debug("no path") |  | ||||||
| 					log.Debugf("%v", dataBucket) |  | ||||||
| 					mappedDocs = append(mappedDocs, dataBucket) |  | ||||||
| 				} else { |  | ||||||
| 					mappedDoc, errorParsing := lib.ReadPath(dataBucket, path) |  | ||||||
| 					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) | ||||||
| 				} | 				} | ||||||
| 				mappedDocs = append(mappedDocs, mappedDoc) | 				mappedDocs = append(mappedDocs, mappedDoc) | ||||||
| 			} | 			} | ||||||
| 			} |  | ||||||
| 			currentIndex = currentIndex + 1 | 			currentIndex = currentIndex + 1 | ||||||
| 		} | 		} | ||||||
| 	}) | 	}) | ||||||
| @ -298,56 +291,64 @@ func readProperty(cmd *cobra.Command, args []string) error { | |||||||
| 		return errorReadingStream | 		return errorReadingStream | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if !updateAll { | 	var encoder = yaml.NewEncoder(cmd.OutOrStdout()) | ||||||
| 		dataBucket = mappedDocs[0] | 	encoder.SetIndent(2) | ||||||
| 	} else { | 	var err error | ||||||
| 		dataBucket = mappedDocs |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	dataStr, err := toString(dataBucket) | 	if rawOutput { | ||||||
|  | 		for _, mappedDoc := range mappedDocs { | ||||||
|  | 			if mappedDoc != nil { | ||||||
|  | 				cmd.Println(mappedDoc.Value) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} else if !updateAll { | ||||||
|  | 		err = encoder.Encode(mappedDocs[0]) | ||||||
|  | 	} else { | ||||||
|  | 		err = encoder.Encode(&yaml.Node{Kind: yaml.SequenceNode, Content: mappedDocs}) | ||||||
|  | 	} | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	cmd.Println(dataStr) | 	encoder.Close() | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func newProperty(cmd *cobra.Command, args []string) error { | // func newProperty(cmd *cobra.Command, args []string) error { | ||||||
| 	updatedData, err := newYaml(args) | // 	updatedData, err := newYaml(args) | ||||||
| 	if err != nil { | // 	if err != nil { | ||||||
| 		return err | // 		return err | ||||||
| 	} | // 	} | ||||||
| 	dataStr, err := toString(updatedData) | // 	dataStr, err := toString(updatedData) | ||||||
| 	if err != nil { | // 	if err != nil { | ||||||
| 		return err | // 		return err | ||||||
| 	} | // 	} | ||||||
| 	cmd.Println(dataStr) | // 	cmd.Println(dataStr) | ||||||
| 	return nil | // 	return nil | ||||||
| } | // } | ||||||
| 
 | 
 | ||||||
| func newYaml(args []string) (interface{}, error) { | // func newYaml(args []string) (interface{}, error) { | ||||||
| 	var writeCommands, writeCommandsError = readWriteCommands(args, 2, "Must provide <path_to_update> <value>") | // 	var writeCommands, writeCommandsError = readWriteCommands(args, 2, "Must provide <path_to_update> <value>") | ||||||
| 	if writeCommandsError != nil { | // 	if writeCommandsError != nil { | ||||||
| 		return nil, writeCommandsError | // 		return nil, writeCommandsError | ||||||
| 	} | // 	} | ||||||
| 
 | 
 | ||||||
| 	var dataBucket interface{} | // 	var dataBucket interface{} | ||||||
| 	var isArray = strings.HasPrefix(writeCommands[0].Key.(string), "[") | // 	var isArray = strings.HasPrefix(writeCommands[0].Key.(string), "[") | ||||||
| 	if isArray { | // 	if isArray { | ||||||
| 		dataBucket = make([]interface{}, 0) | // 		dataBucket = make([]interface{}, 0) | ||||||
| 	} else { | // 	} else { | ||||||
| 		dataBucket = make(yaml.MapSlice, 0) | // 		dataBucket = make(yaml.MapSlice, 0) | ||||||
| 	} | // 	} | ||||||
| 
 | 
 | ||||||
| 	for _, entry := range writeCommands { | // 	for _, entry := range writeCommands { | ||||||
| 		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 = lib.WritePath(dataBucket, path, value) | // 		dataBucket = lib.WritePath(dataBucket, path, value) | ||||||
| 	} | // 	} | ||||||
| 
 | 
 | ||||||
| 	return dataBucket, nil | // 	return dataBucket, nil | ||||||
| } | // } | ||||||
| 
 | 
 | ||||||
| func parseDocumentIndex() (bool, int, error) { | func parseDocumentIndex() (bool, int, error) { | ||||||
| 	if docIndex == "*" { | 	if docIndex == "*" { | ||||||
| @ -360,11 +361,11 @@ func parseDocumentIndex() (bool, int, error) { | |||||||
| 	return false, int(docIndexInt64), nil | 	return false, int(docIndexInt64), nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type updateDataFn func(dataBucket interface{}, currentIndex int) (interface{}, error) | type updateDataFn func(dataBucket *yaml.Node, currentIndex int) error | ||||||
| 
 | 
 | ||||||
| func mapYamlDecoder(updateData updateDataFn, encoder *yaml.Encoder) yamlDecoderFn { | func mapYamlDecoder(updateData updateDataFn, encoder *yaml.Encoder) yamlDecoderFn { | ||||||
| 	return func(decoder *yaml.Decoder) error { | 	return func(decoder *yaml.Decoder) error { | ||||||
| 		var dataBucket interface{} | 		var dataBucket yaml.Node | ||||||
| 		var errorReading error | 		var errorReading error | ||||||
| 		var errorWriting error | 		var errorWriting error | ||||||
| 		var errorUpdating error | 		var errorUpdating error | ||||||
| @ -387,12 +388,12 @@ func mapYamlDecoder(updateData updateDataFn, encoder *yaml.Encoder) yamlDecoderF | |||||||
| 			} else if errorReading != nil { | 			} else if errorReading != nil { | ||||||
| 				return errors.Wrapf(errorReading, "Error reading document at index %v, %v", currentIndex, errorReading) | 				return errors.Wrapf(errorReading, "Error reading document at index %v, %v", currentIndex, errorReading) | ||||||
| 			} | 			} | ||||||
| 			dataBucket, errorUpdating = updateData(dataBucket, currentIndex) | 			errorUpdating = updateData(&dataBucket, currentIndex) | ||||||
| 			if errorUpdating != nil { | 			if errorUpdating != nil { | ||||||
| 				return errors.Wrapf(errorUpdating, "Error updating document at index %v", currentIndex) | 				return errors.Wrapf(errorUpdating, "Error updating document at index %v", currentIndex) | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			errorWriting = encoder.Encode(dataBucket) | 			errorWriting = encoder.Encode(&dataBucket) | ||||||
| 
 | 
 | ||||||
| 			if errorWriting != nil { | 			if errorWriting != nil { | ||||||
| 				return errors.Wrapf(errorWriting, "Error writing document at index %v, %v", currentIndex, errorWriting) | 				return errors.Wrapf(errorWriting, "Error writing document at index %v, %v", currentIndex, errorWriting) | ||||||
| @ -412,43 +413,47 @@ func writeProperty(cmd *cobra.Command, args []string) error { | |||||||
| 		return errorParsingDocIndex | 		return errorParsingDocIndex | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	var updateData = func(dataBucket interface{}, currentIndex int) (interface{}, error) { | 	var updateData = func(dataBucket *yaml.Node, currentIndex int) error { | ||||||
| 		if updateAll || currentIndex == docIndexInt { | 		if updateAll || currentIndex == docIndexInt { | ||||||
| 			log.Debugf("Updating doc %v", currentIndex) | 			log.Debugf("Updating doc %v", currentIndex) | ||||||
| 			for _, entry := range writeCommands { | 			for _, entry := range writeCommands { | ||||||
| 				path := entry.Key.(string) | 				path := entry.Key | ||||||
| 				value := entry.Value | 				changesToApply := entry.Value | ||||||
| 				log.Debugf("setting %v to %v", path, value) | 				var paths = parsePath(path) | ||||||
| 				dataBucket = lib.WritePath(dataBucket, path, value) | 
 | ||||||
|  | 				errorUpdating := updateChild(dataBucket, paths, changesToApply) | ||||||
|  | 				if errorUpdating != nil { | ||||||
|  | 					return errorUpdating | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 		return dataBucket, nil | 		} | ||||||
|  | 		return nil | ||||||
| 	} | 	} | ||||||
| 	return readAndUpdate(cmd.OutOrStdout(), args[0], updateData) | 	return readAndUpdate(cmd.OutOrStdout(), args[0], updateData) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func prefixProperty(cmd *cobra.Command, args []string) error { | // func prefixProperty(cmd *cobra.Command, args []string) error { | ||||||
| 	if len(args) != 2 { | // 	if len(args) != 2 { | ||||||
| 		return errors.New("Must provide <filename> <prefixed_path>") | // 		return errors.New("Must provide <filename> <prefixed_path>") | ||||||
| 	} | // 	} | ||||||
| 	prefixPath := args[1] | // 	prefixPath := args[1] | ||||||
| 
 | 
 | ||||||
| 	var updateAll, docIndexInt, errorParsingDocIndex = parseDocumentIndex() | // 	var updateAll, docIndexInt, errorParsingDocIndex = parseDocumentIndex() | ||||||
| 	if errorParsingDocIndex != nil { | // 	if errorParsingDocIndex != nil { | ||||||
| 		return errorParsingDocIndex | // 		return errorParsingDocIndex | ||||||
| 	} | // 	} | ||||||
| 
 | 
 | ||||||
| 	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("Prefixing %v to doc %v", prefixPath, currentIndex) | // 			log.Debugf("Prefixing %v to doc %v", prefixPath, currentIndex) | ||||||
| 			var mapDataBucket = lib.PrefixPath(dataBucket, prefixPath) | // 			var mapDataBucket = lib.PrefixPath(dataBucket, prefixPath) | ||||||
| 			return mapDataBucket, nil | // 			return mapDataBucket, nil | ||||||
| 		} | // 		} | ||||||
| 		return dataBucket, nil | // 		return dataBucket, nil | ||||||
| 	} | // 	} | ||||||
| 	return readAndUpdate(cmd.OutOrStdout(), args[0], updateData) | // 	return readAndUpdate(cmd.OutOrStdout(), args[0], updateData) | ||||||
| } | // } | ||||||
| 
 | 
 | ||||||
| func readAndUpdate(stdOut io.Writer, inputFile string, updateData updateDataFn) error { | func readAndUpdate(stdOut io.Writer, inputFile string, updateData updateDataFn) error { | ||||||
| 	var destination io.Writer | 	var destination io.Writer | ||||||
| @ -479,90 +484,137 @@ func readAndUpdate(stdOut io.Writer, inputFile string, updateData updateDataFn) | |||||||
| 		defer safelyFlush(writer) | 		defer safelyFlush(writer) | ||||||
| 	} | 	} | ||||||
| 	var encoder = yaml.NewEncoder(destination) | 	var encoder = yaml.NewEncoder(destination) | ||||||
|  | 	encoder.SetIndent(2) | ||||||
| 	log.Debugf("Writing to %v from %v", destinationName, inputFile) | 	log.Debugf("Writing to %v from %v", destinationName, inputFile) | ||||||
| 	return readStream(inputFile, mapYamlDecoder(updateData, encoder)) | 	return readStream(inputFile, mapYamlDecoder(updateData, encoder)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func deleteProperty(cmd *cobra.Command, args []string) error { | // func deleteProperty(cmd *cobra.Command, args []string) error { | ||||||
| 	if len(args) < 2 { | // 	if len(args) < 2 { | ||||||
| 		return errors.New("Must provide <filename> <path_to_delete>") | // 		return errors.New("Must provide <filename> <path_to_delete>") | ||||||
| 	} | // 	} | ||||||
| 	var deletePath = args[1] | // 	var deletePath = args[1] | ||||||
| 	var updateAll, docIndexInt, errorParsingDocIndex = parseDocumentIndex() | // 	var updateAll, docIndexInt, errorParsingDocIndex = parseDocumentIndex() | ||||||
| 	if errorParsingDocIndex != nil { | // 	if errorParsingDocIndex != nil { | ||||||
| 		return errorParsingDocIndex | // 		return errorParsingDocIndex | ||||||
| 	} | // 	} | ||||||
| 
 | 
 | ||||||
| 	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 lib.DeletePath(dataBucket, deletePath) | // 			return lib.DeletePath(dataBucket, deletePath) | ||||||
| 		} | // 		} | ||||||
| 		return dataBucket, nil | // 		return dataBucket, nil | ||||||
| 	} | // 	} | ||||||
| 
 | 
 | ||||||
| 	return readAndUpdate(cmd.OutOrStdout(), args[0], updateData) | // 	return readAndUpdate(cmd.OutOrStdout(), args[0], updateData) | ||||||
|  | // } | ||||||
|  | 
 | ||||||
|  | // 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:] | ||||||
|  | // 	var updateAll, docIndexInt, errorParsingDocIndex = parseDocumentIndex() | ||||||
|  | // 	if errorParsingDocIndex != nil { | ||||||
|  | // 		return errorParsingDocIndex | ||||||
|  | // 	} | ||||||
|  | 
 | ||||||
|  | // 	var updateData = func(dataBucket interface{}, currentIndex int) (interface{}, error) { | ||||||
|  | // 		if updateAll || currentIndex == docIndexInt { | ||||||
|  | // 			log.Debugf("Merging doc %v", currentIndex) | ||||||
|  | // 			var mergedData map[interface{}]interface{} | ||||||
|  | // 			// merge only works for maps, so put everything in a temporary | ||||||
|  | // 			// map | ||||||
|  | // 			var mapDataBucket = make(map[interface{}]interface{}) | ||||||
|  | // 			mapDataBucket["root"] = dataBucket | ||||||
|  | // 			if err := lib.Merge(&mergedData, mapDataBucket, overwriteFlag, appendFlag); err != nil { | ||||||
|  | // 				return nil, err | ||||||
|  | // 			} | ||||||
|  | // 			for _, f := range filesToMerge { | ||||||
|  | // 				var fileToMerge interface{} | ||||||
|  | // 				if err := readData(f, 0, &fileToMerge); err != nil { | ||||||
|  | // 					if allowEmptyFlag && err == io.EOF { | ||||||
|  | // 						continue | ||||||
|  | // 					} | ||||||
|  | // 					return nil, err | ||||||
|  | // 				} | ||||||
|  | // 				mapDataBucket["root"] = fileToMerge | ||||||
|  | // 				if err := lib.Merge(&mergedData, mapDataBucket, overwriteFlag, appendFlag); err != nil { | ||||||
|  | // 					return nil, err | ||||||
|  | // 				} | ||||||
|  | // 			} | ||||||
|  | // 			return mergedData["root"], nil | ||||||
|  | // 		} | ||||||
|  | // 		return dataBucket, nil | ||||||
|  | // 	} | ||||||
|  | // 	return readAndUpdate(cmd.OutOrStdout(), input, updateData) | ||||||
|  | // } | ||||||
|  | 
 | ||||||
|  | type rawWriteCommand struct { | ||||||
|  | 	// Command string TODO | ||||||
|  | 	Key   string | ||||||
|  | 	Value yaml.Node | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func mergeProperties(cmd *cobra.Command, args []string) error { | func readWriteCommands(args []string, expectedArgs int, badArgsMessage string) ([]rawWriteCommand, error) { | ||||||
| 	if len(args) < 2 { | 	var writeCommands []rawWriteCommand | ||||||
| 		return errors.New("Must provide at least 2 yaml files") |  | ||||||
| 	} |  | ||||||
| 	var input = args[0] |  | ||||||
| 	var filesToMerge = args[1:] |  | ||||||
| 	var updateAll, docIndexInt, errorParsingDocIndex = parseDocumentIndex() |  | ||||||
| 	if errorParsingDocIndex != nil { |  | ||||||
| 		return errorParsingDocIndex |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	var updateData = func(dataBucket interface{}, currentIndex int) (interface{}, error) { |  | ||||||
| 		if updateAll || currentIndex == docIndexInt { |  | ||||||
| 			log.Debugf("Merging doc %v", currentIndex) |  | ||||||
| 			var mergedData map[interface{}]interface{} |  | ||||||
| 			// merge only works for maps, so put everything in a temporary |  | ||||||
| 			// map |  | ||||||
| 			var mapDataBucket = make(map[interface{}]interface{}) |  | ||||||
| 			mapDataBucket["root"] = dataBucket |  | ||||||
| 			if err := lib.Merge(&mergedData, mapDataBucket, overwriteFlag, appendFlag); err != nil { |  | ||||||
| 				return nil, err |  | ||||||
| 			} |  | ||||||
| 			for _, f := range filesToMerge { |  | ||||||
| 				var fileToMerge interface{} |  | ||||||
| 				if err := readData(f, 0, &fileToMerge); err != nil { |  | ||||||
| 					if allowEmptyFlag && err == io.EOF { |  | ||||||
| 						continue |  | ||||||
| 					} |  | ||||||
| 					return nil, err |  | ||||||
| 				} |  | ||||||
| 				mapDataBucket["root"] = fileToMerge |  | ||||||
| 				if err := lib.Merge(&mergedData, mapDataBucket, overwriteFlag, appendFlag); err != nil { |  | ||||||
| 					return nil, err |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 			return mergedData["root"], nil |  | ||||||
| 		} |  | ||||||
| 		return dataBucket, 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) { |  | ||||||
| 	var writeCommands yaml.MapSlice |  | ||||||
| 	if writeScript != "" { | 	if writeScript != "" { | ||||||
| 		if err := readData(writeScript, 0, &writeCommands); err != nil { | 		var rawCommands yaml.Node | ||||||
|  | 		if err := readData(writeScript, 0, &rawCommands); err != nil { | ||||||
| 			return nil, err | 			return nil, err | ||||||
| 		} | 		} | ||||||
|  | 		log.Debugf("Read write commands file '%v'", rawCommands) | ||||||
|  | 		var key string | ||||||
|  | 		for index, content := range rawCommands.Content[0].Content { | ||||||
|  | 			if index%2 == 0 { // must be the key | ||||||
|  | 				key = content.Value | ||||||
|  | 			} else { // its the value | ||||||
|  | 				writeCommands = append(writeCommands, rawWriteCommand{Key: key, Value: *content}) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		log.Debugf("Read write commands '%v'", writeCommands) | ||||||
| 	} else if len(args) < expectedArgs { | 	} else if len(args) < expectedArgs { | ||||||
| 		return nil, errors.New(badArgsMessage) | 		return nil, errors.New(badArgsMessage) | ||||||
| 	} else { | 	} else { | ||||||
| 		writeCommands = make(yaml.MapSlice, 1) | 		writeCommands = make([]rawWriteCommand, 1) | ||||||
| 		writeCommands[0] = yaml.MapItem{Key: args[expectedArgs-2], Value: valueParser.ParseValue(args[expectedArgs-1])} | 		writeCommands[0] = rawWriteCommand{Key: args[expectedArgs-2], Value: parseValue(args[expectedArgs-1])} | ||||||
| 	} | 	} | ||||||
| 	return writeCommands, nil | 	return writeCommands, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func parseValue(argument string) yaml.Node { | ||||||
|  | 	var err interface{} | ||||||
|  | 	var tag = customTag | ||||||
|  | 
 | ||||||
|  | 	var inQuotes = len(argument) > 0 && argument[0] == '"' | ||||||
|  | 	if tag == "" && !inQuotes { | ||||||
|  | 
 | ||||||
|  | 		_, err = strconv.ParseBool(argument) | ||||||
|  | 		if err == nil { | ||||||
|  | 			tag = "!!bool" | ||||||
|  | 		} | ||||||
|  | 		_, err = strconv.ParseFloat(argument, 64) | ||||||
|  | 		if err == nil { | ||||||
|  | 			tag = "!!float" | ||||||
|  | 		} | ||||||
|  | 		_, err = strconv.ParseInt(argument, 10, 64) | ||||||
|  | 		if err == nil { | ||||||
|  | 			tag = "!!int" | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if argument == "null" { | ||||||
|  | 			tag = "!!null" | ||||||
|  | 		} | ||||||
|  | 		if argument == "[]" { | ||||||
|  | 			return yaml.Node{Tag: "!!seq", Kind: yaml.SequenceNode} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	log.Debugf("Updating node to value '%v', tag: '%v'", argument, tag) | ||||||
|  | 	return yaml.Node{Value: argument, Tag: tag, Kind: yaml.ScalarNode} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func toString(context interface{}) (string, error) { | func toString(context interface{}) (string, error) { | ||||||
| 	if outputToJSON { | 	if outputToJSON { | ||||||
| 		return jsonConverter.JsonToString(context) | 		return jsonConverter.JsonToString(context) | ||||||
| @ -572,7 +624,7 @@ func toString(context interface{}) (string, error) { | |||||||
| 
 | 
 | ||||||
| func safelyRenameFile(from string, to string) { | func safelyRenameFile(from string, to string) { | ||||||
| 	if renameError := os.Rename(from, to); renameError != nil { | 	if renameError := os.Rename(from, to); renameError != nil { | ||||||
| 		log.Debugf("Error renaming from %v to %v, attemting to copy contents", from, to) | 		log.Debugf("Error renaming from %v to %v, attempting to copy contents", from, to) | ||||||
| 		log.Debug(renameError.Error()) | 		log.Debug(renameError.Error()) | ||||||
| 		// can't do this rename when running in docker to a file targeted in a mounted volume, | 		// can't do this rename when running in docker to a file targeted in a mounted volume, | ||||||
| 		// so gracefully degrade to copying the entire contents. | 		// so gracefully degrade to copying the entire contents. | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user