yq/pkg/yqlib/operator_contains.go

112 lines
2.8 KiB
Go

package yqlib
import (
"fmt"
"strings"
yaml "gopkg.in/yaml.v3"
)
func containsOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
return crossFunction(d, context.ReadOnlyClone(), expressionNode, containsWithNodes, false)
}
func containsArrayElement(array *yaml.Node, item *yaml.Node) (bool, error) {
for index := 0; index < len(array.Content); index = index + 1 {
containedInArray, err := contains(array.Content[index], item)
if err != nil {
return false, err
}
if containedInArray {
return true, nil
}
}
return false, nil
}
func containsArray(lhs *yaml.Node, rhs *yaml.Node) (bool, error) {
if rhs.Kind != yaml.SequenceNode {
return containsArrayElement(lhs, rhs)
}
for index := 0; index < len(rhs.Content); index = index + 1 {
itemInArray, err := containsArrayElement(lhs, rhs.Content[index])
if err != nil {
return false, err
}
if !itemInArray {
return false, nil
}
}
return true, nil
}
func containsObject(lhs *yaml.Node, rhs *yaml.Node) (bool, error) {
if rhs.Kind != yaml.MappingNode {
return false, nil
}
for index := 0; index < len(rhs.Content); index = index + 2 {
rhsKey := rhs.Content[index]
rhsValue := rhs.Content[index+1]
log.Debugf("Looking for %v in the lhs", rhsKey.Value)
lhsKeyIndex := findInArray(lhs, rhsKey)
log.Debugf("index is %v", lhsKeyIndex)
if lhsKeyIndex < 0 || lhsKeyIndex%2 != 0 {
return false, nil
}
lhsValue := lhs.Content[lhsKeyIndex+1]
log.Debugf("lhsValue is %v", lhsValue.Value)
itemInArray, err := contains(lhsValue, rhsValue)
log.Debugf("rhsValue is %v", rhsValue.Value)
if err != nil {
return false, err
}
if !itemInArray {
return false, nil
}
}
return true, nil
}
func containsScalars(lhs *yaml.Node, rhs *yaml.Node) (bool, error) {
if lhs.Tag == "!!str" {
return strings.Contains(lhs.Value, rhs.Value), nil
}
return lhs.Value == rhs.Value, nil
}
func contains(lhs *yaml.Node, rhs *yaml.Node) (bool, error) {
switch lhs.Kind {
case yaml.MappingNode:
return containsObject(lhs, rhs)
case yaml.SequenceNode:
return containsArray(lhs, rhs)
case yaml.ScalarNode:
if rhs.Kind != yaml.ScalarNode || lhs.Tag != rhs.Tag {
return false, nil
}
if lhs.Tag == "!!null" {
return rhs.Tag == "!!null", nil
}
return containsScalars(lhs, rhs)
}
return false, fmt.Errorf("%v not yet supported for contains", lhs.Tag)
}
func containsWithNodes(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
lhs.Node = unwrapDoc(lhs.Node)
rhs.Node = unwrapDoc(rhs.Node)
if lhs.Node.Kind != rhs.Node.Kind {
return nil, fmt.Errorf("%v cannot check contained in %v", rhs.Node.Tag, lhs.Node.Tag)
}
result, err := contains(lhs.Node, rhs.Node)
if err != nil {
return nil, err
}
return createBooleanCandidate(lhs, result), nil
}