mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-14 18:53:35 +00:00
rebase: update K8s packages to v0.32.1
Update K8s packages in go.mod to v0.32.1 Signed-off-by: Praveen M <m.praveen@ibm.com>
This commit is contained in:
6
vendor/github.com/google/cel-go/ext/BUILD.bazel
generated
vendored
6
vendor/github.com/google/cel-go/ext/BUILD.bazel
generated
vendored
@ -24,6 +24,7 @@ go_library(
|
||||
"//cel:go_default_library",
|
||||
"//checker:go_default_library",
|
||||
"//common/ast:go_default_library",
|
||||
"//common/decls:go_default_library",
|
||||
"//common/overloads:go_default_library",
|
||||
"//common/operators:go_default_library",
|
||||
"//common/types:go_default_library",
|
||||
@ -31,6 +32,7 @@ go_library(
|
||||
"//common/types/ref:go_default_library",
|
||||
"//common/types/traits:go_default_library",
|
||||
"//interpreter:go_default_library",
|
||||
"//parser:go_default_library",
|
||||
"@org_golang_google_protobuf//proto:go_default_library",
|
||||
"@org_golang_google_protobuf//reflect/protoreflect:go_default_library",
|
||||
"@org_golang_google_protobuf//types/known/structpb",
|
||||
@ -61,8 +63,8 @@ go_test(
|
||||
"//common/types/ref:go_default_library",
|
||||
"//common/types/traits:go_default_library",
|
||||
"//test:go_default_library",
|
||||
"//test/proto2pb:go_default_library",
|
||||
"//test/proto3pb:go_default_library",
|
||||
"//test/proto2pb:go_default_library",
|
||||
"//test/proto3pb:go_default_library",
|
||||
"@org_golang_google_protobuf//proto:go_default_library",
|
||||
"@org_golang_google_protobuf//types/known/wrapperspb:go_default_library",
|
||||
"@org_golang_google_protobuf//encoding/protojson:go_default_library",
|
||||
|
493
vendor/github.com/google/cel-go/ext/README.md
generated
vendored
493
vendor/github.com/google/cel-go/ext/README.md
generated
vendored
@ -3,12 +3,12 @@
|
||||
CEL extensions are a related set of constants, functions, macros, or other
|
||||
features which may not be covered by the core CEL spec.
|
||||
|
||||
## Bindings
|
||||
## Bindings
|
||||
|
||||
Returns a cel.EnvOption to configure support for local variable bindings
|
||||
in expressions.
|
||||
|
||||
# Cel.Bind
|
||||
### Cel.Bind
|
||||
|
||||
Binds a simple identifier to an initialization expression which may be used
|
||||
in a subsequenct result expression. Bindings may also be nested within each
|
||||
@ -19,11 +19,11 @@ other.
|
||||
Examples:
|
||||
|
||||
cel.bind(a, 'hello',
|
||||
cel.bind(b, 'world', a + b + b + a)) // "helloworldworldhello"
|
||||
cel.bind(b, 'world', a + b + b + a)) // "helloworldworldhello"
|
||||
|
||||
// Avoid a list allocation within the exists comprehension.
|
||||
cel.bind(valid_values, [a, b, c],
|
||||
[d, e, f].exists(elem, elem in valid_values))
|
||||
[d, e, f].exists(elem, elem in valid_values))
|
||||
|
||||
Local bindings are not guaranteed to be evaluated before use.
|
||||
|
||||
@ -100,7 +100,8 @@ argument. Simple numeric and list literals are supported as valid argument
|
||||
types; however, other literals will be flagged as errors during macro
|
||||
expansion. If the argument expression does not resolve to a numeric or
|
||||
list(numeric) type during type-checking, or during runtime then an error
|
||||
will be produced. If a list argument is empty, this too will produce an error.
|
||||
will be produced. If a list argument is empty, this too will produce an
|
||||
error.
|
||||
|
||||
math.least(<arg>, ...) -> <double|int|uint>
|
||||
|
||||
@ -117,6 +118,244 @@ Examples:
|
||||
math.least(a, b) // check-time error if a or b is non-numeric
|
||||
math.least(dyn('string')) // runtime error
|
||||
|
||||
### Math.BitOr
|
||||
|
||||
Introduced at version: 1
|
||||
|
||||
Performs a bitwise-OR operation over two int or uint values.
|
||||
|
||||
math.bitOr(<int>, <int>) -> <int>
|
||||
math.bitOr(<uint>, <uint>) -> <uint>
|
||||
|
||||
Examples:
|
||||
|
||||
math.bitOr(1u, 2u) // returns 3u
|
||||
math.bitOr(-2, -4) // returns -2
|
||||
|
||||
### Math.BitAnd
|
||||
|
||||
Introduced at version: 1
|
||||
|
||||
Performs a bitwise-AND operation over two int or uint values.
|
||||
|
||||
math.bitAnd(<int>, <int>) -> <int>
|
||||
math.bitAnd(<uint>, <uint>) -> <uint>
|
||||
|
||||
Examples:
|
||||
|
||||
math.bitAnd(3u, 2u) // return 2u
|
||||
math.bitAnd(3, 5) // returns 3
|
||||
math.bitAnd(-3, -5) // returns -7
|
||||
|
||||
### Math.BitXor
|
||||
|
||||
Introduced at version: 1
|
||||
|
||||
math.bitXor(<int>, <int>) -> <int>
|
||||
math.bitXor(<uint>, <uint>) -> <uint>
|
||||
|
||||
Performs a bitwise-XOR operation over two int or uint values.
|
||||
|
||||
Examples:
|
||||
|
||||
math.bitXor(3u, 5u) // returns 6u
|
||||
math.bitXor(1, 3) // returns 2
|
||||
|
||||
### Math.BitNot
|
||||
|
||||
Introduced at version: 1
|
||||
|
||||
Function which accepts a single int or uint and performs a bitwise-NOT
|
||||
ones-complement of the given binary value.
|
||||
|
||||
math.bitNot(<int>) -> <int>
|
||||
math.bitNot(<uint>) -> <uint>
|
||||
|
||||
Examples
|
||||
|
||||
math.bitNot(1) // returns -1
|
||||
math.bitNot(-1) // return 0
|
||||
math.bitNot(0u) // returns 18446744073709551615u
|
||||
|
||||
### Math.BitShiftLeft
|
||||
|
||||
Introduced at version: 1
|
||||
|
||||
Perform a left shift of bits on the first parameter, by the amount of bits
|
||||
specified in the second parameter. The first parameter is either a uint or
|
||||
an int. The second parameter must be an int.
|
||||
|
||||
When the second parameter is 64 or greater, 0 will be always be returned
|
||||
since the number of bits shifted is greater than or equal to the total bit
|
||||
length of the number being shifted. Negative valued bit shifts will result
|
||||
in a runtime error.
|
||||
|
||||
math.bitShiftLeft(<int>, <int>) -> <int>
|
||||
math.bitShiftLeft(<uint>, <int>) -> <uint>
|
||||
|
||||
Examples
|
||||
|
||||
math.bitShiftLeft(1, 2) // returns 4
|
||||
math.bitShiftLeft(-1, 2) // returns -4
|
||||
math.bitShiftLeft(1u, 2) // return 4u
|
||||
math.bitShiftLeft(1u, 200) // returns 0u
|
||||
|
||||
### Math.BitShiftRight
|
||||
|
||||
Introduced at version: 1
|
||||
|
||||
Perform a right shift of bits on the first parameter, by the amount of bits
|
||||
specified in the second parameter. The first parameter is either a uint or
|
||||
an int. The second parameter must be an int.
|
||||
|
||||
When the second parameter is 64 or greater, 0 will always be returned since
|
||||
the number of bits shifted is greater than or equal to the total bit length
|
||||
of the number being shifted. Negative valued bit shifts will result in a
|
||||
runtime error.
|
||||
|
||||
The sign bit extension will not be preserved for this operation: vacant bits
|
||||
on the left are filled with 0.
|
||||
|
||||
math.bitShiftRight(<int>, <int>) -> <int>
|
||||
math.bitShiftRight(<uint>, <int>) -> <uint>
|
||||
|
||||
Examples
|
||||
|
||||
math.bitShiftRight(1024, 2) // returns 256
|
||||
math.bitShiftRight(1024u, 2) // returns 256u
|
||||
math.bitShiftRight(1024u, 64) // returns 0u
|
||||
|
||||
### Math.Ceil
|
||||
|
||||
Introduced at version: 1
|
||||
|
||||
Compute the ceiling of a double value.
|
||||
|
||||
math.ceil(<double>) -> <double>
|
||||
|
||||
Examples:
|
||||
|
||||
math.ceil(1.2) // returns 2.0
|
||||
math.ceil(-1.2) // returns -1.0
|
||||
|
||||
### Math.Floor
|
||||
|
||||
Introduced at version: 1
|
||||
|
||||
Compute the floor of a double value.
|
||||
|
||||
math.floor(<double>) -> <double>
|
||||
|
||||
Examples:
|
||||
|
||||
math.floor(1.2) // returns 1.0
|
||||
math.floor(-1.2) // returns -2.0
|
||||
|
||||
### Math.Round
|
||||
|
||||
Introduced at version: 1
|
||||
|
||||
Rounds the double value to the nearest whole number with ties rounding away
|
||||
from zero, e.g. 1.5 -> 2.0, -1.5 -> -2.0.
|
||||
|
||||
math.round(<double>) -> <double>
|
||||
|
||||
Examples:
|
||||
|
||||
math.round(1.2) // returns 1.0
|
||||
math.round(1.5) // returns 2.0
|
||||
math.round(-1.5) // returns -2.0
|
||||
|
||||
### Math.Trunc
|
||||
|
||||
Introduced at version: 1
|
||||
|
||||
Truncates the fractional portion of the double value.
|
||||
|
||||
math.trunc(<double>) -> <double>
|
||||
|
||||
Examples:
|
||||
|
||||
math.trunc(-1.3) // returns -1.0
|
||||
math.trunc(1.3) // returns 1.0
|
||||
|
||||
### Math.Abs
|
||||
|
||||
Introduced at version: 1
|
||||
|
||||
Returns the absolute value of the numeric type provided as input. If the
|
||||
value is NaN, the output is NaN. If the input is int64 min, the function
|
||||
will result in an overflow error.
|
||||
|
||||
math.abs(<double>) -> <double>
|
||||
math.abs(<int>) -> <int>
|
||||
math.abs(<uint>) -> <uint>
|
||||
|
||||
Examples:
|
||||
|
||||
math.abs(-1) // returns 1
|
||||
math.abs(1) // returns 1
|
||||
math.abs(-9223372036854775808) // overlflow error
|
||||
|
||||
### Math.Sign
|
||||
|
||||
Introduced at version: 1
|
||||
|
||||
Returns the sign of the numeric type, either -1, 0, 1 as an int, double, or
|
||||
uint depending on the overload. For floating point values, if NaN is
|
||||
provided as input, the output is also NaN. The implementation does not
|
||||
differentiate between positive and negative zero.
|
||||
|
||||
math.sign(<double>) -> <double>
|
||||
math.sign(<int>) -> <int>
|
||||
math.sign(<uint>) -> <uint>
|
||||
|
||||
Examples:
|
||||
|
||||
math.sign(-42) // returns -1
|
||||
math.sign(0) // returns 0
|
||||
math.sign(42) // returns 1
|
||||
|
||||
### Math.IsInf
|
||||
|
||||
Introduced at version: 1
|
||||
|
||||
Returns true if the input double value is -Inf or +Inf.
|
||||
|
||||
math.isInf(<double>) -> <bool>
|
||||
|
||||
Examples:
|
||||
|
||||
math.isInf(1.0/0.0) // returns true
|
||||
math.isInf(1.2) // returns false
|
||||
|
||||
### Math.IsNaN
|
||||
|
||||
Introduced at version: 1
|
||||
|
||||
Returns true if the input double value is NaN, false otherwise.
|
||||
|
||||
math.isNaN(<double>) -> <bool>
|
||||
|
||||
Examples:
|
||||
|
||||
math.isNaN(0.0/0.0) // returns true
|
||||
math.isNaN(1.2) // returns false
|
||||
|
||||
### Math.IsFinite
|
||||
|
||||
Introduced at version: 1
|
||||
|
||||
Returns true if the value is a finite number. Equivalent in behavior to:
|
||||
!math.isNaN(double) && !math.isInf(double)
|
||||
|
||||
math.isFinite(<double>) -> <bool>
|
||||
|
||||
Examples:
|
||||
|
||||
math.isFinite(0.0/0.0) // returns false
|
||||
math.isFinite(1.2) // returns true
|
||||
|
||||
## Protos
|
||||
|
||||
Protos configure extended macros and functions for proto manipulation.
|
||||
@ -154,6 +393,65 @@ Example:
|
||||
Extended functions for list manipulation. As a general note, all indices are
|
||||
zero-based.
|
||||
|
||||
### Distinct
|
||||
|
||||
**Introduced in version 2**
|
||||
|
||||
Returns the distinct elements of a list.
|
||||
|
||||
<list(T)>.distinct() -> <list(T)>
|
||||
|
||||
Examples:
|
||||
|
||||
[1, 2, 2, 3, 3, 3].distinct() // return [1, 2, 3]
|
||||
["b", "b", "c", "a", "c"].distinct() // return ["b", "c", "a"]
|
||||
[1, "b", 2, "b"].distinct() // return [1, "b", 2]
|
||||
|
||||
### Flatten
|
||||
|
||||
**Introduced in version 1**
|
||||
|
||||
Flattens a list recursively.
|
||||
If an optional depth is provided, the list is flattened to a the specificied level.
|
||||
A negative depth value will result in an error.
|
||||
|
||||
<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) // error
|
||||
|
||||
### Range
|
||||
|
||||
**Introduced in version 2**
|
||||
|
||||
Returns a list of integers from 0 to n-1.
|
||||
|
||||
lists.range(<int>) -> <list(int)>
|
||||
|
||||
Examples:
|
||||
|
||||
lists.range(5) -> [0, 1, 2, 3, 4]
|
||||
|
||||
|
||||
### Reverse
|
||||
|
||||
**Introduced in version 2**
|
||||
|
||||
Returns the elements of a list in reverse order.
|
||||
|
||||
<list(T)>.reverse() -> <list(T)>
|
||||
|
||||
Examples:
|
||||
|
||||
[5, 3, 1, 2].reverse() // return [2, 1, 3, 5]
|
||||
|
||||
|
||||
### Slice
|
||||
|
||||
|
||||
@ -164,7 +462,43 @@ Returns a new sub-list using the indexes provided.
|
||||
Examples:
|
||||
|
||||
[1,2,3,4].slice(1, 3) // return [2, 3]
|
||||
[1,2,3,4].slice(2, 4) // return [3 ,4]
|
||||
[1,2,3,4].slice(2, 4) // return [3, 4]
|
||||
|
||||
### Sort
|
||||
|
||||
**Introduced in version 2**
|
||||
|
||||
Sorts a list with comparable elements. If the element type is not comparable
|
||||
or the element types are not the same, the function will produce an error.
|
||||
|
||||
<list(T)>.sort() -> <list(T)>
|
||||
T in {int, uint, double, bool, duration, timestamp, string, bytes}
|
||||
|
||||
Examples:
|
||||
|
||||
[3, 2, 1].sort() // return [1, 2, 3]
|
||||
["b", "c", "a"].sort() // return ["a", "b", "c"]
|
||||
[1, "b"].sort() // error
|
||||
[[1, 2, 3]].sort() // error
|
||||
|
||||
### SortBy
|
||||
|
||||
**Introduced in version 2**
|
||||
|
||||
Sorts a list by a key value, i.e., the order is determined by the result of
|
||||
an expression applied to each element of the list.
|
||||
|
||||
<list(T)>.sortBy(<bindingName>, <keyExpr>) -> <list(T)>
|
||||
keyExpr returns a value in {int, uint, double, bool, duration, timestamp, string, bytes}
|
||||
|
||||
Examples:
|
||||
|
||||
[
|
||||
Player { name: "foo", score: 0 },
|
||||
Player { name: "bar", score: -10 },
|
||||
Player { name: "baz", score: 1000 },
|
||||
].sortBy(e, e.score).map(e, e.name)
|
||||
== ["bar", "foo", "baz"]
|
||||
|
||||
## Sets
|
||||
|
||||
@ -259,7 +593,8 @@ Examples:
|
||||
'hello mellow'.indexOf('jello') // returns -1
|
||||
'hello mellow'.indexOf('', 2) // returns 2
|
||||
'hello mellow'.indexOf('ello', 2) // returns 7
|
||||
'hello mellow'.indexOf('ello', 20) // error
|
||||
'hello mellow'.indexOf('ello', 20) // returns -1
|
||||
'hello mellow'.indexOf('ello', -1) // error
|
||||
|
||||
### Join
|
||||
|
||||
@ -273,10 +608,10 @@ elements in the resulting string.
|
||||
|
||||
Examples:
|
||||
|
||||
['hello', 'mellow'].join() // returns 'hellomellow'
|
||||
['hello', 'mellow'].join(' ') // returns 'hello mellow'
|
||||
[].join() // returns ''
|
||||
[].join('/') // returns ''
|
||||
['hello', 'mellow'].join() // returns 'hellomellow'
|
||||
['hello', 'mellow'].join(' ') // returns 'hello mellow'
|
||||
[].join() // returns ''
|
||||
[].join('/') // returns ''
|
||||
|
||||
### LastIndexOf
|
||||
|
||||
@ -297,6 +632,7 @@ Examples:
|
||||
'hello mellow'.lastIndexOf('ello') // returns 7
|
||||
'hello mellow'.lastIndexOf('jello') // returns -1
|
||||
'hello mellow'.lastIndexOf('ello', 6) // returns 1
|
||||
'hello mellow'.lastIndexOf('ello', 20) // returns -1
|
||||
'hello mellow'.lastIndexOf('ello', -1) // error
|
||||
|
||||
### LowerAscii
|
||||
@ -427,4 +763,137 @@ It can be located in Version 3 of strings.
|
||||
Examples:
|
||||
|
||||
'gums'.reverse() // returns 'smug'
|
||||
'John Smith'.reverse() // returns 'htimS nhoJ'
|
||||
'John Smith'.reverse() // returns 'htimS nhoJ'
|
||||
|
||||
## TwoVarComprehensions
|
||||
|
||||
TwoVarComprehensions introduces support for two-variable comprehensions.
|
||||
|
||||
The two-variable form of comprehensions looks similar to the one-variable
|
||||
counterparts. Where possible, the same macro names were used and additional
|
||||
macro signatures added. The notable distinction for two-variable comprehensions
|
||||
is the introduction of `transformList`, `transformMap`, and `transformMapEntry`
|
||||
support for list and map types rather than the more traditional `map` and
|
||||
`filter` macros.
|
||||
|
||||
### All
|
||||
|
||||
Comprehension which tests whether all elements in the list or map satisfy a
|
||||
given predicate. The `all` macro evaluates in a manner consistent with logical
|
||||
AND and will short-circuit when encountering a `false` value.
|
||||
|
||||
<list>.all(indexVar, valueVar, <predicate>) -> bool
|
||||
<map>.all(keyVar, valueVar, <predicate>) -> bool
|
||||
|
||||
Examples:
|
||||
|
||||
[1, 2, 3].all(i, j, i < j) // returns true
|
||||
{'hello': 'world', 'taco': 'taco'}.all(k, v, k != v) // returns false
|
||||
|
||||
// Combines two-variable comprehension with single variable
|
||||
{'h': ['hello', 'hi'], 'j': ['joke', 'jog']}
|
||||
.all(k, vals, vals.all(v, v.startsWith(k))) // returns true
|
||||
|
||||
### Exists
|
||||
|
||||
Comprehension which tests whether any element in a list or map exists which
|
||||
satisfies a given predicate. The `exists` macro evaluates in a manner consistent
|
||||
with logical OR and will short-circuit when encountering a `true` value.
|
||||
|
||||
<list>.exists(indexVar, valueVar, <predicate>) -> bool
|
||||
<map>.exists(keyVar, valueVar, <predicate>) -> bool
|
||||
|
||||
Examples:
|
||||
|
||||
{'greeting': 'hello', 'farewell': 'goodbye'}
|
||||
.exists(k, v, k.startsWith('good') || v.endsWith('bye')) // returns true
|
||||
[1, 2, 4, 8, 16].exists(i, v, v == 1024 && i == 10) // returns false
|
||||
|
||||
### ExistsOne
|
||||
|
||||
Comprehension which tests whether exactly one element in a list or map exists
|
||||
which satisfies a given predicate expression. This comprehension does not
|
||||
short-circuit in keeping with the one-variable exists one macro semantics.
|
||||
|
||||
<list>.existsOne(indexVar, valueVar, <predicate>)
|
||||
<map>.existsOne(keyVar, valueVar, <predicate>)
|
||||
|
||||
This macro may also be used with the `exists_one` function name, for
|
||||
compatibility with the one-variable macro of the same name.
|
||||
|
||||
Examples:
|
||||
|
||||
[1, 2, 1, 3, 1, 4].existsOne(i, v, i == 1 || v == 1) // returns false
|
||||
[1, 1, 2, 2, 3, 3].existsOne(i, v, i == 2 && v == 2) // returns true
|
||||
{'i': 0, 'j': 1, 'k': 2}.existsOne(i, v, i == 'l' || v == 1) // returns true
|
||||
|
||||
### TransformList
|
||||
|
||||
Comprehension which converts a map or a list into a list value. The output
|
||||
expression of the comprehension determines the contents of the output list.
|
||||
Elements in the list may optionally be filtered according to a predicate
|
||||
expression, where elements that satisfy the predicate are transformed.
|
||||
|
||||
<list>.transformList(indexVar, valueVar, <transform>)
|
||||
<list>.transformList(indexVar, valueVar, <filter>, <transform>)
|
||||
<map>.transformList(keyVar, valueVar, <transform>)
|
||||
<map>.transformList(keyVar, valueVar, <filter>, <transform>)
|
||||
|
||||
Examples:
|
||||
|
||||
[1, 2, 3].transformList(indexVar, valueVar,
|
||||
(indexVar * valueVar) + valueVar) // returns [1, 4, 9]
|
||||
[1, 2, 3].transformList(indexVar, valueVar, indexVar % 2 == 0
|
||||
(indexVar * valueVar) + valueVar) // returns [1, 9]
|
||||
{'greeting': 'hello', 'farewell': 'goodbye'}
|
||||
.transformList(k, _, k) // returns ['greeting', 'farewell']
|
||||
{'greeting': 'hello', 'farewell': 'goodbye'}
|
||||
.transformList(_, v, v) // returns ['hello', 'goodbye']
|
||||
|
||||
### TransformMap
|
||||
|
||||
Comprehension which converts a map or a list into a map value. The output
|
||||
expression of the comprehension determines the value of the output map entry;
|
||||
however, the key remains fixed. Elements in the map may optionally be filtered
|
||||
according to a predicate expression, where elements that satisfy the predicate
|
||||
are transformed.
|
||||
|
||||
<list>.transformMap(indexVar, valueVar, <transform>)
|
||||
<list>.transformMap(indexVar, valueVar, <filter>, <transform>)
|
||||
<map>.transformMap(keyVar, valueVar, <transform>)
|
||||
<map>.transformMap(keyVar, valueVar, <filter>, <transform>)
|
||||
|
||||
Examples:
|
||||
|
||||
[1, 2, 3].transformMap(indexVar, valueVar,
|
||||
(indexVar * valueVar) + valueVar) // returns {0: 1, 1: 4, 2: 9}
|
||||
[1, 2, 3].transformMap(indexVar, valueVar, indexVar % 2 == 0
|
||||
(indexVar * valueVar) + valueVar) // returns {0: 1, 2: 9}
|
||||
{'greeting': 'hello'}.transformMap(k, v, v + '!') // returns {'greeting': 'hello!'}
|
||||
|
||||
### TransformMapEntry
|
||||
|
||||
Comprehension which converts a map or a list into a map value; however, this
|
||||
transform expects the entry expression be a map literal. If the transform
|
||||
produces an entry which duplicates a key in the target map, the comprehension
|
||||
will error. Note, that key equality is determined using CEL equality which
|
||||
asserts that numeric values which are equal, even if they don't have the same
|
||||
type will cause a key collision.
|
||||
|
||||
Elements in the map may optionally be filtered according to a predicate
|
||||
expression, where elements that satisfy the predicate are transformed.
|
||||
|
||||
<list>.transformMap(indexVar, valueVar, <transform>)
|
||||
<list>.transformMap(indexVar, valueVar, <filter>, <transform>)
|
||||
<map>.transformMap(keyVar, valueVar, <transform>)
|
||||
<map>.transformMap(keyVar, valueVar, <filter>, <transform>)
|
||||
|
||||
Examples:
|
||||
|
||||
// returns {'hello': 'greeting'}
|
||||
{'greeting': 'hello'}.transformMapEntry(keyVar, valueVar, {valueVar: keyVar})
|
||||
// reverse lookup, require all values in list be unique
|
||||
[1, 2, 3].transformMapEntry(indexVar, valueVar, {valueVar: indexVar})
|
||||
|
||||
{'greeting': 'aloha', 'farewell': 'aloha'}
|
||||
.transformMapEntry(keyVar, valueVar, {valueVar: keyVar}) // error, duplicate key
|
||||
|
254
vendor/github.com/google/cel-go/ext/bindings.go
generated
vendored
254
vendor/github.com/google/cel-go/ext/bindings.go
generated
vendored
@ -15,9 +15,19 @@
|
||||
package ext
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/google/cel-go/cel"
|
||||
"github.com/google/cel-go/common/ast"
|
||||
"github.com/google/cel-go/common/types"
|
||||
"github.com/google/cel-go/common/types/ref"
|
||||
"github.com/google/cel-go/common/types/traits"
|
||||
"github.com/google/cel-go/interpreter"
|
||||
)
|
||||
|
||||
// Bindings returns a cel.EnvOption to configure support for local variable
|
||||
@ -41,35 +51,120 @@ import (
|
||||
// [d, e, f].exists(elem, elem in valid_values))
|
||||
//
|
||||
// Local bindings are not guaranteed to be evaluated before use.
|
||||
func Bindings() cel.EnvOption {
|
||||
return cel.Lib(celBindings{})
|
||||
func Bindings(options ...BindingsOption) cel.EnvOption {
|
||||
b := &celBindings{version: math.MaxUint32}
|
||||
for _, o := range options {
|
||||
b = o(b)
|
||||
}
|
||||
return cel.Lib(b)
|
||||
}
|
||||
|
||||
const (
|
||||
celNamespace = "cel"
|
||||
bindMacro = "bind"
|
||||
blockFunc = "@block"
|
||||
unusedIterVar = "#unused"
|
||||
)
|
||||
|
||||
type celBindings struct{}
|
||||
// BindingsOption declares a functional operator for configuring the Bindings library behavior.
|
||||
type BindingsOption func(*celBindings) *celBindings
|
||||
|
||||
func (celBindings) LibraryName() string {
|
||||
// BindingsVersion sets the version of the bindings library to an explicit version.
|
||||
func BindingsVersion(version uint32) BindingsOption {
|
||||
return func(lib *celBindings) *celBindings {
|
||||
lib.version = version
|
||||
return lib
|
||||
}
|
||||
}
|
||||
|
||||
type celBindings struct {
|
||||
version uint32
|
||||
}
|
||||
|
||||
func (*celBindings) LibraryName() string {
|
||||
return "cel.lib.ext.cel.bindings"
|
||||
}
|
||||
|
||||
func (celBindings) CompileOptions() []cel.EnvOption {
|
||||
return []cel.EnvOption{
|
||||
func (lib *celBindings) CompileOptions() []cel.EnvOption {
|
||||
opts := []cel.EnvOption{
|
||||
cel.Macros(
|
||||
// cel.bind(var, <init>, <expr>)
|
||||
cel.ReceiverMacro(bindMacro, 3, celBind),
|
||||
),
|
||||
}
|
||||
if lib.version >= 1 {
|
||||
// The cel.@block signature takes a list of subexpressions and a typed expression which is
|
||||
// used as the output type.
|
||||
paramType := cel.TypeParamType("T")
|
||||
opts = append(opts,
|
||||
cel.Function("cel.@block",
|
||||
cel.Overload("cel_block_list",
|
||||
[]*cel.Type{cel.ListType(cel.DynType), paramType}, paramType)),
|
||||
)
|
||||
opts = append(opts, cel.ASTValidators(blockValidationExemption{}))
|
||||
}
|
||||
return opts
|
||||
}
|
||||
|
||||
func (celBindings) ProgramOptions() []cel.ProgramOption {
|
||||
func (lib *celBindings) ProgramOptions() []cel.ProgramOption {
|
||||
if lib.version >= 1 {
|
||||
celBlockPlan := func(i interpreter.Interpretable) (interpreter.Interpretable, error) {
|
||||
call, ok := i.(interpreter.InterpretableCall)
|
||||
if !ok {
|
||||
return i, nil
|
||||
}
|
||||
switch call.Function() {
|
||||
case "cel.@block":
|
||||
args := call.Args()
|
||||
if len(args) != 2 {
|
||||
return nil, fmt.Errorf("cel.@block expects two arguments, but got %d", len(args))
|
||||
}
|
||||
expr := args[1]
|
||||
// Non-empty block
|
||||
if block, ok := args[0].(interpreter.InterpretableConstructor); ok {
|
||||
slotExprs := block.InitVals()
|
||||
return newDynamicBlock(slotExprs, expr), nil
|
||||
}
|
||||
// Constant valued block which can happen during runtime optimization.
|
||||
if cons, ok := args[0].(interpreter.InterpretableConst); ok {
|
||||
if cons.Value().Type() == types.ListType {
|
||||
l := cons.Value().(traits.Lister)
|
||||
if l.Size().Equal(types.IntZero) == types.True {
|
||||
return args[1], nil
|
||||
}
|
||||
return newConstantBlock(l, expr), nil
|
||||
}
|
||||
}
|
||||
return nil, errors.New("cel.@block expects a list constructor as the first argument")
|
||||
default:
|
||||
return i, nil
|
||||
}
|
||||
}
|
||||
return []cel.ProgramOption{cel.CustomDecorator(celBlockPlan)}
|
||||
}
|
||||
return []cel.ProgramOption{}
|
||||
}
|
||||
|
||||
type blockValidationExemption struct{}
|
||||
|
||||
// Name returns the name of the validator.
|
||||
func (blockValidationExemption) Name() string {
|
||||
return "cel.lib.ext.validate.functions.cel.block"
|
||||
}
|
||||
|
||||
// Configure implements the ASTValidatorConfigurer interface and augments the list of functions to skip
|
||||
// during homogeneous aggregate literal type-checks.
|
||||
func (blockValidationExemption) Configure(config cel.MutableValidatorConfig) error {
|
||||
functions := config.GetOrDefault(cel.HomogeneousAggregateLiteralExemptFunctions, []string{}).([]string)
|
||||
functions = append(functions, "cel.@block")
|
||||
return config.Set(cel.HomogeneousAggregateLiteralExemptFunctions, functions)
|
||||
}
|
||||
|
||||
// Validate is a no-op as the intent is to simply disable strong type-checks for list literals during
|
||||
// when they occur within cel.@block calls as the arg types have already been validated.
|
||||
func (blockValidationExemption) Validate(env *cel.Env, _ cel.ValidatorConfig, a *ast.AST, iss *cel.Issues) {
|
||||
}
|
||||
|
||||
func celBind(mef cel.MacroExprFactory, target ast.Expr, args []ast.Expr) (ast.Expr, *cel.Error) {
|
||||
if !macroTargetMatchesNamespace(celNamespace, target) {
|
||||
return nil, nil
|
||||
@ -94,3 +189,148 @@ func celBind(mef cel.MacroExprFactory, target ast.Expr, args []ast.Expr) (ast.Ex
|
||||
resultExpr,
|
||||
), nil
|
||||
}
|
||||
|
||||
func newDynamicBlock(slotExprs []interpreter.Interpretable, expr interpreter.Interpretable) interpreter.Interpretable {
|
||||
bs := &dynamicBlock{
|
||||
slotExprs: slotExprs,
|
||||
expr: expr,
|
||||
}
|
||||
bs.slotActivationPool = &sync.Pool{
|
||||
New: func() any {
|
||||
slotCount := len(slotExprs)
|
||||
sa := &dynamicSlotActivation{
|
||||
slotExprs: slotExprs,
|
||||
slotCount: slotCount,
|
||||
slotVals: make([]*slotVal, slotCount),
|
||||
}
|
||||
for i := 0; i < slotCount; i++ {
|
||||
sa.slotVals[i] = &slotVal{}
|
||||
}
|
||||
return sa
|
||||
},
|
||||
}
|
||||
return bs
|
||||
}
|
||||
|
||||
type dynamicBlock struct {
|
||||
slotExprs []interpreter.Interpretable
|
||||
expr interpreter.Interpretable
|
||||
slotActivationPool *sync.Pool
|
||||
}
|
||||
|
||||
// ID implements the Interpretable interface method.
|
||||
func (b *dynamicBlock) ID() int64 {
|
||||
return b.expr.ID()
|
||||
}
|
||||
|
||||
// Eval implements the Interpretable interface method.
|
||||
func (b *dynamicBlock) Eval(activation interpreter.Activation) ref.Val {
|
||||
sa := b.slotActivationPool.Get().(*dynamicSlotActivation)
|
||||
sa.Activation = activation
|
||||
defer b.clearSlots(sa)
|
||||
return b.expr.Eval(sa)
|
||||
}
|
||||
|
||||
func (b *dynamicBlock) clearSlots(sa *dynamicSlotActivation) {
|
||||
sa.reset()
|
||||
b.slotActivationPool.Put(sa)
|
||||
}
|
||||
|
||||
type slotVal struct {
|
||||
value *ref.Val
|
||||
visited bool
|
||||
}
|
||||
|
||||
type dynamicSlotActivation struct {
|
||||
interpreter.Activation
|
||||
slotExprs []interpreter.Interpretable
|
||||
slotCount int
|
||||
slotVals []*slotVal
|
||||
}
|
||||
|
||||
// ResolveName implements the Activation interface method but handles variables prefixed with `@index`
|
||||
// as special variables which exist within the slot-based memory of the cel.@block() where each slot
|
||||
// refers to an expression which must be computed only once.
|
||||
func (sa *dynamicSlotActivation) ResolveName(name string) (any, bool) {
|
||||
if idx, found := matchSlot(name, sa.slotCount); found {
|
||||
v := sa.slotVals[idx]
|
||||
if v.visited {
|
||||
// Return not found if the index expression refers to itself
|
||||
if v.value == nil {
|
||||
return nil, false
|
||||
}
|
||||
return *v.value, true
|
||||
}
|
||||
v.visited = true
|
||||
val := sa.slotExprs[idx].Eval(sa)
|
||||
v.value = &val
|
||||
return val, true
|
||||
}
|
||||
return sa.Activation.ResolveName(name)
|
||||
}
|
||||
|
||||
func (sa *dynamicSlotActivation) reset() {
|
||||
sa.Activation = nil
|
||||
for _, sv := range sa.slotVals {
|
||||
sv.visited = false
|
||||
sv.value = nil
|
||||
}
|
||||
}
|
||||
|
||||
func newConstantBlock(slots traits.Lister, expr interpreter.Interpretable) interpreter.Interpretable {
|
||||
count := slots.Size().(types.Int)
|
||||
return &constantBlock{slots: slots, slotCount: int(count), expr: expr}
|
||||
}
|
||||
|
||||
type constantBlock struct {
|
||||
slots traits.Lister
|
||||
slotCount int
|
||||
expr interpreter.Interpretable
|
||||
}
|
||||
|
||||
// ID implements the interpreter.Interpretable interface method.
|
||||
func (b *constantBlock) ID() int64 {
|
||||
return b.expr.ID()
|
||||
}
|
||||
|
||||
// Eval implements the interpreter.Interpretable interface method, and will proxy @index prefixed variable
|
||||
// lookups into a set of constant slots determined from the plan step.
|
||||
func (b *constantBlock) Eval(activation interpreter.Activation) ref.Val {
|
||||
vars := constantSlotActivation{Activation: activation, slots: b.slots, slotCount: b.slotCount}
|
||||
return b.expr.Eval(vars)
|
||||
}
|
||||
|
||||
type constantSlotActivation struct {
|
||||
interpreter.Activation
|
||||
slots traits.Lister
|
||||
slotCount int
|
||||
}
|
||||
|
||||
// ResolveName implements Activation interface method and proxies @index prefixed lookups into the slot
|
||||
// activation associated with the block scope.
|
||||
func (sa constantSlotActivation) ResolveName(name string) (any, bool) {
|
||||
if idx, found := matchSlot(name, sa.slotCount); found {
|
||||
return sa.slots.Get(types.Int(idx)), true
|
||||
}
|
||||
return sa.Activation.ResolveName(name)
|
||||
}
|
||||
|
||||
func matchSlot(name string, slotCount int) (int, bool) {
|
||||
if idx, found := strings.CutPrefix(name, indexPrefix); found {
|
||||
idx, err := strconv.Atoi(idx)
|
||||
// Return not found if the index is not numeric
|
||||
if err != nil {
|
||||
return -1, false
|
||||
}
|
||||
// Return not found if the index is not a valid slot
|
||||
if idx < 0 || idx >= slotCount {
|
||||
return -1, false
|
||||
}
|
||||
return idx, true
|
||||
}
|
||||
return -1, false
|
||||
}
|
||||
|
||||
var (
|
||||
indexPrefix = "@index"
|
||||
)
|
||||
|
410
vendor/github.com/google/cel-go/ext/comprehensions.go
generated
vendored
Normal file
410
vendor/github.com/google/cel-go/ext/comprehensions.go
generated
vendored
Normal file
@ -0,0 +1,410 @@
|
||||
// Copyright 2024 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ext
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/google/cel-go/cel"
|
||||
"github.com/google/cel-go/common/ast"
|
||||
"github.com/google/cel-go/common/operators"
|
||||
"github.com/google/cel-go/common/types"
|
||||
"github.com/google/cel-go/common/types/ref"
|
||||
"github.com/google/cel-go/common/types/traits"
|
||||
"github.com/google/cel-go/parser"
|
||||
)
|
||||
|
||||
const (
|
||||
mapInsert = "cel.@mapInsert"
|
||||
mapInsertOverloadMap = "@mapInsert_map_map"
|
||||
mapInsertOverloadKeyValue = "@mapInsert_map_key_value"
|
||||
)
|
||||
|
||||
// TwoVarComprehensions introduces support for two-variable comprehensions.
|
||||
//
|
||||
// The two-variable form of comprehensions looks similar to the one-variable counterparts.
|
||||
// Where possible, the same macro names were used and additional macro signatures added.
|
||||
// The notable distinction for two-variable comprehensions is the introduction of
|
||||
// `transformList`, `transformMap`, and `transformMapEntry` support for list and map types
|
||||
// rather than the more traditional `map` and `filter` macros.
|
||||
//
|
||||
// # All
|
||||
//
|
||||
// Comprehension which tests whether all elements in the list or map satisfy a given
|
||||
// predicate. The `all` macro evaluates in a manner consistent with logical AND and will
|
||||
// short-circuit when encountering a `false` value.
|
||||
//
|
||||
// <list>.all(indexVar, valueVar, <predicate>) -> bool
|
||||
// <map>.all(keyVar, valueVar, <predicate>) -> bool
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// [1, 2, 3].all(i, j, i < j) // returns true
|
||||
// {'hello': 'world', 'taco': 'taco'}.all(k, v, k != v) // returns false
|
||||
//
|
||||
// // Combines two-variable comprehension with single variable
|
||||
// {'h': ['hello', 'hi'], 'j': ['joke', 'jog']}
|
||||
// .all(k, vals, vals.all(v, v.startsWith(k))) // returns true
|
||||
//
|
||||
// # Exists
|
||||
//
|
||||
// Comprehension which tests whether any element in a list or map exists which satisfies
|
||||
// a given predicate. The `exists` macro evaluates in a manner consistent with logical OR
|
||||
// and will short-circuit when encountering a `true` value.
|
||||
//
|
||||
// <list>.exists(indexVar, valueVar, <predicate>) -> bool
|
||||
// <map>.exists(keyVar, valueVar, <predicate>) -> bool
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// {'greeting': 'hello', 'farewell': 'goodbye'}
|
||||
// .exists(k, v, k.startsWith('good') || v.endsWith('bye')) // returns true
|
||||
// [1, 2, 4, 8, 16].exists(i, v, v == 1024 && i == 10) // returns false
|
||||
//
|
||||
// # ExistsOne
|
||||
//
|
||||
// Comprehension which tests whether exactly one element in a list or map exists which
|
||||
// satisfies a given predicate expression. This comprehension does not short-circuit in
|
||||
// keeping with the one-variable exists one macro semantics.
|
||||
//
|
||||
// <list>.existsOne(indexVar, valueVar, <predicate>)
|
||||
// <map>.existsOne(keyVar, valueVar, <predicate>)
|
||||
//
|
||||
// This macro may also be used with the `exists_one` function name, for compatibility
|
||||
// with the one-variable macro of the same name.
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// [1, 2, 1, 3, 1, 4].existsOne(i, v, i == 1 || v == 1) // returns false
|
||||
// [1, 1, 2, 2, 3, 3].existsOne(i, v, i == 2 && v == 2) // returns true
|
||||
// {'i': 0, 'j': 1, 'k': 2}.existsOne(i, v, i == 'l' || v == 1) // returns true
|
||||
//
|
||||
// # TransformList
|
||||
//
|
||||
// Comprehension which converts a map or a list into a list value. The output expression
|
||||
// of the comprehension determines the contents of the output list. Elements in the list
|
||||
// may optionally be filtered according to a predicate expression, where elements that
|
||||
// satisfy the predicate are transformed.
|
||||
//
|
||||
// <list>.transformList(indexVar, valueVar, <transform>)
|
||||
// <list>.transformList(indexVar, valueVar, <filter>, <transform>)
|
||||
// <map>.transformList(keyVar, valueVar, <transform>)
|
||||
// <map>.transformList(keyVar, valueVar, <filter>, <transform>)
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// [1, 2, 3].transformList(indexVar, valueVar,
|
||||
// (indexVar * valueVar) + valueVar) // returns [1, 4, 9]
|
||||
// [1, 2, 3].transformList(indexVar, valueVar, indexVar % 2 == 0
|
||||
// (indexVar * valueVar) + valueVar) // returns [1, 9]
|
||||
// {'greeting': 'hello', 'farewell': 'goodbye'}
|
||||
// .transformList(k, _, k) // returns ['greeting', 'farewell']
|
||||
// {'greeting': 'hello', 'farewell': 'goodbye'}
|
||||
// .transformList(_, v, v) // returns ['hello', 'goodbye']
|
||||
//
|
||||
// # TransformMap
|
||||
//
|
||||
// Comprehension which converts a map or a list into a map value. The output expression
|
||||
// of the comprehension determines the value of the output map entry; however, the key
|
||||
// remains fixed. Elements in the map may optionally be filtered according to a predicate
|
||||
// expression, where elements that satisfy the predicate are transformed.
|
||||
//
|
||||
// <list>.transformMap(indexVar, valueVar, <transform>)
|
||||
// <list>.transformMap(indexVar, valueVar, <filter>, <transform>)
|
||||
// <map>.transformMap(keyVar, valueVar, <transform>)
|
||||
// <map>.transformMap(keyVar, valueVar, <filter>, <transform>)
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// [1, 2, 3].transformMap(indexVar, valueVar,
|
||||
// (indexVar * valueVar) + valueVar) // returns {0: 1, 1: 4, 2: 9}
|
||||
// [1, 2, 3].transformMap(indexVar, valueVar, indexVar % 2 == 0
|
||||
// (indexVar * valueVar) + valueVar) // returns {0: 1, 2: 9}
|
||||
// {'greeting': 'hello'}.transformMap(k, v, v + '!') // returns {'greeting': 'hello!'}
|
||||
//
|
||||
// # TransformMapEntry
|
||||
//
|
||||
// Comprehension which converts a map or a list into a map value; however, this transform
|
||||
// expects the entry expression be a map literal. If the tranform produces an entry which
|
||||
// duplicates a key in the target map, the comprehension will error. Note, that key
|
||||
// equality is determined using CEL equality which asserts that numeric values which are
|
||||
// equal, even if they don't have the same type will cause a key collision.
|
||||
//
|
||||
// Elements in the map may optionally be filtered according to a predicate expression, where
|
||||
// elements that satisfy the predicate are transformed.
|
||||
//
|
||||
// <list>.transformMap(indexVar, valueVar, <transform>)
|
||||
// <list>.transformMap(indexVar, valueVar, <filter>, <transform>)
|
||||
// <map>.transformMap(keyVar, valueVar, <transform>)
|
||||
// <map>.transformMap(keyVar, valueVar, <filter>, <transform>)
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// // returns {'hello': 'greeting'}
|
||||
// {'greeting': 'hello'}.transformMapEntry(keyVar, valueVar, {valueVar: keyVar})
|
||||
// // reverse lookup, require all values in list be unique
|
||||
// [1, 2, 3].transformMapEntry(indexVar, valueVar, {valueVar: indexVar})
|
||||
//
|
||||
// {'greeting': 'aloha', 'farewell': 'aloha'}
|
||||
// .transformMapEntry(keyVar, valueVar, {valueVar: keyVar}) // error, duplicate key
|
||||
func TwoVarComprehensions() cel.EnvOption {
|
||||
return cel.Lib(compreV2Lib{})
|
||||
}
|
||||
|
||||
type compreV2Lib struct{}
|
||||
|
||||
// LibraryName implements that SingletonLibrary interface method.
|
||||
func (compreV2Lib) LibraryName() string {
|
||||
return "cel.lib.ext.comprev2"
|
||||
}
|
||||
|
||||
// CompileOptions implements the cel.Library interface method.
|
||||
func (compreV2Lib) CompileOptions() []cel.EnvOption {
|
||||
kType := cel.TypeParamType("K")
|
||||
vType := cel.TypeParamType("V")
|
||||
mapKVType := cel.MapType(kType, vType)
|
||||
opts := []cel.EnvOption{
|
||||
cel.Macros(
|
||||
cel.ReceiverMacro("all", 3, quantifierAll),
|
||||
cel.ReceiverMacro("exists", 3, quantifierExists),
|
||||
cel.ReceiverMacro("existsOne", 3, quantifierExistsOne),
|
||||
cel.ReceiverMacro("exists_one", 3, quantifierExistsOne),
|
||||
cel.ReceiverMacro("transformList", 3, transformList),
|
||||
cel.ReceiverMacro("transformList", 4, transformList),
|
||||
cel.ReceiverMacro("transformMap", 3, transformMap),
|
||||
cel.ReceiverMacro("transformMap", 4, transformMap),
|
||||
cel.ReceiverMacro("transformMapEntry", 3, transformMapEntry),
|
||||
cel.ReceiverMacro("transformMapEntry", 4, transformMapEntry),
|
||||
),
|
||||
cel.Function(mapInsert,
|
||||
cel.Overload(mapInsertOverloadKeyValue, []*cel.Type{mapKVType, kType, vType}, mapKVType,
|
||||
cel.FunctionBinding(func(args ...ref.Val) ref.Val {
|
||||
m := args[0].(traits.Mapper)
|
||||
k := args[1]
|
||||
v := args[2]
|
||||
return types.InsertMapKeyValue(m, k, v)
|
||||
})),
|
||||
cel.Overload(mapInsertOverloadMap, []*cel.Type{mapKVType, mapKVType}, mapKVType,
|
||||
cel.BinaryBinding(func(targetMap, updateMap ref.Val) ref.Val {
|
||||
tm := targetMap.(traits.Mapper)
|
||||
um := updateMap.(traits.Mapper)
|
||||
umIt := um.Iterator()
|
||||
for umIt.HasNext() == types.True {
|
||||
k := umIt.Next()
|
||||
updateOrErr := types.InsertMapKeyValue(tm, k, um.Get(k))
|
||||
if types.IsError(updateOrErr) {
|
||||
return updateOrErr
|
||||
}
|
||||
tm = updateOrErr.(traits.Mapper)
|
||||
}
|
||||
return tm
|
||||
})),
|
||||
),
|
||||
}
|
||||
return opts
|
||||
}
|
||||
|
||||
// ProgramOptions implements the cel.Library interface method
|
||||
func (compreV2Lib) ProgramOptions() []cel.ProgramOption {
|
||||
return []cel.ProgramOption{}
|
||||
}
|
||||
|
||||
func quantifierAll(mef cel.MacroExprFactory, target ast.Expr, args []ast.Expr) (ast.Expr, *cel.Error) {
|
||||
iterVar1, iterVar2, err := extractIterVars(mef, args[0], args[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return mef.NewComprehensionTwoVar(
|
||||
target,
|
||||
iterVar1,
|
||||
iterVar2,
|
||||
parser.AccumulatorName,
|
||||
/*accuInit=*/ mef.NewLiteral(types.True),
|
||||
/*condition=*/ mef.NewCall(operators.NotStrictlyFalse, mef.NewAccuIdent()),
|
||||
/*step=*/ mef.NewCall(operators.LogicalAnd, mef.NewAccuIdent(), args[2]),
|
||||
/*result=*/ mef.NewAccuIdent(),
|
||||
), nil
|
||||
}
|
||||
|
||||
func quantifierExists(mef cel.MacroExprFactory, target ast.Expr, args []ast.Expr) (ast.Expr, *cel.Error) {
|
||||
iterVar1, iterVar2, err := extractIterVars(mef, args[0], args[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return mef.NewComprehensionTwoVar(
|
||||
target,
|
||||
iterVar1,
|
||||
iterVar2,
|
||||
parser.AccumulatorName,
|
||||
/*accuInit=*/ mef.NewLiteral(types.False),
|
||||
/*condition=*/ mef.NewCall(operators.NotStrictlyFalse, mef.NewCall(operators.LogicalNot, mef.NewAccuIdent())),
|
||||
/*step=*/ mef.NewCall(operators.LogicalOr, mef.NewAccuIdent(), args[2]),
|
||||
/*result=*/ mef.NewAccuIdent(),
|
||||
), nil
|
||||
}
|
||||
|
||||
func quantifierExistsOne(mef cel.MacroExprFactory, target ast.Expr, args []ast.Expr) (ast.Expr, *cel.Error) {
|
||||
iterVar1, iterVar2, err := extractIterVars(mef, args[0], args[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return mef.NewComprehensionTwoVar(
|
||||
target,
|
||||
iterVar1,
|
||||
iterVar2,
|
||||
parser.AccumulatorName,
|
||||
/*accuInit=*/ mef.NewLiteral(types.Int(0)),
|
||||
/*condition=*/ mef.NewLiteral(types.True),
|
||||
/*step=*/ mef.NewCall(operators.Conditional, args[2],
|
||||
mef.NewCall(operators.Add, mef.NewAccuIdent(), mef.NewLiteral(types.Int(1))),
|
||||
mef.NewAccuIdent()),
|
||||
/*result=*/ mef.NewCall(operators.Equals, mef.NewAccuIdent(), mef.NewLiteral(types.Int(1))),
|
||||
), nil
|
||||
}
|
||||
|
||||
func transformList(mef cel.MacroExprFactory, target ast.Expr, args []ast.Expr) (ast.Expr, *cel.Error) {
|
||||
iterVar1, iterVar2, err := extractIterVars(mef, args[0], args[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var transform ast.Expr
|
||||
var filter ast.Expr
|
||||
if len(args) == 4 {
|
||||
filter = args[2]
|
||||
transform = args[3]
|
||||
} else {
|
||||
filter = nil
|
||||
transform = args[2]
|
||||
}
|
||||
|
||||
// __result__ = __result__ + [transform]
|
||||
step := mef.NewCall(operators.Add, mef.NewAccuIdent(), mef.NewList(transform))
|
||||
if filter != nil {
|
||||
// __result__ = (filter) ? __result__ + [transform] : __result__
|
||||
step = mef.NewCall(operators.Conditional, filter, step, mef.NewAccuIdent())
|
||||
}
|
||||
|
||||
return mef.NewComprehensionTwoVar(
|
||||
target,
|
||||
iterVar1,
|
||||
iterVar2,
|
||||
parser.AccumulatorName,
|
||||
/*accuInit=*/ mef.NewList(),
|
||||
/*condition=*/ mef.NewLiteral(types.True),
|
||||
step,
|
||||
/*result=*/ mef.NewAccuIdent(),
|
||||
), nil
|
||||
}
|
||||
|
||||
func transformMap(mef cel.MacroExprFactory, target ast.Expr, args []ast.Expr) (ast.Expr, *cel.Error) {
|
||||
iterVar1, iterVar2, err := extractIterVars(mef, args[0], args[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var transform ast.Expr
|
||||
var filter ast.Expr
|
||||
if len(args) == 4 {
|
||||
filter = args[2]
|
||||
transform = args[3]
|
||||
} else {
|
||||
filter = nil
|
||||
transform = args[2]
|
||||
}
|
||||
|
||||
// __result__ = cel.@mapInsert(__result__, iterVar1, transform)
|
||||
step := mef.NewCall(mapInsert, mef.NewAccuIdent(), mef.NewIdent(iterVar1), transform)
|
||||
if filter != nil {
|
||||
// __result__ = (filter) ? cel.@mapInsert(__result__, iterVar1, transform) : __result__
|
||||
step = mef.NewCall(operators.Conditional, filter, step, mef.NewAccuIdent())
|
||||
}
|
||||
return mef.NewComprehensionTwoVar(
|
||||
target,
|
||||
iterVar1,
|
||||
iterVar2,
|
||||
parser.AccumulatorName,
|
||||
/*accuInit=*/ mef.NewMap(),
|
||||
/*condition=*/ mef.NewLiteral(types.True),
|
||||
step,
|
||||
/*result=*/ mef.NewAccuIdent(),
|
||||
), nil
|
||||
}
|
||||
|
||||
func transformMapEntry(mef cel.MacroExprFactory, target ast.Expr, args []ast.Expr) (ast.Expr, *cel.Error) {
|
||||
iterVar1, iterVar2, err := extractIterVars(mef, args[0], args[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var transform ast.Expr
|
||||
var filter ast.Expr
|
||||
if len(args) == 4 {
|
||||
filter = args[2]
|
||||
transform = args[3]
|
||||
} else {
|
||||
filter = nil
|
||||
transform = args[2]
|
||||
}
|
||||
|
||||
// __result__ = cel.@mapInsert(__result__, transform)
|
||||
step := mef.NewCall(mapInsert, mef.NewAccuIdent(), transform)
|
||||
if filter != nil {
|
||||
// __result__ = (filter) ? cel.@mapInsert(__result__, transform) : __result__
|
||||
step = mef.NewCall(operators.Conditional, filter, step, mef.NewAccuIdent())
|
||||
}
|
||||
return mef.NewComprehensionTwoVar(
|
||||
target,
|
||||
iterVar1,
|
||||
iterVar2,
|
||||
parser.AccumulatorName,
|
||||
/*accuInit=*/ mef.NewMap(),
|
||||
/*condition=*/ mef.NewLiteral(types.True),
|
||||
step,
|
||||
/*result=*/ mef.NewAccuIdent(),
|
||||
), nil
|
||||
}
|
||||
|
||||
func extractIterVars(mef cel.MacroExprFactory, arg0, arg1 ast.Expr) (string, string, *cel.Error) {
|
||||
iterVar1, err := extractIterVar(mef, arg0)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
iterVar2, err := extractIterVar(mef, arg1)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
if iterVar1 == iterVar2 {
|
||||
return "", "", mef.NewError(arg1.ID(), fmt.Sprintf("duplicate variable name: %s", iterVar1))
|
||||
}
|
||||
if iterVar1 == parser.AccumulatorName {
|
||||
return "", "", mef.NewError(arg0.ID(), "iteration variable overwrites accumulator variable")
|
||||
}
|
||||
if iterVar2 == parser.AccumulatorName {
|
||||
return "", "", mef.NewError(arg1.ID(), "iteration variable overwrites accumulator variable")
|
||||
}
|
||||
return iterVar1, iterVar2, nil
|
||||
}
|
||||
|
||||
func extractIterVar(mef cel.MacroExprFactory, target ast.Expr) (string, *cel.Error) {
|
||||
iterVar, found := extractIdent(target)
|
||||
if !found {
|
||||
return "", mef.NewError(target.ID(), "argument must be a simple name")
|
||||
}
|
||||
return iterVar, nil
|
||||
}
|
11
vendor/github.com/google/cel-go/ext/encoders.go
generated
vendored
11
vendor/github.com/google/cel-go/ext/encoders.go
generated
vendored
@ -36,7 +36,7 @@ import (
|
||||
// Examples:
|
||||
//
|
||||
// base64.decode('aGVsbG8=') // return b'hello'
|
||||
// base64.decode('aGVsbG8') // error
|
||||
// base64.decode('aGVsbG8') // return b'hello'
|
||||
//
|
||||
// # Base64.Encode
|
||||
//
|
||||
@ -79,7 +79,14 @@ func (encoderLib) ProgramOptions() []cel.ProgramOption {
|
||||
}
|
||||
|
||||
func base64DecodeString(str string) ([]byte, error) {
|
||||
return base64.StdEncoding.DecodeString(str)
|
||||
b, err := base64.StdEncoding.DecodeString(str)
|
||||
if err == nil {
|
||||
return b, nil
|
||||
}
|
||||
if _, tryAltEncoding := err.(base64.CorruptInputError); tryAltEncoding {
|
||||
return base64.RawStdEncoding.DecodeString(str)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func base64EncodeBytes(bytes []byte) (string, error) {
|
||||
|
2
vendor/github.com/google/cel-go/ext/formatting.go
generated
vendored
2
vendor/github.com/google/cel-go/ext/formatting.go
generated
vendored
@ -484,7 +484,7 @@ func matchConstantFormatStringWithListLiteralArgs(a *ast.AST) ast.ExprMatcher {
|
||||
}
|
||||
}
|
||||
formatString := call.Target()
|
||||
if formatString.Kind() != ast.LiteralKind && formatString.AsLiteral().Type() != cel.StringType {
|
||||
if formatString.Kind() != ast.LiteralKind || formatString.AsLiteral().Type() != cel.StringType {
|
||||
return false
|
||||
}
|
||||
args := call.Args()
|
||||
|
16
vendor/github.com/google/cel-go/ext/guards.go
generated
vendored
16
vendor/github.com/google/cel-go/ext/guards.go
generated
vendored
@ -50,14 +50,18 @@ func listStringOrError(strs []string, err error) ref.Val {
|
||||
return types.DefaultTypeAdapter.NativeToValue(strs)
|
||||
}
|
||||
|
||||
func macroTargetMatchesNamespace(ns string, target ast.Expr) bool {
|
||||
func extractIdent(target ast.Expr) (string, bool) {
|
||||
switch target.Kind() {
|
||||
case ast.IdentKind:
|
||||
if target.AsIdent() != ns {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
return target.AsIdent(), true
|
||||
default:
|
||||
return false
|
||||
return "", false
|
||||
}
|
||||
}
|
||||
|
||||
func macroTargetMatchesNamespace(ns string, target ast.Expr) bool {
|
||||
if id, found := extractIdent(target); found {
|
||||
return id == ns
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
476
vendor/github.com/google/cel-go/ext/lists.go
generated
vendored
476
vendor/github.com/google/cel-go/ext/lists.go
generated
vendored
@ -16,15 +16,70 @@ package ext
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
"github.com/google/cel-go/cel"
|
||||
"github.com/google/cel-go/common/ast"
|
||||
"github.com/google/cel-go/common/decls"
|
||||
"github.com/google/cel-go/common/types"
|
||||
"github.com/google/cel-go/common/types/ref"
|
||||
"github.com/google/cel-go/common/types/traits"
|
||||
"github.com/google/cel-go/parser"
|
||||
)
|
||||
|
||||
var comparableTypes = []*cel.Type{
|
||||
cel.IntType,
|
||||
cel.UintType,
|
||||
cel.DoubleType,
|
||||
cel.BoolType,
|
||||
cel.DurationType,
|
||||
cel.TimestampType,
|
||||
cel.StringType,
|
||||
cel.BytesType,
|
||||
}
|
||||
|
||||
// Lists returns a cel.EnvOption to configure extended functions for list manipulation.
|
||||
// As a general note, all indices are zero-based.
|
||||
//
|
||||
// # Distinct
|
||||
//
|
||||
// Introduced in version: 2
|
||||
//
|
||||
// Returns the distinct elements of a list.
|
||||
//
|
||||
// <list(T)>.distinct() -> <list(T)>
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// [1, 2, 2, 3, 3, 3].distinct() // return [1, 2, 3]
|
||||
// ["b", "b", "c", "a", "c"].distinct() // return ["b", "c", "a"]
|
||||
// [1, "b", 2, "b"].distinct() // return [1, "b", 2]
|
||||
//
|
||||
// # Range
|
||||
//
|
||||
// Introduced in version: 2
|
||||
//
|
||||
// Returns a list of integers from 0 to n-1.
|
||||
//
|
||||
// lists.range(<int>) -> <list(int)>
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// lists.range(5) -> [0, 1, 2, 3, 4]
|
||||
//
|
||||
// # Reverse
|
||||
//
|
||||
// Introduced in version: 2
|
||||
//
|
||||
// Returns the elements of a list in reverse order.
|
||||
//
|
||||
// <list(T)>.reverse() -> <list(T)>
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// [5, 3, 1, 2].reverse() // return [2, 1, 3, 5]
|
||||
//
|
||||
// # Slice
|
||||
//
|
||||
// Returns a new sub-list using the indexes provided.
|
||||
@ -35,21 +90,105 @@ 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 will result in an error.
|
||||
//
|
||||
// <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) // error
|
||||
//
|
||||
// # Sort
|
||||
//
|
||||
// Introduced in version: 2
|
||||
//
|
||||
// Sorts a list with comparable elements. If the element type is not comparable
|
||||
// or the element types are not the same, the function will produce an error.
|
||||
//
|
||||
// <list(T)>.sort() -> <list(T)>
|
||||
// T in {int, uint, double, bool, duration, timestamp, string, bytes}
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// [3, 2, 1].sort() // return [1, 2, 3]
|
||||
// ["b", "c", "a"].sort() // return ["a", "b", "c"]
|
||||
// [1, "b"].sort() // error
|
||||
// [[1, 2, 3]].sort() // error
|
||||
//
|
||||
// # SortBy
|
||||
//
|
||||
// Sorts a list by a key value, i.e., the order is determined by the result of
|
||||
// an expression applied to each element of the list.
|
||||
// The output of the key expression must be a comparable type, otherwise the
|
||||
// function will return an error.
|
||||
//
|
||||
// <list(T)>.sortBy(<bindingName>, <keyExpr>) -> <list(T)>
|
||||
// keyExpr returns a value in {int, uint, double, bool, duration, timestamp, string, bytes}
|
||||
|
||||
// Examples:
|
||||
//
|
||||
// [
|
||||
// Player { name: "foo", score: 0 },
|
||||
// Player { name: "bar", score: -10 },
|
||||
// Player { name: "baz", score: 1000 },
|
||||
// ].sortBy(e, e.score).map(e, e.name)
|
||||
// == ["bar", "foo", "baz"]
|
||||
|
||||
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{
|
||||
listListType := cel.ListType(listType)
|
||||
listDyn := cel.ListType(cel.DynType)
|
||||
opts := []cel.EnvOption{
|
||||
cel.Function("slice",
|
||||
cel.MemberOverload("list_slice",
|
||||
[]*cel.Type{listType, cel.IntType, cel.IntType}, listType,
|
||||
@ -66,6 +205,151 @@ func (listsLib) CompileOptions() []cel.EnvOption {
|
||||
),
|
||||
),
|
||||
}
|
||||
if lib.version >= 1 {
|
||||
opts = append(opts,
|
||||
cel.Function("flatten",
|
||||
cel.MemberOverload("list_flatten",
|
||||
[]*cel.Type{listListType}, listType,
|
||||
cel.UnaryBinding(func(arg ref.Val) ref.Val {
|
||||
list, ok := arg.(traits.Lister)
|
||||
if !ok {
|
||||
return types.MaybeNoSuchOverloadErr(arg)
|
||||
}
|
||||
flatList, err := flatten(list, 1)
|
||||
if err != nil {
|
||||
return types.WrapErr(err)
|
||||
}
|
||||
|
||||
return types.DefaultTypeAdapter.NativeToValue(flatList)
|
||||
}),
|
||||
),
|
||||
cel.MemberOverload("list_flatten_int",
|
||||
[]*cel.Type{listDyn, types.IntType}, listDyn,
|
||||
cel.BinaryBinding(func(arg1, arg2 ref.Val) ref.Val {
|
||||
list, ok := arg1.(traits.Lister)
|
||||
if !ok {
|
||||
return types.MaybeNoSuchOverloadErr(arg1)
|
||||
}
|
||||
depth, ok := arg2.(types.Int)
|
||||
if !ok {
|
||||
return types.MaybeNoSuchOverloadErr(arg2)
|
||||
}
|
||||
flatList, err := flatten(list, int64(depth))
|
||||
if err != nil {
|
||||
return types.WrapErr(err)
|
||||
}
|
||||
|
||||
return types.DefaultTypeAdapter.NativeToValue(flatList)
|
||||
}),
|
||||
),
|
||||
// To handle the case where a variable of just `list(T)` is provided at runtime
|
||||
// with a graceful failure more, disable the type guards since the implementation
|
||||
// can handle lists which are already flat.
|
||||
decls.DisableTypeGuards(true),
|
||||
),
|
||||
)
|
||||
}
|
||||
if lib.version >= 2 {
|
||||
sortDecl := cel.Function("sort",
|
||||
append(
|
||||
templatedOverloads(comparableTypes, func(t *cel.Type) cel.FunctionOpt {
|
||||
return cel.MemberOverload(
|
||||
fmt.Sprintf("list_%s_sort", t.TypeName()),
|
||||
[]*cel.Type{cel.ListType(t)}, cel.ListType(t),
|
||||
)
|
||||
}),
|
||||
cel.SingletonUnaryBinding(
|
||||
func(arg ref.Val) ref.Val {
|
||||
list, ok := arg.(traits.Lister)
|
||||
if !ok {
|
||||
return types.MaybeNoSuchOverloadErr(arg)
|
||||
}
|
||||
sorted, err := sortList(list)
|
||||
if err != nil {
|
||||
return types.WrapErr(err)
|
||||
}
|
||||
|
||||
return sorted
|
||||
},
|
||||
// List traits
|
||||
traits.ListerType,
|
||||
),
|
||||
)...,
|
||||
)
|
||||
opts = append(opts, sortDecl)
|
||||
opts = append(opts, cel.Macros(cel.ReceiverMacro("sortBy", 2, sortByMacro)))
|
||||
opts = append(opts, cel.Function("@sortByAssociatedKeys",
|
||||
append(
|
||||
templatedOverloads(comparableTypes, func(u *cel.Type) cel.FunctionOpt {
|
||||
return cel.MemberOverload(
|
||||
fmt.Sprintf("list_%s_sortByAssociatedKeys", u.TypeName()),
|
||||
[]*cel.Type{listType, cel.ListType(u)}, listType,
|
||||
)
|
||||
}),
|
||||
cel.SingletonBinaryBinding(
|
||||
func(arg1 ref.Val, arg2 ref.Val) ref.Val {
|
||||
list, ok := arg1.(traits.Lister)
|
||||
if !ok {
|
||||
return types.MaybeNoSuchOverloadErr(arg1)
|
||||
}
|
||||
keys, ok := arg2.(traits.Lister)
|
||||
if !ok {
|
||||
return types.MaybeNoSuchOverloadErr(arg2)
|
||||
}
|
||||
sorted, err := sortListByAssociatedKeys(list, keys)
|
||||
if err != nil {
|
||||
return types.WrapErr(err)
|
||||
}
|
||||
|
||||
return sorted
|
||||
},
|
||||
// List traits
|
||||
traits.ListerType,
|
||||
),
|
||||
)...,
|
||||
))
|
||||
|
||||
opts = append(opts, cel.Function("lists.range",
|
||||
cel.Overload("lists_range",
|
||||
[]*cel.Type{cel.IntType}, cel.ListType(cel.IntType),
|
||||
cel.FunctionBinding(func(args ...ref.Val) ref.Val {
|
||||
n := args[0].(types.Int)
|
||||
result, err := genRange(n)
|
||||
if err != nil {
|
||||
return types.WrapErr(err)
|
||||
}
|
||||
return result
|
||||
}),
|
||||
),
|
||||
))
|
||||
opts = append(opts, cel.Function("reverse",
|
||||
cel.MemberOverload("list_reverse",
|
||||
[]*cel.Type{listType}, listType,
|
||||
cel.FunctionBinding(func(args ...ref.Val) ref.Val {
|
||||
list := args[0].(traits.Lister)
|
||||
result, err := reverseList(list)
|
||||
if err != nil {
|
||||
return types.WrapErr(err)
|
||||
}
|
||||
return result
|
||||
}),
|
||||
),
|
||||
))
|
||||
opts = append(opts, cel.Function("distinct",
|
||||
cel.MemberOverload("list_distinct",
|
||||
[]*cel.Type{listType}, listType,
|
||||
cel.UnaryBinding(func(list ref.Val) ref.Val {
|
||||
result, err := distinctList(list.(traits.Lister))
|
||||
if err != nil {
|
||||
return types.WrapErr(err)
|
||||
}
|
||||
return result
|
||||
}),
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
return opts
|
||||
}
|
||||
|
||||
// ProgramOptions implements the Library interface method.
|
||||
@ -73,6 +357,24 @@ func (listsLib) ProgramOptions() []cel.ProgramOption {
|
||||
return []cel.ProgramOption{}
|
||||
}
|
||||
|
||||
func genRange(n types.Int) (ref.Val, error) {
|
||||
var newList []ref.Val
|
||||
for i := types.Int(0); i < n; i++ {
|
||||
newList = append(newList, i)
|
||||
}
|
||||
return types.DefaultTypeAdapter.NativeToValue(newList), nil
|
||||
}
|
||||
|
||||
func reverseList(list traits.Lister) (ref.Val, error) {
|
||||
var newList []ref.Val
|
||||
listLength := list.Size().(types.Int)
|
||||
for i := types.Int(0); i < listLength; i++ {
|
||||
val := list.Get(listLength - i - 1)
|
||||
newList = append(newList, val)
|
||||
}
|
||||
return types.DefaultTypeAdapter.NativeToValue(newList), nil
|
||||
}
|
||||
|
||||
func slice(list traits.Lister, start, end types.Int) (ref.Val, error) {
|
||||
listLength := list.Size().(types.Int)
|
||||
if start < 0 || end < 0 {
|
||||
@ -92,3 +394,167 @@ 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, error) {
|
||||
if depth < 0 {
|
||||
return nil, fmt.Errorf("level must be non-negative")
|
||||
}
|
||||
|
||||
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 {
|
||||
flattenedList, err := flatten(nestedList, depth-1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newList = append(newList, flattenedList...)
|
||||
}
|
||||
}
|
||||
|
||||
return newList, nil
|
||||
}
|
||||
|
||||
func sortList(list traits.Lister) (ref.Val, error) {
|
||||
return sortListByAssociatedKeys(list, list)
|
||||
}
|
||||
|
||||
// Internal function used for the implementation of sort() and sortBy().
|
||||
//
|
||||
// Sorts a list of arbitrary elements, according to the order produced by sorting
|
||||
// another list of comparable elements. If the element type of the keys is not
|
||||
// comparable or the element types are not the same, the function will produce an error.
|
||||
//
|
||||
// <list(T)>.@sortByAssociatedKeys(<list(U)>) -> <list(T)>
|
||||
// U in {int, uint, double, bool, duration, timestamp, string, bytes}
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// ["foo", "bar", "baz"].@sortByAssociatedKeys([3, 1, 2]) // return ["bar", "baz", "foo"]
|
||||
func sortListByAssociatedKeys(list, keys traits.Lister) (ref.Val, error) {
|
||||
listLength := list.Size().(types.Int)
|
||||
keysLength := keys.Size().(types.Int)
|
||||
if listLength != keysLength {
|
||||
return nil, fmt.Errorf(
|
||||
"@sortByAssociatedKeys() expected a list of the same size as the associated keys list, but got %d and %d elements respectively",
|
||||
listLength,
|
||||
keysLength,
|
||||
)
|
||||
}
|
||||
if listLength == 0 {
|
||||
return list, nil
|
||||
}
|
||||
elem := keys.Get(types.IntZero)
|
||||
if _, ok := elem.(traits.Comparer); !ok {
|
||||
return nil, fmt.Errorf("list elements must be comparable")
|
||||
}
|
||||
|
||||
sortedIndices := make([]ref.Val, 0, listLength)
|
||||
for i := types.IntZero; i < listLength; i++ {
|
||||
if keys.Get(i).Type() != elem.Type() {
|
||||
return nil, fmt.Errorf("list elements must have the same type")
|
||||
}
|
||||
sortedIndices = append(sortedIndices, i)
|
||||
}
|
||||
|
||||
sort.Slice(sortedIndices, func(i, j int) bool {
|
||||
iKey := keys.Get(sortedIndices[i])
|
||||
jKey := keys.Get(sortedIndices[j])
|
||||
return iKey.(traits.Comparer).Compare(jKey) == types.IntNegOne
|
||||
})
|
||||
|
||||
sorted := make([]ref.Val, 0, listLength)
|
||||
|
||||
for _, sortedIdx := range sortedIndices {
|
||||
sorted = append(sorted, list.Get(sortedIdx))
|
||||
}
|
||||
return types.DefaultTypeAdapter.NativeToValue(sorted), nil
|
||||
}
|
||||
|
||||
// sortByMacro transforms an expression like:
|
||||
//
|
||||
// mylistExpr.sortBy(e, -math.abs(e))
|
||||
//
|
||||
// into something equivalent to:
|
||||
//
|
||||
// cel.bind(
|
||||
// __sortBy_input__,
|
||||
// myListExpr,
|
||||
// __sortBy_input__.@sortByAssociatedKeys(__sortBy_input__.map(e, -math.abs(e))
|
||||
// )
|
||||
func sortByMacro(meh cel.MacroExprFactory, target ast.Expr, args []ast.Expr) (ast.Expr, *cel.Error) {
|
||||
varIdent := meh.NewIdent("@__sortBy_input__")
|
||||
varName := varIdent.AsIdent()
|
||||
|
||||
targetKind := target.Kind()
|
||||
if targetKind != ast.ListKind &&
|
||||
targetKind != ast.SelectKind &&
|
||||
targetKind != ast.IdentKind &&
|
||||
targetKind != ast.ComprehensionKind && targetKind != ast.CallKind {
|
||||
return nil, meh.NewError(target.ID(), fmt.Sprintf("sortBy can only be applied to a list, identifier, comprehension, call or select expression"))
|
||||
}
|
||||
|
||||
mapCompr, err := parser.MakeMap(meh, meh.Copy(varIdent), args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
callExpr := meh.NewMemberCall("@sortByAssociatedKeys",
|
||||
meh.Copy(varIdent),
|
||||
mapCompr,
|
||||
)
|
||||
|
||||
bindExpr := meh.NewComprehension(
|
||||
meh.NewList(),
|
||||
"#unused",
|
||||
varName,
|
||||
target,
|
||||
meh.NewLiteral(types.False),
|
||||
varIdent,
|
||||
callExpr,
|
||||
)
|
||||
|
||||
return bindExpr, nil
|
||||
}
|
||||
|
||||
func distinctList(list traits.Lister) (ref.Val, error) {
|
||||
listLength := list.Size().(types.Int)
|
||||
if listLength == 0 {
|
||||
return list, nil
|
||||
}
|
||||
uniqueList := make([]ref.Val, 0, listLength)
|
||||
for i := types.IntZero; i < listLength; i++ {
|
||||
val := list.Get(i)
|
||||
seen := false
|
||||
for j := types.IntZero; j < types.Int(len(uniqueList)); j++ {
|
||||
if i == j {
|
||||
continue
|
||||
}
|
||||
other := uniqueList[j]
|
||||
if val.Equal(other) == types.True {
|
||||
seen = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !seen {
|
||||
uniqueList = append(uniqueList, val)
|
||||
}
|
||||
}
|
||||
|
||||
return types.DefaultTypeAdapter.NativeToValue(uniqueList), nil
|
||||
}
|
||||
|
||||
func templatedOverloads(types []*cel.Type, template func(t *cel.Type) cel.FunctionOpt) []cel.FunctionOpt {
|
||||
overloads := make([]cel.FunctionOpt, len(types))
|
||||
for i, t := range types {
|
||||
overloads[i] = template(t)
|
||||
}
|
||||
return overloads
|
||||
}
|
||||
|
559
vendor/github.com/google/cel-go/ext/math.go
generated
vendored
559
vendor/github.com/google/cel-go/ext/math.go
generated
vendored
@ -16,6 +16,7 @@ package ext
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
|
||||
"github.com/google/cel-go/cel"
|
||||
@ -86,28 +87,312 @@ import (
|
||||
// math.least('string') // parse error
|
||||
// math.least(a, b) // check-time error if a or b is non-numeric
|
||||
// math.least(dyn('string')) // runtime error
|
||||
func Math() cel.EnvOption {
|
||||
return cel.Lib(mathLib{})
|
||||
//
|
||||
// # Math.BitOr
|
||||
//
|
||||
// Introduced at version: 1
|
||||
//
|
||||
// Performs a bitwise-OR operation over two int or uint values.
|
||||
//
|
||||
// math.bitOr(<int>, <int>) -> <int>
|
||||
// math.bitOr(<uint>, <uint>) -> <uint>
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// math.bitOr(1u, 2u) // returns 3u
|
||||
// math.bitOr(-2, -4) // returns -2
|
||||
//
|
||||
// # Math.BitAnd
|
||||
//
|
||||
// Introduced at version: 1
|
||||
//
|
||||
// Performs a bitwise-AND operation over two int or uint values.
|
||||
//
|
||||
// math.bitAnd(<int>, <int>) -> <int>
|
||||
// math.bitAnd(<uint>, <uint>) -> <uint>
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// math.bitAnd(3u, 2u) // return 2u
|
||||
// math.bitAnd(3, 5) // returns 3
|
||||
// math.bitAnd(-3, -5) // returns -7
|
||||
//
|
||||
// # Math.BitXor
|
||||
//
|
||||
// Introduced at version: 1
|
||||
//
|
||||
// math.bitXor(<int>, <int>) -> <int>
|
||||
// math.bitXor(<uint>, <uint>) -> <uint>
|
||||
//
|
||||
// Performs a bitwise-XOR operation over two int or uint values.
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// math.bitXor(3u, 5u) // returns 6u
|
||||
// math.bitXor(1, 3) // returns 2
|
||||
//
|
||||
// # Math.BitNot
|
||||
//
|
||||
// Introduced at version: 1
|
||||
//
|
||||
// Function which accepts a single int or uint and performs a bitwise-NOT
|
||||
// ones-complement of the given binary value.
|
||||
//
|
||||
// math.bitNot(<int>) -> <int>
|
||||
// math.bitNot(<uint>) -> <uint>
|
||||
//
|
||||
// Examples
|
||||
//
|
||||
// math.bitNot(1) // returns -1
|
||||
// math.bitNot(-1) // return 0
|
||||
// math.bitNot(0u) // returns 18446744073709551615u
|
||||
//
|
||||
// # Math.BitShiftLeft
|
||||
//
|
||||
// Introduced at version: 1
|
||||
//
|
||||
// Perform a left shift of bits on the first parameter, by the amount of bits
|
||||
// specified in the second parameter. The first parameter is either a uint or
|
||||
// an int. The second parameter must be an int.
|
||||
//
|
||||
// When the second parameter is 64 or greater, 0 will be always be returned
|
||||
// since the number of bits shifted is greater than or equal to the total bit
|
||||
// length of the number being shifted. Negative valued bit shifts will result
|
||||
// in a runtime error.
|
||||
//
|
||||
// math.bitShiftLeft(<int>, <int>) -> <int>
|
||||
// math.bitShiftLeft(<uint>, <int>) -> <uint>
|
||||
//
|
||||
// Examples
|
||||
//
|
||||
// math.bitShiftLeft(1, 2) // returns 4
|
||||
// math.bitShiftLeft(-1, 2) // returns -4
|
||||
// math.bitShiftLeft(1u, 2) // return 4u
|
||||
// math.bitShiftLeft(1u, 200) // returns 0u
|
||||
//
|
||||
// # Math.BitShiftRight
|
||||
//
|
||||
// Introduced at version: 1
|
||||
//
|
||||
// Perform a right shift of bits on the first parameter, by the amount of bits
|
||||
// specified in the second parameter. The first parameter is either a uint or
|
||||
// an int. The second parameter must be an int.
|
||||
//
|
||||
// When the second parameter is 64 or greater, 0 will always be returned since
|
||||
// the number of bits shifted is greater than or equal to the total bit length
|
||||
// of the number being shifted. Negative valued bit shifts will result in a
|
||||
// runtime error.
|
||||
//
|
||||
// The sign bit extension will not be preserved for this operation: vacant bits
|
||||
// on the left are filled with 0.
|
||||
//
|
||||
// math.bitShiftRight(<int>, <int>) -> <int>
|
||||
// math.bitShiftRight(<uint>, <int>) -> <uint>
|
||||
//
|
||||
// Examples
|
||||
//
|
||||
// math.bitShiftRight(1024, 2) // returns 256
|
||||
// math.bitShiftRight(1024u, 2) // returns 256u
|
||||
// math.bitShiftRight(1024u, 64) // returns 0u
|
||||
//
|
||||
// # Math.Ceil
|
||||
//
|
||||
// Introduced at version: 1
|
||||
//
|
||||
// Compute the ceiling of a double value.
|
||||
//
|
||||
// math.ceil(<double>) -> <double>
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// math.ceil(1.2) // returns 2.0
|
||||
// math.ceil(-1.2) // returns -1.0
|
||||
//
|
||||
// # Math.Floor
|
||||
//
|
||||
// Introduced at version: 1
|
||||
//
|
||||
// Compute the floor of a double value.
|
||||
//
|
||||
// math.floor(<double>) -> <double>
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// math.floor(1.2) // returns 1.0
|
||||
// math.floor(-1.2) // returns -2.0
|
||||
//
|
||||
// # Math.Round
|
||||
//
|
||||
// Introduced at version: 1
|
||||
//
|
||||
// Rounds the double value to the nearest whole number with ties rounding away
|
||||
// from zero, e.g. 1.5 -> 2.0, -1.5 -> -2.0.
|
||||
//
|
||||
// math.round(<double>) -> <double>
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// math.round(1.2) // returns 1.0
|
||||
// math.round(1.5) // returns 2.0
|
||||
// math.round(-1.5) // returns -2.0
|
||||
//
|
||||
// # Math.Trunc
|
||||
//
|
||||
// Introduced at version: 1
|
||||
//
|
||||
// Truncates the fractional portion of the double value.
|
||||
//
|
||||
// math.trunc(<double>) -> <double>
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// math.trunc(-1.3) // returns -1.0
|
||||
// math.trunc(1.3) // returns 1.0
|
||||
//
|
||||
// # Math.Abs
|
||||
//
|
||||
// Introduced at version: 1
|
||||
//
|
||||
// Returns the absolute value of the numeric type provided as input. If the
|
||||
// value is NaN, the output is NaN. If the input is int64 min, the function
|
||||
// will result in an overflow error.
|
||||
//
|
||||
// math.abs(<double>) -> <double>
|
||||
// math.abs(<int>) -> <int>
|
||||
// math.abs(<uint>) -> <uint>
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// math.abs(-1) // returns 1
|
||||
// math.abs(1) // returns 1
|
||||
// math.abs(-9223372036854775808) // overflow error
|
||||
//
|
||||
// # Math.Sign
|
||||
//
|
||||
// Introduced at version: 1
|
||||
//
|
||||
// Returns the sign of the numeric type, either -1, 0, 1 as an int, double, or
|
||||
// uint depending on the overload. For floating point values, if NaN is
|
||||
// provided as input, the output is also NaN. The implementation does not
|
||||
// differentiate between positive and negative zero.
|
||||
//
|
||||
// math.sign(<double>) -> <double>
|
||||
// math.sign(<int>) -> <int>
|
||||
// math.sign(<uint>) -> <uint>
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// math.sign(-42) // returns -1
|
||||
// math.sign(0) // returns 0
|
||||
// math.sign(42) // returns 1
|
||||
//
|
||||
// # Math.IsInf
|
||||
//
|
||||
// Introduced at version: 1
|
||||
//
|
||||
// Returns true if the input double value is -Inf or +Inf.
|
||||
//
|
||||
// math.isInf(<double>) -> <bool>
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// math.isInf(1.0/0.0) // returns true
|
||||
// math.isInf(1.2) // returns false
|
||||
//
|
||||
// # Math.IsNaN
|
||||
//
|
||||
// Introduced at version: 1
|
||||
//
|
||||
// Returns true if the input double value is NaN, false otherwise.
|
||||
//
|
||||
// math.isNaN(<double>) -> <bool>
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// math.isNaN(0.0/0.0) // returns true
|
||||
// math.isNaN(1.2) // returns false
|
||||
//
|
||||
// # Math.IsFinite
|
||||
//
|
||||
// Introduced at version: 1
|
||||
//
|
||||
// Returns true if the value is a finite number. Equivalent in behavior to:
|
||||
// !math.isNaN(double) && !math.isInf(double)
|
||||
//
|
||||
// math.isFinite(<double>) -> <bool>
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// math.isFinite(0.0/0.0) // returns false
|
||||
// math.isFinite(1.2) // returns true
|
||||
func Math(options ...MathOption) cel.EnvOption {
|
||||
m := &mathLib{version: math.MaxUint32}
|
||||
for _, o := range options {
|
||||
m = o(m)
|
||||
}
|
||||
return cel.Lib(m)
|
||||
}
|
||||
|
||||
const (
|
||||
mathNamespace = "math"
|
||||
leastMacro = "least"
|
||||
greatestMacro = "greatest"
|
||||
minFunc = "math.@min"
|
||||
maxFunc = "math.@max"
|
||||
|
||||
// Min-max functions
|
||||
minFunc = "math.@min"
|
||||
maxFunc = "math.@max"
|
||||
|
||||
// Rounding functions
|
||||
ceilFunc = "math.ceil"
|
||||
floorFunc = "math.floor"
|
||||
roundFunc = "math.round"
|
||||
truncFunc = "math.trunc"
|
||||
|
||||
// Floating point helper functions
|
||||
isInfFunc = "math.isInf"
|
||||
isNanFunc = "math.isNaN"
|
||||
isFiniteFunc = "math.isFinite"
|
||||
|
||||
// Signedness functions
|
||||
absFunc = "math.abs"
|
||||
signFunc = "math.sign"
|
||||
|
||||
// Bitwise functions
|
||||
bitAndFunc = "math.bitAnd"
|
||||
bitOrFunc = "math.bitOr"
|
||||
bitXorFunc = "math.bitXor"
|
||||
bitNotFunc = "math.bitNot"
|
||||
bitShiftLeftFunc = "math.bitShiftLeft"
|
||||
bitShiftRightFunc = "math.bitShiftRight"
|
||||
)
|
||||
|
||||
type mathLib struct{}
|
||||
var (
|
||||
errIntOverflow = types.NewErr("integer overflow")
|
||||
)
|
||||
|
||||
// MathOption declares a functional operator for configuring math extensions.
|
||||
type MathOption func(*mathLib) *mathLib
|
||||
|
||||
// MathVersion sets the library version for math extensions.
|
||||
func MathVersion(version uint32) MathOption {
|
||||
return func(lib *mathLib) *mathLib {
|
||||
lib.version = version
|
||||
return lib
|
||||
}
|
||||
}
|
||||
|
||||
type mathLib struct {
|
||||
version uint32
|
||||
}
|
||||
|
||||
// LibraryName implements the SingletonLibrary interface method.
|
||||
func (mathLib) LibraryName() string {
|
||||
func (*mathLib) LibraryName() string {
|
||||
return "cel.lib.ext.math"
|
||||
}
|
||||
|
||||
// CompileOptions implements the Library interface method.
|
||||
func (mathLib) CompileOptions() []cel.EnvOption {
|
||||
return []cel.EnvOption{
|
||||
func (lib *mathLib) CompileOptions() []cel.EnvOption {
|
||||
opts := []cel.EnvOption{
|
||||
cel.Macros(
|
||||
// math.least(num, ...)
|
||||
cel.ReceiverVarArgMacro(leastMacro, mathLeast),
|
||||
@ -179,10 +464,95 @@ func (mathLib) CompileOptions() []cel.EnvOption {
|
||||
cel.UnaryBinding(maxList)),
|
||||
),
|
||||
}
|
||||
if lib.version >= 1 {
|
||||
opts = append(opts,
|
||||
// Rounding function declarations
|
||||
cel.Function(ceilFunc,
|
||||
cel.Overload("math_ceil_double", []*cel.Type{cel.DoubleType}, cel.DoubleType,
|
||||
cel.UnaryBinding(ceil))),
|
||||
cel.Function(floorFunc,
|
||||
cel.Overload("math_floor_double", []*cel.Type{cel.DoubleType}, cel.DoubleType,
|
||||
cel.UnaryBinding(floor))),
|
||||
cel.Function(roundFunc,
|
||||
cel.Overload("math_round_double", []*cel.Type{cel.DoubleType}, cel.DoubleType,
|
||||
cel.UnaryBinding(round))),
|
||||
cel.Function(truncFunc,
|
||||
cel.Overload("math_trunc_double", []*cel.Type{cel.DoubleType}, cel.DoubleType,
|
||||
cel.UnaryBinding(trunc))),
|
||||
|
||||
// Floating point helpers
|
||||
cel.Function(isInfFunc,
|
||||
cel.Overload("math_isInf_double", []*cel.Type{cel.DoubleType}, cel.BoolType,
|
||||
cel.UnaryBinding(isInf))),
|
||||
cel.Function(isNanFunc,
|
||||
cel.Overload("math_isNaN_double", []*cel.Type{cel.DoubleType}, cel.BoolType,
|
||||
cel.UnaryBinding(isNaN))),
|
||||
cel.Function(isFiniteFunc,
|
||||
cel.Overload("math_isFinite_double", []*cel.Type{cel.DoubleType}, cel.BoolType,
|
||||
cel.UnaryBinding(isFinite))),
|
||||
|
||||
// Signedness functions
|
||||
cel.Function(absFunc,
|
||||
cel.Overload("math_abs_double", []*cel.Type{cel.DoubleType}, cel.DoubleType,
|
||||
cel.UnaryBinding(absDouble)),
|
||||
cel.Overload("math_abs_int", []*cel.Type{cel.IntType}, cel.IntType,
|
||||
cel.UnaryBinding(absInt)),
|
||||
cel.Overload("math_abs_uint", []*cel.Type{cel.UintType}, cel.UintType,
|
||||
cel.UnaryBinding(identity)),
|
||||
),
|
||||
cel.Function(signFunc,
|
||||
cel.Overload("math_sign_double", []*cel.Type{cel.DoubleType}, cel.DoubleType,
|
||||
cel.UnaryBinding(sign)),
|
||||
cel.Overload("math_sign_int", []*cel.Type{cel.IntType}, cel.IntType,
|
||||
cel.UnaryBinding(sign)),
|
||||
cel.Overload("math_sign_uint", []*cel.Type{cel.UintType}, cel.UintType,
|
||||
cel.UnaryBinding(sign)),
|
||||
),
|
||||
|
||||
// Bitwise operator declarations
|
||||
cel.Function(bitAndFunc,
|
||||
cel.Overload("math_bitAnd_int_int", []*cel.Type{cel.IntType, cel.IntType}, cel.IntType,
|
||||
cel.BinaryBinding(bitAndPairInt)),
|
||||
cel.Overload("math_bitAnd_uint_uint", []*cel.Type{cel.UintType, cel.UintType}, cel.UintType,
|
||||
cel.BinaryBinding(bitAndPairUint)),
|
||||
),
|
||||
cel.Function(bitOrFunc,
|
||||
cel.Overload("math_bitOr_int_int", []*cel.Type{cel.IntType, cel.IntType}, cel.IntType,
|
||||
cel.BinaryBinding(bitOrPairInt)),
|
||||
cel.Overload("math_bitOr_uint_uint", []*cel.Type{cel.UintType, cel.UintType}, cel.UintType,
|
||||
cel.BinaryBinding(bitOrPairUint)),
|
||||
),
|
||||
cel.Function(bitXorFunc,
|
||||
cel.Overload("math_bitXor_int_int", []*cel.Type{cel.IntType, cel.IntType}, cel.IntType,
|
||||
cel.BinaryBinding(bitXorPairInt)),
|
||||
cel.Overload("math_bitXor_uint_uint", []*cel.Type{cel.UintType, cel.UintType}, cel.UintType,
|
||||
cel.BinaryBinding(bitXorPairUint)),
|
||||
),
|
||||
cel.Function(bitNotFunc,
|
||||
cel.Overload("math_bitNot_int_int", []*cel.Type{cel.IntType}, cel.IntType,
|
||||
cel.UnaryBinding(bitNotInt)),
|
||||
cel.Overload("math_bitNot_uint_uint", []*cel.Type{cel.UintType}, cel.UintType,
|
||||
cel.UnaryBinding(bitNotUint)),
|
||||
),
|
||||
cel.Function(bitShiftLeftFunc,
|
||||
cel.Overload("math_bitShiftLeft_int_int", []*cel.Type{cel.IntType, cel.IntType}, cel.IntType,
|
||||
cel.BinaryBinding(bitShiftLeftIntInt)),
|
||||
cel.Overload("math_bitShiftLeft_uint_int", []*cel.Type{cel.UintType, cel.IntType}, cel.UintType,
|
||||
cel.BinaryBinding(bitShiftLeftUintInt)),
|
||||
),
|
||||
cel.Function(bitShiftRightFunc,
|
||||
cel.Overload("math_bitShiftRight_int_int", []*cel.Type{cel.IntType, cel.IntType}, cel.IntType,
|
||||
cel.BinaryBinding(bitShiftRightIntInt)),
|
||||
cel.Overload("math_bitShiftRight_uint_int", []*cel.Type{cel.UintType, cel.IntType}, cel.UintType,
|
||||
cel.BinaryBinding(bitShiftRightUintInt)),
|
||||
),
|
||||
)
|
||||
}
|
||||
return opts
|
||||
}
|
||||
|
||||
// ProgramOptions implements the Library interface method.
|
||||
func (mathLib) ProgramOptions() []cel.ProgramOption {
|
||||
func (*mathLib) ProgramOptions() []cel.ProgramOption {
|
||||
return []cel.ProgramOption{}
|
||||
}
|
||||
|
||||
@ -194,7 +564,7 @@ func mathLeast(meh cel.MacroExprFactory, target ast.Expr, args []ast.Expr) (ast.
|
||||
case 0:
|
||||
return nil, meh.NewError(target.ID(), "math.least() requires at least one argument")
|
||||
case 1:
|
||||
if isListLiteralWithValidArgs(args[0]) || isValidArgType(args[0]) {
|
||||
if isListLiteralWithNumericArgs(args[0]) || isNumericArgType(args[0]) {
|
||||
return meh.NewCall(minFunc, args[0]), nil
|
||||
}
|
||||
return nil, meh.NewError(args[0].ID(), "math.least() invalid single argument value")
|
||||
@ -221,7 +591,7 @@ func mathGreatest(mef cel.MacroExprFactory, target ast.Expr, args []ast.Expr) (a
|
||||
case 0:
|
||||
return nil, mef.NewError(target.ID(), "math.greatest() requires at least one argument")
|
||||
case 1:
|
||||
if isListLiteralWithValidArgs(args[0]) || isValidArgType(args[0]) {
|
||||
if isListLiteralWithNumericArgs(args[0]) || isNumericArgType(args[0]) {
|
||||
return mef.NewCall(maxFunc, args[0]), nil
|
||||
}
|
||||
return nil, mef.NewError(args[0].ID(), "math.greatest() invalid single argument value")
|
||||
@ -244,6 +614,165 @@ func identity(val ref.Val) ref.Val {
|
||||
return val
|
||||
}
|
||||
|
||||
func ceil(val ref.Val) ref.Val {
|
||||
v := val.(types.Double)
|
||||
return types.Double(math.Ceil(float64(v)))
|
||||
}
|
||||
|
||||
func floor(val ref.Val) ref.Val {
|
||||
v := val.(types.Double)
|
||||
return types.Double(math.Floor(float64(v)))
|
||||
}
|
||||
|
||||
func round(val ref.Val) ref.Val {
|
||||
v := val.(types.Double)
|
||||
return types.Double(math.Round(float64(v)))
|
||||
}
|
||||
|
||||
func trunc(val ref.Val) ref.Val {
|
||||
v := val.(types.Double)
|
||||
return types.Double(math.Trunc(float64(v)))
|
||||
}
|
||||
|
||||
func isInf(val ref.Val) ref.Val {
|
||||
v := val.(types.Double)
|
||||
return types.Bool(math.IsInf(float64(v), 0))
|
||||
}
|
||||
|
||||
func isFinite(val ref.Val) ref.Val {
|
||||
v := float64(val.(types.Double))
|
||||
return types.Bool(!math.IsInf(v, 0) && !math.IsNaN(v))
|
||||
}
|
||||
|
||||
func isNaN(val ref.Val) ref.Val {
|
||||
v := val.(types.Double)
|
||||
return types.Bool(math.IsNaN(float64(v)))
|
||||
}
|
||||
|
||||
func absDouble(val ref.Val) ref.Val {
|
||||
v := float64(val.(types.Double))
|
||||
return types.Double(math.Abs(v))
|
||||
}
|
||||
|
||||
func absInt(val ref.Val) ref.Val {
|
||||
v := int64(val.(types.Int))
|
||||
if v == math.MinInt64 {
|
||||
return errIntOverflow
|
||||
}
|
||||
if v >= 0 {
|
||||
return val
|
||||
}
|
||||
return -types.Int(v)
|
||||
}
|
||||
|
||||
func sign(val ref.Val) ref.Val {
|
||||
switch v := val.(type) {
|
||||
case types.Double:
|
||||
if isNaN(v) == types.True {
|
||||
return v
|
||||
}
|
||||
zero := types.Double(0)
|
||||
if v > zero {
|
||||
return types.Double(1)
|
||||
}
|
||||
if v < zero {
|
||||
return types.Double(-1)
|
||||
}
|
||||
return zero
|
||||
case types.Int:
|
||||
return v.Compare(types.IntZero)
|
||||
case types.Uint:
|
||||
if v == types.Uint(0) {
|
||||
return types.Uint(0)
|
||||
}
|
||||
return types.Uint(1)
|
||||
default:
|
||||
return maybeSuffixError(val, "math.sign")
|
||||
}
|
||||
}
|
||||
|
||||
func bitAndPairInt(first, second ref.Val) ref.Val {
|
||||
l := first.(types.Int)
|
||||
r := second.(types.Int)
|
||||
return l & r
|
||||
}
|
||||
|
||||
func bitAndPairUint(first, second ref.Val) ref.Val {
|
||||
l := first.(types.Uint)
|
||||
r := second.(types.Uint)
|
||||
return l & r
|
||||
}
|
||||
|
||||
func bitOrPairInt(first, second ref.Val) ref.Val {
|
||||
l := first.(types.Int)
|
||||
r := second.(types.Int)
|
||||
return l | r
|
||||
}
|
||||
|
||||
func bitOrPairUint(first, second ref.Val) ref.Val {
|
||||
l := first.(types.Uint)
|
||||
r := second.(types.Uint)
|
||||
return l | r
|
||||
}
|
||||
|
||||
func bitXorPairInt(first, second ref.Val) ref.Val {
|
||||
l := first.(types.Int)
|
||||
r := second.(types.Int)
|
||||
return l ^ r
|
||||
}
|
||||
|
||||
func bitXorPairUint(first, second ref.Val) ref.Val {
|
||||
l := first.(types.Uint)
|
||||
r := second.(types.Uint)
|
||||
return l ^ r
|
||||
}
|
||||
|
||||
func bitNotInt(value ref.Val) ref.Val {
|
||||
v := value.(types.Int)
|
||||
return ^v
|
||||
}
|
||||
|
||||
func bitNotUint(value ref.Val) ref.Val {
|
||||
v := value.(types.Uint)
|
||||
return ^v
|
||||
}
|
||||
|
||||
func bitShiftLeftIntInt(value, bits ref.Val) ref.Val {
|
||||
v := value.(types.Int)
|
||||
bs := bits.(types.Int)
|
||||
if bs < types.IntZero {
|
||||
return types.NewErr("math.bitShiftLeft() negative offset: %d", bs)
|
||||
}
|
||||
return v << bs
|
||||
}
|
||||
|
||||
func bitShiftLeftUintInt(value, bits ref.Val) ref.Val {
|
||||
v := value.(types.Uint)
|
||||
bs := bits.(types.Int)
|
||||
if bs < types.IntZero {
|
||||
return types.NewErr("math.bitShiftLeft() negative offset: %d", bs)
|
||||
}
|
||||
return v << bs
|
||||
}
|
||||
|
||||
func bitShiftRightIntInt(value, bits ref.Val) ref.Val {
|
||||
v := value.(types.Int)
|
||||
bs := bits.(types.Int)
|
||||
if bs < types.IntZero {
|
||||
return types.NewErr("math.bitShiftRight() negative offset: %d", bs)
|
||||
}
|
||||
return types.Int(types.Uint(v) >> bs)
|
||||
}
|
||||
|
||||
func bitShiftRightUintInt(value, bits ref.Val) ref.Val {
|
||||
v := value.(types.Uint)
|
||||
bs := bits.(types.Int)
|
||||
if bs < types.IntZero {
|
||||
return types.NewErr("math.bitShiftRight() negative offset: %d", bs)
|
||||
}
|
||||
return v >> bs
|
||||
}
|
||||
|
||||
func minPair(first, second ref.Val) ref.Val {
|
||||
cmp, ok := first.(traits.Comparer)
|
||||
if !ok {
|
||||
@ -321,13 +850,13 @@ func checkInvalidArgs(meh cel.MacroExprFactory, funcName string, args []ast.Expr
|
||||
}
|
||||
|
||||
func checkInvalidArgLiteral(funcName string, arg ast.Expr) error {
|
||||
if !isValidArgType(arg) {
|
||||
if !isNumericArgType(arg) {
|
||||
return fmt.Errorf("%s simple literal arguments must be numeric", funcName)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func isValidArgType(arg ast.Expr) bool {
|
||||
func isNumericArgType(arg ast.Expr) bool {
|
||||
switch arg.Kind() {
|
||||
case ast.LiteralKind:
|
||||
c := ref.Val(arg.AsLiteral())
|
||||
@ -344,7 +873,7 @@ func isValidArgType(arg ast.Expr) bool {
|
||||
}
|
||||
}
|
||||
|
||||
func isListLiteralWithValidArgs(arg ast.Expr) bool {
|
||||
func isListLiteralWithNumericArgs(arg ast.Expr) bool {
|
||||
switch arg.Kind() {
|
||||
case ast.ListKind:
|
||||
list := arg.AsList()
|
||||
@ -352,7 +881,7 @@ func isListLiteralWithValidArgs(arg ast.Expr) bool {
|
||||
return false
|
||||
}
|
||||
for _, e := range list.Elements() {
|
||||
if !isValidArgType(e) {
|
||||
if !isNumericArgType(e) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
212
vendor/github.com/google/cel-go/ext/native.go
generated
vendored
212
vendor/github.com/google/cel-go/ext/native.go
generated
vendored
@ -15,6 +15,7 @@
|
||||
package ext
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
@ -77,12 +78,45 @@ var (
|
||||
// same advice holds if you are using custom type adapters and type providers. The native type
|
||||
// provider composes over whichever type adapter and provider is configured in the cel.Env at
|
||||
// the time that it is invoked.
|
||||
func NativeTypes(refTypes ...any) cel.EnvOption {
|
||||
//
|
||||
// There is also the possibility to rename the fields of native structs by setting the `cel` tag
|
||||
// for fields you want to override. In order to enable this feature, pass in the `EnableStructTag`
|
||||
// option. Here is an example to see it in action:
|
||||
//
|
||||
// ```go
|
||||
// package identity
|
||||
//
|
||||
// type Account struct {
|
||||
// ID int
|
||||
// OwnerName string `cel:"owner"`
|
||||
// }
|
||||
//
|
||||
// ```
|
||||
//
|
||||
// The `OwnerName` field is now accessible in CEL via `owner`, e.g. `identity.Account{owner: 'bob'}`.
|
||||
// In case there are duplicated field names in the struct, an error will be returned.
|
||||
func NativeTypes(args ...any) cel.EnvOption {
|
||||
return func(env *cel.Env) (*cel.Env, error) {
|
||||
tp, err := newNativeTypeProvider(env.CELTypeAdapter(), env.CELTypeProvider(), refTypes...)
|
||||
nativeTypes := make([]any, 0, len(args))
|
||||
tpOptions := nativeTypeOptions{}
|
||||
|
||||
for _, v := range args {
|
||||
switch v := v.(type) {
|
||||
case NativeTypesOption:
|
||||
err := v(&tpOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
nativeTypes = append(nativeTypes, v)
|
||||
}
|
||||
}
|
||||
|
||||
tp, err := newNativeTypeProvider(tpOptions, env.CELTypeAdapter(), env.CELTypeProvider(), nativeTypes...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
env, err = cel.CustomTypeAdapter(tp)(env)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -91,12 +125,79 @@ func NativeTypes(refTypes ...any) cel.EnvOption {
|
||||
}
|
||||
}
|
||||
|
||||
func newNativeTypeProvider(adapter types.Adapter, provider types.Provider, refTypes ...any) (*nativeTypeProvider, error) {
|
||||
// NativeTypesOption is a functional interface for configuring handling of native types.
|
||||
type NativeTypesOption func(*nativeTypeOptions) error
|
||||
|
||||
// NativeTypesFieldNameHandler is a handler for mapping a reflect.StructField to a CEL field name.
|
||||
// This can be used to override the default Go struct field to CEL field name mapping.
|
||||
type NativeTypesFieldNameHandler = func(field reflect.StructField) string
|
||||
|
||||
func fieldNameByTag(structTagToParse string) func(field reflect.StructField) string {
|
||||
return func(field reflect.StructField) string {
|
||||
tag, found := field.Tag.Lookup(structTagToParse)
|
||||
if found {
|
||||
splits := strings.Split(tag, ",")
|
||||
if len(splits) > 0 {
|
||||
// We make the assumption that the leftmost entry in the tag is the name.
|
||||
// This seems to be true for most tags that have the concept of a name/key, such as:
|
||||
// https://pkg.go.dev/encoding/xml#Marshal
|
||||
// https://pkg.go.dev/encoding/json#Marshal
|
||||
// https://pkg.go.dev/go.mongodb.org/mongo-driver/bson#hdr-Structs
|
||||
// https://pkg.go.dev/gopkg.in/yaml.v2#Marshal
|
||||
name := splits[0]
|
||||
return name
|
||||
}
|
||||
}
|
||||
|
||||
return field.Name
|
||||
}
|
||||
}
|
||||
|
||||
type nativeTypeOptions struct {
|
||||
// fieldNameHandler controls how CEL should perform struct field renames.
|
||||
// This is most commonly used for switching to parsing based off the struct field tag,
|
||||
// such as "cel" or "json".
|
||||
fieldNameHandler NativeTypesFieldNameHandler
|
||||
}
|
||||
|
||||
// ParseStructTags configures if native types field names should be overridable by CEL struct tags.
|
||||
// This is equivalent to ParseStructTag("cel")
|
||||
func ParseStructTags(enabled bool) NativeTypesOption {
|
||||
return func(ntp *nativeTypeOptions) error {
|
||||
if enabled {
|
||||
ntp.fieldNameHandler = fieldNameByTag("cel")
|
||||
} else {
|
||||
ntp.fieldNameHandler = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// ParseStructTag configures the struct tag to parse. The 0th item in the tag is used as the name of the CEL field.
|
||||
// For example:
|
||||
// If the tag to parse is "cel" and the struct field has tag cel:"foo", the CEL struct field will be "foo".
|
||||
// If the tag to parse is "json" and the struct field has tag json:"foo,omitempty", the CEL struct field will be "foo".
|
||||
func ParseStructTag(tag string) NativeTypesOption {
|
||||
return func(ntp *nativeTypeOptions) error {
|
||||
ntp.fieldNameHandler = fieldNameByTag(tag)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// ParseStructField configures how to parse Go struct fields. It can be used to customize struct field parsing.
|
||||
func ParseStructField(handler NativeTypesFieldNameHandler) NativeTypesOption {
|
||||
return func(ntp *nativeTypeOptions) error {
|
||||
ntp.fieldNameHandler = handler
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func newNativeTypeProvider(tpOptions nativeTypeOptions, adapter types.Adapter, provider types.Provider, refTypes ...any) (*nativeTypeProvider, error) {
|
||||
nativeTypes := make(map[string]*nativeType, len(refTypes))
|
||||
for _, refType := range refTypes {
|
||||
switch rt := refType.(type) {
|
||||
case reflect.Type:
|
||||
result, err := newNativeTypes(rt)
|
||||
result, err := newNativeTypes(tpOptions.fieldNameHandler, rt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -104,7 +205,7 @@ func newNativeTypeProvider(adapter types.Adapter, provider types.Provider, refTy
|
||||
nativeTypes[result[idx].TypeName()] = result[idx]
|
||||
}
|
||||
case reflect.Value:
|
||||
result, err := newNativeTypes(rt.Type())
|
||||
result, err := newNativeTypes(tpOptions.fieldNameHandler, rt.Type())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -119,6 +220,7 @@ func newNativeTypeProvider(adapter types.Adapter, provider types.Provider, refTy
|
||||
nativeTypes: nativeTypes,
|
||||
baseAdapter: adapter,
|
||||
baseProvider: provider,
|
||||
options: tpOptions,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -126,6 +228,7 @@ type nativeTypeProvider struct {
|
||||
nativeTypes map[string]*nativeType
|
||||
baseAdapter types.Adapter
|
||||
baseProvider types.Provider
|
||||
options nativeTypeOptions
|
||||
}
|
||||
|
||||
// EnumValue proxies to the types.Provider configured at the times the NativeTypes
|
||||
@ -155,6 +258,14 @@ func (tp *nativeTypeProvider) FindStructType(typeName string) (*types.Type, bool
|
||||
return tp.baseProvider.FindStructType(typeName)
|
||||
}
|
||||
|
||||
func toFieldName(fieldNameHandler NativeTypesFieldNameHandler, f reflect.StructField) string {
|
||||
if fieldNameHandler == nil {
|
||||
return f.Name
|
||||
}
|
||||
|
||||
return fieldNameHandler(f)
|
||||
}
|
||||
|
||||
// FindStructFieldNames looks up the type definition first from the native types, then from
|
||||
// the backing provider type set. If found, a set of field names corresponding to the type
|
||||
// will be returned.
|
||||
@ -163,7 +274,7 @@ func (tp *nativeTypeProvider) FindStructFieldNames(typeName string) ([]string, b
|
||||
fieldCount := t.refType.NumField()
|
||||
fields := make([]string, fieldCount)
|
||||
for i := 0; i < fieldCount; i++ {
|
||||
fields[i] = t.refType.Field(i).Name
|
||||
fields[i] = toFieldName(tp.options.fieldNameHandler, t.refType.Field(i))
|
||||
}
|
||||
return fields, true
|
||||
}
|
||||
@ -192,13 +303,13 @@ func (tp *nativeTypeProvider) FindStructFieldType(typeName, fieldName string) (*
|
||||
Type: celType,
|
||||
IsSet: func(obj any) bool {
|
||||
refVal := reflect.Indirect(reflect.ValueOf(obj))
|
||||
refField := refVal.FieldByName(fieldName)
|
||||
refField := refVal.FieldByName(refField.Name)
|
||||
return !refField.IsZero()
|
||||
},
|
||||
GetFrom: func(obj any) (any, error) {
|
||||
refVal := reflect.Indirect(reflect.ValueOf(obj))
|
||||
refField := refVal.FieldByName(fieldName)
|
||||
return getFieldValue(tp, refField), nil
|
||||
refField := refVal.FieldByName(refField.Name)
|
||||
return getFieldValue(refField), nil
|
||||
},
|
||||
}, true
|
||||
}
|
||||
@ -249,6 +360,9 @@ func (tp *nativeTypeProvider) NativeToValue(val any) ref.Val {
|
||||
case []byte:
|
||||
return tp.baseAdapter.NativeToValue(val)
|
||||
default:
|
||||
if refVal.Type().Elem() == reflect.TypeOf(byte(0)) {
|
||||
return tp.baseAdapter.NativeToValue(val)
|
||||
}
|
||||
return types.NewDynamicList(tp, val)
|
||||
}
|
||||
case reflect.Map:
|
||||
@ -259,7 +373,7 @@ func (tp *nativeTypeProvider) NativeToValue(val any) ref.Val {
|
||||
time.Time:
|
||||
return tp.baseAdapter.NativeToValue(val)
|
||||
default:
|
||||
return newNativeObject(tp, val, rawVal)
|
||||
return tp.newNativeObject(val, rawVal)
|
||||
}
|
||||
default:
|
||||
return tp.baseAdapter.NativeToValue(val)
|
||||
@ -319,13 +433,13 @@ func convertToCelType(refType reflect.Type) (*cel.Type, bool) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func newNativeObject(adapter types.Adapter, val any, refValue reflect.Value) ref.Val {
|
||||
valType, err := newNativeType(refValue.Type())
|
||||
func (tp *nativeTypeProvider) newNativeObject(val any, refValue reflect.Value) ref.Val {
|
||||
valType, err := newNativeType(tp.options.fieldNameHandler, refValue.Type())
|
||||
if err != nil {
|
||||
return types.NewErr(err.Error())
|
||||
}
|
||||
return &nativeObj{
|
||||
Adapter: adapter,
|
||||
Adapter: tp,
|
||||
val: val,
|
||||
valType: valType,
|
||||
refValue: refValue,
|
||||
@ -372,12 +486,13 @@ func (o *nativeObj) ConvertToNative(typeDesc reflect.Type) (any, error) {
|
||||
if !fieldValue.IsValid() || fieldValue.IsZero() {
|
||||
continue
|
||||
}
|
||||
fieldName := toFieldName(o.valType.fieldNameHandler, fieldType)
|
||||
fieldCELVal := o.NativeToValue(fieldValue.Interface())
|
||||
fieldJSONVal, err := fieldCELVal.ConvertToNative(jsonValueType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fields[fieldType.Name] = fieldJSONVal.(*structpb.Value)
|
||||
fields[fieldName] = fieldJSONVal.(*structpb.Value)
|
||||
}
|
||||
return &structpb.Struct{Fields: fields}, nil
|
||||
}
|
||||
@ -469,8 +584,8 @@ func (o *nativeObj) Value() any {
|
||||
return o.val
|
||||
}
|
||||
|
||||
func newNativeTypes(rawType reflect.Type) ([]*nativeType, error) {
|
||||
nt, err := newNativeType(rawType)
|
||||
func newNativeTypes(fieldNameHandler NativeTypesFieldNameHandler, rawType reflect.Type) ([]*nativeType, error) {
|
||||
nt, err := newNativeType(fieldNameHandler, rawType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -489,7 +604,7 @@ func newNativeTypes(rawType reflect.Type) ([]*nativeType, error) {
|
||||
return
|
||||
}
|
||||
alreadySeen[t.String()] = struct{}{}
|
||||
nt, ntErr := newNativeType(t)
|
||||
nt, ntErr := newNativeType(fieldNameHandler, t)
|
||||
if ntErr != nil {
|
||||
err = ntErr
|
||||
return
|
||||
@ -505,7 +620,11 @@ func newNativeTypes(rawType reflect.Type) ([]*nativeType, error) {
|
||||
return result, err
|
||||
}
|
||||
|
||||
func newNativeType(rawType reflect.Type) (*nativeType, error) {
|
||||
var (
|
||||
errDuplicatedFieldName = errors.New("field name already exists in struct")
|
||||
)
|
||||
|
||||
func newNativeType(fieldNameHandler NativeTypesFieldNameHandler, rawType reflect.Type) (*nativeType, error) {
|
||||
refType := rawType
|
||||
if refType.Kind() == reflect.Pointer {
|
||||
refType = refType.Elem()
|
||||
@ -513,15 +632,34 @@ func newNativeType(rawType reflect.Type) (*nativeType, error) {
|
||||
if !isValidObjectType(refType) {
|
||||
return nil, fmt.Errorf("unsupported reflect.Type %v, must be reflect.Struct", rawType)
|
||||
}
|
||||
|
||||
// Since naming collisions can only happen with struct tag parsing, we only check for them if it is enabled.
|
||||
if fieldNameHandler != nil {
|
||||
fieldNames := make(map[string]struct{})
|
||||
|
||||
for idx := 0; idx < refType.NumField(); idx++ {
|
||||
field := refType.Field(idx)
|
||||
fieldName := toFieldName(fieldNameHandler, field)
|
||||
|
||||
if _, found := fieldNames[fieldName]; found {
|
||||
return nil, fmt.Errorf("invalid field name `%s` in struct `%s`: %w", fieldName, refType.Name(), errDuplicatedFieldName)
|
||||
} else {
|
||||
fieldNames[fieldName] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &nativeType{
|
||||
typeName: fmt.Sprintf("%s.%s", simplePkgAlias(refType.PkgPath()), refType.Name()),
|
||||
refType: refType,
|
||||
typeName: fmt.Sprintf("%s.%s", simplePkgAlias(refType.PkgPath()), refType.Name()),
|
||||
refType: refType,
|
||||
fieldNameHandler: fieldNameHandler,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type nativeType struct {
|
||||
typeName string
|
||||
refType reflect.Type
|
||||
typeName string
|
||||
refType reflect.Type
|
||||
fieldNameHandler NativeTypesFieldNameHandler
|
||||
}
|
||||
|
||||
// ConvertToNative implements ref.Val.ConvertToNative.
|
||||
@ -569,9 +707,26 @@ func (t *nativeType) Value() any {
|
||||
return t.typeName
|
||||
}
|
||||
|
||||
// fieldByName returns the corresponding reflect.StructField for the give name either by matching
|
||||
// field tag or field name.
|
||||
func (t *nativeType) fieldByName(fieldName string) (reflect.StructField, bool) {
|
||||
if t.fieldNameHandler == nil {
|
||||
return t.refType.FieldByName(fieldName)
|
||||
}
|
||||
|
||||
for i := 0; i < t.refType.NumField(); i++ {
|
||||
f := t.refType.Field(i)
|
||||
if toFieldName(t.fieldNameHandler, f) == fieldName {
|
||||
return f, true
|
||||
}
|
||||
}
|
||||
|
||||
return reflect.StructField{}, false
|
||||
}
|
||||
|
||||
// hasField returns whether a field name has a corresponding Golang reflect.StructField
|
||||
func (t *nativeType) hasField(fieldName string) (reflect.StructField, bool) {
|
||||
f, found := t.refType.FieldByName(fieldName)
|
||||
f, found := t.fieldByName(fieldName)
|
||||
if !found || !f.IsExported() || !isSupportedType(f.Type) {
|
||||
return reflect.StructField{}, false
|
||||
}
|
||||
@ -579,21 +734,16 @@ func (t *nativeType) hasField(fieldName string) (reflect.StructField, bool) {
|
||||
}
|
||||
|
||||
func adaptFieldValue(adapter types.Adapter, refField reflect.Value) ref.Val {
|
||||
return adapter.NativeToValue(getFieldValue(adapter, refField))
|
||||
return adapter.NativeToValue(getFieldValue(refField))
|
||||
}
|
||||
|
||||
func getFieldValue(adapter types.Adapter, refField reflect.Value) any {
|
||||
func getFieldValue(refField reflect.Value) any {
|
||||
if refField.IsZero() {
|
||||
switch refField.Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
return types.NewDynamicList(adapter, []ref.Val{})
|
||||
case reflect.Map:
|
||||
return types.NewDynamicMap(adapter, map[ref.Val]ref.Val{})
|
||||
case reflect.Struct:
|
||||
if refField.Type() == timestampType {
|
||||
return types.Timestamp{Time: time.Unix(0, 0)}
|
||||
return time.Unix(0, 0)
|
||||
}
|
||||
return reflect.New(refField.Type()).Elem().Interface()
|
||||
case reflect.Pointer:
|
||||
return reflect.New(refField.Type().Elem()).Interface()
|
||||
}
|
||||
|
18
vendor/github.com/google/cel-go/ext/strings.go
generated
vendored
18
vendor/github.com/google/cel-go/ext/strings.go
generated
vendored
@ -119,7 +119,8 @@ const (
|
||||
// 'hello mellow'.indexOf('jello') // returns -1
|
||||
// 'hello mellow'.indexOf('', 2) // returns 2
|
||||
// 'hello mellow'.indexOf('ello', 2) // returns 7
|
||||
// 'hello mellow'.indexOf('ello', 20) // error
|
||||
// 'hello mellow'.indexOf('ello', 20) // returns -1
|
||||
// 'hello mellow'.indexOf('ello', -1) // error
|
||||
//
|
||||
// # Join
|
||||
//
|
||||
@ -155,6 +156,7 @@ const (
|
||||
// 'hello mellow'.lastIndexOf('ello') // returns 7
|
||||
// 'hello mellow'.lastIndexOf('jello') // returns -1
|
||||
// 'hello mellow'.lastIndexOf('ello', 6) // returns 1
|
||||
// 'hello mellow'.lastIndexOf('ello', 20) // returns -1
|
||||
// 'hello mellow'.lastIndexOf('ello', -1) // error
|
||||
//
|
||||
// # LowerAscii
|
||||
@ -520,7 +522,7 @@ func (lib *stringLib) CompileOptions() []cel.EnvOption {
|
||||
if lib.version >= 3 {
|
||||
opts = append(opts,
|
||||
cel.Function("reverse",
|
||||
cel.MemberOverload("reverse", []*cel.Type{cel.StringType}, cel.StringType,
|
||||
cel.MemberOverload("string_reverse", []*cel.Type{cel.StringType}, cel.StringType,
|
||||
cel.UnaryBinding(func(str ref.Val) ref.Val {
|
||||
s := str.(types.String)
|
||||
return stringOrError(reverse(string(s)))
|
||||
@ -561,9 +563,13 @@ func indexOfOffset(str, substr string, offset int64) (int64, error) {
|
||||
off := int(offset)
|
||||
runes := []rune(str)
|
||||
subrunes := []rune(substr)
|
||||
if off < 0 || off >= len(runes) {
|
||||
if off < 0 {
|
||||
return -1, fmt.Errorf("index out of range: %d", off)
|
||||
}
|
||||
// If the offset exceeds the length, return -1 rather than error.
|
||||
if off >= len(runes) {
|
||||
return -1, nil
|
||||
}
|
||||
for i := off; i < len(runes)-(len(subrunes)-1); i++ {
|
||||
found := true
|
||||
for j := 0; j < len(subrunes); j++ {
|
||||
@ -594,9 +600,13 @@ func lastIndexOfOffset(str, substr string, offset int64) (int64, error) {
|
||||
off := int(offset)
|
||||
runes := []rune(str)
|
||||
subrunes := []rune(substr)
|
||||
if off < 0 || off >= len(runes) {
|
||||
if off < 0 {
|
||||
return -1, fmt.Errorf("index out of range: %d", off)
|
||||
}
|
||||
// If the offset is far greater than the length return -1
|
||||
if off >= len(runes) {
|
||||
return -1, nil
|
||||
}
|
||||
if off > len(runes)-len(subrunes) {
|
||||
off = len(runes) - len(subrunes)
|
||||
}
|
||||
|
Reference in New Issue
Block a user