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"
|
2020-10-13 01:51:37 +00:00
|
|
|
|
2020-10-21 02:54:51 +00:00
|
|
|
"github.com/jinzhu/copier"
|
2020-11-20 04:50:15 +00:00
|
|
|
yaml "gopkg.in/yaml.v3"
|
2020-10-13 01:51:37 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type CandidateNode struct {
|
2021-11-12 04:02:28 +00:00
|
|
|
Node *yaml.Node // the actual node
|
|
|
|
Parent *CandidateNode // parent node
|
2021-11-23 22:57:35 +00:00
|
|
|
Key *yaml.Node // 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
|
|
|
|
|
|
|
Path []interface{} /// the path we took to get to this node
|
|
|
|
Document uint // the document index of this node
|
2020-11-20 04:50:15 +00:00
|
|
|
Filename string
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
func (n *CandidateNode) GetKey() string {
|
2021-02-08 02:58:46 +00:00
|
|
|
keyPrefix := ""
|
|
|
|
if n.IsMapKey {
|
|
|
|
keyPrefix = "key-"
|
|
|
|
}
|
|
|
|
return fmt.Sprintf("%v%v - %v", keyPrefix, n.Document, n.Path)
|
2020-10-13 01:51:37 +00:00
|
|
|
}
|
|
|
|
|
2021-12-04 23:53:37 +00:00
|
|
|
func (n *CandidateNode) GetNiceTag() string {
|
|
|
|
return unwrapDoc(n.Node).Tag
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n *CandidateNode) GetNicePath() string {
|
|
|
|
if n.Path != nil && len(n.Path) >= 0 {
|
|
|
|
pathStr := make([]string, len(n.Path))
|
|
|
|
for i, v := range n.Path {
|
|
|
|
pathStr[i] = fmt.Sprintf("%v", v)
|
|
|
|
}
|
|
|
|
return strings.Join(pathStr, ".")
|
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2021-10-22 03:53:39 +00:00
|
|
|
func (n *CandidateNode) AsList() *list.List {
|
|
|
|
elMap := list.New()
|
|
|
|
elMap.PushBack(n)
|
|
|
|
return elMap
|
|
|
|
}
|
|
|
|
|
2021-11-23 22:57:35 +00:00
|
|
|
func (n *CandidateNode) CreateChildInMap(key *yaml.Node, node *yaml.Node) *CandidateNode {
|
2021-12-20 22:30:08 +00:00
|
|
|
var value interface{}
|
2021-11-23 22:57:35 +00:00
|
|
|
if key != nil {
|
|
|
|
value = key.Value
|
|
|
|
}
|
|
|
|
return &CandidateNode{
|
|
|
|
Node: node,
|
|
|
|
Path: n.createChildPath(value),
|
|
|
|
Parent: n,
|
|
|
|
Key: key,
|
|
|
|
Document: n.Document,
|
|
|
|
Filename: n.Filename,
|
|
|
|
FileIndex: n.FileIndex,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n *CandidateNode) CreateChildInArray(index int, node *yaml.Node) *CandidateNode {
|
2021-01-12 08:36:28 +00:00
|
|
|
return &CandidateNode{
|
|
|
|
Node: node,
|
2021-11-23 22:57:35 +00:00
|
|
|
Path: n.createChildPath(index),
|
2021-04-25 02:05:56 +00:00
|
|
|
Parent: n,
|
2021-11-23 22:57:35 +00:00
|
|
|
Key: &yaml.Node{Kind: yaml.ScalarNode, Value: fmt.Sprintf("%v", index), Tag: "!!int"},
|
|
|
|
Document: n.Document,
|
|
|
|
Filename: n.Filename,
|
|
|
|
FileIndex: n.FileIndex,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n *CandidateNode) CreateReplacement(node *yaml.Node) *CandidateNode {
|
|
|
|
return &CandidateNode{
|
2022-05-31 06:28:53 +00:00
|
|
|
Node: node,
|
|
|
|
Path: n.createChildPath(nil),
|
|
|
|
Parent: n.Parent,
|
|
|
|
Key: n.Key,
|
|
|
|
IsMapKey: n.IsMapKey,
|
|
|
|
Document: n.Document,
|
|
|
|
Filename: n.Filename,
|
|
|
|
FileIndex: n.FileIndex,
|
2021-01-12 08:36:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-31 06:28:53 +00:00
|
|
|
func (n *CandidateNode) CreateReplacementWithDocWrappers(node *yaml.Node) *CandidateNode {
|
|
|
|
replacement := n.CreateReplacement(node)
|
|
|
|
replacement.LeadingContent = n.LeadingContent
|
|
|
|
replacement.TrailingContent = n.TrailingContent
|
|
|
|
return replacement
|
|
|
|
}
|
|
|
|
|
2021-01-12 08:36:28 +00:00
|
|
|
func (n *CandidateNode) createChildPath(path interface{}) []interface{} {
|
|
|
|
if path == nil {
|
|
|
|
newPath := make([]interface{}, len(n.Path))
|
|
|
|
copy(newPath, n.Path)
|
|
|
|
return newPath
|
|
|
|
}
|
|
|
|
|
2020-12-25 01:46:08 +00:00
|
|
|
//don't use append as they may actually modify the path of the orignal node!
|
|
|
|
newPath := make([]interface{}, len(n.Path)+1)
|
|
|
|
copy(newPath, n.Path)
|
|
|
|
newPath[len(n.Path)] = path
|
|
|
|
return newPath
|
|
|
|
}
|
|
|
|
|
2020-11-13 03:07:11 +00:00
|
|
|
func (n *CandidateNode) Copy() (*CandidateNode, error) {
|
2020-10-21 02:54:51 +00:00
|
|
|
clone := &CandidateNode{}
|
2020-11-13 03:07:11 +00:00
|
|
|
err := copier.Copy(clone, n)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-02-20 03:29:52 +00:00
|
|
|
clone.Node = deepClone(n.Node)
|
2020-11-13 03:07:11 +00:00
|
|
|
return clone, nil
|
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.
|
2022-02-24 22:14:41 +00:00
|
|
|
if (n.Node.Kind != yaml.ScalarNode && len(n.Node.Content) == 0) ||
|
|
|
|
// if the tag has changed (e.g. from str to bool)
|
|
|
|
(guessTagFromCustomType(n.Node) != guessTagFromCustomType(other.Node)) {
|
2022-02-20 03:29:52 +00:00
|
|
|
n.Node.Style = other.Node.Style
|
|
|
|
}
|
|
|
|
|
|
|
|
n.Node.Content = deepCloneContent(other.Node.Content)
|
|
|
|
n.Node.Kind = other.Node.Kind
|
2020-10-16 01:29:26 +00:00
|
|
|
n.Node.Value = other.Node.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) {
|
2021-02-08 02:58:46 +00:00
|
|
|
log.Debug("UpdateAttributesFrom: n: %v other: %v", n.GetKey(), other.GetKey())
|
2020-10-19 05:36:46 +00:00
|
|
|
if n.Node.Kind != other.Node.Kind {
|
|
|
|
// clear out the contents when switching to a different type
|
|
|
|
// e.g. map to array
|
|
|
|
n.Node.Content = make([]*yaml.Node, 0)
|
|
|
|
n.Node.Value = ""
|
|
|
|
}
|
2020-10-16 01:29:26 +00:00
|
|
|
n.Node.Kind = other.Node.Kind
|
2022-01-22 02:47:22 +00:00
|
|
|
|
|
|
|
// don't clobber custom tags...
|
2022-08-29 05:37:25 +00:00
|
|
|
if prefs.ClobberCustomTags || strings.HasPrefix(n.Node.Tag, "!!") || n.Node.Tag == "" {
|
2022-01-22 02:47:22 +00:00
|
|
|
n.Node.Tag = other.Node.Tag
|
|
|
|
}
|
|
|
|
|
2021-02-05 03:40:16 +00:00
|
|
|
n.Node.Alias = other.Node.Alias
|
2021-12-02 22:23:16 +00:00
|
|
|
|
|
|
|
if !prefs.DontOverWriteAnchor {
|
|
|
|
n.Node.Anchor = other.Node.Anchor
|
|
|
|
}
|
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
|
|
|
|
2022-02-20 03:29:52 +00:00
|
|
|
if n.Node.Style == 0 {
|
2020-10-28 00:34:01 +00:00
|
|
|
n.Node.Style = other.Node.Style
|
|
|
|
}
|
2021-03-19 01:54:03 +00:00
|
|
|
|
|
|
|
if other.Node.FootComment != "" {
|
|
|
|
n.Node.FootComment = other.Node.FootComment
|
|
|
|
}
|
2022-05-25 00:54:03 +00:00
|
|
|
if other.TrailingContent != "" {
|
|
|
|
n.TrailingContent = other.TrailingContent
|
|
|
|
}
|
2021-03-19 01:54:03 +00:00
|
|
|
if other.Node.HeadComment != "" {
|
|
|
|
n.Node.HeadComment = other.Node.HeadComment
|
|
|
|
}
|
|
|
|
if other.Node.LineComment != "" {
|
|
|
|
n.Node.LineComment = other.Node.LineComment
|
|
|
|
}
|
2020-10-16 01:29:26 +00:00
|
|
|
}
|