Skip to content

Commit 0e43390

Browse files
authored
[pkg/ottl] Add new FloatLikeGetter and FloatGetter (#21896)
* add new getters * changelog * Add support for converting bool * Add to function parser * Add to floatgetter tests * Add to floatgetter slice support
1 parent 67bdff0 commit 0e43390

File tree

5 files changed

+402
-0
lines changed

5 files changed

+402
-0
lines changed

.chloggen/ottl-floatlikegetter.yaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
2+
change_type: enhancement
3+
4+
# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
5+
component: pkg/ottl
6+
7+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
8+
note: Add `FloatLikeGetter` and `FloatGetter` to facilitate float retrival for functions.
9+
10+
# One or more tracking issues related to the change
11+
issues: [21896]
12+
13+
# (Optional) One or more lines of additional information to render under the primary note.
14+
# These lines will be padded with 2 spaces and then inserted directly into the document.
15+
# Use pipe (|) for multiline entries.
16+
subtext:

pkg/ottl/expression.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"context"
1919
"encoding/hex"
2020
"fmt"
21+
"strconv"
2122

2223
jsoniter "github.com/json-iterator/go"
2324
"go.opentelemetry.io/collector/pdata/pcommon"
@@ -153,6 +154,10 @@ type IntGetter[K any] interface {
153154
Get(ctx context.Context, tCtx K) (int64, error)
154155
}
155156

157+
type FloatGetter[K any] interface {
158+
Get(ctx context.Context, tCtx K) (float64, error)
159+
}
160+
156161
type PMapGetter[K any] interface {
157162
Get(ctx context.Context, tCtx K) (pcommon.Map, error)
158163
}
@@ -225,6 +230,70 @@ func (g StandardStringLikeGetter[K]) Get(ctx context.Context, tCtx K) (*string,
225230
return &result, nil
226231
}
227232

233+
// FloatLikeGetter is a Getter that returns a float64 by converting the underlying value to a float64 if necessary.
234+
type FloatLikeGetter[K any] interface {
235+
// Get retrieves a float64 value.
236+
// Unlike `FloatGetter`, the expectation is that the underlying value is converted to a float64 if possible.
237+
// If the value cannot be converted to a float64, nil and an error are returned.
238+
// If the value is nil, nil is returned without an error.
239+
Get(ctx context.Context, tCtx K) (*float64, error)
240+
}
241+
242+
type StandardFloatLikeGetter[K any] struct {
243+
Getter func(ctx context.Context, tCtx K) (interface{}, error)
244+
}
245+
246+
func (g StandardFloatLikeGetter[K]) Get(ctx context.Context, tCtx K) (*float64, error) {
247+
val, err := g.Getter(ctx, tCtx)
248+
if err != nil {
249+
return nil, err
250+
}
251+
if val == nil {
252+
return nil, nil
253+
}
254+
var result float64
255+
switch v := val.(type) {
256+
case float64:
257+
result = v
258+
case int64:
259+
result = float64(v)
260+
case string:
261+
result, err = strconv.ParseFloat(v, 64)
262+
if err != nil {
263+
return nil, err
264+
}
265+
case bool:
266+
if v {
267+
result = float64(1)
268+
} else {
269+
result = float64(0)
270+
}
271+
case pcommon.Value:
272+
switch v.Type() {
273+
case pcommon.ValueTypeDouble:
274+
result = v.Double()
275+
case pcommon.ValueTypeInt:
276+
result = float64(v.Int())
277+
case pcommon.ValueTypeStr:
278+
result, err = strconv.ParseFloat(v.Str(), 64)
279+
if err != nil {
280+
return nil, err
281+
}
282+
case pcommon.ValueTypeBool:
283+
if v.Bool() {
284+
result = float64(1)
285+
} else {
286+
result = float64(0)
287+
}
288+
default:
289+
return nil, fmt.Errorf("unsupported value type: %v", v.Type())
290+
}
291+
default:
292+
return nil, fmt.Errorf("unsupported type: %T", v)
293+
}
294+
return &result, nil
295+
}
296+
228297
func (p *Parser[K]) newGetter(val value) (Getter[K], error) {
229298
if val.IsNil != nil && *val.IsNil {
230299
return &literal[K]{value: nil}, nil

pkg/ottl/expression_test.go

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -808,3 +808,166 @@ func Test_StandardStringLikeGetter(t *testing.T) {
808808
})
809809
}
810810
}
811+
812+
func Test_StandardFloatLikeGetter(t *testing.T) {
813+
tests := []struct {
814+
name string
815+
getter FloatLikeGetter[interface{}]
816+
want interface{}
817+
valid bool
818+
expectedErrorMsg string
819+
}{
820+
{
821+
name: "string type",
822+
getter: StandardFloatLikeGetter[interface{}]{
823+
Getter: func(ctx context.Context, tCtx interface{}) (interface{}, error) {
824+
return "1.0", nil
825+
},
826+
},
827+
want: 1.0,
828+
valid: true,
829+
},
830+
{
831+
name: "int64 type",
832+
getter: StandardFloatLikeGetter[interface{}]{
833+
Getter: func(ctx context.Context, tCtx interface{}) (interface{}, error) {
834+
return int64(1), nil
835+
},
836+
},
837+
want: float64(1),
838+
valid: true,
839+
},
840+
{
841+
name: "float64 type",
842+
getter: StandardFloatLikeGetter[interface{}]{
843+
Getter: func(ctx context.Context, tCtx interface{}) (interface{}, error) {
844+
return 1.1, nil
845+
},
846+
},
847+
want: 1.1,
848+
valid: true,
849+
},
850+
{
851+
name: "float64 bool true",
852+
getter: StandardFloatLikeGetter[interface{}]{
853+
Getter: func(ctx context.Context, tCtx interface{}) (interface{}, error) {
854+
return true, nil
855+
},
856+
},
857+
want: float64(1),
858+
valid: true,
859+
},
860+
{
861+
name: "float64 bool false",
862+
getter: StandardFloatLikeGetter[interface{}]{
863+
Getter: func(ctx context.Context, tCtx interface{}) (interface{}, error) {
864+
return false, nil
865+
},
866+
},
867+
want: float64(0),
868+
valid: true,
869+
},
870+
{
871+
name: "pcommon.value type int",
872+
getter: StandardFloatLikeGetter[interface{}]{
873+
Getter: func(ctx context.Context, tCtx interface{}) (interface{}, error) {
874+
v := pcommon.NewValueInt(int64(100))
875+
return v, nil
876+
},
877+
},
878+
want: float64(100),
879+
valid: true,
880+
},
881+
{
882+
name: "pcommon.value type float",
883+
getter: StandardFloatLikeGetter[interface{}]{
884+
Getter: func(ctx context.Context, tCtx interface{}) (interface{}, error) {
885+
v := pcommon.NewValueDouble(float64(1.1))
886+
return v, nil
887+
},
888+
},
889+
want: 1.1,
890+
valid: true,
891+
},
892+
{
893+
name: "pcommon.value type string",
894+
getter: StandardFloatLikeGetter[interface{}]{
895+
Getter: func(ctx context.Context, tCtx interface{}) (interface{}, error) {
896+
v := pcommon.NewValueStr("1.1")
897+
return v, nil
898+
},
899+
},
900+
want: 1.1,
901+
valid: true,
902+
},
903+
{
904+
name: "pcommon.value type bool true",
905+
getter: StandardFloatLikeGetter[interface{}]{
906+
Getter: func(ctx context.Context, tCtx interface{}) (interface{}, error) {
907+
v := pcommon.NewValueBool(true)
908+
return v, nil
909+
},
910+
},
911+
want: float64(1),
912+
valid: true,
913+
},
914+
{
915+
name: "pcommon.value type bool false",
916+
getter: StandardFloatLikeGetter[interface{}]{
917+
Getter: func(ctx context.Context, tCtx interface{}) (interface{}, error) {
918+
v := pcommon.NewValueBool(false)
919+
return v, nil
920+
},
921+
},
922+
want: float64(0),
923+
valid: true,
924+
},
925+
{
926+
name: "nil",
927+
getter: StandardFloatLikeGetter[interface{}]{
928+
Getter: func(ctx context.Context, tCtx interface{}) (interface{}, error) {
929+
return nil, nil
930+
},
931+
},
932+
want: nil,
933+
valid: true,
934+
},
935+
{
936+
name: "invalid type",
937+
getter: StandardFloatLikeGetter[interface{}]{
938+
Getter: func(ctx context.Context, tCtx interface{}) (interface{}, error) {
939+
return []byte{}, nil
940+
},
941+
},
942+
valid: false,
943+
expectedErrorMsg: "unsupported type: []uint8",
944+
},
945+
{
946+
name: "invalid pcommon.Value type",
947+
getter: StandardFloatLikeGetter[interface{}]{
948+
Getter: func(ctx context.Context, tCtx interface{}) (interface{}, error) {
949+
v := pcommon.NewValueMap()
950+
return v, nil
951+
},
952+
},
953+
valid: false,
954+
expectedErrorMsg: "unsupported value type: Map",
955+
},
956+
}
957+
958+
for _, tt := range tests {
959+
t.Run(tt.name, func(t *testing.T) {
960+
val, err := tt.getter.Get(context.Background(), nil)
961+
if tt.valid {
962+
assert.NoError(t, err)
963+
if tt.want == nil {
964+
assert.Nil(t, val)
965+
} else {
966+
assert.Equal(t, tt.want, *val)
967+
}
968+
} else {
969+
assert.EqualError(t, err, tt.expectedErrorMsg)
970+
}
971+
})
972+
}
973+
}

pkg/ottl/functions.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,18 @@ func (p *Parser[K]) buildSliceArg(argVal value, argType reflect.Type) (any, erro
155155
return nil, err
156156
}
157157
return arg, nil
158+
case strings.HasPrefix(name, "FloatGetter"):
159+
arg, err := buildSlice[FloatGetter[K]](argVal, argType, p.buildArg, name)
160+
if err != nil {
161+
return nil, err
162+
}
163+
return arg, nil
164+
case strings.HasPrefix(name, "FloatLikeGetter"):
165+
arg, err := buildSlice[FloatLikeGetter[K]](argVal, argType, p.buildArg, name)
166+
if err != nil {
167+
return nil, err
168+
}
169+
return arg, nil
158170
default:
159171
return nil, fmt.Errorf("unsupported slice type '%s' for function", argType.Elem().Name())
160172
}
@@ -193,6 +205,18 @@ func (p *Parser[K]) buildArg(argVal value, argType reflect.Type) (any, error) {
193205
return nil, err
194206
}
195207
return StandardStringLikeGetter[K]{Getter: arg.Get}, nil
208+
case strings.HasPrefix(name, "FloatGetter"):
209+
arg, err := p.newGetter(argVal)
210+
if err != nil {
211+
return nil, err
212+
}
213+
return StandardTypeGetter[K, float64]{Getter: arg.Get}, nil
214+
case strings.HasPrefix(name, "FloatLikeGetter"):
215+
arg, err := p.newGetter(argVal)
216+
if err != nil {
217+
return nil, err
218+
}
219+
return StandardFloatLikeGetter[K]{Getter: arg.Get}, nil
196220
case strings.HasPrefix(name, "IntGetter"):
197221
arg, err := p.newGetter(argVal)
198222
if err != nil {

0 commit comments

Comments
 (0)