yq/pkg/yqlib/operator_compare.go

135 lines
3.8 KiB
Go
Raw Normal View History

2022-03-17 03:08:08 +00:00
package yqlib
import (
"fmt"
"strconv"
)
type compareTypePref struct {
OrEqual bool
Greater bool
}
func compareOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
log.Debugf("-- compareOperator")
prefs := expressionNode.Operation.Preferences.(compareTypePref)
return crossFunction(d, context, expressionNode, compare(prefs), true)
2022-03-17 03:08:08 +00:00
}
func compare(prefs compareTypePref) func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
return func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
log.Debugf("-- compare cross function")
if lhs == nil && rhs == nil {
owner := &CandidateNode{}
return createBooleanCandidate(owner, prefs.OrEqual), nil
} else if lhs == nil {
log.Debugf("lhs nil, but rhs is not")
return createBooleanCandidate(rhs, false), nil
} else if rhs == nil {
log.Debugf("rhs nil, but rhs is not")
return createBooleanCandidate(lhs, false), nil
}
2023-04-08 09:56:35 +00:00
lhsU := lhs.unwrapDocument()
rhsU := rhs.unwrapDocument()
2022-03-17 03:08:08 +00:00
2023-04-08 09:56:35 +00:00
switch lhsU.Kind {
case MappingNode:
2022-03-17 03:08:08 +00:00
return nil, fmt.Errorf("maps not yet supported for comparison")
2023-04-08 09:56:35 +00:00
case SequenceNode:
2022-03-17 03:08:08 +00:00
return nil, fmt.Errorf("arrays not yet supported for comparison")
default:
2023-04-08 09:56:35 +00:00
if rhsU.Kind != ScalarNode {
return nil, fmt.Errorf("%v (%v) cannot be subtracted from %v", rhsU.Tag, rhs.Path, lhsU.Tag)
2022-03-17 03:08:08 +00:00
}
2023-04-08 09:56:35 +00:00
target := lhs.CreateReplacement()
boolV, err := compareScalars(context, prefs, lhsU, rhsU)
2022-03-17 03:08:08 +00:00
return createBooleanCandidate(target, boolV), err
}
}
}
2023-04-08 09:56:35 +00:00
func compareDateTime(layout string, prefs compareTypePref, lhs *CandidateNode, rhs *CandidateNode) (bool, error) {
2022-11-04 01:21:12 +00:00
lhsTime, err := parseDateTime(layout, lhs.Value)
2022-03-17 03:08:08 +00:00
if err != nil {
return false, err
}
2022-11-04 01:21:12 +00:00
rhsTime, err := parseDateTime(layout, rhs.Value)
2022-03-17 03:08:08 +00:00
if err != nil {
return false, err
}
if prefs.OrEqual && lhsTime.Equal(rhsTime) {
return true, nil
}
if prefs.Greater {
return lhsTime.After(rhsTime), nil
}
return lhsTime.Before(rhsTime), nil
}
2023-04-08 09:56:35 +00:00
func compareScalars(context Context, prefs compareTypePref, lhs *CandidateNode, rhs *CandidateNode) (bool, error) {
lhsTag := lhs.guessTagFromCustomType()
rhsTag := rhs.guessTagFromCustomType()
2022-03-17 03:08:08 +00:00
isDateTime := lhs.Tag == "!!timestamp"
// if the lhs is a string, it might be a timestamp in a custom format.
if lhsTag == "!!str" {
2022-11-04 01:21:12 +00:00
_, err := parseDateTime(context.GetDateTimeLayout(), lhs.Value)
2022-03-17 03:08:08 +00:00
isDateTime = err == nil
}
if isDateTime {
return compareDateTime(context.GetDateTimeLayout(), prefs, lhs, rhs)
} else if lhsTag == "!!int" && rhsTag == "!!int" {
2022-05-06 03:46:14 +00:00
_, lhsNum, err := parseInt64(lhs.Value)
2022-03-17 03:08:08 +00:00
if err != nil {
return false, err
}
2022-05-06 03:46:14 +00:00
_, rhsNum, err := parseInt64(rhs.Value)
2022-03-17 03:08:08 +00:00
if err != nil {
return false, err
}
if prefs.OrEqual && lhsNum == rhsNum {
return true, nil
}
if prefs.Greater {
return lhsNum > rhsNum, nil
}
return lhsNum < rhsNum, nil
} else if (lhsTag == "!!int" || lhsTag == "!!float") && (rhsTag == "!!int" || rhsTag == "!!float") {
lhsNum, err := strconv.ParseFloat(lhs.Value, 64)
if err != nil {
return false, err
}
rhsNum, err := strconv.ParseFloat(rhs.Value, 64)
if err != nil {
return false, err
}
if prefs.OrEqual && lhsNum == rhsNum {
return true, nil
}
if prefs.Greater {
return lhsNum > rhsNum, nil
}
return lhsNum < rhsNum, nil
} else if lhsTag == "!!str" && rhsTag == "!!str" {
if prefs.OrEqual && lhs.Value == rhs.Value {
return true, nil
}
if prefs.Greater {
return lhs.Value > rhs.Value, nil
}
return lhs.Value < rhs.Value, nil
} else if lhsTag == "!!null" && rhsTag == "!!null" && prefs.OrEqual {
return true, nil
} else if lhsTag == "!!null" || rhsTag == "!!null" {
return false, nil
2022-03-17 03:08:08 +00:00
}
return false, fmt.Errorf("%v not yet supported for comparison", lhs.Tag)
}