2021-01-14 03:46:50 +00:00
package yqlib
import (
"container/list"
"fmt"
2021-04-15 00:09:41 +00:00
"regexp"
2021-01-14 03:46:50 +00:00
"strings"
)
2024-03-05 03:10:12 +00:00
var StringInterpolationEnabled = true
2022-02-22 05:17:23 +00:00
type changeCasePrefs struct {
ToUpperCase bool
}
2024-03-05 03:10:12 +00:00
func encodeToYamlString ( node * CandidateNode ) ( string , error ) {
encoderPrefs := encoderPreferences {
format : YamlFormat ,
indent : ConfiguredYamlPreferences . Indent ,
}
result , err := encodeToString ( node , encoderPrefs )
if err != nil {
return "" , err
}
return chomper . ReplaceAllString ( result , "" ) , nil
}
func evaluate ( d * dataTreeNavigator , context Context , expStr string ) ( string , error ) {
exp , err := ExpressionParser . ParseExpression ( expStr )
if err != nil {
return "" , err
}
result , err := d . GetMatchingNodes ( context , exp )
if err != nil {
return "" , err
}
if result . MatchingNodes . Len ( ) == 0 {
return "" , nil
}
node := result . MatchingNodes . Front ( ) . Value . ( * CandidateNode )
if node . Kind != ScalarNode {
return encodeToYamlString ( node )
}
return node . Value , nil
}
func interpolate ( d * dataTreeNavigator , context Context , str string ) ( string , error ) {
var sb strings . Builder
var expSb strings . Builder
inExpression := false
nestedBracketsCounter := 0
runes := [ ] rune ( str )
for i := 0 ; i < len ( runes ) ; i ++ {
char := runes [ i ]
if ! inExpression {
2024-04-14 08:52:08 +00:00
if char == '\\' && i < len ( runes ) - 1 {
switch runes [ i + 1 ] {
case '(' :
inExpression = true
// skip the lparen
i ++
continue
case '\\' :
// skip the escaped backslash
i ++
default :
log . Debugf ( "Ignoring non-escaping backslash @ %v[%d]" , str , i )
}
2024-03-05 03:10:12 +00:00
}
sb . WriteRune ( char )
} else { // we are in an expression
2024-04-14 08:52:08 +00:00
if char == ')' {
if nestedBracketsCounter == 0 {
// finished the expression!
log . Debugf ( "Expression is :%v" , expSb . String ( ) )
value , err := evaluate ( d , context , expSb . String ( ) )
if err != nil {
return "" , err
}
inExpression = false
expSb = strings . Builder { } // reset this
2024-03-05 03:10:12 +00:00
2024-04-14 08:52:08 +00:00
sb . WriteString ( value )
continue
}
nestedBracketsCounter --
2024-03-05 03:10:12 +00:00
} else if char == '(' {
nestedBracketsCounter ++
2024-04-14 08:52:08 +00:00
} else if char == '\\' && i < len ( runes ) - 1 {
switch esc := runes [ i + 1 ] ; esc {
case ')' , '\\' :
// write escaped character
expSb . WriteRune ( esc )
i ++
continue
default :
log . Debugf ( "Ignoring non-escaping backslash @ %v[%d]" , str , i )
}
2024-03-05 03:10:12 +00:00
}
expSb . WriteRune ( char )
}
}
if inExpression {
2024-06-29 05:20:07 +00:00
log . Warning ( "unclosed interpolation string, skipping interpolation" )
return str , nil
2024-03-05 03:10:12 +00:00
}
return sb . String ( ) , nil
}
func stringInterpolationOperator ( d * dataTreeNavigator , context Context , expressionNode * ExpressionNode ) ( Context , error ) {
if ! StringInterpolationEnabled {
return context . SingleChildContext (
createScalarNode ( expressionNode . Operation . StringValue , expressionNode . Operation . StringValue ) ,
) , nil
}
if context . MatchingNodes . Len ( ) == 0 {
value , err := interpolate ( d , context , expressionNode . Operation . StringValue )
if err != nil {
return Context { } , err
}
node := createScalarNode ( value , value )
return context . SingleChildContext ( node ) , nil
}
var results = list . New ( )
for el := context . MatchingNodes . Front ( ) ; el != nil ; el = el . Next ( ) {
candidate := el . Value . ( * CandidateNode )
value , err := interpolate ( d , context . SingleChildContext ( candidate ) , expressionNode . Operation . StringValue )
if err != nil {
return Context { } , err
}
node := createScalarNode ( value , value )
results . PushBack ( node )
}
return context . ChildContext ( results ) , nil
}
2024-01-11 02:17:34 +00:00
func trimSpaceOperator ( _ * dataTreeNavigator , context Context , _ * ExpressionNode ) ( Context , error ) {
2022-08-08 03:35:57 +00:00
results := list . New ( )
for el := context . MatchingNodes . Front ( ) ; el != nil ; el = el . Next ( ) {
2023-10-18 01:11:53 +00:00
node := el . Value . ( * CandidateNode )
2022-08-08 03:35:57 +00:00
2023-10-18 01:11:53 +00:00
if node . guessTagFromCustomType ( ) != "!!str" {
2022-08-08 03:35:57 +00:00
return Context { } , fmt . Errorf ( "cannot trim %v, can only operate on strings. " , node . Tag )
}
2023-10-18 01:11:53 +00:00
newStringNode := node . CreateReplacement ( ScalarNode , node . Tag , strings . TrimSpace ( node . Value ) )
newStringNode . Style = node . Style
results . PushBack ( newStringNode )
2022-08-08 03:35:57 +00:00
}
return context . ChildContext ( results ) , nil
}
2024-03-04 23:40:55 +00:00
func toStringOperator ( _ * dataTreeNavigator , context Context , _ * ExpressionNode ) ( Context , error ) {
results := list . New ( )
for el := context . MatchingNodes . Front ( ) ; el != nil ; el = el . Next ( ) {
node := el . Value . ( * CandidateNode )
var newStringNode * CandidateNode
if node . Tag == "!!str" {
newStringNode = node . CreateReplacement ( ScalarNode , "!!str" , node . Value )
} else if node . Kind == ScalarNode {
newStringNode = node . CreateReplacement ( ScalarNode , "!!str" , node . Value )
newStringNode . Style = DoubleQuotedStyle
} else {
2024-03-05 03:10:12 +00:00
result , err := encodeToYamlString ( node )
2024-03-04 23:40:55 +00:00
if err != nil {
return Context { } , err
}
newStringNode = node . CreateReplacement ( ScalarNode , "!!str" , result )
newStringNode . Style = DoubleQuotedStyle
}
newStringNode . Tag = "!!str"
results . PushBack ( newStringNode )
}
return context . ChildContext ( results ) , nil
}
2024-01-11 02:17:34 +00:00
func changeCaseOperator ( _ * dataTreeNavigator , context Context , expressionNode * ExpressionNode ) ( Context , error ) {
2022-02-22 05:17:23 +00:00
results := list . New ( )
prefs := expressionNode . Operation . Preferences . ( changeCasePrefs )
for el := context . MatchingNodes . Front ( ) ; el != nil ; el = el . Next ( ) {
2023-10-18 01:11:53 +00:00
node := el . Value . ( * CandidateNode )
2022-02-22 05:17:23 +00:00
2023-10-18 01:11:53 +00:00
if node . guessTagFromCustomType ( ) != "!!str" {
2022-02-22 05:17:23 +00:00
return Context { } , fmt . Errorf ( "cannot change case with %v, can only operate on strings. " , node . Tag )
}
2023-10-18 01:11:53 +00:00
value := ""
2022-02-22 05:17:23 +00:00
if prefs . ToUpperCase {
2023-10-18 01:11:53 +00:00
value = strings . ToUpper ( node . Value )
2022-02-22 05:17:23 +00:00
} else {
2023-10-18 01:11:53 +00:00
value = strings . ToLower ( node . Value )
2022-02-22 05:17:23 +00:00
}
2023-10-18 01:11:53 +00:00
newStringNode := node . CreateReplacement ( ScalarNode , node . Tag , value )
newStringNode . Style = node . Style
results . PushBack ( newStringNode )
2022-02-22 05:17:23 +00:00
}
return context . ChildContext ( results ) , nil
}
2021-04-15 00:09:41 +00:00
func getSubstituteParameters ( d * dataTreeNavigator , block * ExpressionNode , context Context ) ( string , string , error ) {
regEx := ""
replacementText := ""
2022-02-07 00:55:55 +00:00
regExNodes , err := d . GetMatchingNodes ( context . ReadOnlyClone ( ) , block . LHS )
2021-04-15 00:09:41 +00:00
if err != nil {
return "" , "" , err
}
if regExNodes . MatchingNodes . Front ( ) != nil {
2023-10-18 01:11:53 +00:00
regEx = regExNodes . MatchingNodes . Front ( ) . Value . ( * CandidateNode ) . Value
2021-04-15 00:09:41 +00:00
}
log . Debug ( "regEx %v" , regEx )
2022-02-07 00:55:55 +00:00
replacementNodes , err := d . GetMatchingNodes ( context , block . RHS )
2021-04-15 00:09:41 +00:00
if err != nil {
return "" , "" , err
}
if replacementNodes . MatchingNodes . Front ( ) != nil {
2023-10-18 01:11:53 +00:00
replacementText = replacementNodes . MatchingNodes . Front ( ) . Value . ( * CandidateNode ) . Value
2021-04-15 00:09:41 +00:00
}
return regEx , replacementText , nil
}
2023-10-18 01:11:53 +00:00
func substitute ( original string , regex * regexp . Regexp , replacement string ) ( Kind , string , string ) {
2021-04-15 00:09:41 +00:00
replacedString := regex . ReplaceAllString ( original , replacement )
2023-10-18 01:11:53 +00:00
return ScalarNode , "!!str" , replacedString
2021-04-15 00:09:41 +00:00
}
func substituteStringOperator ( d * dataTreeNavigator , context Context , expressionNode * ExpressionNode ) ( Context , error ) {
//rhs block operator
//lhs of block = regex
//rhs of block = replacement expression
2022-02-07 00:55:55 +00:00
block := expressionNode . RHS
2021-04-15 00:09:41 +00:00
regExStr , replacementText , err := getSubstituteParameters ( d , block , context )
if err != nil {
return Context { } , err
}
regEx , err := regexp . Compile ( regExStr )
if err != nil {
return Context { } , err
}
var results = list . New ( )
for el := context . MatchingNodes . Front ( ) ; el != nil ; el = el . Next ( ) {
2023-10-18 01:11:53 +00:00
node := el . Value . ( * CandidateNode )
if node . guessTagFromCustomType ( ) != "!!str" {
2021-12-20 22:30:08 +00:00
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 )
2021-04-15 00:09:41 +00:00
}
2023-10-18 01:11:53 +00:00
result := node . CreateReplacement ( substitute ( node . Value , regEx , replacementText ) )
2021-04-15 00:09:41 +00:00
results . PushBack ( result )
}
return context . ChildContext ( results ) , nil
}
2023-10-18 01:11:53 +00:00
func addMatch ( original [ ] * CandidateNode , match string , offset int , name string ) [ ] * CandidateNode {
2021-07-11 01:08:18 +00:00
2021-07-07 12:47:16 +00:00
newContent := append ( original ,
2021-07-11 01:08:18 +00:00
createScalarNode ( "string" , "string" ) )
if offset < 0 {
// offset of -1 means there was no match, force a null value like jq
newContent = append ( newContent ,
createScalarNode ( nil , "null" ) ,
)
} else {
newContent = append ( newContent ,
createScalarNode ( match , match ) ,
)
}
newContent = append ( newContent ,
2021-07-07 12:40:46 +00:00
createScalarNode ( "offset" , "offset" ) ,
createScalarNode ( offset , fmt . Sprintf ( "%v" , offset ) ) ,
createScalarNode ( "length" , "length" ) ,
createScalarNode ( len ( match ) , fmt . Sprintf ( "%v" , len ( match ) ) ) )
2021-07-07 12:47:16 +00:00
if name != "" {
newContent = append ( newContent ,
createScalarNode ( "name" , "name" ) ,
createScalarNode ( name , name ) ,
)
}
return newContent
2021-07-07 12:40:46 +00:00
}
2021-07-09 05:33:41 +00:00
type matchPreferences struct {
Global bool
}
2021-07-11 01:08:18 +00:00
func getMatches ( matchPrefs matchPreferences , regEx * regexp . Regexp , value string ) ( [ ] [ ] string , [ ] [ ] int ) {
2021-07-09 05:33:41 +00:00
var allMatches [ ] [ ] string
var allIndices [ ] [ ] int
if matchPrefs . Global {
allMatches = regEx . FindAllStringSubmatch ( value , - 1 )
allIndices = regEx . FindAllStringSubmatchIndex ( value , - 1 )
} else {
allMatches = [ ] [ ] string { regEx . FindStringSubmatch ( value ) }
allIndices = [ ] [ ] int { regEx . FindStringSubmatchIndex ( value ) }
}
2021-07-07 12:40:46 +00:00
2021-07-09 05:54:56 +00:00
log . Debug ( "allMatches, %v" , allMatches )
2021-07-11 01:08:18 +00:00
return allMatches , allIndices
}
func match ( matchPrefs matchPreferences , regEx * regexp . Regexp , candidate * CandidateNode , value string , results * list . List ) {
subNames := regEx . SubexpNames ( )
allMatches , allIndices := getMatches ( matchPrefs , regEx , value )
2021-07-09 05:54:56 +00:00
// if all matches just has an empty array in it,
// then nothing matched
if len ( allMatches ) > 0 && len ( allMatches [ 0 ] ) == 0 {
return
}
2021-07-07 12:40:46 +00:00
for i , matches := range allMatches {
2023-10-18 01:11:53 +00:00
capturesListNode := & CandidateNode { Kind : SequenceNode }
2021-07-07 12:40:46 +00:00
match , submatches := matches [ 0 ] , matches [ 1 : ]
for j , submatch := range submatches {
2023-10-18 01:11:53 +00:00
captureNode := & CandidateNode { Kind : MappingNode }
captureNode . AddChildren ( addMatch ( captureNode . Content , submatch , allIndices [ i ] [ 2 + j * 2 ] , subNames [ j + 1 ] ) )
capturesListNode . AddChild ( captureNode )
2021-07-07 12:40:46 +00:00
}
2023-10-18 01:11:53 +00:00
node := candidate . CreateReplacement ( MappingNode , "!!map" , "" )
node . AddChildren ( addMatch ( node . Content , match , allIndices [ i ] [ 0 ] , "" ) )
node . AddKeyValueChild ( createScalarNode ( "captures" , "captures" ) , capturesListNode )
results . PushBack ( node )
2021-07-07 12:40:46 +00:00
}
}
2021-07-11 01:08:18 +00:00
func capture ( matchPrefs matchPreferences , regEx * regexp . Regexp , candidate * CandidateNode , value string , results * list . List ) {
subNames := regEx . SubexpNames ( )
allMatches , allIndices := getMatches ( matchPrefs , regEx , value )
// if all matches just has an empty array in it,
// then nothing matched
if len ( allMatches ) > 0 && len ( allMatches [ 0 ] ) == 0 {
return
}
for i , matches := range allMatches {
2023-10-18 01:11:53 +00:00
capturesNode := candidate . CreateReplacement ( MappingNode , "!!map" , "" )
2021-07-11 01:08:18 +00:00
_ , submatches := matches [ 0 ] , matches [ 1 : ]
for j , submatch := range submatches {
2023-10-18 01:11:53 +00:00
keyNode := createScalarNode ( subNames [ j + 1 ] , subNames [ j + 1 ] )
var valueNode * CandidateNode
2021-07-11 01:08:18 +00:00
offset := allIndices [ i ] [ 2 + j * 2 ]
// offset of -1 means there was no match, force a null value like jq
if offset < 0 {
2023-10-18 01:11:53 +00:00
valueNode = createScalarNode ( nil , "null" )
2021-07-11 01:08:18 +00:00
} else {
2023-10-18 01:11:53 +00:00
valueNode = createScalarNode ( submatch , submatch )
2021-07-11 01:08:18 +00:00
}
2023-10-18 01:11:53 +00:00
capturesNode . AddKeyValueChild ( keyNode , valueNode )
2021-07-11 01:08:18 +00:00
}
2023-10-18 01:11:53 +00:00
results . PushBack ( capturesNode )
2021-07-11 01:08:18 +00:00
}
}
2021-07-09 05:54:56 +00:00
func extractMatchArguments ( d * dataTreeNavigator , context Context , expressionNode * ExpressionNode ) ( * regexp . Regexp , matchPreferences , error ) {
2022-02-07 00:55:55 +00:00
regExExpNode := expressionNode . RHS
2021-07-09 05:33:41 +00:00
matchPrefs := matchPreferences { }
2021-07-07 12:40:46 +00:00
2021-07-09 05:33:41 +00:00
// we got given parameters e.g. match(exp; params)
2022-02-07 00:55:55 +00:00
if expressionNode . RHS . Operation . OperationType == blockOpType {
block := expressionNode . RHS
regExExpNode = block . LHS
replacementNodes , err := d . GetMatchingNodes ( context , block . RHS )
2021-07-09 05:33:41 +00:00
if err != nil {
2021-07-09 05:54:56 +00:00
return nil , matchPrefs , err
2021-07-09 05:33:41 +00:00
}
paramText := ""
if replacementNodes . MatchingNodes . Front ( ) != nil {
2023-10-18 01:11:53 +00:00
paramText = replacementNodes . MatchingNodes . Front ( ) . Value . ( * CandidateNode ) . Value
2021-07-09 05:33:41 +00:00
}
if strings . Contains ( paramText , "g" ) {
paramText = strings . ReplaceAll ( paramText , "g" , "" )
matchPrefs . Global = true
}
if strings . Contains ( paramText , "i" ) {
2021-07-09 05:54:56 +00:00
return nil , matchPrefs , fmt . Errorf ( ` 'i' is not a valid option for match. To ignore case, use an expression like match("(?i)cat") ` )
2021-07-09 05:33:41 +00:00
}
if len ( paramText ) > 0 {
2021-07-09 05:54:56 +00:00
return nil , matchPrefs , fmt . Errorf ( ` Unrecognised match params '%v', please see docs at https://mikefarah.gitbook.io/yq/operators/string-operators ` , paramText )
2021-07-09 05:33:41 +00:00
}
}
regExNodes , err := d . GetMatchingNodes ( context . ReadOnlyClone ( ) , regExExpNode )
2021-07-07 12:40:46 +00:00
if err != nil {
2021-07-09 05:54:56 +00:00
return nil , matchPrefs , err
2021-07-07 12:40:46 +00:00
}
log . Debug ( NodesToString ( regExNodes . MatchingNodes ) )
regExStr := ""
if regExNodes . MatchingNodes . Front ( ) != nil {
2023-10-18 01:11:53 +00:00
regExStr = regExNodes . MatchingNodes . Front ( ) . Value . ( * CandidateNode ) . Value
2021-07-07 12:40:46 +00:00
}
log . Debug ( "regEx %v" , regExStr )
2021-07-09 05:54:56 +00:00
regEx , err := regexp . Compile ( regExStr )
return regEx , matchPrefs , err
2021-07-09 05:33:41 +00:00
}
func matchOperator ( d * dataTreeNavigator , context Context , expressionNode * ExpressionNode ) ( Context , error ) {
2021-07-09 05:54:56 +00:00
regEx , matchPrefs , err := extractMatchArguments ( d , context , expressionNode )
2021-07-09 05:33:41 +00:00
if err != nil {
return Context { } , err
}
2021-07-07 12:40:46 +00:00
2021-07-09 05:54:56 +00:00
var results = list . New ( )
for el := context . MatchingNodes . Front ( ) ; el != nil ; el = el . Next ( ) {
2023-10-18 01:11:53 +00:00
node := el . Value . ( * CandidateNode )
if node . guessTagFromCustomType ( ) != "!!str" {
2021-07-09 05:54:56 +00:00
return Context { } , fmt . Errorf ( "cannot match with %v, can only match strings. Hint: Most often you'll want to use '|=' over '=' for this operation" , node . Tag )
}
2023-10-18 01:11:53 +00:00
match ( matchPrefs , regEx , node , node . Value , results )
2021-07-09 05:54:56 +00:00
}
return context . ChildContext ( results ) , nil
}
2021-07-11 01:08:18 +00:00
func captureOperator ( d * dataTreeNavigator , context Context , expressionNode * ExpressionNode ) ( Context , error ) {
regEx , matchPrefs , err := extractMatchArguments ( d , context , expressionNode )
if err != nil {
return Context { } , err
}
var results = list . New ( )
for el := context . MatchingNodes . Front ( ) ; el != nil ; el = el . Next ( ) {
2023-10-18 01:11:53 +00:00
node := el . Value . ( * CandidateNode )
if node . guessTagFromCustomType ( ) != "!!str" {
2021-07-11 01:08:18 +00:00
return Context { } , fmt . Errorf ( "cannot match with %v, can only match strings. Hint: Most often you'll want to use '|=' over '=' for this operation" , node . Tag )
}
2023-10-18 01:11:53 +00:00
capture ( matchPrefs , regEx , node , node . Value , results )
2021-07-11 01:08:18 +00:00
}
return context . ChildContext ( results ) , nil
}
2021-07-09 05:54:56 +00:00
func testOperator ( d * dataTreeNavigator , context Context , expressionNode * ExpressionNode ) ( Context , error ) {
regEx , _ , err := extractMatchArguments ( d , context , expressionNode )
2021-07-07 12:40:46 +00:00
if err != nil {
return Context { } , err
}
var results = list . New ( )
for el := context . MatchingNodes . Front ( ) ; el != nil ; el = el . Next ( ) {
2023-10-18 01:11:53 +00:00
node := el . Value . ( * CandidateNode )
if node . guessTagFromCustomType ( ) != "!!str" {
2021-07-09 05:33:41 +00:00
return Context { } , fmt . Errorf ( "cannot match with %v, can only match strings. Hint: Most often you'll want to use '|=' over '=' for this operation" , node . Tag )
2021-07-07 12:40:46 +00:00
}
2021-07-09 05:54:56 +00:00
matches := regEx . FindStringSubmatch ( node . Value )
2023-10-18 01:11:53 +00:00
results . PushBack ( createBooleanCandidate ( node , len ( matches ) > 0 ) )
2021-07-07 12:40:46 +00:00
}
return context . ChildContext ( results ) , nil
}
2021-02-02 07:17:59 +00:00
func joinStringOperator ( d * dataTreeNavigator , context Context , expressionNode * ExpressionNode ) ( Context , error ) {
2024-02-15 22:41:33 +00:00
log . Debugf ( "joinStringOperator" )
2021-01-14 03:46:50 +00:00
joinStr := ""
2022-02-07 00:55:55 +00:00
rhs , err := d . GetMatchingNodes ( context . ReadOnlyClone ( ) , expressionNode . RHS )
2021-01-14 03:46:50 +00:00
if err != nil {
2021-02-02 07:17:59 +00:00
return Context { } , err
2021-01-14 03:46:50 +00:00
}
2021-02-02 07:17:59 +00:00
if rhs . MatchingNodes . Front ( ) != nil {
2023-10-18 01:11:53 +00:00
joinStr = rhs . MatchingNodes . Front ( ) . Value . ( * CandidateNode ) . Value
2021-01-14 03:46:50 +00:00
}
var results = list . New ( )
2021-02-02 07:17:59 +00:00
for el := context . MatchingNodes . Front ( ) ; el != nil ; el = el . Next ( ) {
2023-10-18 01:11:53 +00:00
node := el . Value . ( * CandidateNode )
if node . Kind != SequenceNode {
2021-04-15 00:09:41 +00:00
return Context { } , fmt . Errorf ( "cannot join with %v, can only join arrays of scalars" , node . Tag )
2021-01-14 03:46:50 +00:00
}
2023-10-18 01:11:53 +00:00
result := node . CreateReplacement ( join ( node . Content , joinStr ) )
2021-01-14 03:46:50 +00:00
results . PushBack ( result )
}
2021-02-02 07:17:59 +00:00
return context . ChildContext ( results ) , nil
2021-01-14 03:46:50 +00:00
}
2023-10-18 01:11:53 +00:00
func join ( content [ ] * CandidateNode , joinStr string ) ( Kind , string , string ) {
2021-01-14 03:46:50 +00:00
var stringsToJoin [ ] string
for _ , node := range content {
str := node . Value
if node . Tag == "!!null" {
str = ""
}
stringsToJoin = append ( stringsToJoin , str )
}
2023-10-18 01:11:53 +00:00
return ScalarNode , "!!str" , strings . Join ( stringsToJoin , joinStr )
2021-01-14 03:46:50 +00:00
}
2021-01-14 04:05:50 +00:00
2021-02-02 07:17:59 +00:00
func splitStringOperator ( d * dataTreeNavigator , context Context , expressionNode * ExpressionNode ) ( Context , error ) {
2024-02-15 22:41:33 +00:00
log . Debugf ( "splitStringOperator" )
2021-01-14 04:05:50 +00:00
splitStr := ""
2022-02-07 00:55:55 +00:00
rhs , err := d . GetMatchingNodes ( context . ReadOnlyClone ( ) , expressionNode . RHS )
2021-01-14 04:05:50 +00:00
if err != nil {
2021-02-02 07:17:59 +00:00
return Context { } , err
2021-01-14 04:05:50 +00:00
}
2021-02-02 07:17:59 +00:00
if rhs . MatchingNodes . Front ( ) != nil {
2023-10-18 01:11:53 +00:00
splitStr = rhs . MatchingNodes . Front ( ) . Value . ( * CandidateNode ) . Value
2021-01-14 04:05:50 +00:00
}
var results = list . New ( )
2021-02-02 07:17:59 +00:00
for el := context . MatchingNodes . Front ( ) ; el != nil ; el = el . Next ( ) {
2023-10-18 01:11:53 +00:00
node := el . Value . ( * CandidateNode )
2021-01-14 04:05:50 +00:00
if node . Tag == "!!null" {
continue
}
2022-02-22 03:50:45 +00:00
2023-10-18 01:11:53 +00:00
if node . guessTagFromCustomType ( ) != "!!str" {
return Context { } , fmt . Errorf ( "cannot split %v, can only split strings" , node . Tag )
2021-01-14 04:05:50 +00:00
}
2023-10-18 01:11:53 +00:00
kind , tag , content := split ( node . Value , splitStr )
result := node . CreateReplacement ( kind , tag , "" )
result . AddChildren ( content )
2021-01-14 04:05:50 +00:00
results . PushBack ( result )
}
2021-02-02 07:17:59 +00:00
return context . ChildContext ( results ) , nil
2021-01-14 04:05:50 +00:00
}
2023-10-18 01:11:53 +00:00
func split ( value string , spltStr string ) ( Kind , string , [ ] * CandidateNode ) {
var contents [ ] * CandidateNode
2021-01-14 04:05:50 +00:00
if value != "" {
2022-11-14 05:40:41 +00:00
log . Debug ( "going to spltStr[%v]" , spltStr )
2021-01-14 04:05:50 +00:00
var newStrings = strings . Split ( value , spltStr )
2023-10-18 01:11:53 +00:00
contents = make ( [ ] * CandidateNode , len ( newStrings ) )
2021-01-14 04:05:50 +00:00
for index , str := range newStrings {
2023-10-18 01:11:53 +00:00
contents [ index ] = & CandidateNode { Kind : ScalarNode , Tag : "!!str" , Value : str }
2021-01-14 04:05:50 +00:00
}
}
2023-10-18 01:11:53 +00:00
return SequenceNode , "!!seq" , contents
2021-01-14 04:05:50 +00:00
}