This commit is contained in:
Mike Farah 2023-04-09 11:14:51 +10:00
parent 4696d11bbc
commit 4c6c653d25
22 changed files with 223 additions and 292 deletions

View File

@ -191,10 +191,11 @@ func (n *CandidateNode) CreateReplacement(kind Kind, tag string, value string) *
} }
} }
func (n *CandidateNode) CreateReplacementWithDocWrappers(kind Kind, tag string, value string) *CandidateNode { func (n *CandidateNode) CreateReplacementWithDocWrappers(kind Kind, tag string, style Style) *CandidateNode {
replacement := n.CreateReplacement(kind, tag, value) replacement := n.CreateReplacement(kind, tag, "")
replacement.LeadingContent = n.LeadingContent replacement.LeadingContent = n.LeadingContent
replacement.TrailingContent = n.TrailingContent replacement.TrailingContent = n.TrailingContent
replacement.Style = style
return replacement return replacement
} }

View File

@ -5,8 +5,6 @@ import (
"strconv" "strconv"
"strings" "strings"
"time" "time"
yaml "gopkg.in/yaml.v3"
) )
func createAddOp(lhs *ExpressionNode, rhs *ExpressionNode) *ExpressionNode { func createAddOp(lhs *ExpressionNode, rhs *ExpressionNode) *ExpressionNode {
@ -19,23 +17,20 @@ func addAssignOperator(d *dataTreeNavigator, context Context, expressionNode *Ex
return compoundAssignFunction(d, context, expressionNode, createAddOp) return compoundAssignFunction(d, context, expressionNode, createAddOp)
} }
func toNodes(candidate *CandidateNode, lhs *CandidateNode) ([]*yaml.Node, error) { func toNodes(candidate *CandidateNode, lhs *CandidateNode) ([]*CandidateNode, error) {
if candidate.Node.Tag == "!!null" { if candidate.Tag == "!!null" {
return []*yaml.Node{}, nil return []*CandidateNode{}, nil
}
clone, err := candidate.Copy()
if err != nil {
return nil, err
} }
clone := candidate.Copy()
switch candidate.Node.Kind { switch candidate.Kind {
case yaml.SequenceNode: case SequenceNode:
return clone.Node.Content, nil return clone.Content, nil
default: default:
if len(lhs.Node.Content) > 0 { if len(lhs.Content) > 0 {
clone.Node.Style = lhs.Node.Content[0].Style clone.Style = lhs.Content[0].Style
} }
return []*yaml.Node{clone.Node}, nil return []*CandidateNode{clone}, nil
} }
} }
@ -47,50 +42,50 @@ func addOperator(d *dataTreeNavigator, context Context, expressionNode *Expressi
} }
func add(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) { func add(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
lhs.Node = unwrapDoc(lhs.Node) lhs = lhs.unwrapDocument()
rhs.Node = unwrapDoc(rhs.Node) rhs = rhs.unwrapDocument()
lhsNode := lhs.Node lhsNode := lhs
if lhsNode.Tag == "!!null" { if lhsNode.Tag == "!!null" {
return lhs.CreateReplacement(rhs.Node), nil return lhs.CreateReplacement(rhs), nil
} }
target := lhs.CreateReplacement(&yaml.Node{ target := lhs.CreateReplacement(&yaml{
Anchor: lhs.Node.Anchor, Anchor: lhs.Anchor,
}) })
switch lhsNode.Kind { switch lhsNode.Kind {
case yaml.MappingNode: case MappingNode:
if rhs.Node.Kind != yaml.MappingNode { if rhs.Kind != MappingNode {
return nil, fmt.Errorf("%v (%v) cannot be added to a %v (%v)", rhs.Node.Tag, rhs.GetNicePath(), lhsNode.Tag, lhs.GetNicePath()) return nil, fmt.Errorf("%v (%v) cannot be added to a %v (%v)", rhs.Tag, rhs.GetNicePath(), lhsNode.Tag, lhs.GetNicePath())
} }
addMaps(target, lhs, rhs) addMaps(target, lhs, rhs)
case yaml.SequenceNode: case SequenceNode:
if err := addSequences(target, lhs, rhs); err != nil { if err := addSequences(target, lhs, rhs); err != nil {
return nil, err return nil, err
} }
case yaml.ScalarNode: case ScalarNode:
if rhs.Node.Kind != yaml.ScalarNode { if rhs.Kind != ScalarNode {
return nil, fmt.Errorf("%v (%v) cannot be added to a %v (%v)", rhs.Node.Tag, rhs.GetNicePath(), lhsNode.Tag, lhs.GetNicePath()) return nil, fmt.Errorf("%v (%v) cannot be added to a %v (%v)", rhs.Tag, rhs.GetNicePath(), lhsNode.Tag, lhs.GetNicePath())
} }
target.Node.Kind = yaml.ScalarNode target.Kind = ScalarNode
target.Node.Style = lhsNode.Style target.Style = lhsNode.Style
if err := addScalars(context, target, lhsNode, rhs.Node); err != nil { if err := addScalars(context, target, lhsNode, rhs); err != nil {
return nil, err return nil, err
} }
} }
return target, nil return target, nil
} }
func addScalars(context Context, target *CandidateNode, lhs *yaml.Node, rhs *yaml.Node) error { func addScalars(context Context, target *CandidateNode, lhs *CandidateNode, rhs *CandidateNode) error {
lhsTag := lhs.Tag lhsTag := lhs.Tag
rhsTag := guessTagFromCustomType(rhs) rhsTag := rhs.guessTagFromCustomType()
lhsIsCustom := false lhsIsCustom := false
if !strings.HasPrefix(lhsTag, "!!") { if !strings.HasPrefix(lhsTag, "!!") {
// custom tag - we have to have a guess // custom tag - we have to have a guess
lhsTag = guessTagFromCustomType(lhs) lhsTag = lhs.guessTagFromCustomType()
lhsIsCustom = true lhsIsCustom = true
} }
@ -106,11 +101,11 @@ func addScalars(context Context, target *CandidateNode, lhs *yaml.Node, rhs *yam
return addDateTimes(context.GetDateTimeLayout(), target, lhs, rhs) return addDateTimes(context.GetDateTimeLayout(), target, lhs, rhs)
} else if lhsTag == "!!str" { } else if lhsTag == "!!str" {
target.Node.Tag = lhs.Tag target.Tag = lhs.Tag
target.Node.Value = lhs.Value + rhs.Value target.Value = lhs.Value + rhs.Value
} else if rhsTag == "!!str" { } else if rhsTag == "!!str" {
target.Node.Tag = rhs.Tag target.Tag = rhs.Tag
target.Node.Value = lhs.Value + rhs.Value target.Value = lhs.Value + rhs.Value
} else if lhsTag == "!!int" && rhsTag == "!!int" { } else if lhsTag == "!!int" && rhsTag == "!!int" {
format, lhsNum, err := parseInt64(lhs.Value) format, lhsNum, err := parseInt64(lhs.Value)
if err != nil { if err != nil {
@ -121,8 +116,8 @@ func addScalars(context Context, target *CandidateNode, lhs *yaml.Node, rhs *yam
return err return err
} }
sum := lhsNum + rhsNum sum := lhsNum + rhsNum
target.Node.Tag = lhs.Tag target.Tag = lhs.Tag
target.Node.Value = fmt.Sprintf(format, sum) target.Value = fmt.Sprintf(format, sum)
} else if (lhsTag == "!!int" || lhsTag == "!!float") && (rhsTag == "!!int" || rhsTag == "!!float") { } else if (lhsTag == "!!int" || lhsTag == "!!float") && (rhsTag == "!!int" || rhsTag == "!!float") {
lhsNum, err := strconv.ParseFloat(lhs.Value, 64) lhsNum, err := strconv.ParseFloat(lhs.Value, 64)
if err != nil { if err != nil {
@ -134,18 +129,18 @@ func addScalars(context Context, target *CandidateNode, lhs *yaml.Node, rhs *yam
} }
sum := lhsNum + rhsNum sum := lhsNum + rhsNum
if lhsIsCustom { if lhsIsCustom {
target.Node.Tag = lhs.Tag target.Tag = lhs.Tag
} else { } else {
target.Node.Tag = "!!float" target.Tag = "!!float"
} }
target.Node.Value = fmt.Sprintf("%v", sum) target.Value = fmt.Sprintf("%v", sum)
} else { } else {
return fmt.Errorf("%v cannot be added to %v", lhsTag, rhsTag) return fmt.Errorf("%v cannot be added to %v", lhsTag, rhsTag)
} }
return nil return nil
} }
func addDateTimes(layout string, target *CandidateNode, lhs *yaml.Node, rhs *yaml.Node) error { func addDateTimes(layout string, target *CandidateNode, lhs *CandidateNode, rhs *CandidateNode) error {
duration, err := time.ParseDuration(rhs.Value) duration, err := time.ParseDuration(rhs.Value)
if err != nil { if err != nil {
@ -158,52 +153,52 @@ func addDateTimes(layout string, target *CandidateNode, lhs *yaml.Node, rhs *yam
} }
newTime := currentTime.Add(duration) newTime := currentTime.Add(duration)
target.Node.Value = newTime.Format(layout) target.Value = newTime.Format(layout)
return nil return nil
} }
func addSequences(target *CandidateNode, lhs *CandidateNode, rhs *CandidateNode) error { func addSequences(target *CandidateNode, lhs *CandidateNode, rhs *CandidateNode) error {
target.Node.Kind = yaml.SequenceNode target.Kind = SequenceNode
if len(lhs.Node.Content) > 0 { if len(lhs.Content) > 0 {
target.Node.Style = lhs.Node.Style target.Style = lhs.Style
} }
target.Node.Tag = lhs.Node.Tag target.Tag = lhs.Tag
extraNodes, err := toNodes(rhs, lhs) extraNodes, err := toNodes(rhs, lhs)
if err != nil { if err != nil {
return err return err
} }
target.Node.Content = append(deepCloneContent(lhs.Node.Content), extraNodes...) target.Content = append(lhs.CopyChildren(), extraNodes...)
return nil return nil
} }
func addMaps(target *CandidateNode, lhsC *CandidateNode, rhsC *CandidateNode) { func addMaps(target *CandidateNode, lhsC *CandidateNode, rhsC *CandidateNode) {
lhs := lhsC.Node lhs := lhsC
rhs := rhsC.Node rhs := rhsC
target.Node.Content = make([]*yaml.Node, len(lhs.Content)) target.Content = make([]*CandidateNode, len(lhs.Content))
copy(target.Node.Content, lhs.Content) copy(target.Content, lhs.Content)
for index := 0; index < len(rhs.Content); index = index + 2 { for index := 0; index < len(rhs.Content); index = index + 2 {
key := rhs.Content[index] key := rhs.Content[index]
value := rhs.Content[index+1] value := rhs.Content[index+1]
log.Debug("finding %v", key.Value) log.Debug("finding %v", key.Value)
indexInLHS := findKeyInMap(target.Node, key) indexInLHS := findKeyInMap(target, key)
log.Debug("indexInLhs %v", indexInLHS) log.Debug("indexInLhs %v", indexInLHS)
if indexInLHS < 0 { if indexInLHS < 0 {
// not in there, append it // not in there, append it
target.Node.Content = append(target.Node.Content, key, value) target.Content = append(target.Content, key, value)
} else { } else {
// it's there, replace it // it's there, replace it
target.Node.Content[indexInLHS+1] = value target.Content[indexInLHS+1] = value
} }
} }
target.Node.Kind = yaml.MappingNode target.Kind = MappingNode
if len(lhs.Content) > 0 { if len(lhs.Content) > 0 {
target.Node.Style = lhs.Style target.Style = lhs.Style
} }
target.Node.Tag = lhs.Tag target.Tag = lhs.Tag
} }

View File

@ -9,7 +9,7 @@ func alternativeOperator(d *dataTreeNavigator, context Context, expressionNode *
if lhs == nil { if lhs == nil {
return nil, nil return nil, nil
} }
truthy, err := isTruthy(lhs) truthy, err := isTruthyNode(lhs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -29,12 +29,8 @@ func alternativeFunc(d *dataTreeNavigator, context Context, lhs *CandidateNode,
if rhs == nil { if rhs == nil {
return lhs, nil return lhs, nil
} }
lhs.Node = unwrapDoc(lhs.Node)
rhs.Node = unwrapDoc(rhs.Node)
log.Debugf("Alternative LHS: %v", lhs.Node.Tag)
log.Debugf("- RHS: %v", rhs.Node.Tag)
isTrue, err := isTruthy(lhs) isTrue, err := isTruthyNode(lhs)
if err != nil { if err != nil {
return nil, err return nil, err
} else if isTrue { } else if isTrue {

View File

@ -8,9 +8,8 @@ type assignPreferences struct {
func assignUpdateFunc(prefs assignPreferences) crossFunctionCalculation { func assignUpdateFunc(prefs assignPreferences) crossFunctionCalculation {
return func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) { return func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
rhs.Node = unwrapDoc(rhs.Node) if !prefs.OnlyWriteNull || lhs.Tag == "!!null" {
if !prefs.OnlyWriteNull || lhs.Node.Tag == "!!null" { lhs.UpdateFrom(rhs.unwrapDocument(), prefs)
lhs.UpdateFrom(rhs, prefs)
} }
return lhs, nil return lhs, nil
} }
@ -60,8 +59,7 @@ func assignUpdateOperator(d *dataTreeNavigator, context Context, expressionNode
if first != nil { if first != nil {
rhsCandidate := first.Value.(*CandidateNode) rhsCandidate := first.Value.(*CandidateNode)
rhsCandidate.Node = unwrapDoc(rhsCandidate.Node) candidate.UpdateFrom(rhsCandidate.unwrapDocument(), prefs)
candidate.UpdateFrom(rhsCandidate, prefs)
} }
} }
@ -92,7 +90,7 @@ func assignAttributesOperator(d *dataTreeNavigator, context Context, expressionN
if expressionNode.Operation.Preferences != nil { if expressionNode.Operation.Preferences != nil {
prefs = expressionNode.Operation.Preferences.(assignPreferences) prefs = expressionNode.Operation.Preferences.(assignPreferences)
} }
if !prefs.OnlyWriteNull || candidate.Node.Tag == "!!null" { if !prefs.OnlyWriteNull || candidate.Tag == "!!null" {
candidate.UpdateAttributesFrom(first.Value.(*CandidateNode), prefs) candidate.UpdateAttributesFrom(first.Value.(*CandidateNode), prefs)
} }
} }

View File

@ -3,36 +3,26 @@ package yqlib
import ( import (
"container/list" "container/list"
"fmt" "fmt"
"strings"
yaml "gopkg.in/yaml.v3"
) )
func isTruthyNode(node *yaml.Node) (bool, error) { func isTruthyNode(candidate *CandidateNode) (bool, error) {
value := true if candidate == nil {
return false, nil
}
node := candidate.unwrapDocument()
if node.Tag == "!!null" { if node.Tag == "!!null" {
return false, nil return false, nil
} }
if node.Kind == yaml.ScalarNode && node.Tag == "!!bool" { if node.Kind == ScalarNode && node.Tag == "!!bool" {
errDecoding := node.Decode(&value) // yes/y/true/on
if errDecoding != nil { return (strings.EqualFold(node.Value, "y") ||
return false, errDecoding strings.EqualFold(node.Value, "yes") ||
} strings.EqualFold(node.Value, "on") ||
strings.EqualFold(node.Value, "true")), nil
} }
return value, nil return true, nil
}
func isTruthy(c *CandidateNode) (bool, error) {
node := unwrapDoc(c.Node)
return isTruthyNode(node)
}
func getBoolean(candidate *CandidateNode) (bool, error) {
if candidate != nil {
candidate.Node = unwrapDoc(candidate.Node)
return isTruthy(candidate)
}
return false, nil
} }
func getOwner(lhs *CandidateNode, rhs *CandidateNode) *CandidateNode { func getOwner(lhs *CandidateNode, rhs *CandidateNode) *CandidateNode {
@ -48,7 +38,7 @@ func getOwner(lhs *CandidateNode, rhs *CandidateNode) *CandidateNode {
func returnRhsTruthy(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) { func returnRhsTruthy(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
owner := getOwner(lhs, rhs) owner := getOwner(lhs, rhs)
rhsBool, err := getBoolean(rhs) rhsBool, err := isTruthyNode(rhs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -61,7 +51,7 @@ func returnLHSWhen(targetBool bool) func(lhs *CandidateNode) (*CandidateNode, er
var err error var err error
var lhsBool bool var lhsBool bool
if lhsBool, err = getBoolean(lhs); err != nil || lhsBool != targetBool { if lhsBool, err = isTruthyNode(lhs); err != nil || lhsBool != targetBool {
return nil, err return nil, err
} }
owner := &CandidateNode{} owner := &CandidateNode{}
@ -72,18 +62,17 @@ func returnLHSWhen(targetBool bool) func(lhs *CandidateNode) (*CandidateNode, er
} }
} }
func findBoolean(wantBool bool, d *dataTreeNavigator, context Context, expressionNode *ExpressionNode, sequenceNode *yaml.Node) (bool, error) { func findBoolean(wantBool bool, d *dataTreeNavigator, context Context, expressionNode *ExpressionNode, sequenceNode *CandidateNode) (bool, error) {
for _, node := range sequenceNode.Content { for _, node := range sequenceNode.Content {
if expressionNode != nil { if expressionNode != nil {
//need to evaluate the expression against the node //need to evaluate the expression against the node
candidate := &CandidateNode{Node: node} rhs, err := d.GetMatchingNodes(context.SingleReadonlyChildContext(node), expressionNode)
rhs, err := d.GetMatchingNodes(context.SingleReadonlyChildContext(candidate), expressionNode)
if err != nil { if err != nil {
return false, err return false, err
} }
if rhs.MatchingNodes.Len() > 0 { if rhs.MatchingNodes.Len() > 0 {
node = rhs.MatchingNodes.Front().Value.(*CandidateNode).Node node = rhs.MatchingNodes.Front().Value.(*CandidateNode)
} else { } else {
// no results found, ignore this entry // no results found, ignore this entry
continue continue
@ -106,8 +95,8 @@ func allOperator(d *dataTreeNavigator, context Context, expressionNode *Expressi
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode) candidate := el.Value.(*CandidateNode)
candidateNode := unwrapDoc(candidate.Node) candidateNode := candidate.unwrapDocument()
if candidateNode.Kind != yaml.SequenceNode { if candidateNode.Kind != SequenceNode {
return Context{}, fmt.Errorf("any only supports arrays, was %v", candidateNode.Tag) return Context{}, fmt.Errorf("any only supports arrays, was %v", candidateNode.Tag)
} }
booleanResult, err := findBoolean(false, d, context, expressionNode.RHS, candidateNode) booleanResult, err := findBoolean(false, d, context, expressionNode.RHS, candidateNode)
@ -125,8 +114,8 @@ func anyOperator(d *dataTreeNavigator, context Context, expressionNode *Expressi
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode) candidate := el.Value.(*CandidateNode)
candidateNode := unwrapDoc(candidate.Node) candidateNode := candidate.unwrapDocument()
if candidateNode.Kind != yaml.SequenceNode { if candidateNode.Kind != SequenceNode {
return Context{}, fmt.Errorf("any only supports arrays, was %v", candidateNode.Tag) return Context{}, fmt.Errorf("any only supports arrays, was %v", candidateNode.Tag)
} }
booleanResult, err := findBoolean(true, d, context, expressionNode.RHS, candidateNode) booleanResult, err := findBoolean(true, d, context, expressionNode.RHS, candidateNode)
@ -164,7 +153,7 @@ func notOperator(d *dataTreeNavigator, context Context, expressionNode *Expressi
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode) candidate := el.Value.(*CandidateNode)
log.Debug("notOperation checking %v", candidate) log.Debug("notOperation checking %v", candidate)
truthy, errDecoding := isTruthy(candidate) truthy, errDecoding := isTruthyNode(candidate)
if errDecoding != nil { if errDecoding != nil {
return Context{}, errDecoding return Context{}, errDecoding
} }

View File

@ -2,12 +2,10 @@ package yqlib
import ( import (
"container/list" "container/list"
yaml "gopkg.in/yaml.v3"
) )
func collectTogether(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (*CandidateNode, error) { func collectTogether(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (*CandidateNode, error) {
collectedNode := &yaml.Node{Kind: yaml.SequenceNode, Tag: "!!seq"} collectedNode := &CandidateNode{Kind: SequenceNode, Tag: "!!seq"}
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode) candidate := el.Value.(*CandidateNode)
collectExpResults, err := d.GetMatchingNodes(context.SingleReadonlyChildContext(candidate), expressionNode) collectExpResults, err := d.GetMatchingNodes(context.SingleReadonlyChildContext(candidate), expressionNode)
@ -17,10 +15,10 @@ func collectTogether(d *dataTreeNavigator, context Context, expressionNode *Expr
for result := collectExpResults.MatchingNodes.Front(); result != nil; result = result.Next() { for result := collectExpResults.MatchingNodes.Front(); result != nil; result = result.Next() {
resultC := result.Value.(*CandidateNode) resultC := result.Value.(*CandidateNode)
log.Debugf("found this: %v", NodeToString(resultC)) log.Debugf("found this: %v", NodeToString(resultC))
collectedNode.Content = append(collectedNode.Content, unwrapDoc(resultC.Node)) collectedNode.Content = append(collectedNode.Content, resultC.unwrapDocument())
} }
} }
return &CandidateNode{Node: collectedNode}, nil return collectedNode, nil
} }
func collectOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { func collectOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
@ -28,9 +26,8 @@ func collectOperator(d *dataTreeNavigator, context Context, expressionNode *Expr
if context.MatchingNodes.Len() == 0 { if context.MatchingNodes.Len() == 0 {
log.Debugf("nothing to collect") log.Debugf("nothing to collect")
node := &yaml.Node{Kind: yaml.SequenceNode, Tag: "!!seq", Value: "[]"} node := &CandidateNode{Kind: SequenceNode, Tag: "!!seq", Value: "[]"}
candidate := &CandidateNode{Node: node} return context.SingleChildContext(node), nil
return context.SingleChildContext(candidate), nil
} }
var evaluateAllTogether = true var evaluateAllTogether = true

View File

@ -2,8 +2,6 @@ package yqlib
import ( import (
"container/list" "container/list"
yaml "gopkg.in/yaml.v3"
) )
/* /*
@ -23,26 +21,26 @@ func collectObjectOperator(d *dataTreeNavigator, originalContext Context, expres
context := originalContext.WritableClone() context := originalContext.WritableClone()
if context.MatchingNodes.Len() == 0 { if context.MatchingNodes.Len() == 0 {
node := &yaml.Node{Kind: yaml.MappingNode, Tag: "!!map", Value: "{}"} candidate := &CandidateNode{Kind: MappingNode, Tag: "!!map", Value: "{}"}
candidate := &CandidateNode{Node: node}
return context.SingleChildContext(candidate), nil return context.SingleChildContext(candidate), nil
} }
first := context.MatchingNodes.Front().Value.(*CandidateNode) first := context.MatchingNodes.Front().Value.(*CandidateNode)
var rotated = make([]*list.List, len(first.Node.Content)) var rotated = make([]*list.List, len(first.Content))
for i := 0; i < len(first.Node.Content); i++ { for i := 0; i < len(first.Content); i++ {
rotated[i] = list.New() rotated[i] = list.New()
} }
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
candidateNode := el.Value.(*CandidateNode) candidateNode := el.Value.(*CandidateNode)
for i := 0; i < len(first.Node.Content); i++ { for i := 0; i < len(first.Content); i++ {
rotated[i].PushBack(candidateNode.CreateChildInArray(i, candidateNode.Node.Content[i]))
rotated[i].PushBack(candidateNode.CreateChildInArray(i, candidateNode.Content[i]))
} }
} }
newObject := list.New() newObject := list.New()
for i := 0; i < len(first.Node.Content); i++ { for i := 0; i < len(first.Content); i++ {
additions, err := collect(d, context.ChildContext(list.New()), rotated[i]) additions, err := collect(d, context.ChildContext(list.New()), rotated[i])
if err != nil { if err != nil {
return Context{}, err return Context{}, err
@ -82,10 +80,7 @@ func collect(d *dataTreeNavigator, context Context, remainingMatches *list.List)
aggCandidate := el.Value.(*CandidateNode) aggCandidate := el.Value.(*CandidateNode)
for splatEl := splatted.MatchingNodes.Front(); splatEl != nil; splatEl = splatEl.Next() { for splatEl := splatted.MatchingNodes.Front(); splatEl != nil; splatEl = splatEl.Next() {
splatCandidate := splatEl.Value.(*CandidateNode) splatCandidate := splatEl.Value.(*CandidateNode)
newCandidate, err := aggCandidate.Copy() newCandidate := aggCandidate.Copy()
if err != nil {
return Context{}, err
}
newCandidate.Path = nil newCandidate.Path = nil

View File

@ -3,8 +3,6 @@ package yqlib
import ( import (
"container/list" "container/list"
"fmt" "fmt"
yaml "gopkg.in/yaml.v3"
) )
func deleteChildOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { func deleteChildOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
@ -17,12 +15,12 @@ func deleteChildOperator(d *dataTreeNavigator, context Context, expressionNode *
for el := nodesToDelete.MatchingNodes.Back(); el != nil; el = el.Prev() { for el := nodesToDelete.MatchingNodes.Back(); el != nil; el = el.Prev() {
candidate := el.Value.(*CandidateNode) candidate := el.Value.(*CandidateNode)
if candidate.Node.Kind == yaml.DocumentNode { if candidate.Kind == DocumentNode {
//need to delete this node from context. //need to delete this node from context.
newResults := list.New() newResults := list.New()
for item := context.MatchingNodes.Front(); item != nil; item = item.Next() { for item := context.MatchingNodes.Front(); item != nil; item = item.Next() {
nodeInContext := item.Value.(*CandidateNode) nodeInContext := item.Value.(*CandidateNode)
if nodeInContext.Node != candidate.Node { if nodeInContext != candidate {
newResults.PushBack(nodeInContext) newResults.PushBack(nodeInContext)
} else { } else {
log.Info("Need to delete this %v", NodeToString(nodeInContext)) log.Info("Need to delete this %v", NodeToString(nodeInContext))
@ -36,12 +34,12 @@ func deleteChildOperator(d *dataTreeNavigator, context Context, expressionNode *
return context, nil return context, nil
} }
parentNode := candidate.Parent.Node parentNode := candidate.Parent
childPath := candidate.Path[len(candidate.Path)-1] childPath := candidate.Path[len(candidate.Path)-1]
if parentNode.Kind == yaml.MappingNode { if parentNode.Kind == MappingNode {
deleteFromMap(candidate.Parent, childPath) deleteFromMap(candidate.Parent, childPath)
} else if parentNode.Kind == yaml.SequenceNode { } else if parentNode.Kind == SequenceNode {
deleteFromArray(candidate.Parent, childPath) deleteFromArray(candidate.Parent, childPath)
} else { } else {
return Context{}, fmt.Errorf("Cannot delete nodes from parent of tag %v", parentNode.Tag) return Context{}, fmt.Errorf("Cannot delete nodes from parent of tag %v", parentNode.Tag)
@ -52,19 +50,17 @@ func deleteChildOperator(d *dataTreeNavigator, context Context, expressionNode *
func deleteFromMap(candidate *CandidateNode, childPath interface{}) { func deleteFromMap(candidate *CandidateNode, childPath interface{}) {
log.Debug("deleteFromMap") log.Debug("deleteFromMap")
node := unwrapDoc(candidate.Node) node := candidate.unwrapDocument()
contents := node.Content contents := node.Content
newContents := make([]*yaml.Node, 0) newContents := make([]*CandidateNode, 0)
for index := 0; index < len(contents); index = index + 2 { for index := 0; index < len(contents); index = index + 2 {
key := contents[index] key := contents[index]
value := contents[index+1] value := contents[index+1]
childCandidate := candidate.CreateChildInMap(key, value)
shouldDelete := key.Value == childPath shouldDelete := key.Value == childPath
log.Debugf("shouldDelete %v ? %v", childCandidate.GetKey(), shouldDelete) log.Debugf("shouldDelete %v ? %v", value.GetKey(), shouldDelete)
if !shouldDelete { if !shouldDelete {
newContents = append(newContents, key, value) newContents = append(newContents, key, value)
@ -75,9 +71,9 @@ func deleteFromMap(candidate *CandidateNode, childPath interface{}) {
func deleteFromArray(candidate *CandidateNode, childPath interface{}) { func deleteFromArray(candidate *CandidateNode, childPath interface{}) {
log.Debug("deleteFromArray") log.Debug("deleteFromArray")
node := unwrapDoc(candidate.Node) node := candidate.unwrapDocument()
contents := node.Content contents := node.Content
newContents := make([]*yaml.Node, 0) newContents := make([]*CandidateNode, 0)
for index := 0; index < len(contents); index = index + 1 { for index := 0; index < len(contents); index = index + 1 {
value := contents[index] value := contents[index]

View File

@ -3,8 +3,6 @@ package yqlib
import ( import (
"container/list" "container/list"
"fmt" "fmt"
"gopkg.in/yaml.v3"
) )
func getDocumentIndexOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { func getDocumentIndexOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
@ -12,8 +10,7 @@ func getDocumentIndexOperator(d *dataTreeNavigator, context Context, expressionN
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode) candidate := el.Value.(*CandidateNode)
node := &yaml.Node{Kind: yaml.ScalarNode, Value: fmt.Sprintf("%v", candidate.Document), Tag: "!!int"} scalar := candidate.CreateReplacement(ScalarNode, "!!int", fmt.Sprintf("%v", candidate.Document))
scalar := candidate.CreateReplacement(node)
results.PushBack(scalar) results.PushBack(scalar)
} }
return context.ChildContext(results), nil return context.ChildContext(results), nil

View File

@ -7,8 +7,6 @@ import (
"errors" "errors"
"regexp" "regexp"
"strings" "strings"
"gopkg.in/yaml.v3"
) )
func configureEncoder(format PrinterOutputFormat, indent int) Encoder { func configureEncoder(format PrinterOutputFormat, indent int) Encoder {
@ -78,7 +76,7 @@ func encodeOperator(d *dataTreeNavigator, context Context, expressionNode *Expre
if originalList != nil && originalList.Len() > 0 && hasOnlyOneNewLine.MatchString(stringValue) { if originalList != nil && originalList.Len() > 0 && hasOnlyOneNewLine.MatchString(stringValue) {
original := originalList.Front().Value.(*CandidateNode) original := originalList.Front().Value.(*CandidateNode)
originalNode := unwrapDoc(original.Node) originalNode := original.unwrapDocument()
// original block did not have a newline at the end, get rid of this one too // original block did not have a newline at the end, get rid of this one too
if !endWithNewLine.MatchString(originalNode.Value) { if !endWithNewLine.MatchString(originalNode.Value) {
stringValue = chomper.ReplaceAllString(stringValue, "") stringValue = chomper.ReplaceAllString(stringValue, "")
@ -92,8 +90,7 @@ func encodeOperator(d *dataTreeNavigator, context Context, expressionNode *Expre
stringValue = chomper.ReplaceAllString(stringValue, "") stringValue = chomper.ReplaceAllString(stringValue, "")
} }
stringContentNode := &yaml.Node{Kind: yaml.ScalarNode, Tag: "!!str", Value: stringValue} results.PushBack(candidate.CreateReplacement(ScalarNode, "!!str", stringValue))
results.PushBack(candidate.CreateReplacement(stringContentNode))
} }
return context.ChildContext(results), nil return context.ChildContext(results), nil
} }
@ -141,9 +138,9 @@ func decodeOperator(d *dataTreeNavigator, context Context, expressionNode *Expre
context.SetVariable("decoded: "+candidate.GetKey(), candidate.AsList()) context.SetVariable("decoded: "+candidate.GetKey(), candidate.AsList())
log.Debugf("got: [%v]", candidate.Node.Value) log.Debugf("got: [%v]", candidate.Value)
err := decoder.Init(strings.NewReader(unwrapDoc(candidate.Node).Value)) err := decoder.Init(strings.NewReader(candidate.unwrapDocument().Value))
if err != nil { if err != nil {
return Context{}, err return Context{}, err
} }
@ -153,9 +150,15 @@ func decodeOperator(d *dataTreeNavigator, context Context, expressionNode *Expre
return Context{}, errorReading return Context{}, errorReading
} }
//first node is a doc //first node is a doc
node := unwrapDoc(decodedNode.Node) node := decodedNode.unwrapDocument()
node.Path = candidate.Path
node.Key = candidate.Key
node.Parent = candidate.Parent
node.Document = candidate.Document
node.FileIndex = candidate.FileIndex
node.Filename = candidate.Filename
results.PushBack(candidate.CreateReplacement(node)) results.PushBack(node)
} }
return context.ChildContext(results), nil return context.ChildContext(results), nil
} }

View File

@ -3,64 +3,60 @@ package yqlib
import ( import (
"container/list" "container/list"
"fmt" "fmt"
yaml "gopkg.in/yaml.v3"
) )
func entrySeqFor(key *yaml.Node, value *yaml.Node) *yaml.Node { func entrySeqFor(key *CandidateNode, value *CandidateNode) *CandidateNode {
var keyKey = &yaml.Node{Kind: yaml.ScalarNode, Tag: "!!str", Value: "key"} var keyKey = &CandidateNode{Kind: ScalarNode, Tag: "!!str", Value: "key"}
var valueKey = &yaml.Node{Kind: yaml.ScalarNode, Tag: "!!str", Value: "value"} var valueKey = &CandidateNode{Kind: ScalarNode, Tag: "!!str", Value: "value"}
return &yaml.Node{ return &CandidateNode{
Kind: yaml.MappingNode, Kind: MappingNode,
Tag: "!!map", Tag: "!!map",
Content: []*yaml.Node{keyKey, key, valueKey, value}, Content: []*CandidateNode{keyKey, key, valueKey, value},
} }
} }
func toEntriesFromMap(candidateNode *CandidateNode) *CandidateNode { func toEntriesFromMap(candidateNode *CandidateNode) *CandidateNode {
var sequence = &yaml.Node{Kind: yaml.SequenceNode, Tag: "!!seq"} var sequence = candidateNode.CreateReplacementWithDocWrappers(SequenceNode, "!!seq", 0)
var entriesNode = candidateNode.CreateReplacementWithDocWrappers(sequence)
var contents = unwrapDoc(candidateNode.Node).Content var contents = candidateNode.unwrapDocument().Content
for index := 0; index < len(contents); index = index + 2 { for index := 0; index < len(contents); index = index + 2 {
key := contents[index] key := contents[index]
value := contents[index+1] value := contents[index+1]
sequence.Content = append(sequence.Content, entrySeqFor(key, value)) sequence.Content = append(sequence.Content, entrySeqFor(key, value))
} }
return entriesNode return sequence
} }
func toEntriesfromSeq(candidateNode *CandidateNode) *CandidateNode { func toEntriesfromSeq(candidateNode *CandidateNode) *CandidateNode {
var sequence = &yaml.Node{Kind: yaml.SequenceNode, Tag: "!!seq"} var sequence = candidateNode.CreateReplacementWithDocWrappers(SequenceNode, "!!seq", 0)
var entriesNode = candidateNode.CreateReplacementWithDocWrappers(sequence)
var contents = unwrapDoc(candidateNode.Node).Content var contents = candidateNode.unwrapDocument().Content
for index := 0; index < len(contents); index = index + 1 { for index := 0; index < len(contents); index = index + 1 {
key := &yaml.Node{Kind: yaml.ScalarNode, Tag: "!!int", Value: fmt.Sprintf("%v", index)} key := &CandidateNode{Kind: ScalarNode, Tag: "!!int", Value: fmt.Sprintf("%v", index)}
value := contents[index] value := contents[index]
sequence.Content = append(sequence.Content, entrySeqFor(key, value)) sequence.Content = append(sequence.Content, entrySeqFor(key, value))
} }
return entriesNode return sequence
} }
func toEntriesOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { func toEntriesOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
var results = list.New() var results = list.New()
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode) candidate := el.Value.(*CandidateNode)
candidateNode := unwrapDoc(candidate.Node) candidateNode := candidate.unwrapDocument()
switch candidateNode.Kind { switch candidateNode.Kind {
case yaml.MappingNode: case MappingNode:
results.PushBack(toEntriesFromMap(candidate)) results.PushBack(toEntriesFromMap(candidate))
case yaml.SequenceNode: case SequenceNode:
results.PushBack(toEntriesfromSeq(candidate)) results.PushBack(toEntriesfromSeq(candidate))
default: default:
if candidateNode.Tag != "!!null" { if candidateNode.Tag != "!!null" {
return Context{}, fmt.Errorf("%v has no keys", candidate.Node.Tag) return Context{}, fmt.Errorf("%v has no keys", candidate.Tag)
} }
} }
} }
@ -68,9 +64,8 @@ func toEntriesOperator(d *dataTreeNavigator, context Context, expressionNode *Ex
return context.ChildContext(results), nil return context.ChildContext(results), nil
} }
func parseEntry(entry *yaml.Node, position int) (*yaml.Node, *yaml.Node, error) { func parseEntry(candidateNode *CandidateNode, position int) (*CandidateNode, *CandidateNode, error) {
prefs := traversePreferences{DontAutoCreate: true} prefs := traversePreferences{DontAutoCreate: true}
candidateNode := &CandidateNode{Node: entry}
keyResults, err := traverseMap(Context{}, candidateNode, createStringScalarNode("key"), prefs, false) keyResults, err := traverseMap(Context{}, candidateNode, createStringScalarNode("key"), prefs, false)
@ -88,15 +83,14 @@ func parseEntry(entry *yaml.Node, position int) (*yaml.Node, *yaml.Node, error)
return nil, nil, fmt.Errorf("expected to find one 'value' entry but found %v in position %v", valueResults.Len(), position) return nil, nil, fmt.Errorf("expected to find one 'value' entry but found %v in position %v", valueResults.Len(), position)
} }
return keyResults.Front().Value.(*CandidateNode).Node, valueResults.Front().Value.(*CandidateNode).Node, nil return keyResults.Front().Value.(*CandidateNode), valueResults.Front().Value.(*CandidateNode), nil
} }
func fromEntries(candidateNode *CandidateNode) (*CandidateNode, error) { func fromEntries(candidateNode *CandidateNode) (*CandidateNode, error) {
var node = &yaml.Node{Kind: yaml.MappingNode, Tag: "!!map"} var node = candidateNode.CopyWithoutContent()
var mapCandidateNode = candidateNode.CreateReplacementWithDocWrappers(node)
var contents = unwrapDoc(candidateNode.Node).Content var contents = candidateNode.unwrapDocument().Content
for index := 0; index < len(contents); index = index + 1 { for index := 0; index < len(contents); index = index + 1 {
key, value, err := parseEntry(contents[index], index) key, value, err := parseEntry(contents[index], index)
@ -106,17 +100,17 @@ func fromEntries(candidateNode *CandidateNode) (*CandidateNode, error) {
node.Content = append(node.Content, key, value) node.Content = append(node.Content, key, value)
} }
return mapCandidateNode, nil return node, nil
} }
func fromEntriesOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { func fromEntriesOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
var results = list.New() var results = list.New()
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode) candidate := el.Value.(*CandidateNode)
candidateNode := unwrapDoc(candidate.Node) candidateNode := candidate.unwrapDocument()
switch candidateNode.Kind { switch candidateNode.Kind {
case yaml.SequenceNode: case SequenceNode:
mapResult, err := fromEntries(candidate) mapResult, err := fromEntries(candidate)
if err != nil { if err != nil {
return Context{}, err return Context{}, err

View File

@ -4,10 +4,8 @@ import (
"container/list" "container/list"
"fmt" "fmt"
"os" "os"
"strings"
parse "github.com/a8m/envsubst/parse" parse "github.com/a8m/envsubst/parse"
yaml "gopkg.in/yaml.v3"
) )
type envOpPreferences struct { type envOpPreferences struct {
@ -18,39 +16,37 @@ type envOpPreferences struct {
} }
func envOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { func envOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
envName := expressionNode.Operation.CandidateNode.Node.Value envName := expressionNode.Operation.CandidateNode.Value
log.Debug("EnvOperator, env name:", envName) log.Debug("EnvOperator, env name:", envName)
rawValue := os.Getenv(envName) rawValue := os.Getenv(envName)
preferences := expressionNode.Operation.Preferences.(envOpPreferences) preferences := expressionNode.Operation.Preferences.(envOpPreferences)
var node *yaml.Node var node *CandidateNode
if preferences.StringValue { if preferences.StringValue {
node = &yaml.Node{ node = &CandidateNode{
Kind: yaml.ScalarNode, Kind: ScalarNode,
Tag: "!!str", Tag: "!!str",
Value: rawValue, Value: rawValue,
} }
} else if rawValue == "" { } else if rawValue == "" {
return Context{}, fmt.Errorf("Value for env variable '%v' not provided in env()", envName) return Context{}, fmt.Errorf("Value for env variable '%v' not provided in env()", envName)
} else { } else {
var dataBucket yaml.Node decoder := NewYamlDecoder(ConfiguredYamlPreferences)
decoder := yaml.NewDecoder(strings.NewReader(rawValue)) result, err := decoder.Decode()
errorReading := decoder.Decode(&dataBucket)
if errorReading != nil { if err != nil {
return Context{}, errorReading return Context{}, err
} }
//first node is a doc node = result.unwrapDocument()
node = unwrapDoc(&dataBucket)
} }
log.Debug("ENV tag", node.Tag) log.Debug("ENV tag", node.Tag)
log.Debug("ENV value", node.Value) log.Debug("ENV value", node.Value)
log.Debug("ENV Kind", node.Kind) log.Debug("ENV Kind", node.Kind)
target := &CandidateNode{Node: node} return context.SingleChildContext(node), nil
return context.SingleChildContext(target), nil
} }
func envsubstOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { func envsubstOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
@ -71,7 +67,7 @@ func envsubstOperator(d *dataTreeNavigator, context Context, expressionNode *Exp
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode) candidate := el.Value.(*CandidateNode)
node := unwrapDoc(candidate.Node) node := candidate.unwrapDocument()
if node.Tag != "!!str" { if node.Tag != "!!str" {
log.Warning("EnvSubstOperator, env name:", node.Tag, node.Value) log.Warning("EnvSubstOperator, env name:", node.Tag, node.Value)
return Context{}, fmt.Errorf("cannot substitute with %v, can only substitute strings. Hint: Most often you'll want to use '|=' over '=' for this operation", node.Tag) return Context{}, fmt.Errorf("cannot substitute with %v, can only substitute strings. Hint: Most often you'll want to use '|=' over '=' for this operation", node.Tag)
@ -81,8 +77,7 @@ func envsubstOperator(d *dataTreeNavigator, context Context, expressionNode *Exp
if err != nil { if err != nil {
return Context{}, err return Context{}, err
} }
targetNode := &yaml.Node{Kind: yaml.ScalarNode, Value: value, Tag: "!!str"} result := candidate.CreateReplacement(ScalarNode, "!!str", value)
result := candidate.CreateReplacement(targetNode)
results.PushBack(result) results.PushBack(result)
} }

View File

@ -3,8 +3,6 @@ package yqlib
import ( import (
"container/list" "container/list"
"fmt" "fmt"
yaml "gopkg.in/yaml.v3"
) )
func getFilenameOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { func getFilenameOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
@ -14,8 +12,7 @@ func getFilenameOperator(d *dataTreeNavigator, context Context, expressionNode *
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode) candidate := el.Value.(*CandidateNode)
node := &yaml.Node{Kind: yaml.ScalarNode, Value: candidate.Filename, Tag: "!!str"} result := candidate.CreateReplacement(ScalarNode, "!!str", candidate.Filename)
result := candidate.CreateReplacement(node)
results.PushBack(result) results.PushBack(result)
} }
@ -29,8 +26,7 @@ func getFileIndexOperator(d *dataTreeNavigator, context Context, expressionNode
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode) candidate := el.Value.(*CandidateNode)
node := &yaml.Node{Kind: yaml.ScalarNode, Value: fmt.Sprintf("%v", candidate.FileIndex), Tag: "!!int"} result := candidate.CreateReplacement(ScalarNode, "!!int", fmt.Sprintf("%v", candidate.FileIndex))
result := candidate.CreateReplacement(node)
results.PushBack(result) results.PushBack(result)
} }

View File

@ -5,8 +5,6 @@ import (
"container/list" "container/list"
"fmt" "fmt"
"os" "os"
"gopkg.in/yaml.v3"
) )
var LoadYamlPreferences = YamlPreferences{ var LoadYamlPreferences = YamlPreferences{
@ -30,7 +28,7 @@ func loadString(filename string) (*CandidateNode, error) {
return nil, err return nil, err
} }
return &CandidateNode{Node: &yaml.Node{Kind: yaml.ScalarNode, Tag: "!!str", Value: string(filebytes)}}, nil return &CandidateNode{Kind: ScalarNode, Tag: "!!str", Value: string(filebytes)}, nil
} }
func loadYaml(filename string, decoder Decoder) (*CandidateNode, error) { func loadYaml(filename string, decoder Decoder) (*CandidateNode, error) {
@ -51,15 +49,15 @@ func loadYaml(filename string, decoder Decoder) (*CandidateNode, error) {
if documents.Len() == 0 { if documents.Len() == 0 {
// return null candidate // return null candidate
return &CandidateNode{Node: &yaml.Node{Kind: yaml.ScalarNode, Tag: "!!null"}}, nil return &CandidateNode{Kind: ScalarNode, Tag: "!!null"}, nil
} else if documents.Len() == 1 { } else if documents.Len() == 1 {
candidate := documents.Front().Value.(*CandidateNode) candidate := documents.Front().Value.(*CandidateNode)
return candidate, nil return candidate, nil
} else { } else {
sequenceNode := &CandidateNode{Node: &yaml.Node{Kind: yaml.SequenceNode}} sequenceNode := &CandidateNode{Kind: SequenceNode}
for doc := documents.Front(); doc != nil; doc = doc.Next() { for doc := documents.Front(); doc != nil; doc = doc.Next() {
sequenceNode.Node.Content = append(sequenceNode.Node.Content, unwrapDoc(doc.Value.(*CandidateNode).Node)) sequenceNode.Content = append(sequenceNode.Content, doc.Value.(*CandidateNode).unwrapDocument())
} }
return sequenceNode, nil return sequenceNode, nil
} }
@ -87,7 +85,7 @@ func loadYamlOperator(d *dataTreeNavigator, context Context, expressionNode *Exp
} }
nameCandidateNode := rhs.MatchingNodes.Front().Value.(*CandidateNode) nameCandidateNode := rhs.MatchingNodes.Front().Value.(*CandidateNode)
filename := nameCandidateNode.Node.Value filename := nameCandidateNode.Value
var contentsCandidate *CandidateNode var contentsCandidate *CandidateNode

View File

@ -3,21 +3,19 @@ package yqlib
import ( import (
"container/list" "container/list"
"fmt" "fmt"
yaml "gopkg.in/yaml.v3"
) )
func createPathNodeFor(pathElement interface{}) *yaml.Node { func createPathNodeFor(pathElement interface{}) *CandidateNode {
switch pathElement := pathElement.(type) { switch pathElement := pathElement.(type) {
case string: case string:
return &yaml.Node{Kind: yaml.ScalarNode, Value: pathElement, Tag: "!!str"} return &CandidateNode{Kind: ScalarNode, Value: pathElement, Tag: "!!str"}
default: default:
return &yaml.Node{Kind: yaml.ScalarNode, Value: fmt.Sprintf("%v", pathElement), Tag: "!!int"} return &CandidateNode{Kind: ScalarNode, Value: fmt.Sprintf("%v", pathElement), Tag: "!!int"}
} }
} }
func getPathArrayFromNode(funcName string, node *yaml.Node) ([]interface{}, error) { func getPathArrayFromNode(funcName string, node *CandidateNode) ([]interface{}, error) {
if node.Kind != yaml.SequenceNode { if node.Kind != SequenceNode {
return nil, fmt.Errorf("%v: expected path array, but got %v instead", funcName, node.Tag) return nil, fmt.Errorf("%v: expected path array, but got %v instead", funcName, node.Tag)
} }
@ -59,7 +57,7 @@ func setPathOperator(d *dataTreeNavigator, context Context, expressionNode *Expr
} }
lhsValue := lhsPathContext.MatchingNodes.Front().Value.(*CandidateNode) lhsValue := lhsPathContext.MatchingNodes.Front().Value.(*CandidateNode)
lhsPath, err := getPathArrayFromNode("SETPATH", lhsValue.Node) lhsPath, err := getPathArrayFromNode("SETPATH", lhsValue)
if err != nil { if err != nil {
return Context{}, err return Context{}, err
@ -110,7 +108,7 @@ func delPathsOperator(d *dataTreeNavigator, context Context, expressionNode *Exp
if pathArraysContext.MatchingNodes.Len() != 1 { if pathArraysContext.MatchingNodes.Len() != 1 {
return Context{}, fmt.Errorf("DELPATHS: expected single value but found %v", pathArraysContext.MatchingNodes.Len()) return Context{}, fmt.Errorf("DELPATHS: expected single value but found %v", pathArraysContext.MatchingNodes.Len())
} }
pathArraysNode := pathArraysContext.MatchingNodes.Front().Value.(*CandidateNode).Node pathArraysNode := pathArraysContext.MatchingNodes.Front().Value.(*CandidateNode)
if pathArraysNode.Tag != "!!seq" { if pathArraysNode.Tag != "!!seq" {
return Context{}, fmt.Errorf("DELPATHS: expected a sequence of sequences, but found %v", pathArraysNode.Tag) return Context{}, fmt.Errorf("DELPATHS: expected a sequence of sequences, but found %v", pathArraysNode.Tag)
@ -156,16 +154,15 @@ func getPathOperator(d *dataTreeNavigator, context Context, expressionNode *Expr
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode) candidate := el.Value.(*CandidateNode)
node := &yaml.Node{Kind: yaml.SequenceNode, Tag: "!!seq"} node := candidate.CreateReplacement(SequenceNode, "!!seq", "")
content := make([]*yaml.Node, len(candidate.Path)) content := make([]*CandidateNode, len(candidate.Path))
for pathIndex := 0; pathIndex < len(candidate.Path); pathIndex++ { for pathIndex := 0; pathIndex < len(candidate.Path); pathIndex++ {
path := candidate.Path[pathIndex] path := candidate.Path[pathIndex]
content[pathIndex] = createPathNodeFor(path) content[pathIndex] = createPathNodeFor(path)
} }
node.Content = content node.Content = content
result := candidate.CreateReplacement(node) results.PushBack(node)
results.PushBack(result)
} }
return context.ChildContext(results), nil return context.ChildContext(results), nil

View File

@ -3,8 +3,6 @@ package yqlib
import ( import (
"container/list" "container/list"
"fmt" "fmt"
yaml "gopkg.in/yaml.v3"
) )
func reverseOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { func reverseOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
@ -13,19 +11,19 @@ func reverseOperator(d *dataTreeNavigator, context Context, expressionNode *Expr
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode) candidate := el.Value.(*CandidateNode)
candidateNode := unwrapDoc(candidate.Node) candidateNode := candidate.unwrapDocument()
if candidateNode.Kind != yaml.SequenceNode { if candidateNode.Kind != SequenceNode {
return context, fmt.Errorf("node at path [%v] is not an array (it's a %v)", candidate.GetNicePath(), candidate.GetNiceTag()) return context, fmt.Errorf("node at path [%v] is not an array (it's a %v)", candidate.GetNicePath(), candidate.GetNiceTag())
} }
reverseList := &yaml.Node{Kind: yaml.SequenceNode, Tag: "!!seq", Style: candidateNode.Style} reverseList := candidate.CreateReplacementWithDocWrappers(SequenceNode, "!!tag", candidateNode.Style)
reverseList.Content = make([]*yaml.Node, len(candidateNode.Content)) reverseList.Content = make([]*CandidateNode, len(candidateNode.Content))
for i, originalNode := range candidateNode.Content { for i, originalNode := range candidateNode.Content {
reverseList.Content[len(candidateNode.Content)-i-1] = originalNode reverseList.Content[len(candidateNode.Content)-i-1] = originalNode
} }
results.PushBack(candidate.CreateReplacementWithDocWrappers(reverseList)) results.PushBack(reverseList)
} }

View File

@ -23,7 +23,7 @@ func selectOperator(d *dataTreeNavigator, context Context, expressionNode *Expre
for resultEl := rhs.MatchingNodes.Front(); resultEl != nil; resultEl = resultEl.Next() { for resultEl := rhs.MatchingNodes.Front(); resultEl != nil; resultEl = resultEl.Next() {
result := resultEl.Value.(*CandidateNode) result := resultEl.Value.(*CandidateNode)
includeResult, errDecoding = isTruthy(result) includeResult, errDecoding = isTruthyNode(result)
log.Debugf("isTruthy %v", includeResult) log.Debugf("isTruthy %v", includeResult)
if errDecoding != nil { if errDecoding != nil {
return Context{}, errDecoding return Context{}, errDecoding

View File

@ -7,8 +7,6 @@ import (
"strconv" "strconv"
"strings" "strings"
"time" "time"
yaml "gopkg.in/yaml.v3"
) )
func sortOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { func sortOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
@ -26,9 +24,9 @@ func sortByOperator(d *dataTreeNavigator, context Context, expressionNode *Expre
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode) candidate := el.Value.(*CandidateNode)
candidateNode := unwrapDoc(candidate.Node) candidateNode := candidate.unwrapDocument()
if candidateNode.Kind != yaml.SequenceNode { if candidateNode.Kind != SequenceNode {
return context, fmt.Errorf("node at path [%v] is not an array (it's a %v)", candidate.GetNicePath(), candidate.GetNiceTag()) return context, fmt.Errorf("node at path [%v] is not an array (it's a %v)", candidate.GetNicePath(), candidate.GetNiceTag())
} }
@ -36,8 +34,7 @@ func sortByOperator(d *dataTreeNavigator, context Context, expressionNode *Expre
for i, originalNode := range candidateNode.Content { for i, originalNode := range candidateNode.Content {
childCandidate := candidate.CreateChildInArray(i, originalNode) compareContext, err := d.GetMatchingNodes(context.SingleReadonlyChildContext(originalNode), expressionNode.RHS)
compareContext, err := d.GetMatchingNodes(context.SingleReadonlyChildContext(childCandidate), expressionNode.RHS)
if err != nil { if err != nil {
return Context{}, err return Context{}, err
} }
@ -48,19 +45,20 @@ func sortByOperator(d *dataTreeNavigator, context Context, expressionNode *Expre
sort.Stable(sortableArray) sort.Stable(sortableArray)
sortedList := &yaml.Node{Kind: yaml.SequenceNode, Tag: "!!seq", Style: candidateNode.Style} sortedList := candidate.CreateReplacementWithDocWrappers(SequenceNode, "!!seq", candidateNode.Style)
sortedList.Content = make([]*yaml.Node, len(candidateNode.Content))
sortedList.Content = make([]*CandidateNode, len(candidateNode.Content))
for i, sortedNode := range sortableArray { for i, sortedNode := range sortableArray {
sortedList.Content[i] = sortedNode.Node sortedList.Content[i] = sortedNode.Node
} }
results.PushBack(candidate.CreateReplacementWithDocWrappers(sortedList)) results.PushBack(sortedList)
} }
return context.ChildContext(results), nil return context.ChildContext(results), nil
} }
type sortableNode struct { type sortableNode struct {
Node *yaml.Node Node *CandidateNode
CompareContext Context CompareContext Context
dateTimeLayout string dateTimeLayout string
} }
@ -79,7 +77,7 @@ func (a sortableNodeArray) Less(i, j int) bool {
lhs := lhsEl.Value.(*CandidateNode) lhs := lhsEl.Value.(*CandidateNode)
rhs := rhsEl.Value.(*CandidateNode) rhs := rhsEl.Value.(*CandidateNode)
result := a.compare(lhs.Node, rhs.Node, a[i].dateTimeLayout) result := a.compare(lhs, rhs, a[i].dateTimeLayout)
if result < 0 { if result < 0 {
return true return true
@ -92,18 +90,18 @@ func (a sortableNodeArray) Less(i, j int) bool {
return false return false
} }
func (a sortableNodeArray) compare(lhs *yaml.Node, rhs *yaml.Node, dateTimeLayout string) int { func (a sortableNodeArray) compare(lhs *CandidateNode, rhs *CandidateNode, dateTimeLayout string) int {
lhsTag := lhs.Tag lhsTag := lhs.Tag
rhsTag := rhs.Tag rhsTag := rhs.Tag
if !strings.HasPrefix(lhsTag, "!!") { if !strings.HasPrefix(lhsTag, "!!") {
// custom tag - we have to have a guess // custom tag - we have to have a guess
lhsTag = guessTagFromCustomType(lhs) lhsTag = lhs.guessTagFromCustomType()
} }
if !strings.HasPrefix(rhsTag, "!!") { if !strings.HasPrefix(rhsTag, "!!") {
// custom tag - we have to have a guess // custom tag - we have to have a guess
rhsTag = guessTagFromCustomType(rhs) rhsTag = rhs.guessTagFromCustomType()
} }
isDateTime := lhsTag == "!!timestamp" && rhsTag == "!!timestamp" isDateTime := lhsTag == "!!timestamp" && rhsTag == "!!timestamp"

View File

@ -3,23 +3,21 @@ package yqlib
import ( import (
"container/list" "container/list"
"fmt" "fmt"
yaml "gopkg.in/yaml.v3"
) )
func parseStyle(customStyle string) (yaml.Style, error) { func parseStyle(customStyle string) (Style, error) {
if customStyle == "tagged" { if customStyle == "tagged" {
return yaml.TaggedStyle, nil return TaggedStyle, nil
} else if customStyle == "double" { } else if customStyle == "double" {
return yaml.DoubleQuotedStyle, nil return DoubleQuotedStyle, nil
} else if customStyle == "single" { } else if customStyle == "single" {
return yaml.SingleQuotedStyle, nil return SingleQuotedStyle, nil
} else if customStyle == "literal" { } else if customStyle == "literal" {
return yaml.LiteralStyle, nil return LiteralStyle, nil
} else if customStyle == "folded" { } else if customStyle == "folded" {
return yaml.FoldedStyle, nil return FoldedStyle, nil
} else if customStyle == "flow" { } else if customStyle == "flow" {
return yaml.FlowStyle, nil return FlowStyle, nil
} else if customStyle != "" { } else if customStyle != "" {
return 0, fmt.Errorf("Unknown style %v", customStyle) return 0, fmt.Errorf("Unknown style %v", customStyle)
} }
@ -29,7 +27,7 @@ func parseStyle(customStyle string) (yaml.Style, error) {
func assignStyleOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { func assignStyleOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
log.Debugf("AssignStyleOperator: %v") log.Debugf("AssignStyleOperator: %v")
var style yaml.Style var style Style
if !expressionNode.Operation.UpdateAssign { if !expressionNode.Operation.UpdateAssign {
rhs, err := d.GetMatchingNodes(context.ReadOnlyClone(), expressionNode.RHS) rhs, err := d.GetMatchingNodes(context.ReadOnlyClone(), expressionNode.RHS)
if err != nil { if err != nil {
@ -37,7 +35,7 @@ func assignStyleOperator(d *dataTreeNavigator, context Context, expressionNode *
} }
if rhs.MatchingNodes.Front() != nil { if rhs.MatchingNodes.Front() != nil {
style, err = parseStyle(rhs.MatchingNodes.Front().Value.(*CandidateNode).Node.Value) style, err = parseStyle(rhs.MatchingNodes.Front().Value.(*CandidateNode).Value)
if err != nil { if err != nil {
return Context{}, err return Context{}, err
} }
@ -60,14 +58,14 @@ func assignStyleOperator(d *dataTreeNavigator, context Context, expressionNode *
} }
if rhs.MatchingNodes.Front() != nil { if rhs.MatchingNodes.Front() != nil {
style, err = parseStyle(rhs.MatchingNodes.Front().Value.(*CandidateNode).Node.Value) style, err = parseStyle(rhs.MatchingNodes.Front().Value.(*CandidateNode).Value)
if err != nil { if err != nil {
return Context{}, err return Context{}, err
} }
} }
} }
candidate.Node.Style = style candidate.Style = style
} }
return context, nil return context, nil
@ -81,26 +79,25 @@ func getStyleOperator(d *dataTreeNavigator, context Context, expressionNode *Exp
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode) candidate := el.Value.(*CandidateNode)
var style string var style string
switch candidate.Node.Style { switch candidate.Style {
case yaml.TaggedStyle: case TaggedStyle:
style = "tagged" style = "tagged"
case yaml.DoubleQuotedStyle: case DoubleQuotedStyle:
style = "double" style = "double"
case yaml.SingleQuotedStyle: case SingleQuotedStyle:
style = "single" style = "single"
case yaml.LiteralStyle: case LiteralStyle:
style = "literal" style = "literal"
case yaml.FoldedStyle: case FoldedStyle:
style = "folded" style = "folded"
case yaml.FlowStyle: case FlowStyle:
style = "flow" style = "flow"
case 0: case 0:
style = "" style = ""
default: default:
style = "<unknown>" style = "<unknown>"
} }
node := &yaml.Node{Kind: yaml.ScalarNode, Value: style, Tag: "!!str"} result := candidate.CreateReplacement(ScalarNode, "!!str", style)
result := candidate.CreateReplacement(node)
results.PushBack(result) results.PushBack(result)
} }

View File

@ -2,8 +2,6 @@ package yqlib
import ( import (
"container/list" "container/list"
yaml "gopkg.in/yaml.v3"
) )
func assignTagOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { func assignTagOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
@ -18,7 +16,7 @@ func assignTagOperator(d *dataTreeNavigator, context Context, expressionNode *Ex
} }
if rhs.MatchingNodes.Front() != nil { if rhs.MatchingNodes.Front() != nil {
tag = rhs.MatchingNodes.Front().Value.(*CandidateNode).Node.Value tag = rhs.MatchingNodes.Front().Value.(*CandidateNode).Value
} }
} }
@ -38,10 +36,10 @@ func assignTagOperator(d *dataTreeNavigator, context Context, expressionNode *Ex
} }
if rhs.MatchingNodes.Front() != nil { if rhs.MatchingNodes.Front() != nil {
tag = rhs.MatchingNodes.Front().Value.(*CandidateNode).Node.Value tag = rhs.MatchingNodes.Front().Value.(*CandidateNode).Value
} }
} }
unwrapDoc(candidate.Node).Tag = tag candidate.unwrapDocument().Tag = tag
} }
return context, nil return context, nil
@ -54,8 +52,7 @@ func getTagOperator(d *dataTreeNavigator, context Context, expressionNode *Expre
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode) candidate := el.Value.(*CandidateNode)
node := &yaml.Node{Kind: yaml.ScalarNode, Value: unwrapDoc(candidate.Node).Tag, Tag: "!!str"} result := candidate.CreateReplacement(ScalarNode, "!!str", candidate.unwrapDocument().Tag)
result := candidate.CreateReplacement(node)
results.PushBack(result) results.PushBack(result)
} }

View File

@ -49,7 +49,7 @@ func uniqueBy(d *dataTreeNavigator, context Context, expressionNode *ExpressionN
newMatches.Set(keyValue, child) newMatches.Set(keyValue, child)
} }
} }
resultNode := candidate.CreateReplacementWithDocWrappers(SequenceNode, "!!seq", "") resultNode := candidate.CreateReplacementWithDocWrappers(SequenceNode, "!!seq", candidateNode.Style)
for el := newMatches.Front(); el != nil; el = el.Next() { for el := newMatches.Front(); el != nil; el = el.Next() {
resultNode.Content = append(resultNode.Content, el.Value.(*CandidateNode)) resultNode.Content = append(resultNode.Content, el.Value.(*CandidateNode))
} }

View File

@ -7,22 +7,16 @@ func referenceOperator(d *dataTreeNavigator, context Context, expressionNode *Ex
} }
func valueOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { func valueOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
log.Debug("value = %v", expressionNode.Operation.CandidateNode.Node.Value) log.Debug("value = %v", expressionNode.Operation.CandidateNode.Value)
if context.MatchingNodes.Len() == 0 { if context.MatchingNodes.Len() == 0 {
clone, err := expressionNode.Operation.CandidateNode.Copy() clone := expressionNode.Operation.CandidateNode.Copy()
if err != nil {
return Context{}, err
}
return context.SingleChildContext(clone), nil return context.SingleChildContext(clone), nil
} }
var results = list.New() var results = list.New()
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
clone, err := expressionNode.Operation.CandidateNode.Copy() clone := expressionNode.Operation.CandidateNode.Copy()
if err != nil {
return Context{}, err
}
results.PushBack(clone) results.PushBack(clone)
} }