Skip to content

Commit 99260b3

Browse files
committed
Add optional.unwrap() / .unwrapOpt() function
This function takes a list of optional values and only returns the non-none values from the list, skipping the none values, and returning a list of the unwrapped values directly.
1 parent 628543b commit 99260b3

File tree

2 files changed

+34
-0
lines changed

2 files changed

+34
-0
lines changed

cel/cel_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2818,6 +2818,10 @@ func TestOptionalValuesEval(t *testing.T) {
28182818
{expr: `['a','b','c'].first()`, out: types.OptionalOf(types.String("a"))},
28192819
{expr: `[].last()`, out: types.OptionalNone},
28202820
{expr: `[1, 2, 3].last()`, out: types.OptionalOf(types.Int(3))},
2821+
{expr: `[].elideOpt()`, out: []any{}},
2822+
{expr: `[optional.none(), optional.none()].elideOpt()`, out: []any{}},
2823+
{expr: `[optional.of(42), optional.none(), optional.of("a")].elideOpt()`, out: []any{types.Int(42), types.String("a")}},
2824+
{expr: `[optional.of(42), optional.of("a")].elideOpt()`, out: []any{types.Int(42), types.String("a")}},
28212825
}
28222826

28232827
for i, tst := range tests {

cel/library.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
package cel
1616

1717
import (
18+
"fmt"
1819
"math"
1920
"strconv"
2021
"strings"
@@ -35,6 +36,7 @@ const (
3536
optMapMacro = "optMap"
3637
optFlatMapMacro = "optFlatMap"
3738
hasValueFunc = "hasValue"
39+
elideOptFunc = "elideOpt"
3840
optionalNoneFunc = "optional.none"
3941
optionalOfFunc = "optional.of"
4042
optionalOfNonZeroValueFunc = "optional.ofNonZeroValue"
@@ -281,6 +283,14 @@ func (stdLibrary) ProgramOptions() []ProgramOption {
281283
//
282284
// This is syntactic sugar for msg.elements[msg.elements.size()-1].
283285

286+
// # ElideOpt
287+
//
288+
// Introduced in version: 2
289+
//
290+
// Returns a list of all the values that are not none in the input list of optional values.
291+
//
292+
// [optional.of(42), optional.none()].elideOpt() == [42]
293+
284294
func OptionalTypes(opts ...OptionalTypesOption) EnvOption {
285295
lib := &optionalLib{version: math.MaxUint32}
286296
for _, opt := range opts {
@@ -324,6 +334,7 @@ func (lib *optionalLib) CompileOptions() []EnvOption {
324334
optionalTypeV := OptionalType(paramTypeV)
325335
listTypeV := ListType(paramTypeV)
326336
mapTypeKV := MapType(paramTypeK, paramTypeV)
337+
listOptionalTypeV := ListType(optionalTypeV)
327338

328339
opts := []EnvOption{
329340
// Enable the optional syntax in the parser.
@@ -427,6 +438,25 @@ func (lib *optionalLib) CompileOptions() []EnvOption {
427438
}),
428439
),
429440
))
441+
442+
opts = append(opts, Function(elideOptFunc,
443+
MemberOverload("optional_elideOpt", []*Type{listOptionalTypeV}, listTypeV,
444+
UnaryBinding(func(value ref.Val) ref.Val {
445+
list := value.(traits.Lister)
446+
var elidedList []ref.Val
447+
iter := list.Iterator()
448+
for iter.HasNext() == types.True {
449+
val := iter.Next()
450+
opt, isOpt := val.(*types.Optional)
451+
if !isOpt {
452+
return types.WrapErr(fmt.Errorf("value %v is not optional", val))
453+
}
454+
if opt.HasValue() {
455+
elidedList = append(elidedList, opt.GetValue())
456+
}
457+
}
458+
return types.DefaultTypeAdapter.NativeToValue(elidedList)
459+
}))))
430460
}
431461

432462
return opts

0 commit comments

Comments
 (0)