2020-11-03 23:48:43 +00:00
|
|
|
package yqlib
|
2020-10-13 01:51:37 +00:00
|
|
|
|
|
|
|
import (
|
2021-10-22 03:53:39 +00:00
|
|
|
"container/list"
|
2020-10-13 01:51:37 +00:00
|
|
|
"fmt"
|
2021-12-04 23:53:37 +00:00
|
|
|
"strings"
|
2023-04-03 06:16:07 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type Kind uint32
|
|
|
|
|
|
|
|
const (
|
|
|
|
DocumentNode Kind = 1 << iota
|
|
|
|
SequenceNode
|
|
|
|
MappingNode
|
|
|
|
ScalarNode
|
|
|
|
AliasNode
|
|
|
|
)
|
|
|
|
|
|
|
|
type Style uint32
|
2020-10-13 01:51:37 +00:00
|
|
|
|
2023-04-03 06:16:07 +00:00
|
|
|
const (
|
|
|
|
TaggedStyle Style = 1 << iota
|
|
|
|
DoubleQuotedStyle
|
|
|
|
SingleQuotedStyle
|
|
|
|
LiteralStyle
|
|
|
|
FoldedStyle
|
|
|
|
FlowStyle
|
2020-10-13 01:51:37 +00:00
|
|
|
)
|
|
|
|
|
2023-04-03 07:27:41 +00:00
|
|
|
func createStringScalarNode(stringValue string) *CandidateNode {
|
|
|
|
var node = &CandidateNode{Kind: ScalarNode}
|
|
|
|
node.Value = stringValue
|
|
|
|
node.Tag = "!!str"
|
|
|
|
return node
|
|
|
|
}
|
|
|
|
|
2023-04-03 06:16:07 +00:00
|
|
|
func createScalarNode(value interface{}, stringValue string) *CandidateNode {
|
|
|
|
var node = &CandidateNode{Kind: ScalarNode}
|
|
|
|
node.Value = stringValue
|
|
|
|
|
|
|
|
switch value.(type) {
|
|
|
|
case float32, float64:
|
|
|
|
node.Tag = "!!float"
|
|
|
|
case int, int64, int32:
|
|
|
|
node.Tag = "!!int"
|
|
|
|
case bool:
|
|
|
|
node.Tag = "!!bool"
|
|
|
|
case string:
|
|
|
|
node.Tag = "!!str"
|
|
|
|
case nil:
|
|
|
|
node.Tag = "!!null"
|
|
|
|
}
|
|
|
|
return node
|
|
|
|
}
|
|
|
|
|
2020-10-13 01:51:37 +00:00
|
|
|
type CandidateNode struct {
|
2023-04-03 06:16:07 +00:00
|
|
|
Kind Kind
|
|
|
|
Style Style
|
|
|
|
|
|
|
|
Tag string
|
|
|
|
Value string
|
|
|
|
Anchor string
|
|
|
|
Alias *CandidateNode
|
|
|
|
Content []*CandidateNode
|
|
|
|
|
|
|
|
HeadComment string
|
|
|
|
LineComment string
|
|
|
|
FootComment string
|
|
|
|
|
2021-11-12 04:02:28 +00:00
|
|
|
Parent *CandidateNode // parent node
|
2023-04-03 06:16:07 +00:00
|
|
|
Key *CandidateNode // node key, if this is a value from a map (or index in an array)
|
2021-11-12 04:02:28 +00:00
|
|
|
|
2022-05-25 00:54:03 +00:00
|
|
|
LeadingContent string
|
|
|
|
TrailingContent string
|
2021-11-12 04:02:28 +00:00
|
|
|
|
2023-04-10 04:13:28 +00:00
|
|
|
Document uint // the document index of this node
|
2023-04-03 06:16:07 +00:00
|
|
|
Filename string
|
|
|
|
|
|
|
|
Line int
|
|
|
|
Column int
|
|
|
|
|
2020-11-20 04:50:15 +00:00
|
|
|
FileIndex int
|
2021-01-17 23:15:31 +00:00
|
|
|
// when performing op against all nodes given, this will treat all the nodes as one
|
2022-05-24 08:18:27 +00:00
|
|
|
// (e.g. top level cross document merge). This property does not propagate to child nodes.
|
2021-01-17 23:15:31 +00:00
|
|
|
EvaluateTogether bool
|
2021-02-08 02:58:46 +00:00
|
|
|
IsMapKey bool
|
2020-10-13 01:51:37 +00:00
|
|
|
}
|
|
|
|
|
2023-04-11 05:33:32 +00:00
|
|
|
func (n *CandidateNode) CreateChild() *CandidateNode {
|
|
|
|
return &CandidateNode{
|
|
|
|
Parent: n,
|
|
|
|
Document: n.Document,
|
|
|
|
Filename: n.Filename,
|
|
|
|
FileIndex: n.FileIndex,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-11 02:39:22 +00:00
|
|
|
func (n *CandidateNode) GetKey() string {
|
|
|
|
keyPrefix := ""
|
|
|
|
if n.IsMapKey {
|
|
|
|
keyPrefix = "key-"
|
|
|
|
}
|
|
|
|
key := ""
|
|
|
|
if n.Key != nil {
|
|
|
|
key = n.Key.Value
|
|
|
|
}
|
|
|
|
return fmt.Sprintf("%v%v - %v", keyPrefix, n.Document, key)
|
|
|
|
}
|
2020-10-13 01:51:37 +00:00
|
|
|
|
2023-04-03 06:16:07 +00:00
|
|
|
func (n *CandidateNode) unwrapDocument() *CandidateNode {
|
|
|
|
if n.Kind == DocumentNode {
|
|
|
|
return n.Content[0]
|
|
|
|
}
|
|
|
|
return n
|
|
|
|
}
|
|
|
|
|
2021-12-04 23:53:37 +00:00
|
|
|
func (n *CandidateNode) GetNiceTag() string {
|
2023-04-03 06:16:07 +00:00
|
|
|
return n.unwrapDocument().Tag
|
2021-12-04 23:53:37 +00:00
|
|
|
}
|
|
|
|
|
2023-04-10 04:13:28 +00:00
|
|
|
func (n *CandidateNode) getParsedKey() interface{} {
|
|
|
|
if n.Key == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if n.Key.Tag == "!!str" {
|
|
|
|
return n.Key.Value
|
|
|
|
}
|
|
|
|
index, err := parseInt(n.Key.Value)
|
|
|
|
if err != nil {
|
|
|
|
return n.Key.Value
|
|
|
|
}
|
|
|
|
return index
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n *CandidateNode) GetPath() []interface{} {
|
|
|
|
if n.Parent != nil {
|
|
|
|
return append(n.Parent.GetPath(), n.getParsedKey())
|
|
|
|
}
|
2023-04-11 05:33:32 +00:00
|
|
|
key := n.getParsedKey()
|
|
|
|
if key != nil {
|
|
|
|
return []interface{}{key}
|
|
|
|
}
|
|
|
|
return make([]interface{}, 0)
|
2023-04-10 04:13:28 +00:00
|
|
|
}
|
|
|
|
|
2021-12-04 23:53:37 +00:00
|
|
|
func (n *CandidateNode) GetNicePath() string {
|
2023-04-10 04:13:28 +00:00
|
|
|
var sb strings.Builder
|
|
|
|
path := n.GetPath()
|
|
|
|
for i, element := range path {
|
|
|
|
elementStr := fmt.Sprintf("%v", element)
|
|
|
|
switch element.(type) {
|
|
|
|
case int:
|
|
|
|
sb.WriteString("[" + elementStr + "]")
|
|
|
|
default:
|
|
|
|
if i == 0 {
|
|
|
|
sb.WriteString(elementStr)
|
|
|
|
} else if strings.ContainsRune(elementStr, '.') {
|
|
|
|
sb.WriteString("[" + elementStr + "]")
|
|
|
|
} else {
|
|
|
|
sb.WriteString("." + elementStr)
|
|
|
|
}
|
2021-12-04 23:53:37 +00:00
|
|
|
}
|
|
|
|
}
|
2023-04-10 04:13:28 +00:00
|
|
|
return sb.String()
|
2021-12-04 23:53:37 +00:00
|
|
|
}
|
|
|
|
|
2021-10-22 03:53:39 +00:00
|
|
|
func (n *CandidateNode) AsList() *list.List {
|
|
|
|
elMap := list.New()
|
|
|
|
elMap.PushBack(n)
|
|
|
|
return elMap
|
|
|
|
}
|
|
|
|
|
2023-04-03 06:16:07 +00:00
|
|
|
func (n *CandidateNode) guessTagFromCustomType() string {
|
|
|
|
if strings.HasPrefix(n.Tag, "!!") {
|
|
|
|
return n.Tag
|
|
|
|
} else if n.Value == "" {
|
|
|
|
log.Debug("guessTagFromCustomType: node has no value to guess the type with")
|
|
|
|
return n.Tag
|
|
|
|
}
|
|
|
|
dataBucket, errorReading := parseSnippet(n.Value)
|
|
|
|
|
|
|
|
if errorReading != nil {
|
|
|
|
log.Debug("guessTagFromCustomType: could not guess underlying tag type %v", errorReading)
|
|
|
|
return n.Tag
|
|
|
|
}
|
2023-04-03 07:27:41 +00:00
|
|
|
guessedTag := dataBucket.unwrapDocument().Tag
|
2023-04-03 06:16:07 +00:00
|
|
|
log.Info("im guessing the tag %v is a %v", n.Tag, guessedTag)
|
|
|
|
return guessedTag
|
|
|
|
}
|
|
|
|
|
2023-04-08 11:27:47 +00:00
|
|
|
func (n *CandidateNode) CreateReplacement(kind Kind, tag string, value string) *CandidateNode {
|
2023-04-11 02:39:22 +00:00
|
|
|
node := &CandidateNode{
|
|
|
|
Kind: kind,
|
|
|
|
Tag: tag,
|
|
|
|
Value: value,
|
2021-01-12 08:36:28 +00:00
|
|
|
}
|
2023-04-11 05:33:32 +00:00
|
|
|
return n.CopyAsReplacement(node)
|
2023-04-11 02:39:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (n *CandidateNode) CopyAsReplacement(replacement *CandidateNode) *CandidateNode {
|
2023-04-11 05:33:32 +00:00
|
|
|
newCopy := replacement.Copy()
|
|
|
|
newCopy.Parent = n.Parent
|
|
|
|
newCopy.Key = n.Key
|
|
|
|
newCopy.IsMapKey = n.IsMapKey
|
|
|
|
newCopy.Document = n.Document
|
|
|
|
newCopy.Filename = n.Filename
|
|
|
|
newCopy.FileIndex = n.FileIndex
|
|
|
|
return newCopy
|
2021-01-12 08:36:28 +00:00
|
|
|
}
|
|
|
|
|
2023-04-09 01:14:51 +00:00
|
|
|
func (n *CandidateNode) CreateReplacementWithDocWrappers(kind Kind, tag string, style Style) *CandidateNode {
|
|
|
|
replacement := n.CreateReplacement(kind, tag, "")
|
2023-04-08 11:27:47 +00:00
|
|
|
replacement.LeadingContent = n.LeadingContent
|
|
|
|
replacement.TrailingContent = n.TrailingContent
|
2023-04-09 01:14:51 +00:00
|
|
|
replacement.Style = style
|
2023-04-08 11:27:47 +00:00
|
|
|
return replacement
|
|
|
|
}
|
2022-05-31 06:28:53 +00:00
|
|
|
|
2023-04-03 06:16:07 +00:00
|
|
|
func (n *CandidateNode) CopyChildren() []*CandidateNode {
|
2023-04-11 05:33:32 +00:00
|
|
|
log.Debug("n? %v", n)
|
|
|
|
log.Debug("n.Content %v", n.Content)
|
|
|
|
log.Debug("n.Content %v", len(n.Content))
|
2023-04-03 06:16:07 +00:00
|
|
|
clonedKids := make([]*CandidateNode, len(n.Content))
|
2023-04-11 05:33:32 +00:00
|
|
|
log.Debug("created clone")
|
2023-04-03 06:16:07 +00:00
|
|
|
for i, child := range n.Content {
|
|
|
|
clonedKids[i] = child.Copy()
|
|
|
|
}
|
2023-04-11 05:33:32 +00:00
|
|
|
log.Debug("finishing clone")
|
2023-04-03 06:16:07 +00:00
|
|
|
return clonedKids
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n *CandidateNode) Copy() *CandidateNode {
|
2023-04-08 10:17:13 +00:00
|
|
|
return n.doCopy(true)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n *CandidateNode) CopyWithoutContent() *CandidateNode {
|
|
|
|
return n.doCopy(false)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n *CandidateNode) doCopy(cloneContent bool) *CandidateNode {
|
|
|
|
var content []*CandidateNode
|
|
|
|
if cloneContent {
|
|
|
|
content = n.CopyChildren()
|
|
|
|
}
|
|
|
|
|
2023-04-11 05:33:32 +00:00
|
|
|
var copyKey *CandidateNode
|
|
|
|
if n.Key != nil {
|
|
|
|
copyKey = n.Key.Copy()
|
|
|
|
}
|
|
|
|
|
2023-04-03 06:16:07 +00:00
|
|
|
return &CandidateNode{
|
|
|
|
Kind: n.Kind,
|
|
|
|
Style: n.Style,
|
|
|
|
|
|
|
|
Tag: n.Tag,
|
|
|
|
Value: n.Value,
|
|
|
|
Anchor: n.Anchor,
|
|
|
|
|
|
|
|
// ok not to clone this,
|
|
|
|
// as its a reference to somewhere else.
|
|
|
|
Alias: n.Alias,
|
2023-04-08 10:17:13 +00:00
|
|
|
Content: content,
|
2023-04-03 06:16:07 +00:00
|
|
|
|
|
|
|
HeadComment: n.HeadComment,
|
|
|
|
LineComment: n.LineComment,
|
|
|
|
FootComment: n.FootComment,
|
|
|
|
|
|
|
|
Parent: n.Parent,
|
2023-04-11 05:33:32 +00:00
|
|
|
Key: copyKey,
|
2023-04-03 06:16:07 +00:00
|
|
|
|
|
|
|
LeadingContent: n.LeadingContent,
|
|
|
|
TrailingContent: n.TrailingContent,
|
|
|
|
|
|
|
|
Document: n.Document,
|
|
|
|
Filename: n.Filename,
|
|
|
|
|
|
|
|
Line: n.Line,
|
|
|
|
Column: n.Column,
|
|
|
|
|
|
|
|
FileIndex: n.FileIndex,
|
|
|
|
EvaluateTogether: n.EvaluateTogether,
|
|
|
|
IsMapKey: n.IsMapKey,
|
2020-11-13 03:07:11 +00:00
|
|
|
}
|
2020-10-21 02:54:51 +00:00
|
|
|
}
|
|
|
|
|
2020-10-16 01:29:26 +00:00
|
|
|
// updates this candidate from the given candidate node
|
2021-12-02 22:23:16 +00:00
|
|
|
func (n *CandidateNode) UpdateFrom(other *CandidateNode, prefs assignPreferences) {
|
2021-01-01 23:27:32 +00:00
|
|
|
|
2022-02-20 03:29:52 +00:00
|
|
|
// if this is an empty map or empty array, use the style of other node.
|
2023-04-03 06:16:07 +00:00
|
|
|
if (n.Kind != ScalarNode && len(n.Content) == 0) ||
|
2022-02-24 22:14:41 +00:00
|
|
|
// if the tag has changed (e.g. from str to bool)
|
2023-04-03 06:16:07 +00:00
|
|
|
(n.guessTagFromCustomType() != other.guessTagFromCustomType()) {
|
|
|
|
n.Style = other.Style
|
2022-02-20 03:29:52 +00:00
|
|
|
}
|
|
|
|
|
2023-04-03 06:16:07 +00:00
|
|
|
n.Content = other.CopyChildren()
|
|
|
|
n.Kind = other.Kind
|
|
|
|
n.Value = other.Value
|
2022-02-20 03:29:52 +00:00
|
|
|
|
|
|
|
n.UpdateAttributesFrom(other, prefs)
|
|
|
|
|
2020-10-19 05:14:29 +00:00
|
|
|
}
|
|
|
|
|
2021-12-02 22:23:16 +00:00
|
|
|
func (n *CandidateNode) UpdateAttributesFrom(other *CandidateNode, prefs assignPreferences) {
|
2023-04-10 04:13:28 +00:00
|
|
|
log.Debug("UpdateAttributesFrom: n: %v other: %v", n.Key.Value, other.Key.Value)
|
2023-04-03 06:16:07 +00:00
|
|
|
if n.Kind != other.Kind {
|
2020-10-19 05:36:46 +00:00
|
|
|
// clear out the contents when switching to a different type
|
|
|
|
// e.g. map to array
|
2023-04-03 06:16:07 +00:00
|
|
|
n.Content = make([]*CandidateNode, 0)
|
|
|
|
n.Value = ""
|
2020-10-19 05:36:46 +00:00
|
|
|
}
|
2023-04-03 06:16:07 +00:00
|
|
|
n.Kind = other.Kind
|
2022-01-22 02:47:22 +00:00
|
|
|
|
|
|
|
// don't clobber custom tags...
|
2023-04-03 06:16:07 +00:00
|
|
|
if prefs.ClobberCustomTags || strings.HasPrefix(n.Tag, "!!") || n.Tag == "" {
|
|
|
|
n.Tag = other.Tag
|
2022-01-22 02:47:22 +00:00
|
|
|
}
|
|
|
|
|
2023-04-03 06:16:07 +00:00
|
|
|
n.Alias = other.Alias
|
2021-12-02 22:23:16 +00:00
|
|
|
|
|
|
|
if !prefs.DontOverWriteAnchor {
|
2023-04-03 06:16:07 +00:00
|
|
|
n.Anchor = other.Anchor
|
2021-12-02 22:23:16 +00:00
|
|
|
}
|
2020-10-28 00:34:01 +00:00
|
|
|
|
|
|
|
// merge will pickup the style of the new thing
|
|
|
|
// when autocreating nodes
|
2022-01-26 22:58:13 +00:00
|
|
|
|
2023-04-03 06:16:07 +00:00
|
|
|
if n.Style == 0 {
|
|
|
|
n.Style = other.Style
|
2020-10-28 00:34:01 +00:00
|
|
|
}
|
2021-03-19 01:54:03 +00:00
|
|
|
|
2023-04-03 06:16:07 +00:00
|
|
|
if other.FootComment != "" {
|
|
|
|
n.FootComment = other.FootComment
|
2021-03-19 01:54:03 +00:00
|
|
|
}
|
2022-05-25 00:54:03 +00:00
|
|
|
if other.TrailingContent != "" {
|
|
|
|
n.TrailingContent = other.TrailingContent
|
|
|
|
}
|
2023-04-03 06:16:07 +00:00
|
|
|
if other.HeadComment != "" {
|
|
|
|
n.HeadComment = other.HeadComment
|
2021-03-19 01:54:03 +00:00
|
|
|
}
|
2023-04-03 06:16:07 +00:00
|
|
|
if other.LineComment != "" {
|
|
|
|
n.LineComment = other.LineComment
|
2021-03-19 01:54:03 +00:00
|
|
|
}
|
2020-10-16 01:29:26 +00:00
|
|
|
}
|