2020-11-22 01:19:57 +00:00
package yqlib
import (
"container/list"
"fmt"
yaml "gopkg.in/yaml.v3"
)
func createPathNodeFor ( pathElement interface { } ) * yaml . Node {
switch pathElement := pathElement . ( type ) {
case string :
return & yaml . Node { Kind : yaml . ScalarNode , Value : pathElement , Tag : "!!str" }
default :
return & yaml . Node { Kind : yaml . ScalarNode , Value : fmt . Sprintf ( "%v" , pathElement ) , Tag : "!!int" }
}
}
2022-10-05 09:09:53 +00:00
func getPathArrayFromNode ( funcName string , node * yaml . Node ) ( [ ] interface { } , error ) {
if node . Kind != yaml . SequenceNode {
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 )
lhsPath , err := getPathArrayFromNode ( "SETPATH" , lhsValue . Node )
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
}
rhsOp := & Operation { OperationType : valueOpType , CandidateNode : targetContextValue . MatchingNodes . Front ( ) . Value . ( * CandidateNode ) }
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 ( ) )
}
pathArraysNode := pathArraysContext . MatchingNodes . Front ( ) . Value . ( * CandidateNode ) . Node
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
}
2021-02-02 07:17:59 +00:00
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 ( )
2021-02-02 07:17:59 +00:00
for el := context . MatchingNodes . Front ( ) ; el != nil ; el = el . Next ( ) {
2020-11-22 01:19:57 +00:00
candidate := el . Value . ( * CandidateNode )
node := & yaml . Node { Kind : yaml . SequenceNode , Tag : "!!seq" }
content := make ( [ ] * yaml . Node , len ( candidate . Path ) )
for pathIndex := 0 ; pathIndex < len ( candidate . Path ) ; pathIndex ++ {
path := candidate . Path [ pathIndex ]
content [ pathIndex ] = createPathNodeFor ( path )
}
node . Content = content
2021-11-23 22:57:35 +00:00
result := candidate . CreateReplacement ( node )
2021-01-12 08:36:28 +00:00
results . PushBack ( result )
2020-11-22 01:19:57 +00:00
}
2021-02-02 07:17:59 +00:00
return context . ChildContext ( results ) , nil
2020-11-22 01:19:57 +00:00
}