2019-11-23 03:52:29 +00:00
|
|
|
package yqlib
|
|
|
|
|
|
|
|
import (
|
2019-12-25 01:11:04 +00:00
|
|
|
"bytes"
|
2019-12-06 05:36:42 +00:00
|
|
|
"fmt"
|
2019-12-25 01:11:04 +00:00
|
|
|
"strconv"
|
2019-12-29 22:21:21 +00:00
|
|
|
"strings"
|
2019-12-06 05:36:42 +00:00
|
|
|
|
2019-11-23 03:52:29 +00:00
|
|
|
logging "gopkg.in/op/go-logging.v1"
|
2019-12-06 04:57:46 +00:00
|
|
|
yaml "gopkg.in/yaml.v3"
|
2019-11-23 03:52:29 +00:00
|
|
|
)
|
|
|
|
|
2019-12-25 01:11:04 +00:00
|
|
|
var log = logging.MustGetLogger("yq")
|
|
|
|
|
2019-12-06 05:36:42 +00:00
|
|
|
type UpdateCommand struct {
|
2020-01-05 04:14:14 +00:00
|
|
|
Command string
|
|
|
|
Path string
|
|
|
|
Value *yaml.Node
|
|
|
|
Overwrite bool
|
2019-12-06 05:36:42 +00:00
|
|
|
}
|
|
|
|
|
2019-12-25 01:11:04 +00:00
|
|
|
func DebugNode(value *yaml.Node) {
|
|
|
|
if value == nil {
|
|
|
|
log.Debug("-- node is nil --")
|
|
|
|
} else if log.IsEnabledFor(logging.DEBUG) {
|
|
|
|
buf := new(bytes.Buffer)
|
|
|
|
encoder := yaml.NewEncoder(buf)
|
2020-01-09 10:27:52 +00:00
|
|
|
errorEncoding := encoder.Encode(value)
|
|
|
|
if errorEncoding != nil {
|
|
|
|
log.Error("Error debugging node, %v", errorEncoding.Error())
|
|
|
|
}
|
2019-12-25 01:11:04 +00:00
|
|
|
encoder.Close()
|
|
|
|
log.Debug("Tag: %v", value.Tag)
|
|
|
|
log.Debug("%v", buf.String())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-09 10:18:24 +00:00
|
|
|
func pathStackToString(pathStack []interface{}) string {
|
|
|
|
return mergePathStackToString(pathStack, false)
|
2020-01-05 04:28:24 +00:00
|
|
|
}
|
|
|
|
|
2020-01-09 10:18:24 +00:00
|
|
|
func mergePathStackToString(pathStack []interface{}, appendArrays bool) string {
|
2019-12-29 22:21:21 +00:00
|
|
|
var sb strings.Builder
|
|
|
|
for index, path := range pathStack {
|
|
|
|
switch path.(type) {
|
|
|
|
case int:
|
2020-01-05 04:28:24 +00:00
|
|
|
if appendArrays {
|
|
|
|
sb.WriteString("[+]")
|
|
|
|
} else {
|
|
|
|
sb.WriteString(fmt.Sprintf("[%v]", path))
|
|
|
|
}
|
|
|
|
|
2019-12-29 22:21:21 +00:00
|
|
|
default:
|
|
|
|
sb.WriteString(fmt.Sprintf("%v", path))
|
|
|
|
}
|
|
|
|
|
|
|
|
if index < len(pathStack)-1 {
|
|
|
|
sb.WriteString(".")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return sb.String()
|
|
|
|
}
|
|
|
|
|
2019-12-31 02:21:39 +00:00
|
|
|
func guessKind(head string, tail []string, guess yaml.Kind) yaml.Kind {
|
2019-12-25 01:11:04 +00:00
|
|
|
log.Debug("tail %v", tail)
|
|
|
|
if len(tail) == 0 && guess == 0 {
|
|
|
|
log.Debug("end of path, must be a scalar")
|
|
|
|
return yaml.ScalarNode
|
|
|
|
} else if len(tail) == 0 {
|
|
|
|
return guess
|
|
|
|
}
|
|
|
|
|
|
|
|
var _, errorParsingInt = strconv.ParseInt(tail[0], 10, 64)
|
|
|
|
if tail[0] == "+" || errorParsingInt == nil {
|
|
|
|
return yaml.SequenceNode
|
|
|
|
}
|
2020-01-11 08:30:27 +00:00
|
|
|
pathParser := NewPathParser()
|
|
|
|
if (pathParser.IsPathExpression(tail[0]) || head == "**") && (guess == yaml.SequenceNode || guess == yaml.MappingNode) {
|
2019-12-25 01:11:04 +00:00
|
|
|
return guess
|
|
|
|
}
|
|
|
|
if guess == yaml.AliasNode {
|
|
|
|
log.Debug("guess was an alias, okey doke.")
|
|
|
|
return guess
|
|
|
|
}
|
|
|
|
log.Debug("forcing a mapping node")
|
2019-12-31 02:21:39 +00:00
|
|
|
log.Debug("yaml.SequenceNode %v", guess == yaml.SequenceNode)
|
|
|
|
log.Debug("yaml.ScalarNode %v", guess == yaml.ScalarNode)
|
2019-12-25 01:11:04 +00:00
|
|
|
return yaml.MappingNode
|
|
|
|
}
|
|
|
|
|
2019-12-01 19:44:44 +00:00
|
|
|
type YqLib interface {
|
2019-12-28 07:19:37 +00:00
|
|
|
Get(rootNode *yaml.Node, path string) ([]*NodeContext, error)
|
2020-01-05 04:14:14 +00:00
|
|
|
Update(rootNode *yaml.Node, updateCommand UpdateCommand, autoCreate bool) error
|
2019-12-16 05:17:01 +00:00
|
|
|
New(path string) yaml.Node
|
2020-01-09 10:18:24 +00:00
|
|
|
|
|
|
|
PathStackToString(pathStack []interface{}) string
|
|
|
|
MergePathStackToString(pathStack []interface{}, appendArrays bool) string
|
2019-12-01 19:44:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type lib struct {
|
2020-01-09 10:27:52 +00:00
|
|
|
parser PathParser
|
2019-12-01 19:44:44 +00:00
|
|
|
}
|
2019-11-23 03:52:29 +00:00
|
|
|
|
2020-01-08 21:17:56 +00:00
|
|
|
func NewYqLib() YqLib {
|
2019-12-01 20:10:42 +00:00
|
|
|
return &lib{
|
2019-12-16 09:38:55 +00:00
|
|
|
parser: NewPathParser(),
|
2019-12-01 19:44:44 +00:00
|
|
|
}
|
2019-11-23 03:52:29 +00:00
|
|
|
}
|
|
|
|
|
2019-12-28 07:19:37 +00:00
|
|
|
func (l *lib) Get(rootNode *yaml.Node, path string) ([]*NodeContext, error) {
|
2019-12-01 19:44:44 +00:00
|
|
|
var paths = l.parser.ParsePath(path)
|
2020-01-11 07:52:15 +00:00
|
|
|
navigationStrategy := ReadNavigationStrategy()
|
|
|
|
navigator := NewDataNavigator(navigationStrategy)
|
2019-12-25 01:11:04 +00:00
|
|
|
error := navigator.Traverse(rootNode, paths)
|
2020-01-11 07:52:15 +00:00
|
|
|
return navigationStrategy.GetVisitedNodes(), error
|
2019-12-25 01:11:04 +00:00
|
|
|
|
2019-11-23 03:52:29 +00:00
|
|
|
}
|
|
|
|
|
2020-01-09 10:18:24 +00:00
|
|
|
func (l *lib) PathStackToString(pathStack []interface{}) string {
|
|
|
|
return pathStackToString(pathStack)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *lib) MergePathStackToString(pathStack []interface{}, appendArrays bool) string {
|
|
|
|
return mergePathStackToString(pathStack, appendArrays)
|
|
|
|
}
|
|
|
|
|
2019-12-16 05:17:01 +00:00
|
|
|
func (l *lib) New(path string) yaml.Node {
|
|
|
|
var paths = l.parser.ParsePath(path)
|
2019-12-31 02:21:39 +00:00
|
|
|
newNode := yaml.Node{Kind: guessKind("", paths, 0)}
|
2019-12-16 05:17:01 +00:00
|
|
|
return newNode
|
2019-12-15 08:34:05 +00:00
|
|
|
}
|
|
|
|
|
2020-01-05 04:14:14 +00:00
|
|
|
func (l *lib) Update(rootNode *yaml.Node, updateCommand UpdateCommand, autoCreate bool) error {
|
2019-12-25 01:11:04 +00:00
|
|
|
log.Debugf("%v to %v", updateCommand.Command, updateCommand.Path)
|
2019-12-06 05:36:42 +00:00
|
|
|
switch updateCommand.Command {
|
|
|
|
case "update":
|
|
|
|
var paths = l.parser.ParsePath(updateCommand.Path)
|
2020-01-05 04:14:14 +00:00
|
|
|
navigator := NewDataNavigator(UpdateNavigationStrategy(updateCommand, autoCreate))
|
2019-12-25 01:11:04 +00:00
|
|
|
return navigator.Traverse(rootNode, paths)
|
2019-12-06 05:36:42 +00:00
|
|
|
case "delete":
|
2019-12-12 09:47:22 +00:00
|
|
|
var paths = l.parser.ParsePath(updateCommand.Path)
|
2019-12-25 01:11:04 +00:00
|
|
|
lastBit, newTail := paths[len(paths)-1], paths[:len(paths)-1]
|
2019-12-28 07:19:37 +00:00
|
|
|
navigator := NewDataNavigator(DeleteNavigationStrategy(lastBit))
|
2019-12-25 01:11:04 +00:00
|
|
|
return navigator.Traverse(rootNode, newTail)
|
2019-12-06 05:36:42 +00:00
|
|
|
default:
|
|
|
|
return fmt.Errorf("Unknown command %v", updateCommand.Command)
|
|
|
|
}
|
|
|
|
|
2019-12-01 20:10:42 +00:00
|
|
|
}
|