cel 0.2.3 → 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.
@@ -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,78 @@ 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
48
41
 
49
- super
42
+ def initialize(name, struct, package: nil)
43
+ @struct = struct ? struct.transform_keys(&:to_sym) : EMPTY_STRUCT
44
+ @name = name
45
+ @package = package
50
46
  end
51
47
 
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)
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
58
54
  end
59
55
 
60
- def field?(key)
61
- !@type.get(key).nil?
56
+ def message_type
57
+ @message_type ||= proto_type || container
62
58
  end
63
59
 
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]
60
+ def proto_type
61
+ @proto_type ||= Protobuf.convert_to_proto_type(@name.to_s, @package)
62
+ end
63
+
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
71
71
  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
- ]
72
+ message_type.to_s
78
73
  end
79
74
  end
75
+
76
+ private
77
+
78
+ def container
79
+ @container ||= Cel.message_container(@name, @struct)
80
+ end
80
81
  end
81
82
 
82
83
  class Invoke
83
- attr_reader :var, :func, :args
84
+ attr_accessor :var
85
+
86
+ attr_reader :func, :args
84
87
 
85
- def self.new(func:, var: nil, args: nil)
88
+ def self.new(func:, var: nil, args: nil, **rest)
86
89
  Protobuf.try_invoke_from(var, func, args) || super
87
90
  end
88
91
 
89
- 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)
90
97
  @var = var
91
98
  @func = func.to_sym
92
99
  @args = args
100
+ @package = package
93
101
  end
94
102
 
95
103
  def ==(other)
@@ -114,12 +122,24 @@ module Cel
114
122
  "#{func}#{"(#{args.map(&:to_s).join(", ")})" if args}"
115
123
  end
116
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
117
137
  end
118
138
 
119
139
  class Function
120
- attr_reader :types, :type
140
+ attr_reader :label, :types, :type
121
141
 
122
- def initialize(*types, return_type: nil, &func)
142
+ def initialize(*types, label: nil, return_type: nil, &func)
123
143
  unless func.nil?
124
144
  types = Array.new(func.arity) { TYPES[:any] } if types.empty?
125
145
  raise(Error, "number of arg types does not match number of yielded args") unless types.size == func.arity
@@ -131,6 +151,7 @@ module Cel
131
151
  return_type.is_a?(Type) ? return_type : TYPES[return_type]
132
152
  end
133
153
  @func = func
154
+ @label = label
134
155
  end
135
156
 
136
157
  def call(*args)
@@ -154,17 +175,28 @@ module Cel
154
175
  end
155
176
 
156
177
  def ==(other)
157
- other = other.value if other.is_a?(Literal)
158
- @value == other || super
178
+ case other
179
+ when Literal
180
+ @type == other.type && @value == other.value
181
+ else
182
+ @value == other
183
+ end
159
184
  end
160
185
 
161
186
  def self.to_cel_type(val)
162
- 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
163
193
 
164
194
  case val
165
- when Literal, Identifier
195
+ when Literal, Identifier, Invoke, Operation, Condition, Group, # already cel
196
+ Protobuf.enum_class # already a usable class a la protobuf
166
197
  val
167
- # TODO: should support byte streams?
198
+ when Protobuf.map_class
199
+ Map.new(val.to_h)
168
200
  when ::String
169
201
  String.new(val)
170
202
  when ::Symbol
@@ -178,9 +210,9 @@ module Cel
178
210
  when ::Array
179
211
  List.new(val)
180
212
  when true, false
181
- Bool.new(val)
213
+ Bool.cast(val)
182
214
  when nil
183
- Null.new
215
+ Null::INSTANCE
184
216
  when Time
185
217
  Timestamp.new(val)
186
218
  else
@@ -188,9 +220,7 @@ module Cel
188
220
  end
189
221
  end
190
222
 
191
- def to_ruby_type
192
- @value
193
- end
223
+ alias_method :to_ruby_type, :value
194
224
 
195
225
  private
196
226
 
@@ -198,21 +228,62 @@ module Cel
198
228
  end
199
229
 
200
230
  class Number < Literal
201
- [:+, :-, *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|
202
241
  class_eval(<<-OUT, __FILE__, __LINE__ + 1)
203
242
  def #{op}(other)
204
- Number.new(@type, super)
243
+ value = super
244
+ check_overflow(value)
245
+ Number.new(@type, value)
205
246
  end
206
247
  OUT
207
248
  end
208
249
 
209
- (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|
210
267
  class_eval(<<-OUT, __FILE__, __LINE__ + 1)
211
- def #{op}(other)
212
- Bool.new(super)
268
+ def #{op}@
269
+ raise EvaluateError, "no such overload" if @type == TYPES[:uint]
270
+ value = super
271
+ Number.new(@type, value)
213
272
  end
214
273
  OUT
215
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
216
287
  end
217
288
 
218
289
  class Bool < Literal
@@ -220,16 +291,27 @@ module Cel
220
291
  super(:bool, value)
221
292
  end
222
293
 
223
- (LOGICAL_OPERATORS - %w[==]).each do |op|
294
+ (LOGICAL_OPERATORS - %w[== != in]).each do |op|
224
295
  class_eval(<<-OUT, __FILE__, __LINE__ + 1)
225
296
  def #{op}(other)
226
- 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)
227
302
  end
228
303
  OUT
229
304
  end
230
305
 
231
306
  def !
232
- 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
233
315
  end
234
316
  end
235
317
 
@@ -237,6 +319,8 @@ module Cel
237
319
  def initialize
238
320
  super(:null_type, nil)
239
321
  end
322
+
323
+ INSTANCE = new.freeze
240
324
  end
241
325
 
242
326
  class String < Literal
@@ -244,18 +328,23 @@ module Cel
244
328
  super(:string, value)
245
329
  end
246
330
 
331
+ # override base String#%
332
+ def %(_other)
333
+ raise NoMethodError, "undefined method '%' for #{self}"
334
+ end
335
+
247
336
  # CEL string functions
248
337
 
249
338
  def contains(string)
250
- Bool.new(@value.include?(string))
339
+ Bool.cast(@value.include?(string))
251
340
  end
252
341
 
253
342
  def endsWith(string)
254
- Bool.new(@value.end_with?(string))
343
+ Bool.cast(@value.end_with?(string))
255
344
  end
256
345
 
257
346
  def startsWith(string)
258
- Bool.new(@value.start_with?(string))
347
+ Bool.cast(@value.start_with?(string))
259
348
  end
260
349
 
261
350
  def matches(pattern)
@@ -272,18 +361,23 @@ module Cel
272
361
  end
273
362
 
274
363
  class Bytes < Literal
364
+ attr_reader :string
365
+
275
366
  def initialize(value)
276
367
  super(:bytes, value)
368
+ @string = value.pack("C*")
277
369
  end
278
370
 
279
371
  def to_ary
280
372
  [self]
281
373
  end
282
374
 
375
+ alias_method :to_ruby_type, :string
376
+
283
377
  (LOGICAL_OPERATORS - %w[==]).each do |op|
284
378
  class_eval(<<-OUT, __FILE__, __LINE__ + 1)
285
379
  def #{op}(other)
286
- Bool.new(super)
380
+ other.is_a?(Cel::Bytes) ? Bool.cast(@string.__send__(__method__, other.string)) : super
287
381
  end
288
382
  OUT
289
383
  end
@@ -291,7 +385,7 @@ module Cel
291
385
  %i[+ -].each do |op|
292
386
  class_eval(<<-OUT, __FILE__, __LINE__ + 1)
293
387
  def #{op}(other)
294
- String.new(@type, super)
388
+ Bytes.new(@value + other.value)
295
389
  end
296
390
  OUT
297
391
  end
@@ -302,7 +396,20 @@ module Cel
302
396
  value = value.map do |v|
303
397
  Literal.to_cel_type(v)
304
398
  end
305
- 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
306
413
  end
307
414
 
308
415
  def to_ary
@@ -312,65 +419,133 @@ module Cel
312
419
  def to_ruby_type
313
420
  value.map(&:to_ruby_type)
314
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
315
452
  end
316
453
 
317
454
  class Map < Literal
455
+ ALLOWED_TYPES = %i[int uint bool string].map { |typ| TYPES[typ] }.freeze
318
456
  def initialize(value)
319
- value = value.to_h do |k, v|
457
+ # store array internally to support repeated keys
458
+ value = value.map do |k, v|
320
459
  [Literal.to_cel_type(k), Literal.to_cel_type(v)]
321
460
  end
322
- super(MapType.new(value), value)
461
+ super(TYPES[:map], value)
323
462
  check
324
463
  end
325
464
 
326
465
  def ==(other)
327
- super || (
328
- other.respond_to?(:to_hash) &&
329
- @value.zip(other).all? { |(x1, y1), (x2, y2)| x1 == x2 && y1 == y2 }
330
- )
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
331
479
  end
332
480
 
333
- def to_ary
334
- [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
335
503
  end
336
504
 
337
505
  def to_ruby_type
338
- value.to_h { |*args| args.map(&:to_ruby_type) }
506
+ value.to_h { |args| args.map(&:to_ruby_type) }
339
507
  end
340
508
 
341
509
  def respond_to_missing?(meth, *args)
342
- super || (@value && @value.keys.any? { |k| k.to_s == meth.to_s })
510
+ super || (@value && @value.any? { |k, _| k.to_s == meth.to_s })
343
511
  end
344
512
 
345
513
  def method_missing(meth, *args)
346
514
  return super unless @value
347
515
 
348
- 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 }
349
517
 
350
- @value[key]
518
+ return super if values.empty?
519
+
520
+ values.first
351
521
  end
352
522
 
353
523
  private
354
524
 
355
- ALLOWED_TYPES = %i[int uint bool string].map { |typ| TYPES[typ] }.freeze
356
-
357
525
  # For a map, the entry keys are sub-expressions that must evaluate to values
358
526
  # of an allowed type (int, uint, bool, or string)
359
527
  def check
360
- 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
361
531
 
362
532
  raise CheckError, "#{self} is invalid (keys must be of an allowed type (int, uint, bool, or string)"
363
533
  end
364
534
  end
365
535
 
366
536
  class Timestamp < Literal
537
+ TIMESTAMP_RANGE = (Time.parse("0001-01-01T00:00:00Z")..Time.parse("9999-12-31T23:59:59.999999999Z"))
538
+
367
539
  def initialize(value)
368
540
  value = case value
369
541
  when ::String then Time.parse(value)
370
542
  when Numeric then Time.at(value)
371
543
  else value
372
544
  end
373
- super(:timestamp, value)
545
+
546
+ raise EvaluateError, "out of range" unless TIMESTAMP_RANGE.include?(value)
547
+
548
+ super(:timestamp, value.round(9))
374
549
  end
375
550
 
376
551
  def +(other)
@@ -389,7 +564,7 @@ module Cel
389
564
  LOGICAL_OPERATORS.each do |op|
390
565
  class_eval(<<-OUT, __FILE__, __LINE__ + 1)
391
566
  def #{op}(other)
392
- other.is_a?(Cel::Literal) ? Bool.new(super) : super
567
+ other.is_a?(Cel::Literal) ? Bool.cast(super) : super
393
568
  end
394
569
  OUT
395
570
  end
@@ -397,7 +572,7 @@ module Cel
397
572
  # Cel Functions
398
573
 
399
574
  def getDate(tz = nil)
400
- to_local_time(tz).day
575
+ Number.new(:int, to_local_time(tz).day)
401
576
  end
402
577
 
403
578
  def getDayOfMonth(tz = nil)
@@ -405,35 +580,39 @@ module Cel
405
580
  end
406
581
 
407
582
  def getDayOfWeek(tz = nil)
408
- to_local_time(tz).wday
583
+ Number.new(:int, to_local_time(tz).wday)
409
584
  end
410
585
 
411
586
  def getDayOfYear(tz = nil)
412
- to_local_time(tz).yday - 1
587
+ Number.new(:int, to_local_time(tz).yday - 1)
413
588
  end
414
589
 
415
590
  def getMonth(tz = nil)
416
- to_local_time(tz).month - 1
591
+ Number.new(:int, to_local_time(tz).month - 1)
417
592
  end
418
593
 
419
594
  def getFullYear(tz = nil)
420
- to_local_time(tz).year
595
+ Number.new(:int, to_local_time(tz).year)
421
596
  end
422
597
 
423
598
  def getHours(tz = nil)
424
- to_local_time(tz).hour
599
+ Number.new(:int, to_local_time(tz).hour)
425
600
  end
426
601
 
427
602
  def getMinutes(tz = nil)
428
- to_local_time(tz).min
603
+ Number.new(:int, to_local_time(tz).min)
429
604
  end
430
605
 
431
606
  def getSeconds(tz = nil)
432
- to_local_time(tz).sec
607
+ Number.new(:int, to_local_time(tz).sec)
433
608
  end
434
609
 
435
610
  def getMilliseconds(tz = nil)
436
- 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)
437
616
  end
438
617
 
439
618
  private
@@ -441,7 +620,12 @@ module Cel
441
620
  def to_local_time(tz = nil)
442
621
  time = @value
443
622
  if tz
444
- 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
445
629
  time = time.getlocal(tz)
446
630
  end
447
631
  time
@@ -450,26 +634,54 @@ module Cel
450
634
 
451
635
  class Duration < Literal
452
636
  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
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
+
464
652
  super(:duration, value)
465
653
  end
466
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
+
467
679
  LOGICAL_OPERATORS.each do |op|
468
680
  class_eval(<<-OUT, __FILE__, __LINE__ + 1)
469
681
  def #{op}(other)
470
682
  case other
471
683
  when Cel::Literal
472
- Bool.new(super)
684
+ Bool.cast(super)
473
685
  when Numeric
474
686
  @value == other
475
687
 
@@ -483,19 +695,23 @@ module Cel
483
695
  # Cel Functions
484
696
 
485
697
  def getHours
486
- (getMinutes / 60).to_i
698
+ Cel::Number.new(:int, (getMinutes / 60).to_i)
487
699
  end
488
700
 
489
701
  def getMinutes
490
- (getSeconds / 60).to_i
702
+ Cel::Number.new(:int, (getSeconds / 60).to_i)
491
703
  end
492
704
 
493
705
  def getSeconds
494
- @value.divmod(1).first
706
+ Cel::Number.new(:int, @value.divmod(1).first)
495
707
  end
496
708
 
497
709
  def getMilliseconds
498
- (@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)
499
715
  end
500
716
 
501
717
  private
@@ -521,7 +737,9 @@ module Cel
521
737
  raise EvaluateError, "#{units} is unsupported"
522
738
  end
523
739
  end
524
- 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
525
743
  end
526
744
  end
527
745
 
@@ -532,6 +750,10 @@ module Cel
532
750
  @value = value
533
751
  end
534
752
 
753
+ def type
754
+ TYPES[:any]
755
+ end
756
+
535
757
  def ==(other)
536
758
  other.is_a?(Group) && @value == other.value
537
759
  end
@@ -581,6 +803,10 @@ module Cel
581
803
  @else = else_
582
804
  end
583
805
 
806
+ def type
807
+ TYPES[:any]
808
+ end
809
+
584
810
  def ==(other)
585
811
  other.is_a?(Condition) && @if == other.if && @then == other.then && @else == other.else
586
812
  end