yq/pkg/yqlib/operator_multiply.go

205 lines
7.3 KiB
Go
Raw Normal View History

2020-11-03 23:48:43 +00:00
package yqlib
2020-10-19 05:14:29 +00:00
import (
2021-11-26 09:24:21 +00:00
"container/list"
2020-10-19 05:14:29 +00:00
"fmt"
2021-01-18 02:28:40 +00:00
"strconv"
2022-01-22 02:47:22 +00:00
"strings"
2020-10-19 05:14:29 +00:00
2021-10-11 03:46:46 +00:00
"github.com/jinzhu/copier"
2020-11-20 11:57:32 +00:00
yaml "gopkg.in/yaml.v3"
2020-10-19 05:14:29 +00:00
)
type multiplyPreferences struct {
AppendArrays bool
DeepMergeArrays bool
TraversePrefs traversePreferences
AssignPrefs assignPreferences
2020-11-27 23:41:09 +00:00
}
2022-01-22 05:40:17 +00:00
func createMultiplyOp(prefs interface{}) func(lhs *ExpressionNode, rhs *ExpressionNode) *ExpressionNode {
return func(lhs *ExpressionNode, rhs *ExpressionNode) *ExpressionNode {
return &ExpressionNode{Operation: &Operation{OperationType: multiplyOpType, Preferences: prefs},
LHS: lhs,
RHS: rhs}
2022-01-22 05:40:17 +00:00
}
}
func multiplyAssignOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
var multiplyPrefs = expressionNode.Operation.Preferences
expressionNode.Operation.Preferences = nil
return compoundAssignFunction(d, context, expressionNode, createMultiplyOp(multiplyPrefs))
}
func multiplyOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
2020-10-20 05:27:30 +00:00
log.Debugf("-- MultiplyOperator")
return crossFunction(d, context, expressionNode, multiply(expressionNode.Operation.Preferences.(multiplyPreferences)), false)
2020-10-19 05:14:29 +00:00
}
2021-11-13 23:51:18 +00:00
func getComments(lhs *CandidateNode, rhs *CandidateNode) (leadingContent string, headComment string, footComment string) {
leadingContent = rhs.LeadingContent
headComment = rhs.Node.HeadComment
footComment = rhs.Node.FootComment
if lhs.Node.HeadComment != "" || lhs.LeadingContent != "" {
headComment = lhs.Node.HeadComment
leadingContent = lhs.LeadingContent
}
2021-11-13 23:51:18 +00:00
if lhs.Node.FootComment != "" {
footComment = lhs.Node.FootComment
}
2021-11-13 23:51:18 +00:00
return leadingContent, headComment, footComment
}
func multiply(preferences multiplyPreferences) func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
return func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
// need to do this before unWrapping the potential document node
2021-11-13 23:51:18 +00:00
leadingContent, headComment, footComment := getComments(lhs, rhs)
2021-01-12 23:00:51 +00:00
lhs.Node = unwrapDoc(lhs.Node)
rhs.Node = unwrapDoc(rhs.Node)
2021-11-25 09:24:51 +00:00
log.Debugf("Multiplying LHS: %v", lhs.Node.Tag)
2020-11-27 23:41:09 +00:00
log.Debugf("- RHS: %v", rhs.Node.Tag)
2020-10-27 05:45:16 +00:00
2020-11-27 23:41:09 +00:00
if lhs.Node.Kind == yaml.MappingNode && rhs.Node.Kind == yaml.MappingNode ||
(lhs.Node.Kind == yaml.SequenceNode && rhs.Node.Kind == yaml.SequenceNode) {
2021-10-11 03:46:46 +00:00
var newBlank = CandidateNode{}
err := copier.CopyWithOption(&newBlank, lhs, copier.Option{IgnoreEmpty: true, DeepCopy: true})
2020-11-27 23:41:09 +00:00
if err != nil {
return nil, err
}
2021-11-13 23:51:18 +00:00
newBlank.LeadingContent = leadingContent
newBlank.Node.HeadComment = headComment
newBlank.Node.FootComment = footComment
2021-10-11 03:46:46 +00:00
return mergeObjects(d, context.WritableClone(), &newBlank, rhs, preferences)
2020-11-27 23:41:09 +00:00
}
2022-01-22 02:47:22 +00:00
return multiplyScalars(lhs, rhs)
2020-10-19 05:14:29 +00:00
}
}
2022-01-22 02:47:22 +00:00
func multiplyScalars(lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
lhsTag := lhs.Node.Tag
2022-02-22 03:50:45 +00:00
rhsTag := guessTagFromCustomType(rhs.Node)
2022-01-22 02:47:22 +00:00
lhsIsCustom := false
if !strings.HasPrefix(lhsTag, "!!") {
// custom tag - we have to have a guess
lhsTag = guessTagFromCustomType(lhs.Node)
lhsIsCustom = true
}
if lhsTag == "!!int" && rhsTag == "!!int" {
return multiplyIntegers(lhs, rhs)
} else if (lhsTag == "!!int" || lhsTag == "!!float") && (rhsTag == "!!int" || rhsTag == "!!float") {
return multiplyFloats(lhs, rhs, lhsIsCustom)
}
return nil, fmt.Errorf("Cannot multiply %v with %v", lhs.Node.Tag, rhs.Node.Tag)
}
func multiplyFloats(lhs *CandidateNode, rhs *CandidateNode, lhsIsCustom bool) (*CandidateNode, error) {
2021-11-23 22:57:35 +00:00
target := lhs.CreateReplacement(&yaml.Node{})
target.Node.Kind = yaml.ScalarNode
target.Node.Style = lhs.Node.Style
2022-01-22 02:47:22 +00:00
if lhsIsCustom {
target.Node.Tag = lhs.Node.Tag
} else {
target.Node.Tag = "!!float"
}
lhsNum, err := strconv.ParseFloat(lhs.Node.Value, 64)
if err != nil {
return nil, err
}
rhsNum, err := strconv.ParseFloat(rhs.Node.Value, 64)
if err != nil {
return nil, err
}
target.Node.Value = fmt.Sprintf("%v", lhsNum*rhsNum)
return target, nil
}
2021-01-18 02:28:40 +00:00
func multiplyIntegers(lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
2021-11-23 22:57:35 +00:00
target := lhs.CreateReplacement(&yaml.Node{})
2021-01-18 02:28:40 +00:00
target.Node.Kind = yaml.ScalarNode
target.Node.Style = lhs.Node.Style
2022-01-22 02:47:22 +00:00
target.Node.Tag = lhs.Node.Tag
2021-01-18 02:28:40 +00:00
2021-09-02 05:26:44 +00:00
format, lhsNum, err := parseInt(lhs.Node.Value)
2021-01-18 02:28:40 +00:00
if err != nil {
return nil, err
}
2021-09-02 05:26:44 +00:00
_, rhsNum, err := parseInt(rhs.Node.Value)
2021-01-18 02:28:40 +00:00
if err != nil {
return nil, err
}
2021-09-02 05:26:44 +00:00
target.Node.Value = fmt.Sprintf(format, lhsNum*rhsNum)
2021-01-18 02:28:40 +00:00
return target, nil
}
func mergeObjects(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode, preferences multiplyPreferences) (*CandidateNode, error) {
2020-10-28 02:00:26 +00:00
var results = list.New()
2020-11-27 23:41:09 +00:00
// only need to recurse the array if we are doing a deep merge
prefs := recursiveDescentPreferences{RecurseArray: preferences.DeepMergeArrays,
2021-02-08 02:58:46 +00:00
TraversePreferences: traversePreferences{DontFollowAlias: true, IncludeMapKeys: true}}
log.Debugf("merge - preferences.DeepMergeArrays %v", preferences.DeepMergeArrays)
log.Debugf("merge - preferences.AppendArrays %v", preferences.AppendArrays)
err := recursiveDecent(results, context.SingleChildContext(rhs), prefs)
2020-11-13 03:07:11 +00:00
if err != nil {
return nil, err
}
2020-10-28 02:00:26 +00:00
var pathIndexToStartFrom int
2020-10-28 02:00:26 +00:00
if results.Front() != nil {
pathIndexToStartFrom = len(results.Front().Value.(*CandidateNode).Path)
}
for el := results.Front(); el != nil; el = el.Next() {
2021-02-08 02:58:46 +00:00
candidate := el.Value.(*CandidateNode)
if candidate.Node.Tag == "!!merge" {
continue
}
err := applyAssignment(d, context, pathIndexToStartFrom, lhs, candidate, preferences)
2020-10-28 02:00:26 +00:00
if err != nil {
return nil, err
}
}
return lhs, nil
}
func applyAssignment(d *dataTreeNavigator, context Context, pathIndexToStartFrom int, lhs *CandidateNode, rhs *CandidateNode, preferences multiplyPreferences) error {
2021-01-13 05:54:28 +00:00
shouldAppendArrays := preferences.AppendArrays
2021-02-08 02:58:46 +00:00
log.Debugf("merge - applyAssignment lhs %v, rhs: %v", lhs.GetKey(), rhs.GetKey())
2020-10-19 05:14:29 +00:00
lhsPath := rhs.Path[pathIndexToStartFrom:]
log.Debugf("merge - lhsPath %v", lhsPath)
2020-10-19 05:14:29 +00:00
assignmentOp := &Operation{OperationType: assignAttributesOpType, Preferences: preferences.AssignPrefs}
if shouldAppendArrays && rhs.Node.Kind == yaml.SequenceNode {
assignmentOp.OperationType = addAssignOpType
log.Debugf("merge - assignmentOp.OperationType = addAssignOpType")
} else if !preferences.DeepMergeArrays && rhs.Node.Kind == yaml.SequenceNode ||
(rhs.Node.Kind == yaml.ScalarNode || rhs.Node.Kind == yaml.AliasNode) {
assignmentOp.OperationType = assignOpType
2021-01-06 09:22:50 +00:00
assignmentOp.UpdateAssign = false
log.Debugf("merge - rhs.Node.Kind == yaml.SequenceNode: %v", rhs.Node.Kind == yaml.SequenceNode)
log.Debugf("merge - rhs.Node.Kind == yaml.ScalarNode: %v", rhs.Node.Kind == yaml.ScalarNode)
log.Debugf("merge - rhs.Node.Kind == yaml.AliasNode: %v", rhs.Node.Kind == yaml.AliasNode)
log.Debugf("merge - assignmentOp.OperationType = assignOpType, no updateassign")
} else {
log.Debugf("merge - assignmentOp := &Operation{OperationType: assignAttributesOpType}")
2020-10-19 05:14:29 +00:00
}
rhsOp := &Operation{OperationType: valueOpType, CandidateNode: rhs}
2020-10-19 05:14:29 +00:00
assignmentOpNode := &ExpressionNode{
Operation: assignmentOp,
LHS: createTraversalTree(lhsPath, preferences.TraversePrefs, rhs.IsMapKey),
RHS: &ExpressionNode{Operation: rhsOp},
}
2020-10-19 05:14:29 +00:00
_, err := d.GetMatchingNodes(context.SingleChildContext(lhs), assignmentOpNode)
2020-10-19 05:14:29 +00:00
return err
}