cel 0.2.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +24 -0
- data/README.md +23 -1
- data/lib/cel/ast/elements/protobuf.rb +411 -66
- data/lib/cel/ast/elements.rb +340 -103
- data/lib/cel/ast/types.rb +151 -15
- data/lib/cel/checker.rb +133 -30
- data/lib/cel/context.rb +55 -6
- data/lib/cel/encoder.rb +31 -13
- data/lib/cel/environment.rb +58 -13
- data/lib/cel/errors.rb +19 -1
- data/lib/cel/macro.rb +91 -26
- data/lib/cel/parser.rb +430 -396
- data/lib/cel/program.rb +255 -48
- data/lib/cel/version.rb +1 -1
- data/lib/cel.rb +232 -1
- metadata +18 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bcb8e069501a6c9cfdf6af67f83701111d67130d04a9208a05482d14ac1c519a
|
4
|
+
data.tar.gz: 0af115f5924dd63f926227fd916c2fe3f2b25bb6a68e682df1c9f5be36b1be6f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d78064a96c11a026f1b6f16a3ebdfed6f3f528170975ab9c26b1e7438c7805419830418f32e7b6557bafeace936c35f1dc18f695a1294862780c2aff9e0bc335
|
7
|
+
data.tar.gz: 9897a6ed3a7708cefaf1b6bd24f86802b1c50709cbbb9bab1fd843e31e3dc1f1f049f4770afa0c34ed89c91b30442a95738b30eb52d91e64d99f1dbc880e0de9
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,29 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.3.0] - 2025-06-12
|
4
|
+
|
5
|
+
### Features
|
6
|
+
|
7
|
+
* Integration with `google-protobuf` (optional dependency) by evaluating messages to protobuf stubs, and making them an integral part of the CEL type set. Some examples:
|
8
|
+
* auto-conversion of protobuf wrapper types.
|
9
|
+
* enums support
|
10
|
+
|
11
|
+
### Improvements
|
12
|
+
|
13
|
+
* More correct parsing, checking and evaluation of CEL expressions, thanks to the issues identified by conformance tests.
|
14
|
+
|
15
|
+
### Chore
|
16
|
+
|
17
|
+
* Incorporation of the oficial cel-spec conformance tests into the test suite, to ensure feature correctness.
|
18
|
+
* `bigdecimal` added as explicit dependency.
|
19
|
+
|
20
|
+
## [0.2.3] - 2023-09-19
|
21
|
+
|
22
|
+
### Bugfixes
|
23
|
+
|
24
|
+
* `cel` is loadable without the need to install `google/protobuf` again.
|
25
|
+
+ expressions containing te operator `<=` were failing while parsing.
|
26
|
+
|
3
27
|
## [0.2.2] - 2023-08-17
|
4
28
|
|
5
29
|
* Reimplements `Cel::Literal#==` in a simpler way, to avoid several comparison issues arising from the previous implementation.
|
data/README.md
CHANGED
@@ -107,9 +107,23 @@ env2 = environment(foo: -> (a, b) { a + b})
|
|
107
107
|
env2.evaluate("foo(2, 2)") #=> 4
|
108
108
|
```
|
109
109
|
|
110
|
+
## Spec Coverage
|
111
|
+
|
112
|
+
`cel` is tested against the conformance suite from the [cel-spec repository](https://github.com/google/cel-spec/tree/master/conformance), and supports all features from the language except:
|
113
|
+
|
114
|
+
* math extensions
|
115
|
+
* string extensions
|
116
|
+
* bindings extensions
|
117
|
+
* block extensions
|
118
|
+
* encoders extensions
|
119
|
+
* comprehensions V2 API
|
120
|
+
* optionals
|
121
|
+
|
122
|
+
If this is something you're interested in (helping out), add a mention in the corresponding issue (or create one when non is available already).
|
123
|
+
|
110
124
|
## Supported Rubies
|
111
125
|
|
112
|
-
All Rubies greater or equal to 2.
|
126
|
+
All Rubies greater or equal to 2.7, and always latest JRuby and Truffleruby.
|
113
127
|
|
114
128
|
## Development
|
115
129
|
|
@@ -118,8 +132,16 @@ Clone the repo in your local machine, where you have `ruby` installed. Then you
|
|
118
132
|
```bash
|
119
133
|
# install dev dependencies
|
120
134
|
> bundle install
|
135
|
+
# create protobuf stubs for tests
|
136
|
+
> git clone --depth 1 https://github.com/google/cel-spec.git
|
137
|
+
> git clone --depth 1 https://github.com/googleapis/googleapis.git
|
138
|
+
> bundle exec rake build_test_protos
|
121
139
|
# run tests
|
122
140
|
> bundle exec rake test
|
141
|
+
# build protobuf stubs for conformance tests
|
142
|
+
> bundle exec rake build_conformance_protos
|
143
|
+
# run conformance tests
|
144
|
+
> bundle exec rake conformance
|
123
145
|
```
|
124
146
|
|
125
147
|
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "google/protobuf/descriptor_pb"
|
3
4
|
require "google/protobuf/struct_pb"
|
4
5
|
require "google/protobuf/wrappers_pb"
|
5
6
|
require "google/protobuf/any_pb"
|
@@ -9,94 +10,287 @@ module Cel
|
|
9
10
|
module Protobuf
|
10
11
|
module_function
|
11
12
|
|
12
|
-
def
|
13
|
+
def base_class
|
14
|
+
Google::Protobuf::MessageExts
|
15
|
+
end
|
16
|
+
|
17
|
+
def map_class
|
18
|
+
Google::Protobuf::Map
|
19
|
+
end
|
20
|
+
|
21
|
+
def timestamp_class
|
22
|
+
Google::Protobuf::Timestamp
|
23
|
+
end
|
24
|
+
|
25
|
+
def duration_class
|
26
|
+
Google::Protobuf::Duration
|
27
|
+
end
|
28
|
+
|
29
|
+
def enum_class
|
30
|
+
Enum
|
31
|
+
end
|
32
|
+
|
33
|
+
def try_convert_from_wrapper(msg)
|
13
34
|
case msg
|
14
35
|
when Google::Protobuf::Any
|
36
|
+
raise EvaluateError, "error conversion for empty Any" unless msg.type_name
|
15
37
|
|
16
|
-
|
38
|
+
type_msg = Object.const_get(Cel.package_to_module_name(msg.type_name))
|
39
|
+
try_convert_from_wrapper(msg.unpack(type_msg))
|
17
40
|
when Google::Protobuf::ListValue
|
18
41
|
msg.to_a
|
19
42
|
when Google::Protobuf::Struct
|
20
43
|
msg.to_h
|
21
44
|
when Google::Protobuf::Value
|
22
|
-
msg.to_ruby
|
45
|
+
msg.to_ruby(true)
|
46
|
+
when Google::Protobuf::BytesValue
|
47
|
+
Cel::Bytes.new(msg.value.bytes)
|
48
|
+
when Google::Protobuf::Int32Value,
|
49
|
+
Google::Protobuf::Int64Value
|
50
|
+
Cel::Number.new(:int, msg.value)
|
51
|
+
when Google::Protobuf::UInt32Value,
|
52
|
+
Google::Protobuf::UInt64Value
|
53
|
+
Cel::Number.new(:uint, msg.value)
|
54
|
+
when Google::Protobuf::DoubleValue,
|
55
|
+
Google::Protobuf::FloatValue
|
56
|
+
Cel::Number.new(:double, msg.value)
|
23
57
|
when Google::Protobuf::BoolValue,
|
24
|
-
Google::Protobuf::BytesValue,
|
25
|
-
Google::Protobuf::DoubleValue,
|
26
|
-
Google::Protobuf::FloatValue,
|
27
|
-
Google::Protobuf::Int32Value,
|
28
|
-
Google::Protobuf::Int64Value,
|
29
|
-
Google::Protobuf::UInt32Value,
|
30
|
-
Google::Protobuf::UInt64Value,
|
31
58
|
Google::Protobuf::NullValue,
|
32
59
|
Google::Protobuf::StringValue
|
60
|
+
# conversion to cel type won't be ambiguous here
|
33
61
|
msg.value
|
34
62
|
when Google::Protobuf::Timestamp
|
35
63
|
msg.to_time
|
36
64
|
when Google::Protobuf::Duration
|
37
65
|
Cel::Duration.new(seconds: msg.seconds, nanos: msg.nanos)
|
38
66
|
else
|
39
|
-
|
67
|
+
msg.extend(CelComparisonMode)
|
68
|
+
msg
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def try_convert_from_wrapper_type(*)
|
73
|
+
TYPES[:map]
|
74
|
+
end
|
75
|
+
|
76
|
+
def convert_to_proto_type(type, package)
|
77
|
+
proto_type = Cel.package_to_module_name(type)
|
78
|
+
|
79
|
+
return unless proto_type
|
80
|
+
|
81
|
+
return Object.const_get(proto_type) if Object.const_defined?(proto_type)
|
82
|
+
|
83
|
+
return unless package
|
84
|
+
|
85
|
+
return unless package.const_defined?(proto_type)
|
86
|
+
|
87
|
+
package.const_get(proto_type)
|
88
|
+
end
|
89
|
+
|
90
|
+
def convert_to_cel_type(proto, package)
|
91
|
+
case proto
|
92
|
+
when Google::Protobuf::FieldDescriptor
|
93
|
+
|
94
|
+
cel_type =
|
95
|
+
case proto.type
|
96
|
+
when :int32, :int64, :sint32, :sint64, :sfixed32, :sfixed64, :enum
|
97
|
+
TYPES[:int]
|
98
|
+
when :uint32, :uint64, :fixed32, :fixed64
|
99
|
+
TYPES[:uint]
|
100
|
+
when :float, :double
|
101
|
+
TYPES[:double]
|
102
|
+
when :bool
|
103
|
+
TYPES[:bool]
|
104
|
+
when :string
|
105
|
+
TYPES[:string]
|
106
|
+
when :bytes
|
107
|
+
TYPES[:bytes]
|
108
|
+
when :message
|
109
|
+
proto_type = convert_to_proto_type(proto.submsg_name, package)
|
110
|
+
|
111
|
+
proto_type = PROTO_TO_CEL_TYPE[proto_type] if PROTO_TO_CEL_TYPE.key?(proto_type)
|
112
|
+
proto_type
|
113
|
+
end
|
114
|
+
|
115
|
+
if proto.label == :repeated
|
116
|
+
return TYPES[:list, cel_type] unless proto.subtype
|
117
|
+
|
118
|
+
case proto.subtype.options
|
119
|
+
when Google::Protobuf::EnumOptions
|
120
|
+
return TYPES[:list, cel_type]
|
121
|
+
when Google::Protobuf::MessageOptions
|
122
|
+
# protobuf doesn't have a way to identify whether this field descriptors is from a map,
|
123
|
+
# nor the types for key and values.
|
124
|
+
return TYPES[:map, cel_type] if proto.subtype.options.map_entry
|
125
|
+
end
|
126
|
+
|
127
|
+
return TYPES[:list, cel_type]
|
128
|
+
end
|
129
|
+
|
130
|
+
cel_type
|
131
|
+
else
|
132
|
+
PROTO_TO_CEL_TYPE[proto]
|
40
133
|
end
|
41
134
|
end
|
42
135
|
|
43
|
-
def
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
136
|
+
def convert_to_proto(proto_type, values, convert_value_to_proto: false)
|
137
|
+
return values.unpack(proto_type) if values.is_a?(Google::Protobuf::Any)
|
138
|
+
|
139
|
+
if proto_type == Google::Protobuf::Struct
|
140
|
+
values = values[:fields] if values.key?(:fields)
|
141
|
+
|
142
|
+
raise EvaluateError, "bad key type" unless values.all? { |k, _| k.is_a?(::String) }
|
143
|
+
|
144
|
+
proto_type.from_hash(values)
|
145
|
+
elsif proto_type == Google::Protobuf::ListValue
|
146
|
+
if values.is_a?(Hash) && values.key?(:values)
|
147
|
+
proto_type.from_a(values[:values])
|
148
|
+
else
|
149
|
+
proto_type.from_a(values)
|
150
|
+
end
|
151
|
+
elsif proto_type == Google::Protobuf::NullValue
|
152
|
+
nil
|
153
|
+
elsif proto_type == Google::Protobuf::Value
|
154
|
+
case values
|
155
|
+
when Google::Protobuf::Duration
|
156
|
+
proto_type.new(string_value: "#{values.seconds}#{if values.nanos.positive?
|
157
|
+
".#{values.nanos / 1_000_000_000.0}"
|
158
|
+
end}s")
|
159
|
+
when Google::Protobuf::Timestamp
|
160
|
+
proto_type.new(string_value: values.to_time.utc.iso8601(9))
|
161
|
+
when Hash
|
162
|
+
if values.key?(:struct_value)
|
163
|
+
|
164
|
+
raise EvaluateError, "bad key type" unless values[:struct_value].all? { |k, _| k.is_a?(::String) }
|
165
|
+
|
166
|
+
values[:struct_value] = Google::Protobuf::Struct.from_hash(values[:struct_value])
|
167
|
+
elsif values.key?(:list_value)
|
168
|
+
values[:list_value] = Google::Protobuf::ListValue.from_a(values[:list_value])
|
169
|
+
elsif values.empty?
|
170
|
+
if convert_value_to_proto
|
171
|
+
values = { struct_value: Google::Protobuf::Struct.from_hash(values) }
|
172
|
+
else
|
173
|
+
values[:null_value] = Google::Protobuf::NullValue::NULL_VALUE
|
174
|
+
end
|
175
|
+
elsif values.any? { |k, _| proto_type.descriptor.lookup(k.to_s).nil? }
|
176
|
+
raise EvaluateError, "bad key type" unless values.all? { |k, _| k.is_a?(::String) }
|
177
|
+
|
178
|
+
values = { struct_value: Google::Protobuf::Struct.from_hash(values) }
|
179
|
+
end
|
180
|
+
proto_type.new(values)
|
181
|
+
when Google::Protobuf::BoolValue
|
182
|
+
proto_type.new(bool_value: values.value)
|
183
|
+
when Google::Protobuf::DoubleValue,
|
184
|
+
Google::Protobuf::FloatValue,
|
185
|
+
Google::Protobuf::Int32Value,
|
186
|
+
Google::Protobuf::UInt32Value
|
187
|
+
proto_type.new(number_value: values.value)
|
188
|
+
when Google::Protobuf::Int64Value,
|
189
|
+
Google::Protobuf::UInt64Value
|
190
|
+
if values.value >= MAX_INT - 1
|
191
|
+
proto_type.new(string_value: values.value.to_s)
|
192
|
+
else
|
193
|
+
proto_type.new(number_value: values.value)
|
194
|
+
end
|
195
|
+
when Google::Protobuf::StringValue
|
196
|
+
proto_type.new(string_value: values.value)
|
197
|
+
when Google::Protobuf::BytesValue
|
198
|
+
proto_type.new(string_value: [values.value].pack("m0"))
|
199
|
+
when Google::Protobuf::NullValue
|
200
|
+
proto_type.new(null_value: Google::Protobuf::NullValue::NULL_VALUE)
|
201
|
+
when Google::Protobuf::Struct
|
202
|
+
proto_type.new(struct_value: values.value)
|
203
|
+
when Google::Protobuf::ListValue
|
204
|
+
proto_type.new(list_value: values.value)
|
205
|
+
when Google::Protobuf::Empty
|
206
|
+
proto_type.from_ruby({})
|
207
|
+
when Google::Protobuf::FieldMask
|
208
|
+
proto_type.from_ruby(values.paths.join(","))
|
209
|
+
else
|
210
|
+
proto_type.from_ruby(values)
|
211
|
+
end
|
212
|
+
elsif proto_type == Google::Protobuf::Any
|
213
|
+
case values
|
214
|
+
when Array
|
215
|
+
proto_type.pack(Google::Protobuf::ListValue.from_a(values))
|
216
|
+
when Hash
|
217
|
+
# type_url: "", value: ""
|
218
|
+
# TODO: consider pattern matching
|
219
|
+
if convert_value_to_proto
|
220
|
+
proto_type.pack(Google::Protobuf::Struct.from_hash(values))
|
221
|
+
else
|
222
|
+
proto_type.new(values)
|
223
|
+
end
|
224
|
+
when base_class
|
225
|
+
proto_type.pack(values)
|
226
|
+
else
|
227
|
+
proto_type.pack(Google::Protobuf::Value.from_ruby(values))
|
228
|
+
end
|
229
|
+
elsif proto_type == Google::Protobuf::Timestamp
|
230
|
+
case values
|
231
|
+
when Time
|
232
|
+
proto_type.from_time(values)
|
233
|
+
when nil
|
234
|
+
nil
|
235
|
+
else
|
236
|
+
proto_type.new(values)
|
237
|
+
end
|
238
|
+
elsif proto_type == Google::Protobuf::Duration
|
239
|
+
case values
|
240
|
+
when Numeric
|
241
|
+
seconds, nanos = values.divmod(1)
|
242
|
+
nanos *= 1_000_000_000
|
243
|
+
proto_type.new(seconds: seconds, nanos: nanos)
|
244
|
+
when nil
|
245
|
+
nil
|
246
|
+
else
|
247
|
+
proto_type.new(values)
|
248
|
+
end
|
249
|
+
elsif values.is_a?(Hash)
|
250
|
+
values = values.to_h do |key, value|
|
251
|
+
field_descriptor = proto_type.descriptor.lookup(key.to_s)
|
252
|
+
if value.nil? && (NILLABLE_WRAPPERS.include?(field_descriptor.subtype.msgclass) ||
|
253
|
+
proto_type.descriptor.enum_for(:each_oneof).one? do |oneof|
|
254
|
+
oneof.enum_for(:each).include?(field_descriptor)
|
255
|
+
end)
|
256
|
+
|
257
|
+
next([key, value])
|
258
|
+
end
|
259
|
+
|
260
|
+
next([key, value]) unless field_descriptor && field_descriptor.type != :enum
|
261
|
+
|
262
|
+
field_subtype = field_descriptor.subtype
|
263
|
+
|
264
|
+
next([key, value]) unless field_subtype
|
265
|
+
|
266
|
+
[
|
267
|
+
key,
|
268
|
+
# discard protobuf enums as well
|
269
|
+
value.is_a?(field_subtype.msgclass) ?
|
270
|
+
value :
|
271
|
+
convert_to_proto(field_subtype.msgclass, value, convert_value_to_proto: true),
|
272
|
+
]
|
71
273
|
end
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
seconds = value.fetch(Identifier.new("seconds"), 0)
|
92
|
-
nanos = value.fetch(Identifier.new("nanos"), 0)
|
93
|
-
value = Timestamp.new(Time.at(seconds, nanos, :nanosecond))
|
94
|
-
when "Duration", "google.protobuf.Duration"
|
95
|
-
seconds = value.fetch(Identifier.new("seconds"), 0)
|
96
|
-
nanos = value.fetch(Identifier.new("nanos"), 0)
|
97
|
-
value = Duration.new(seconds: seconds, nanos: nanos)
|
274
|
+
|
275
|
+
# protobuf sets maps as abstract classes, and there's apparently no way to identify them
|
276
|
+
# regardless, since their type is "message". Checking for ruby class name is the best
|
277
|
+
# proxy I could come up with.
|
278
|
+
return values unless proto_type.name
|
279
|
+
|
280
|
+
proto_type.new(values)
|
281
|
+
elsif values.nil?
|
282
|
+
Google::Protobuf::Value.from_ruby(values)
|
283
|
+
else
|
284
|
+
proto_type.new(value: values)
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
def convert_to_enum(value, proto_type = nil)
|
289
|
+
if proto_type
|
290
|
+
Enum.new(value, proto_type)
|
291
|
+
else
|
292
|
+
Enum.new(value)
|
98
293
|
end
|
99
|
-
value
|
100
294
|
end
|
101
295
|
|
102
296
|
def try_invoke_from(var, func, args)
|
@@ -129,5 +323,156 @@ module Cel
|
|
129
323
|
Literal.to_cel_type(value)
|
130
324
|
end
|
131
325
|
end
|
326
|
+
|
327
|
+
# finds value for +attribute+ declared in the +proto+.
|
328
|
+
#
|
329
|
+
# if none is found, and if +attribute+ is another proto message, then return an instance of the type.
|
330
|
+
# if field is an enum,
|
331
|
+
def lookup(proto, attribute)
|
332
|
+
value = proto.public_send(attribute)
|
333
|
+
|
334
|
+
field = proto.class.descriptor.lookup(attribute.to_s)
|
335
|
+
|
336
|
+
case field.label
|
337
|
+
when :repeated
|
338
|
+
case value
|
339
|
+
when Google::Protobuf::Map
|
340
|
+
value.to_h { |k, v| [k, convert_from_proto_field_type(field, v)] } # rubocop:disable Style/HashTransformValues
|
341
|
+
else
|
342
|
+
value.map { |v| convert_from_proto_field_type(field, v) }
|
343
|
+
end
|
344
|
+
else
|
345
|
+
convert_from_proto_field_type(field, value)
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
def convert_from_proto_field_type(field, value)
|
350
|
+
if value.nil? && (field.type == :message)
|
351
|
+
subtype = field.subtype.msgclass
|
352
|
+
|
353
|
+
if NILLABLE_WRAPPERS.include?(subtype) ||
|
354
|
+
subtype == Google::Protobuf::Value
|
355
|
+
# if a wrapper type, ignore null
|
356
|
+
else
|
357
|
+
value = subtype.new
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
case field.type
|
362
|
+
when :message
|
363
|
+
value
|
364
|
+
when :enum
|
365
|
+
enum_type = const_get(Cel.package_to_module_name(field.submsg_name))
|
366
|
+
|
367
|
+
return Enum.new(value, enum_type) if value.is_a?(Integer)
|
368
|
+
|
369
|
+
enum_mod = Cel.package_to_module_name(field.submsg_name)
|
370
|
+
|
371
|
+
Enum.new(const_get(enum_mod).resolve(value), enum_type)
|
372
|
+
when :int32, :int64, :sint32, :sint64, :sfixed32, :sfixed64
|
373
|
+
return unless value
|
374
|
+
|
375
|
+
Number.new(:int, value)
|
376
|
+
when :uint32, :uint64, :fixed32, :fixed64
|
377
|
+
return unless value
|
378
|
+
|
379
|
+
Number.new(:uint, value)
|
380
|
+
when :float, :double
|
381
|
+
return unless value
|
382
|
+
|
383
|
+
Number.new(:double, value)
|
384
|
+
when :bool
|
385
|
+
return unless value
|
386
|
+
|
387
|
+
Bool.cast(value)
|
388
|
+
when :string
|
389
|
+
return unless value
|
390
|
+
|
391
|
+
String.new(value)
|
392
|
+
when :bytes
|
393
|
+
return unless value
|
394
|
+
|
395
|
+
Bytes.new(value)
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
399
|
+
module CelComparisonMode
|
400
|
+
def ==(other)
|
401
|
+
super || begin
|
402
|
+
return false unless other.is_a?(Protobuf.base_class)
|
403
|
+
|
404
|
+
a1 = self
|
405
|
+
a2 = other
|
406
|
+
|
407
|
+
if a1.is_a?(Google::Protobuf::Any) && !a1.type_url.empty?
|
408
|
+
a1 = a1.unpack(Object.const_get(Cel.package_to_module_name(a1.type_name)))
|
409
|
+
a1.extend(CelComparisonMode)
|
410
|
+
end
|
411
|
+
|
412
|
+
if a2.is_a?(Google::Protobuf::Any) && !a2.type_url.empty?
|
413
|
+
a2 = a2.unpack(Object.const_get(Cel.package_to_module_name(a2.type_name)))
|
414
|
+
end
|
415
|
+
|
416
|
+
return false unless a1.class == a2.class
|
417
|
+
|
418
|
+
if a1.is_a?(Google::Protobuf::Any) && a1.type_url.empty?
|
419
|
+
a2.is_a?(Google::Protobuf::Any) && a2.type_url.empty?
|
420
|
+
return a1.value == a2.value
|
421
|
+
end
|
422
|
+
|
423
|
+
a1.class.descriptor.each do |field|
|
424
|
+
f1 = a1.public_send(field.name)
|
425
|
+
f1.extend(CelComparisonMode) if f1.is_a?(Protobuf.base_class)
|
426
|
+
f2 = a2.public_send(field.name)
|
427
|
+
return false unless f1 == f2
|
428
|
+
end
|
429
|
+
true
|
430
|
+
end
|
431
|
+
end
|
432
|
+
end
|
433
|
+
|
434
|
+
class Enum < Cel::Number
|
435
|
+
# protocol buffer enum fields can accept any signed 32-bit number, values outside that range will raise an error.
|
436
|
+
MAX_ENUM_INT = (2**31)
|
437
|
+
|
438
|
+
def initialize(value, enum_type = nil)
|
439
|
+
super(:int, value)
|
440
|
+
@enum_type = enum_type
|
441
|
+
check_overflow(@value)
|
442
|
+
end
|
443
|
+
|
444
|
+
def enum_type
|
445
|
+
@enum_type || type
|
446
|
+
end
|
447
|
+
|
448
|
+
private
|
449
|
+
|
450
|
+
def check_overflow(value)
|
451
|
+
raise EvaluateError, "return error for overflow" unless (-(MAX_ENUM_INT - 1)...MAX_ENUM_INT).cover?(value)
|
452
|
+
end
|
453
|
+
end
|
454
|
+
|
455
|
+
PROTO_TO_CEL_TYPE = {
|
456
|
+
Google::Protobuf::Any => TYPES[:any],
|
457
|
+
Google::Protobuf::Value => TYPES[:any],
|
458
|
+
Google::Protobuf::ListValue => TYPES[:list],
|
459
|
+
Google::Protobuf::Struct => TYPES[:map],
|
460
|
+
Google::Protobuf::BoolValue => TYPES[:bool],
|
461
|
+
Google::Protobuf::BytesValue => TYPES[:bytes],
|
462
|
+
Google::Protobuf::DoubleValue => TYPES[:double],
|
463
|
+
Google::Protobuf::FloatValue => TYPES[:double],
|
464
|
+
Google::Protobuf::Int32Value => TYPES[:int],
|
465
|
+
Google::Protobuf::Int64Value => TYPES[:int],
|
466
|
+
Google::Protobuf::UInt32Value => TYPES[:uint],
|
467
|
+
Google::Protobuf::UInt64Value => TYPES[:uint],
|
468
|
+
Google::Protobuf::NullValue => TYPES[:null],
|
469
|
+
Google::Protobuf::StringValue => TYPES[:string],
|
470
|
+
Google::Protobuf::Timestamp => TYPES[:timestamp],
|
471
|
+
Google::Protobuf::Duration => TYPES[:duration],
|
472
|
+
}.freeze
|
473
|
+
|
474
|
+
NILLABLE_WRAPPERS = (PROTO_TO_CEL_TYPE.keys - [Google::Protobuf::Value, Google::Protobuf::ListValue,
|
475
|
+
Google::Protobuf::Struct, Google::Protobuf::Duration,
|
476
|
+
Google::Protobuf::Timestamp]).freeze
|
132
477
|
end
|
133
478
|
end
|