123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217 |
- // Copyright The OpenTelemetry Authors
- // SPDX-License-Identifier: Apache-2.0
- package ottl // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl"
- import (
- "bytes"
- "time"
- "go.uber.org/zap"
- "golang.org/x/exp/constraints"
- )
- // The functions in this file implement a general-purpose comparison of two
- // values of type any, which for the purposes of OTTL mean values that are one of
- // int, float, string, bool, or pointers to those, or []byte, or nil.
- // invalidComparison returns false for everything except NE (where it returns true to indicate that the
- // objects were definitely not equivalent).
- // It also gives us an opportunity to log something.
- func (p *Parser[K]) invalidComparison(msg string, op compareOp) bool {
- p.telemetrySettings.Logger.Debug(msg, zap.Any("op", op))
- return op == NE
- }
- // comparePrimitives implements a generic comparison helper for all Ordered types (derived from Float, Int, or string).
- // According to benchmarks, it's faster than explicit comparison functions for these types.
- func comparePrimitives[T constraints.Ordered](a T, b T, op compareOp) bool {
- switch op {
- case EQ:
- return a == b
- case NE:
- return a != b
- case LT:
- return a < b
- case LTE:
- return a <= b
- case GTE:
- return a >= b
- case GT:
- return a > b
- default:
- return false
- }
- }
- func compareBools(a bool, b bool, op compareOp) bool {
- switch op {
- case EQ:
- return a == b
- case NE:
- return a != b
- case LT:
- return !a && b
- case LTE:
- return !a || b
- case GTE:
- return a || !b
- case GT:
- return a && !b
- default:
- return false
- }
- }
- func compareBytes(a []byte, b []byte, op compareOp) bool {
- switch op {
- case EQ:
- return bytes.Equal(a, b)
- case NE:
- return !bytes.Equal(a, b)
- case LT:
- return bytes.Compare(a, b) < 0
- case LTE:
- return bytes.Compare(a, b) <= 0
- case GTE:
- return bytes.Compare(a, b) >= 0
- case GT:
- return bytes.Compare(a, b) > 0
- default:
- return false
- }
- }
- func (p *Parser[K]) compareBool(a bool, b any, op compareOp) bool {
- switch v := b.(type) {
- case bool:
- return compareBools(a, v, op)
- default:
- return p.invalidComparison("bool to non-bool", op)
- }
- }
- func (p *Parser[K]) compareString(a string, b any, op compareOp) bool {
- switch v := b.(type) {
- case string:
- return comparePrimitives(a, v, op)
- default:
- return p.invalidComparison("string to non-string", op)
- }
- }
- func (p *Parser[K]) compareByte(a []byte, b any, op compareOp) bool {
- switch v := b.(type) {
- case nil:
- return op == NE
- case []byte:
- if v == nil {
- return op == NE
- }
- return compareBytes(a, v, op)
- default:
- return p.invalidComparison("Bytes to non-Bytes", op)
- }
- }
- func (p *Parser[K]) compareInt64(a int64, b any, op compareOp) bool {
- switch v := b.(type) {
- case int64:
- return comparePrimitives(a, v, op)
- case float64:
- return comparePrimitives(float64(a), v, op)
- default:
- return p.invalidComparison("int to non-numeric value", op)
- }
- }
- func (p *Parser[K]) compareFloat64(a float64, b any, op compareOp) bool {
- switch v := b.(type) {
- case int64:
- return comparePrimitives(a, float64(v), op)
- case float64:
- return comparePrimitives(a, v, op)
- default:
- return p.invalidComparison("float to non-numeric value", op)
- }
- }
- func (p *Parser[K]) compareDuration(a time.Duration, b any, op compareOp) bool {
- switch v := b.(type) {
- case time.Duration:
- ansecs := a.Nanoseconds()
- vnsecs := v.Nanoseconds()
- return comparePrimitives(ansecs, vnsecs, op)
- default:
- return p.invalidComparison("cannot compare invalid duration", op)
- }
- }
- func (p *Parser[K]) compareTime(a time.Time, b any, op compareOp) bool {
- switch v := b.(type) {
- case time.Time:
- switch op {
- case EQ:
- return a.Equal(v)
- case NE:
- return !a.Equal(v)
- case LT:
- return a.Before(v)
- case LTE:
- return a.Before(v) || a.Equal(v)
- case GTE:
- return a.After(v) || a.Equal(v)
- case GT:
- return a.After(v)
- default:
- return p.invalidComparison("invalid comparison operator", op)
- }
- default:
- return p.invalidComparison("time to non-time value", op)
- }
- }
- // a and b are the return values from a Getter; we try to compare them
- // according to the given operator.
- func (p *Parser[K]) compare(a any, b any, op compareOp) bool {
- // nils are equal to each other and never equal to anything else,
- // so if they're both nil, report equality.
- if a == nil && b == nil {
- return op == EQ || op == LTE || op == GTE
- }
- // Anything else, we switch on the left side first.
- switch v := a.(type) {
- case nil:
- // If a was nil, it means b wasn't and inequalities don't apply,
- // so let's swap and give it the chance to get evaluated.
- return p.compare(b, nil, op)
- case bool:
- return p.compareBool(v, b, op)
- case int64:
- return p.compareInt64(v, b, op)
- case float64:
- return p.compareFloat64(v, b, op)
- case string:
- return p.compareString(v, b, op)
- case []byte:
- if v == nil {
- return p.compare(b, nil, op)
- }
- return p.compareByte(v, b, op)
- case time.Duration:
- return p.compareDuration(v, b, op)
- case time.Time:
- return p.compareTime(v, b, op)
- default:
- // If we don't know what type it is, we can't do inequalities yet. So we can fall back to the old behavior where we just
- // use Go's standard equality.
- switch op {
- case EQ:
- return a == b
- case NE:
- return a != b
- default:
- return p.invalidComparison("unsupported type for inequality on left", op)
- }
- }
- }
|