Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 98 additions & 5 deletions ext/lists.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package ext

import (
"fmt"
"math"

"github.com/google/cel-go/cel"
"github.com/google/cel-go/common/types"
Expand All @@ -35,21 +36,67 @@ import (
//
// [1,2,3,4].slice(1, 3) // return [2, 3]
// [1,2,3,4].slice(2, 4) // return [3 ,4]
func Lists() cel.EnvOption {
return cel.Lib(listsLib{})
//
// # Flatten
//
// Flattens a list recursively.
// If an optional depth is provided, the list is flattened to a the specificied level.
// A negative depth value flattens the list recursively to its deepest level.
//
// <list>.flatten(<list>) -> <list>
// <list>.flatten(<list>, <int>) -> <list>
//
// Examples:
//
// [1,[2,3],[4]].flatten() // return [1, 2, 3, 4]
// [1,[2,[3,4]]].flatten() // return [1, 2, [3, 4]]
// [1,2,[],[],[3,4]].flatten() // return [1, 2, 3, 4]
// [1,[2,[3,[4]]]].flatten(2) // return [1, 2, 3, [4]]
// [1,[2,[3,[4]]]].flatten(-1) // return [1, 2, 3, 4]
func Lists(options ...ListsOption) cel.EnvOption {
l := &listsLib{
version: math.MaxUint32,
}
for _, o := range options {
l = o(l)
}

return cel.Lib(l)
}

type listsLib struct{}
type listsLib struct {
version uint32
}

// LibraryName implements the SingletonLibrary interface method.
func (listsLib) LibraryName() string {
return "cel.lib.ext.lists"
}

// ListsOption is a functional interface for configuring the strings library.
type ListsOption func(*listsLib) *listsLib

// ListsVersion configures the version of the string library.
//
// The version limits which functions are available. Only functions introduced
// below or equal to the given version included in the library. If this option
// is not set, all functions are available.
//
// See the library documentation to determine which version a function was introduced.
// If the documentation does not state which version a function was introduced, it can
// be assumed to be introduced at version 0, when the library was first created.
func ListsVersion(version uint32) ListsOption {
return func(lib *listsLib) *listsLib {
lib.version = version
return lib
}
}

// CompileOptions implements the Library interface method.
func (listsLib) CompileOptions() []cel.EnvOption {
func (lib listsLib) CompileOptions() []cel.EnvOption {
listType := cel.ListType(cel.TypeParamType("T"))
return []cel.EnvOption{
listDyn := cel.ListType(cel.DynType)
opts := []cel.EnvOption{
cel.Function("slice",
cel.MemberOverload("list_slice",
[]*cel.Type{listType, cel.IntType, cel.IntType}, listType,
Expand All @@ -66,6 +113,33 @@ func (listsLib) CompileOptions() []cel.EnvOption {
),
),
}
if lib.version >= 1 {
opts = append(opts,
cel.Function("flatten",
cel.MemberOverload("list_flatten",
[]*cel.Type{listDyn}, listDyn,
cel.UnaryBinding(func(arg ref.Val) ref.Val {
list := arg.(traits.Lister)
flatList := flatten(list, 1)
return types.DefaultTypeAdapter.NativeToValue(flatList)
}),
),
),
cel.Function("flatten",
cel.MemberOverload("list_flatten_int",
[]*cel.Type{listDyn, types.IntType}, listDyn,
cel.BinaryBinding(func(arg1, arg2 ref.Val) ref.Val {
list := arg1.(traits.Lister)
depth := arg2.(types.Int)
flatList := flatten(list, int64(depth))
return types.DefaultTypeAdapter.NativeToValue(flatList)
}),
),
),
)
}

return opts
}

// ProgramOptions implements the Library interface method.
Expand All @@ -92,3 +166,22 @@ func slice(list traits.Lister, start, end types.Int) (ref.Val, error) {
}
return types.DefaultTypeAdapter.NativeToValue(newList), nil
}

func flatten(list traits.Lister, depth int64) []ref.Val {
var newList []ref.Val
iter := list.Iterator()

for iter.HasNext() == types.True {
val := iter.Next()
nestedList, isList := val.(traits.Lister)

if !isList || depth == 0 {
newList = append(newList, val)
continue
} else {
newList = append(newList, flatten(nestedList, depth-1)...)
}
}

return newList
}
6 changes: 6 additions & 0 deletions ext/lists_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ func TestLists(t *testing.T) {
{expr: `[1,2,3,4].slice(0, 10)`, err: "cannot slice(0, 10), list is length 4"},
{expr: `[1,2,3,4].slice(-5, 10)`, err: "cannot slice(-5, 10), negative indexes not supported"},
{expr: `[1,2,3,4].slice(-5, -3)`, err: "cannot slice(-5, -3), negative indexes not supported"},
{expr: `[].flatten() == []`},
{expr: `[1,2,3,4].flatten() == [1,2,3,4]`},
{expr: `[1,[2,[3,4]]].flatten() == [1,2,[3,4]]`},
{expr: `[1,2,[],[],[3,4]].flatten() == [1,2,3,4]`},
{expr: `[1,[2,[3,4]]].flatten(2) == [1,2,3,4]`},
{expr: `[1,[2,[3,[4]]]].flatten(-1) == [1,2,3,4]`},
}

env := testListsEnv(t)
Expand Down