yq/pkg/yqlib/lib.go

167 lines
4.4 KiB
Go
Raw Normal View History

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
logging "gopkg.in/op/go-logging.v1"
2019-12-06 04:57:46 +00:00
yaml "gopkg.in/yaml.v3"
)
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) {
2020-02-12 04:40:21 +00:00
case int, int64:
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:
s := fmt.Sprintf("%v", path)
2020-02-12 04:40:21 +00:00
var _, errParsingInt = strconv.ParseInt(s, 10, 64) // nolint
hasDot := strings.Contains(s, ".")
2020-02-12 04:40:21 +00:00
if hasDot || errParsingInt == nil {
sb.WriteString("\"")
}
sb.WriteString(s)
2020-02-12 04:40:21 +00:00
if hasDot || errParsingInt == nil {
sb.WriteString("\"")
}
2019-12-29 22:21:21 +00:00
}
if index < len(pathStack)-1 {
sb.WriteString(".")
}
}
2020-02-12 04:40:21 +00:00
var pathString = sb.String()
log.Debug("got a path string: %v", pathString)
return pathString
2019-12-29 22:21:21 +00:00
}
2020-02-12 04:40:21 +00:00
func guessKind(head interface{}, tail []interface{}, guess yaml.Kind) yaml.Kind {
log.Debug("guessKind: tail %v", tail)
2019-12-25 01:11:04 +00:00
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
}
2020-02-12 04:40:21 +00:00
var next = tail[0]
switch next.(type) {
case int64:
2019-12-25 01:11:04 +00:00
return yaml.SequenceNode
2020-02-12 04:40:21 +00:00
default:
var nextString = fmt.Sprintf("%v", next)
if nextString == "+" {
return yaml.SequenceNode
}
pathParser := NewPathParser()
if (pathParser.IsPathExpression(nextString) || head == "**") && (guess == yaml.SequenceNode || guess == yaml.MappingNode) {
return guess
}
if guess == yaml.AliasNode {
log.Debug("guess was an alias, okey doke.")
return guess
}
log.Debug("forcing a mapping node")
log.Debug("yaml.SequenceNode %v", guess == yaml.SequenceNode)
log.Debug("yaml.ScalarNode %v", guess == yaml.ScalarNode)
return yaml.MappingNode
2019-12-25 01:11:04 +00:00
}
}
type YqLib interface {
2020-02-07 05:32:39 +00:00
Get(rootNode *yaml.Node, path string, deeplyTraverseArrays bool) ([]*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
}
type lib struct {
2020-01-09 10:27:52 +00:00
parser PathParser
}
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(),
}
}
2020-02-07 05:32:39 +00:00
func (l *lib) Get(rootNode *yaml.Node, path string, deeplyTraverseArrays bool) ([]*NodeContext, error) {
var paths = l.parser.ParsePath(path)
2020-02-07 05:32:39 +00:00
navigationStrategy := ReadNavigationStrategy(deeplyTraverseArrays)
2020-01-11 07:52:15 +00:00
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
}
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
}