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