yq/pkg/yqlib/lib.go

194 lines
5.2 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 {
Command string
Path string
Value *yaml.Node
Overwrite bool
DontUpdateNodeValue bool
2019-12-06 05:36:42 +00:00
}
2020-02-13 07:08:13 +00:00
func KindString(kind yaml.Kind) string {
switch kind {
case yaml.ScalarNode:
return "ScalarNode"
case yaml.SequenceNode:
return "SequenceNode"
case yaml.MappingNode:
return "MappingNode"
case yaml.DocumentNode:
return "DocumentNode"
case yaml.AliasNode:
return "AliasNode"
default:
return "unknown!"
}
}
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()
2020-02-13 07:08:13 +00:00
log.Debug("Tag: %v, Kind: %v", value.Tag, KindString(value.Kind))
2019-12-25 01:11:04 +00:00
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
hasSpecial := strings.Contains(s, ".") || strings.Contains(s, "[") || strings.Contains(s, "]") || strings.Contains(s, "\"")
hasDoubleQuotes := strings.Contains(s, "\"")
wrappingCharacterStart := "\""
wrappingCharacterEnd := "\""
if hasDoubleQuotes {
wrappingCharacterStart = "("
wrappingCharacterEnd = ")"
}
if hasSpecial || errParsingInt == nil {
sb.WriteString(wrappingCharacterStart)
}
sb.WriteString(s)
if hasSpecial || errParsingInt == nil {
sb.WriteString(wrappingCharacterEnd)
}
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()
2020-02-13 07:08:13 +00:00
if pathParser.IsPathExpression(nextString) && (guess == yaml.SequenceNode || guess == yaml.MappingNode) {
2020-02-12 04:40:21 +00:00
return guess
2020-02-13 07:08:13 +00:00
} else if guess == yaml.AliasNode {
2020-02-12 04:40:21 +00:00
log.Debug("guess was an alias, okey doke.")
return guess
2020-02-13 07:08:13 +00:00
} else if head == "**" {
log.Debug("deep wildcard, go with the guess")
return guess
2020-02-12 04:40:21 +00:00
}
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
}