cel 0.2.3 → 0.3.1

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.
@@ -4,18 +4,6 @@ require "time"
4
4
  require "delegate"
5
5
 
6
6
  module Cel
7
- begin
8
- require_relative "elements/protobuf"
9
- rescue LoadError
10
- module Protobuf
11
- module_function
12
-
13
- def method_missing(*) # rubocop:disable Style/MissingRespondToMissing
14
- raise Error, "\"google/protobuf\" is required in order to use this feature"
15
- end
16
- end
17
- end
18
-
19
7
  LOGICAL_OPERATORS = %w[<= >= < > == != in].freeze
20
8
  MULTI_OPERATORS = %w[* / %].freeze
21
9
 
@@ -24,9 +12,10 @@ module Cel
24
12
 
25
13
  attr_accessor :type
26
14
 
27
- def initialize(identifier)
15
+ def initialize(identifier, package = nil)
28
16
  @id = identifier
29
17
  @type = TYPES[:any]
18
+ @package = package
30
19
  super(@id)
31
20
  end
32
21
 
@@ -37,59 +26,80 @@ module Cel
37
26
  def to_s
38
27
  @id.to_s
39
28
  end
29
+
30
+ alias_method :to_ruby_type, :to_s
31
+
32
+ def try_convert_to_proto_type
33
+ Protobuf.convert_to_proto_type(@id.to_s, @package)
34
+ end
40
35
  end
41
36
 
42
- class Message < SimpleDelegator
43
- attr_reader :type, :struct
37
+ class Message
38
+ EMPTY_STRUCT = {}.freeze
44
39
 
45
- def self.new(type, struct)
46
- value = convert_from_type(type, struct)
47
- return value if value.is_a?(Null) || value != struct
40
+ attr_reader :name, :struct, :package, :depth
48
41
 
49
- super
42
+ def initialize(name, struct, package: nil, depth: 1)
43
+ @struct = struct ? struct.transform_keys(&:to_sym) : EMPTY_STRUCT
44
+ @name = name
45
+ @package = package
46
+ @depth = depth
50
47
  end
51
48
 
52
- def initialize(type, struct)
53
- @struct = Struct.new(*struct.keys.map(&:to_sym)).new(*struct.values)
54
- @type = type.is_a?(Type) ? type : MapType.new(struct.to_h do |k, v|
55
- [Literal.to_cel_type(k), Literal.to_cel_type(v)]
56
- end)
57
- super(@struct)
49
+ def type
50
+ @type ||= if proto_type
51
+ Protobuf.convert_to_cel_type(proto_type, @package) || proto_type
52
+ else
53
+ Protobuf.try_convert_from_wrapper_type(@name.to_s)
54
+ end
58
55
  end
59
56
 
60
- def field?(key)
61
- !@type.get(key).nil?
57
+ def message_type
58
+ @message_type ||= proto_type || container
62
59
  end
63
60
 
64
- def self.convert_from_type(type, value)
65
- case type
66
- when Invoke, Identifier
67
- spread_type = type.to_s
68
- Protobuf.convert_from_type(spread_type, value)
69
- when Type
70
- [type, value]
61
+ def proto_type
62
+ @proto_type ||= Protobuf.convert_to_proto_type(@name.to_s, @package)
63
+ end
64
+
65
+ def ==(other)
66
+ other.is_a?(Cel::Message) && other.name == @name && other.struct == @struct
67
+ end
68
+
69
+ def to_s
70
+ if proto_type
71
+ proto_type.descriptor.name
71
72
  else
72
- [
73
- MapType.new(struct.to_h do |k, v|
74
- [Literal.to_cel_type(k), Literal.to_cel_type(v)]
75
- end),
76
- Struct.new(*struct.keys.map(&:to_sym)).new(*struct.values),
77
- ]
73
+ message_type.to_s
78
74
  end
79
75
  end
76
+
77
+ private
78
+
79
+ def container
80
+ @container ||= Cel.message_container(@name, @struct)
81
+ end
80
82
  end
81
83
 
82
84
  class Invoke
83
- attr_reader :var, :func, :args
85
+ attr_accessor :var
86
+
87
+ attr_reader :func, :args, :depth
84
88
 
85
- def self.new(func:, var: nil, args: nil)
89
+ def self.new(func:, var: nil, args: nil, **rest)
86
90
  Protobuf.try_invoke_from(var, func, args) || super
87
91
  end
88
92
 
89
- def initialize(func:, var: nil, args: nil)
93
+ def type
94
+ TYPES[:any]
95
+ end
96
+
97
+ def initialize(func:, var: nil, args: nil, depth: 1, package: nil)
90
98
  @var = var
91
99
  @func = func.to_sym
92
100
  @args = args
101
+ @package = package
102
+ @depth = depth
93
103
  end
94
104
 
95
105
  def ==(other)
@@ -105,7 +115,7 @@ module Cel
105
115
 
106
116
  def to_s
107
117
  if var
108
- if func == :[]
118
+ if indexing?
109
119
  "#{var}[#{args}]"
110
120
  else
111
121
  "#{var}.#{func}#{"(#{args.map(&:to_s).join(", ")})" if args}"
@@ -114,12 +124,28 @@ module Cel
114
124
  "#{func}#{"(#{args.map(&:to_s).join(", ")})" if args}"
115
125
  end
116
126
  end
127
+
128
+ def try_convert_to_proto_type
129
+ return unless @var
130
+
131
+ proto_type = Protobuf.convert_to_proto_type(@var.to_s, @package)
132
+
133
+ return unless proto_type
134
+
135
+ return proto_type unless proto_type.const_defined?(@func)
136
+
137
+ proto_type.const_get(@func)
138
+ end
139
+
140
+ def indexing?
141
+ @func == :[]
142
+ end
117
143
  end
118
144
 
119
145
  class Function
120
- attr_reader :types, :type
146
+ attr_reader :label, :types, :type
121
147
 
122
- def initialize(*types, return_type: nil, &func)
148
+ def initialize(*types, label: nil, return_type: nil, &func)
123
149
  unless func.nil?
124
150
  types = Array.new(func.arity) { TYPES[:any] } if types.empty?
125
151
  raise(Error, "number of arg types does not match number of yielded args") unless types.size == func.arity
@@ -131,6 +157,7 @@ module Cel
131
157
  return_type.is_a?(Type) ? return_type : TYPES[return_type]
132
158
  end
133
159
  @func = func
160
+ @label = label
134
161
  end
135
162
 
136
163
  def call(*args)
@@ -154,17 +181,28 @@ module Cel
154
181
  end
155
182
 
156
183
  def ==(other)
157
- other = other.value if other.is_a?(Literal)
158
- @value == other || super
184
+ case other
185
+ when Literal
186
+ @type == other.type && @value == other.value
187
+ else
188
+ @value == other
189
+ end
159
190
  end
160
191
 
161
192
  def self.to_cel_type(val)
162
- val = Protobuf.convert_from_protobuf(val) if val.is_a?(Google::Protobuf::MessageExts)
193
+ if val.is_a?(Protobuf.base_class) ||
194
+ val.is_a?(Struct) # no-protobuf mode
195
+ val = Protobuf.try_convert_from_wrapper(val)
196
+
197
+ return val if val.is_a?(Protobuf.base_class) # still
198
+ end
163
199
 
164
200
  case val
165
- when Literal, Identifier
201
+ when Literal, Identifier, Invoke, Operation, Condition, Group, # already cel
202
+ Protobuf.enum_class # already a usable class a la protobuf
166
203
  val
167
- # TODO: should support byte streams?
204
+ when Protobuf.map_class
205
+ Map.new(val.to_h)
168
206
  when ::String
169
207
  String.new(val)
170
208
  when ::Symbol
@@ -178,9 +216,9 @@ module Cel
178
216
  when ::Array
179
217
  List.new(val)
180
218
  when true, false
181
- Bool.new(val)
219
+ Bool.cast(val)
182
220
  when nil
183
- Null.new
221
+ Null::INSTANCE
184
222
  when Time
185
223
  Timestamp.new(val)
186
224
  else
@@ -188,9 +226,7 @@ module Cel
188
226
  end
189
227
  end
190
228
 
191
- def to_ruby_type
192
- @value
193
- end
229
+ alias_method :to_ruby_type, :value
194
230
 
195
231
  private
196
232
 
@@ -198,21 +234,62 @@ module Cel
198
234
  end
199
235
 
200
236
  class Number < Literal
201
- [:+, :-, *MULTI_OPERATORS].each do |op|
237
+ def ==(other)
238
+ case other
239
+ when Number
240
+ @value == other.value
241
+ else
242
+ super
243
+ end
244
+ end
245
+
246
+ ([:+, :-, *(MULTI_OPERATORS - %w[%])] - %i[%]).each do |op|
202
247
  class_eval(<<-OUT, __FILE__, __LINE__ + 1)
203
248
  def #{op}(other)
204
- Number.new(@type, super)
249
+ value = super
250
+ check_overflow(value)
251
+ Number.new(@type, value)
205
252
  end
206
253
  OUT
207
254
  end
208
255
 
209
- (LOGICAL_OPERATORS - %w[==]).each do |op|
256
+ def %(other)
257
+ raise EvaluateError, "no matching overload" if @type == :double && other.type == :double
258
+
259
+ value = if negative?
260
+ if other.negative?
261
+ -(abs % other.abs)
262
+ else
263
+ -(abs % other)
264
+ end
265
+ else
266
+ @value % other.abs
267
+ end
268
+ check_overflow(value)
269
+ Number.new(@type, value)
270
+ end
271
+
272
+ %i[+ -].each do |op|
210
273
  class_eval(<<-OUT, __FILE__, __LINE__ + 1)
211
- def #{op}(other)
212
- Bool.new(super)
274
+ def #{op}@
275
+ raise EvaluateError, "no such overload" if @type == TYPES[:uint]
276
+ value = super
277
+ Number.new(@type, value)
213
278
  end
214
279
  OUT
215
280
  end
281
+
282
+ def check_overflow(value = @value)
283
+ case @type
284
+ when TYPES[:double]
285
+ return if value.nan? || value.infinite?
286
+ raise EvaluateError, "return error for overflow" unless (-(MAX_FLOAT - 1)...MAX_FLOAT).cover?(value)
287
+ when TYPES[:uint]
288
+ raise EvaluateError, "return error for overflow" unless (0...MAX_INT).cover?(value)
289
+ else
290
+ raise EvaluateError, "return error for overflow" unless (-(MAX_INT - 1)...MAX_INT).cover?(value)
291
+ end
292
+ end
216
293
  end
217
294
 
218
295
  class Bool < Literal
@@ -220,16 +297,27 @@ module Cel
220
297
  super(:bool, value)
221
298
  end
222
299
 
223
- (LOGICAL_OPERATORS - %w[==]).each do |op|
300
+ (LOGICAL_OPERATORS - %w[== != in]).each do |op|
224
301
  class_eval(<<-OUT, __FILE__, __LINE__ + 1)
225
302
  def #{op}(other)
226
- Bool.new(super)
303
+ return super unless other.is_a?(Bool)
304
+
305
+ lhs = @value ? 1 : 0
306
+ rhs = other.value ? 1 : 0
307
+ lhs.__send__(__method__, rhs)
227
308
  end
228
309
  OUT
229
310
  end
230
311
 
231
312
  def !
232
- Bool.new(super)
313
+ Bool.cast(super)
314
+ end
315
+
316
+ TRUE_VALUE = new(true)
317
+ FALSE_VALUE = new(false)
318
+
319
+ def self.cast(val)
320
+ val ? TRUE_VALUE : FALSE_VALUE
233
321
  end
234
322
  end
235
323
 
@@ -237,6 +325,8 @@ module Cel
237
325
  def initialize
238
326
  super(:null_type, nil)
239
327
  end
328
+
329
+ INSTANCE = new.freeze
240
330
  end
241
331
 
242
332
  class String < Literal
@@ -244,18 +334,23 @@ module Cel
244
334
  super(:string, value)
245
335
  end
246
336
 
337
+ # override base String#%
338
+ def %(_other)
339
+ raise NoMethodError, "undefined method '%' for #{self}"
340
+ end
341
+
247
342
  # CEL string functions
248
343
 
249
344
  def contains(string)
250
- Bool.new(@value.include?(string))
345
+ Bool.cast(@value.include?(string))
251
346
  end
252
347
 
253
348
  def endsWith(string)
254
- Bool.new(@value.end_with?(string))
349
+ Bool.cast(@value.end_with?(string))
255
350
  end
256
351
 
257
352
  def startsWith(string)
258
- Bool.new(@value.start_with?(string))
353
+ Bool.cast(@value.start_with?(string))
259
354
  end
260
355
 
261
356
  def matches(pattern)
@@ -272,18 +367,23 @@ module Cel
272
367
  end
273
368
 
274
369
  class Bytes < Literal
370
+ attr_reader :string
371
+
275
372
  def initialize(value)
276
373
  super(:bytes, value)
374
+ @string = value.pack("C*")
277
375
  end
278
376
 
279
377
  def to_ary
280
378
  [self]
281
379
  end
282
380
 
381
+ alias_method :to_ruby_type, :string
382
+
283
383
  (LOGICAL_OPERATORS - %w[==]).each do |op|
284
384
  class_eval(<<-OUT, __FILE__, __LINE__ + 1)
285
385
  def #{op}(other)
286
- Bool.new(super)
386
+ other.is_a?(Cel::Bytes) ? Bool.cast(@string.__send__(__method__, other.string)) : super
287
387
  end
288
388
  OUT
289
389
  end
@@ -291,18 +391,34 @@ module Cel
291
391
  %i[+ -].each do |op|
292
392
  class_eval(<<-OUT, __FILE__, __LINE__ + 1)
293
393
  def #{op}(other)
294
- String.new(@type, super)
394
+ Bytes.new(@value + other.value)
295
395
  end
296
396
  OUT
297
397
  end
298
398
  end
299
399
 
300
400
  class List < Literal
301
- def initialize(value)
401
+ attr_reader :depth
402
+
403
+ def initialize(value, depth: 1)
302
404
  value = value.map do |v|
303
405
  Literal.to_cel_type(v)
304
406
  end
305
- super(ListType.new(value), value)
407
+ super(TYPES[:list], value)
408
+ @depth = depth
409
+ end
410
+
411
+ def [](key)
412
+ case key
413
+ when Number
414
+ if key.type == :double
415
+ val = key.value
416
+
417
+ raise InvalidArgumentError, key unless (val % 1).zero?
418
+ end
419
+ end
420
+
421
+ super
306
422
  end
307
423
 
308
424
  def to_ary
@@ -312,65 +428,142 @@ module Cel
312
428
  def to_ruby_type
313
429
  value.map(&:to_ruby_type)
314
430
  end
431
+
432
+ %i[+ -].each do |op|
433
+ class_eval(<<-OUT, __FILE__, __LINE__ + 1)
434
+ def #{op}(other)
435
+ List.new(@value.send(__method__, other.value))
436
+ end
437
+ OUT
438
+ end
439
+
440
+ def by_max_depth
441
+ max = @value.max { |a1, a2| calc_depth(a1, 0) <=> calc_depth(a2, 0) }
442
+
443
+ # return the last value if all options have the same depth
444
+ return @value.last if (max == @value.first) && (calc_depth(max, 0) == calc_depth(@value.last, 0))
445
+
446
+ max
447
+ end
448
+
449
+ private
450
+
451
+ def calc_depth(element, acc)
452
+ case element
453
+ when List
454
+ element.value.map { |el| calc_depth(el, acc + 1) }.max || (acc + 1)
455
+ when Map
456
+ element.value.map { |(_, el)| calc_depth(el, acc + 1) }.max || (acc + 1)
457
+ else
458
+ acc
459
+ end
460
+ end
315
461
  end
316
462
 
317
463
  class Map < Literal
318
- def initialize(value)
319
- value = value.to_h do |k, v|
464
+ ALLOWED_TYPES = %i[int uint bool string].map { |typ| TYPES[typ] }.freeze
465
+
466
+ attr_reader :depth
467
+
468
+ def initialize(value, depth: 1)
469
+ # store array internally to support repeated keys
470
+ value = value.map do |k, v|
320
471
  [Literal.to_cel_type(k), Literal.to_cel_type(v)]
321
472
  end
322
- super(MapType.new(value), value)
323
- check
473
+ super(TYPES[:map], value)
474
+ @depth = depth
324
475
  end
325
476
 
326
477
  def ==(other)
327
- super || (
328
- other.respond_to?(:to_hash) &&
329
- @value.zip(other).all? { |(x1, y1), (x2, y2)| x1 == x2 && y1 == y2 }
330
- )
478
+ case other
479
+ when Map
480
+ @value.all? do |args|
481
+ other.value.include?(args)
482
+ end
483
+ when Array
484
+ # calls to != will downgrade maps to arrays
485
+ @value.all? do |args|
486
+ other.include?(args)
487
+ end
488
+ else
489
+ super
490
+ end
331
491
  end
332
492
 
333
- def to_ary
334
- [self]
493
+ (LOGICAL_OPERATORS - %w[== != in]).each do |op|
494
+ class_eval(<<-OUT, __FILE__, __LINE__ + 1)
495
+ def #{op}(*)
496
+ raise NoMethodError
497
+ end
498
+ OUT
499
+ end
500
+
501
+ def key?(key)
502
+ @value.any? { |k, _v| k == key }
503
+ end
504
+
505
+ alias_method :include?, :key?
506
+
507
+ def [](key)
508
+ values = @value.filter_map { |k, v| v if k == key }
509
+
510
+ raise NoSuchKeyError.new(self, key) if values.empty?
511
+
512
+ raise EvaluateError, "Failed with repeated key" if values.size > 1
513
+
514
+ values.first
335
515
  end
336
516
 
337
517
  def to_ruby_type
338
- value.to_h { |*args| args.map(&:to_ruby_type) }
518
+ value.to_h { |args| args.map(&:to_ruby_type) }
339
519
  end
340
520
 
341
521
  def respond_to_missing?(meth, *args)
342
- super || (@value && @value.keys.any? { |k| k.to_s == meth.to_s })
522
+ return true if super
523
+ return false unless @value
524
+
525
+ meth_s = meth.to_s
526
+
527
+ @value.any? { |k, _| k == meth_s }
343
528
  end
344
529
 
345
530
  def method_missing(meth, *args)
346
531
  return super unless @value
347
532
 
348
- key = @value.keys.find { |k| k.to_s == meth.to_s } or return super
533
+ meth_s = meth.to_s
534
+ values = @value.filter_map { |k, v| v if k == meth_s }
535
+
536
+ return super if values.empty?
349
537
 
350
- @value[key]
538
+ values.first
351
539
  end
352
540
 
353
541
  private
354
542
 
355
- ALLOWED_TYPES = %i[int uint bool string].map { |typ| TYPES[typ] }.freeze
356
-
357
543
  # For a map, the entry keys are sub-expressions that must evaluate to values
358
544
  # of an allowed type (int, uint, bool, or string)
359
545
  def check
360
- return if @value.each_key.all? { |key| key.is_a?(Identifier) || ALLOWED_TYPES.include?(key.type) }
546
+ return if @value.all? do |key, _|
547
+ key.is_a?(Identifier) || ALLOWED_TYPES.include?(key.type)
548
+ end
361
549
 
362
550
  raise CheckError, "#{self} is invalid (keys must be of an allowed type (int, uint, bool, or string)"
363
551
  end
364
552
  end
365
553
 
366
554
  class Timestamp < Literal
555
+ TIMESTAMP_RANGE = (Time.parse("0001-01-01T00:00:00Z")..Time.parse("9999-12-31T23:59:59.999999999Z"))
556
+
367
557
  def initialize(value)
368
558
  value = case value
369
559
  when ::String then Time.parse(value)
370
560
  when Numeric then Time.at(value)
371
561
  else value
372
562
  end
373
- super(:timestamp, value)
563
+
564
+ raise EvaluateError, "out of range" unless TIMESTAMP_RANGE.include?(value)
565
+
566
+ super(:timestamp, value.round(9))
374
567
  end
375
568
 
376
569
  def +(other)
@@ -389,7 +582,7 @@ module Cel
389
582
  LOGICAL_OPERATORS.each do |op|
390
583
  class_eval(<<-OUT, __FILE__, __LINE__ + 1)
391
584
  def #{op}(other)
392
- other.is_a?(Cel::Literal) ? Bool.new(super) : super
585
+ other.is_a?(Cel::Literal) ? Bool.cast(super) : super
393
586
  end
394
587
  OUT
395
588
  end
@@ -397,7 +590,7 @@ module Cel
397
590
  # Cel Functions
398
591
 
399
592
  def getDate(tz = nil)
400
- to_local_time(tz).day
593
+ Number.new(:int, to_local_time(tz).day)
401
594
  end
402
595
 
403
596
  def getDayOfMonth(tz = nil)
@@ -405,35 +598,39 @@ module Cel
405
598
  end
406
599
 
407
600
  def getDayOfWeek(tz = nil)
408
- to_local_time(tz).wday
601
+ Number.new(:int, to_local_time(tz).wday)
409
602
  end
410
603
 
411
604
  def getDayOfYear(tz = nil)
412
- to_local_time(tz).yday - 1
605
+ Number.new(:int, to_local_time(tz).yday - 1)
413
606
  end
414
607
 
415
608
  def getMonth(tz = nil)
416
- to_local_time(tz).month - 1
609
+ Number.new(:int, to_local_time(tz).month - 1)
417
610
  end
418
611
 
419
612
  def getFullYear(tz = nil)
420
- to_local_time(tz).year
613
+ Number.new(:int, to_local_time(tz).year)
421
614
  end
422
615
 
423
616
  def getHours(tz = nil)
424
- to_local_time(tz).hour
617
+ Number.new(:int, to_local_time(tz).hour)
425
618
  end
426
619
 
427
620
  def getMinutes(tz = nil)
428
- to_local_time(tz).min
621
+ Number.new(:int, to_local_time(tz).min)
429
622
  end
430
623
 
431
624
  def getSeconds(tz = nil)
432
- to_local_time(tz).sec
625
+ Number.new(:int, to_local_time(tz).sec)
433
626
  end
434
627
 
435
628
  def getMilliseconds(tz = nil)
436
- to_local_time(tz).nsec / 1_000_000
629
+ Number.new(:int, to_local_time(tz).nsec / 1_000_000)
630
+ end
631
+
632
+ def to_ruby_type
633
+ Protobuf.timestamp_class.from_time(@value)
437
634
  end
438
635
 
439
636
  private
@@ -441,7 +638,12 @@ module Cel
441
638
  def to_local_time(tz = nil)
442
639
  time = @value
443
640
  if tz
444
- tz = TZInfo::Timezone.get(tz) unless tz.match?(/\A[+-]\d{2,}:\d{2,}\z/)
641
+ tz = tz.value
642
+ if tz.match?(/\A[+-]?\d{2,}:\d{2,}\z/)
643
+ tz.prepend("+") unless tz.start_with?("+", "-")
644
+ else
645
+ tz = TZInfo::Timezone.get(tz)
646
+ end
445
647
  time = time.getlocal(tz)
446
648
  end
447
649
  time
@@ -450,26 +652,54 @@ module Cel
450
652
 
451
653
  class Duration < Literal
452
654
  def initialize(value)
453
- value = case value
454
- when ::String
455
- init_from_string(value)
456
- when Hash
457
- seconds, nanos = value.values_at(:seconds, :nanos)
458
- seconds ||= 0
459
- nanos ||= 0
460
- seconds + (nanos / 1_000_000_000.0)
461
- else
462
- value
463
- end
655
+ value =
656
+ case value
657
+ when ::String
658
+ init_from_string(value)
659
+ when Hash
660
+ seconds, nanos = value.values_at(:seconds, :nanos)
661
+ seconds ||= 0
662
+ nanos ||= 0
663
+ seconds + (nanos / 1_000_000_000.0)
664
+ else
665
+ value
666
+ end
667
+
668
+ raise EvaluateError, "out of range" unless (value * 1_000_000_000).between?(-MAX_INT, MAX_INT)
669
+
464
670
  super(:duration, value)
465
671
  end
466
672
 
673
+ def to_s
674
+ seconds = getSeconds
675
+ millis = getMilliseconds
676
+ millis.positive? ? "#{seconds}s#{millis}m" : "#{seconds}s"
677
+ end
678
+
679
+ def +(other)
680
+ case other
681
+ when Cel::Duration
682
+ Cel::Duration.new(@value + other.value)
683
+ when Cel::Timestamp
684
+ Cel::Timestamp.new(other.value + @value)
685
+ end
686
+ end
687
+
688
+ def -(other)
689
+ case other
690
+ when Cel::Duration
691
+ Cel::Duration.new(@value - other.value)
692
+ else
693
+ raise Error, "invalid operand #{other} for `-`"
694
+ end
695
+ end
696
+
467
697
  LOGICAL_OPERATORS.each do |op|
468
698
  class_eval(<<-OUT, __FILE__, __LINE__ + 1)
469
699
  def #{op}(other)
470
700
  case other
471
701
  when Cel::Literal
472
- Bool.new(super)
702
+ Bool.cast(super)
473
703
  when Numeric
474
704
  @value == other
475
705
 
@@ -483,19 +713,23 @@ module Cel
483
713
  # Cel Functions
484
714
 
485
715
  def getHours
486
- (getMinutes / 60).to_i
716
+ Cel::Number.new(:int, (getMinutes / 60).to_i)
487
717
  end
488
718
 
489
719
  def getMinutes
490
- (getSeconds / 60).to_i
720
+ Cel::Number.new(:int, (getSeconds / 60).to_i)
491
721
  end
492
722
 
493
723
  def getSeconds
494
- @value.divmod(1).first
724
+ Cel::Number.new(:int, @value.divmod(1).first)
495
725
  end
496
726
 
497
727
  def getMilliseconds
498
- (@value.divmod(1).last * 1000).round
728
+ Cel::Number.new(:int, (@value.divmod(1).last * 1000).round)
729
+ end
730
+
731
+ def to_ruby_type
732
+ Protobuf.duration_class.new(seconds: getSeconds.value, nanos: getMilliseconds.value * 1_000_000_000.0)
499
733
  end
500
734
 
501
735
  private
@@ -521,7 +755,9 @@ module Cel
521
755
  raise EvaluateError, "#{units} is unsupported"
522
756
  end
523
757
  end
524
- seconds + (nanos / 1_000_000_000.0)
758
+ duration = seconds + (nanos / 1_000_000_000.0)
759
+ duration = -duration if value.start_with?("-")
760
+ duration
525
761
  end
526
762
  end
527
763
 
@@ -532,20 +768,25 @@ module Cel
532
768
  @value = value
533
769
  end
534
770
 
771
+ def type
772
+ TYPES[:any]
773
+ end
774
+
535
775
  def ==(other)
536
776
  other.is_a?(Group) && @value == other.value
537
777
  end
538
778
  end
539
779
 
540
780
  class Operation
541
- attr_reader :op, :operands
781
+ attr_reader :op, :operands, :depth
542
782
 
543
783
  attr_accessor :type
544
784
 
545
- def initialize(op, operands)
785
+ def initialize(op, operands, depth: 1)
546
786
  @op = op
547
787
  @operands = operands
548
788
  @type = TYPES[:any]
789
+ @depth = depth
549
790
  end
550
791
 
551
792
  def ==(other)
@@ -573,12 +814,17 @@ module Cel
573
814
  end
574
815
 
575
816
  class Condition
576
- attr_reader :if, :then, :else
817
+ attr_reader :if, :then, :else, :depth
577
818
 
578
- def initialize(if_, then_, else_)
819
+ def initialize(if_, then_, else_, depth: 1)
579
820
  @if = if_
580
821
  @then = then_
581
822
  @else = else_
823
+ @depth = depth
824
+ end
825
+
826
+ def type
827
+ TYPES[:any]
582
828
  end
583
829
 
584
830
  def ==(other)