yq/pkg/yqlib/operator_path.go

172 lines
5.1 KiB
Go
Raw Normal View History

2020-11-22 01:19:57 +00:00
package yqlib
import (
"container/list"
"fmt"
)
2023-04-09 01:14:51 +00:00
func createPathNodeFor(pathElement interface{}) *CandidateNode {
2020-11-22 01:19:57 +00:00
switch pathElement := pathElement.(type) {
case string:
2023-04-09 01:14:51 +00:00
return &CandidateNode{Kind: ScalarNode, Value: pathElement, Tag: "!!str"}
2020-11-22 01:19:57 +00:00
default:
2023-04-09 01:14:51 +00:00
return &CandidateNode{Kind: ScalarNode, Value: fmt.Sprintf("%v", pathElement), Tag: "!!int"}
2020-11-22 01:19:57 +00:00
}
}
2023-04-09 01:14:51 +00:00
func getPathArrayFromNode(funcName string, node *CandidateNode) ([]interface{}, error) {
if node.Kind != SequenceNode {
2022-10-05 09:09:53 +00:00
return nil, fmt.Errorf("%v: expected path array, but got %v instead", funcName, node.Tag)
2022-10-05 03:12:08 +00:00
}
2022-10-05 09:09:53 +00:00
path := make([]interface{}, len(node.Content))
2022-10-05 03:12:08 +00:00
2022-10-05 09:09:53 +00:00
for i, childNode := range node.Content {
2022-10-05 03:12:08 +00:00
if childNode.Tag == "!!str" {
path[i] = childNode.Value
} else if childNode.Tag == "!!int" {
number, err := parseInt(childNode.Value)
if err != nil {
2022-10-05 09:09:53 +00:00
return nil, fmt.Errorf("%v: could not parse %v as an int: %w", funcName, childNode.Value, err)
2022-10-05 03:12:08 +00:00
}
path[i] = number
} else {
2022-10-05 09:09:53 +00:00
return nil, fmt.Errorf("%v: expected either a !!str or !!int in the path, found %v instead", funcName, childNode.Tag)
2022-10-05 03:12:08 +00:00
}
}
return path, nil
}
// SETPATH(pathArray; value)
func setPathOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
log.Debugf("SetPath")
if expressionNode.RHS.Operation.OperationType != blockOpType {
return Context{}, fmt.Errorf("SETPATH must be given a block (;), got %v instead", expressionNode.RHS.Operation.OperationType.Type)
}
2022-10-05 09:09:53 +00:00
lhsPathContext, err := d.GetMatchingNodes(context.ReadOnlyClone(), expressionNode.RHS.LHS)
if err != nil {
return Context{}, err
}
if lhsPathContext.MatchingNodes.Len() != 1 {
return Context{}, fmt.Errorf("SETPATH: expected single path but found %v results instead", lhsPathContext.MatchingNodes.Len())
}
lhsValue := lhsPathContext.MatchingNodes.Front().Value.(*CandidateNode)
2023-04-09 01:14:51 +00:00
lhsPath, err := getPathArrayFromNode("SETPATH", lhsValue)
2022-10-05 03:12:08 +00:00
if err != nil {
return Context{}, err
}
lhsTraversalTree := createTraversalTree(lhsPath, traversePreferences{}, false)
assignmentOp := &Operation{OperationType: assignOpType}
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode)
targetContextValue, err := d.GetMatchingNodes(context.SingleReadonlyChildContext(candidate), expressionNode.RHS.RHS)
if err != nil {
return Context{}, err
}
if targetContextValue.MatchingNodes.Len() != 1 {
2022-10-05 09:09:53 +00:00
return Context{}, fmt.Errorf("SETPATH: expected single value on RHS but found %v", targetContextValue.MatchingNodes.Len())
2022-10-05 03:12:08 +00:00
}
2023-01-12 04:11:45 +00:00
rhsOp := &Operation{OperationType: referenceOpType, CandidateNode: targetContextValue.MatchingNodes.Front().Value.(*CandidateNode)}
2022-10-05 03:12:08 +00:00
assignmentOpNode := &ExpressionNode{
Operation: assignmentOp,
LHS: lhsTraversalTree,
RHS: &ExpressionNode{Operation: rhsOp},
}
_, err = d.GetMatchingNodes(context.SingleChildContext(candidate), assignmentOpNode)
if err != nil {
return Context{}, err
}
}
return context, nil
}
2022-10-05 09:09:53 +00:00
func delPathsOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
log.Debugf("delPaths")
// single RHS expression that returns an array of paths (array of arrays)
pathArraysContext, err := d.GetMatchingNodes(context.ReadOnlyClone(), expressionNode.RHS)
if err != nil {
return Context{}, err
}
if pathArraysContext.MatchingNodes.Len() != 1 {
return Context{}, fmt.Errorf("DELPATHS: expected single value but found %v", pathArraysContext.MatchingNodes.Len())
}
2023-04-09 01:14:51 +00:00
pathArraysNode := pathArraysContext.MatchingNodes.Front().Value.(*CandidateNode)
2022-10-05 09:09:53 +00:00
if pathArraysNode.Tag != "!!seq" {
return Context{}, fmt.Errorf("DELPATHS: expected a sequence of sequences, but found %v", pathArraysNode.Tag)
}
updatedContext := context
for i, child := range pathArraysNode.Content {
if child.Tag != "!!seq" {
return Context{}, fmt.Errorf("DELPATHS: expected entry [%v] to be a sequence, but its a %v. Note that delpaths takes an array of path arrays, e.g. [[\"a\", \"b\"]]", i, child.Tag)
}
childPath, err := getPathArrayFromNode("DELPATHS", child)
if err != nil {
return Context{}, err
}
childTraversalExp := createTraversalTree(childPath, traversePreferences{}, false)
deleteChildOp := &Operation{OperationType: deleteChildOpType}
deleteChildOpNode := &ExpressionNode{
Operation: deleteChildOp,
RHS: childTraversalExp,
}
updatedContext, err = d.GetMatchingNodes(updatedContext, deleteChildOpNode)
if err != nil {
return Context{}, err
}
}
return updatedContext, nil
}
func getPathOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
2020-11-22 01:19:57 +00:00
log.Debugf("GetPath")
var results = list.New()
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
2020-11-22 01:19:57 +00:00
candidate := el.Value.(*CandidateNode)
2023-04-09 01:14:51 +00:00
node := candidate.CreateReplacement(SequenceNode, "!!seq", "")
2020-11-22 01:19:57 +00:00
2023-04-10 04:13:28 +00:00
path := candidate.GetPath()
content := make([]*CandidateNode, len(path))
for pathIndex := 0; pathIndex < len(path); pathIndex++ {
path := path[pathIndex]
2020-11-22 01:19:57 +00:00
content[pathIndex] = createPathNodeFor(path)
}
node.Content = content
2023-04-09 01:14:51 +00:00
results.PushBack(node)
2020-11-22 01:19:57 +00:00
}
return context.ChildContext(results), nil
2020-11-22 01:19:57 +00:00
}