avro 1.8.2 → 1.9.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2725b644b8e1c2579bb7aa83a4f60a3f14486122
4
- data.tar.gz: ce3c78d318872ff111c58d9cfab710fa207006ad
3
+ metadata.gz: 1e128a7db859ace5ff9b558f8c43a5a7f1c79ee5
4
+ data.tar.gz: dedca07a2a2f32894edded0b785d0c7954b65246
5
5
  SHA512:
6
- metadata.gz: 54a416f6e98d93cf33de8d6ac2d6fac40337793597cd010e98576501880ca4377398aefb4fc07f7e7d4dd7859eb062ec78e923ebf7d45b8c3084d25c4d389be8
7
- data.tar.gz: 224c3f0ee1cc0f52bda339f446caf4c66c31fb1200bb9af4fc4ea1b4daa4e5087f5749a524140300ce9271cdf406cac94f33f1d31a3658583e9ff16ec168b995
6
+ metadata.gz: 5393149d588e12974d8036cd76b63b96b2698cc02fec3608fea7d88ecec936e5c5b81444252b0e1d42932f08299ff89b81828a7e7f877e7c67faf4713b07d979
7
+ data.tar.gz: b0f8c39b2fdabe08e5769e05baf1036969753be9e93ecabd1c6230df9f839167b43cc1bbea523439a430ed624d5243c7926b44867961422785010412f132dd91
data/Manifest CHANGED
@@ -9,9 +9,12 @@ lib/avro.rb
9
9
  lib/avro/data_file.rb
10
10
  lib/avro/io.rb
11
11
  lib/avro/ipc.rb
12
+ lib/avro/logical_types.rb
12
13
  lib/avro/protocol.rb
13
14
  lib/avro/schema.rb
15
+ lib/avro/schema_compatibility.rb
14
16
  lib/avro/schema_normalization.rb
17
+ lib/avro/schema_validator.rb
15
18
  test/case_finder.rb
16
19
  test/random_data.rb
17
20
  test/sample_ipc_client.rb
@@ -22,8 +25,11 @@ test/test_datafile.rb
22
25
  test/test_fingerprints.rb
23
26
  test/test_help.rb
24
27
  test/test_io.rb
28
+ test/test_logical_types.rb
25
29
  test/test_protocol.rb
26
30
  test/test_schema.rb
31
+ test/test_schema_compatibility.rb
27
32
  test/test_schema_normalization.rb
33
+ test/test_schema_validator.rb
28
34
  test/test_socket_transport.rb
29
35
  test/tool.rb
@@ -1,35 +1,35 @@
1
1
  # -*- encoding: utf-8 -*-
2
- # stub: avro 1.8.2 ruby lib
2
+ # stub: avro 1.9.0 ruby lib
3
3
 
4
4
  Gem::Specification.new do |s|
5
- s.name = "avro"
6
- s.version = "1.8.2"
5
+ s.name = "avro".freeze
6
+ s.version = "1.9.0"
7
7
 
8
- s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
9
- s.require_paths = ["lib"]
10
- s.authors = ["Apache Software Foundation"]
11
- s.date = "2017-05-07"
12
- s.description = "Avro is a data serialization and RPC format"
13
- s.email = "dev@avro.apache.org"
14
- s.extra_rdoc_files = ["CHANGELOG", "LICENSE", "lib/avro.rb", "lib/avro/data_file.rb", "lib/avro/io.rb", "lib/avro/ipc.rb", "lib/avro/protocol.rb", "lib/avro/schema.rb", "lib/avro/schema_normalization.rb"]
15
- s.files = ["CHANGELOG", "LICENSE", "Manifest", "NOTICE", "Rakefile", "avro.gemspec", "interop/test_interop.rb", "lib/avro.rb", "lib/avro/data_file.rb", "lib/avro/io.rb", "lib/avro/ipc.rb", "lib/avro/protocol.rb", "lib/avro/schema.rb", "lib/avro/schema_normalization.rb", "test/case_finder.rb", "test/random_data.rb", "test/sample_ipc_client.rb", "test/sample_ipc_http_client.rb", "test/sample_ipc_http_server.rb", "test/sample_ipc_server.rb", "test/test_datafile.rb", "test/test_fingerprints.rb", "test/test_help.rb", "test/test_io.rb", "test/test_protocol.rb", "test/test_schema.rb", "test/test_schema_normalization.rb", "test/test_socket_transport.rb", "test/tool.rb"]
16
- s.homepage = "http://avro.apache.org/"
17
- s.licenses = ["Apache License 2.0 (Apache-2.0)"]
18
- s.rdoc_options = ["--line-numbers", "--title", "Avro"]
19
- s.rubyforge_project = "avro"
20
- s.rubygems_version = "2.2.2"
21
- s.summary = "Apache Avro for Ruby"
22
- s.test_files = ["test/test_datafile.rb", "test/test_fingerprints.rb", "test/test_help.rb", "test/test_io.rb", "test/test_protocol.rb", "test/test_schema.rb", "test/test_schema_normalization.rb", "test/test_socket_transport.rb"]
8
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2".freeze) if s.respond_to? :required_rubygems_version=
9
+ s.require_paths = ["lib".freeze]
10
+ s.authors = ["Apache Software Foundation".freeze]
11
+ s.date = "2019-05-21"
12
+ s.description = "Avro is a data serialization and RPC format".freeze
13
+ s.email = "dev@avro.apache.org".freeze
14
+ s.extra_rdoc_files = ["CHANGELOG".freeze, "LICENSE".freeze, "lib/avro.rb".freeze, "lib/avro/data_file.rb".freeze, "lib/avro/io.rb".freeze, "lib/avro/ipc.rb".freeze, "lib/avro/logical_types.rb".freeze, "lib/avro/protocol.rb".freeze, "lib/avro/schema.rb".freeze, "lib/avro/schema_compatibility.rb".freeze, "lib/avro/schema_normalization.rb".freeze, "lib/avro/schema_validator.rb".freeze]
15
+ s.files = ["CHANGELOG".freeze, "LICENSE".freeze, "Manifest".freeze, "NOTICE".freeze, "Rakefile".freeze, "avro.gemspec".freeze, "interop/test_interop.rb".freeze, "lib/avro.rb".freeze, "lib/avro/data_file.rb".freeze, "lib/avro/io.rb".freeze, "lib/avro/ipc.rb".freeze, "lib/avro/logical_types.rb".freeze, "lib/avro/protocol.rb".freeze, "lib/avro/schema.rb".freeze, "lib/avro/schema_compatibility.rb".freeze, "lib/avro/schema_normalization.rb".freeze, "lib/avro/schema_validator.rb".freeze, "test/case_finder.rb".freeze, "test/random_data.rb".freeze, "test/sample_ipc_client.rb".freeze, "test/sample_ipc_http_client.rb".freeze, "test/sample_ipc_http_server.rb".freeze, "test/sample_ipc_server.rb".freeze, "test/test_datafile.rb".freeze, "test/test_fingerprints.rb".freeze, "test/test_help.rb".freeze, "test/test_io.rb".freeze, "test/test_logical_types.rb".freeze, "test/test_protocol.rb".freeze, "test/test_schema.rb".freeze, "test/test_schema_compatibility.rb".freeze, "test/test_schema_normalization.rb".freeze, "test/test_schema_validator.rb".freeze, "test/test_socket_transport.rb".freeze, "test/tool.rb".freeze]
16
+ s.homepage = "http://avro.apache.org/".freeze
17
+ s.licenses = ["Apache License 2.0 (Apache-2.0)".freeze]
18
+ s.rdoc_options = ["--line-numbers".freeze, "--title".freeze, "Avro".freeze]
19
+ s.rubyforge_project = "avro".freeze
20
+ s.rubygems_version = "2.5.2.1".freeze
21
+ s.summary = "Apache Avro for Ruby".freeze
22
+ s.test_files = ["test/test_schema_validator.rb".freeze, "test/test_help.rb".freeze, "test/test_schema_normalization.rb".freeze, "test/test_datafile.rb".freeze, "test/test_schema.rb".freeze, "test/test_io.rb".freeze, "test/test_socket_transport.rb".freeze, "test/test_schema_compatibility.rb".freeze, "test/test_logical_types.rb".freeze, "test/test_fingerprints.rb".freeze, "test/test_protocol.rb".freeze]
23
23
 
24
24
  if s.respond_to? :specification_version then
25
25
  s.specification_version = 4
26
26
 
27
27
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
28
- s.add_runtime_dependency(%q<multi_json>, [">= 0"])
28
+ s.add_runtime_dependency(%q<multi_json>.freeze, [">= 0"])
29
29
  else
30
- s.add_dependency(%q<multi_json>, [">= 0"])
30
+ s.add_dependency(%q<multi_json>.freeze, [">= 0"])
31
31
  end
32
32
  else
33
- s.add_dependency(%q<multi_json>, [">= 0"])
33
+ s.add_dependency(%q<multi_json>.freeze, [">= 0"])
34
34
  end
35
35
  end
@@ -32,6 +32,15 @@ module Avro
32
32
  super(msg)
33
33
  end
34
34
  end
35
+
36
+ class << self
37
+ attr_writer :disable_field_default_validation
38
+
39
+ def disable_field_default_validation
40
+ @disable_field_default_validation ||=
41
+ ENV.fetch('AVRO_DISABLE_FIELD_DEFAULT_VALIDATION', '') != ''
42
+ end
43
+ end
35
44
  end
36
45
 
37
46
  require 'avro/schema'
@@ -40,3 +49,5 @@ require 'avro/data_file'
40
49
  require 'avro/protocol'
41
50
  require 'avro/ipc'
42
51
  require 'avro/schema_normalization'
52
+ require 'avro/schema_validator'
53
+ require 'avro/schema_compatibility'
@@ -5,9 +5,9 @@
5
5
  # to you under the Apache License, Version 2.0 (the
6
6
  # "License"); you may not use this file except in compliance
7
7
  # with the License. You may obtain a copy of the License at
8
- #
8
+ #
9
9
  # http://www.apache.org/licenses/LICENSE-2.0
10
- #
10
+ #
11
11
  # Unless required by applicable law or agreed to in writing, software
12
12
  # distributed under the License is distributed on an "AS IS" BASIS,
13
13
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -43,9 +43,9 @@ module Avro
43
43
  end
44
44
 
45
45
  def byte!
46
- @reader.read(1).unpack('C').first
46
+ @reader.readbyte
47
47
  end
48
-
48
+
49
49
  def read_null
50
50
  # null is written as zero byte's
51
51
  nil
@@ -76,7 +76,7 @@ module Avro
76
76
  # The float is converted into a 32-bit integer using a method
77
77
  # equivalent to Java's floatToIntBits and then encoded in
78
78
  # little-endian format.
79
- @reader.read(4).unpack('e')[0]
79
+ read_and_unpack(4, 'e'.freeze)
80
80
  end
81
81
 
82
82
  def read_double
@@ -84,7 +84,7 @@ module Avro
84
84
  # The double is converted into a 64-bit integer using a method
85
85
  # equivalent to Java's doubleToLongBits and then encoded in
86
86
  # little-endian format.
87
- @reader.read(8).unpack('E')[0]
87
+ read_and_unpack(8, 'E'.freeze)
88
88
  end
89
89
 
90
90
  def read_bytes
@@ -97,7 +97,7 @@ module Avro
97
97
  # A string is encoded as a long followed by that many bytes of
98
98
  # UTF-8 encoded character data.
99
99
  read_bytes.tap do |string|
100
- string.force_encoding("UTF-8") if string.respond_to? :force_encoding
100
+ string.force_encoding('UTF-8'.freeze) if string.respond_to? :force_encoding
101
101
  end
102
102
  end
103
103
 
@@ -144,6 +144,23 @@ module Avro
144
144
  def skip(n)
145
145
  reader.seek(reader.tell() + n)
146
146
  end
147
+
148
+ private
149
+
150
+ # Optimize unpacking strings when `unpack1` is available (ruby >= 2.4)
151
+ if String.instance_methods.include?(:unpack1)
152
+
153
+ def read_and_unpack(byte_count, format)
154
+ @reader.read(byte_count).unpack1(format)
155
+ end
156
+
157
+ else
158
+
159
+ def read_and_unpack(byte_count, format)
160
+ @reader.read(byte_count).unpack(format)[0]
161
+ end
162
+
163
+ end
147
164
  end
148
165
 
149
166
  # Write leaf values
@@ -159,7 +176,7 @@ module Avro
159
176
  nil
160
177
  end
161
178
 
162
- # a boolean is written as a single byte
179
+ # a boolean is written as a single byte
163
180
  # whose value is either 0 (false) or 1 (true).
164
181
  def write_boolean(datum)
165
182
  on_disk = datum ? 1.chr : 0.chr
@@ -175,7 +192,6 @@ module Avro
175
192
  # int and long values are written using variable-length,
176
193
  # zig-zag coding.
177
194
  def write_long(n)
178
- foo = n
179
195
  n = (n << 1) ^ (n >> 63)
180
196
  while (n & ~0x7F) != 0
181
197
  @writer.write(((n & 0x7f) | 0x80).chr)
@@ -189,7 +205,7 @@ module Avro
189
205
  # equivalent to Java's floatToIntBits and then encoded in
190
206
  # little-endian format.
191
207
  def write_float(datum)
192
- @writer.write([datum].pack('e'))
208
+ @writer.write([datum].pack('e'.freeze))
193
209
  end
194
210
 
195
211
  # A double is written as 8 bytes.
@@ -197,7 +213,7 @@ module Avro
197
213
  # equivalent to Java's doubleToLongBits and then encoded in
198
214
  # little-endian format.
199
215
  def write_double(datum)
200
- @writer.write([datum].pack('E'))
216
+ @writer.write([datum].pack('E'.freeze))
201
217
  end
202
218
 
203
219
  # Bytes are encoded as a long followed by that many bytes of data.
@@ -209,7 +225,7 @@ module Avro
209
225
  # A string is encoded as a long followed by that many bytes of
210
226
  # UTF-8 encoded character data
211
227
  def write_string(datum)
212
- datum = datum.encode('utf-8') if datum.respond_to? :encode
228
+ datum = datum.encode('utf-8'.freeze) if datum.respond_to? :encode
213
229
  write_bytes(datum)
214
230
  end
215
231
 
@@ -221,46 +237,7 @@ module Avro
221
237
 
222
238
  class DatumReader
223
239
  def self.match_schemas(writers_schema, readers_schema)
224
- w_type = writers_schema.type_sym
225
- r_type = readers_schema.type_sym
226
-
227
- # This conditional is begging for some OO love.
228
- if w_type == :union || r_type == :union
229
- return true
230
- end
231
-
232
- if w_type == r_type
233
- return true if Schema::PRIMITIVE_TYPES_SYM.include?(r_type)
234
-
235
- case r_type
236
- when :record
237
- return writers_schema.fullname == readers_schema.fullname
238
- when :error
239
- return writers_schema.fullname == readers_schema.fullname
240
- when :request
241
- return true
242
- when :fixed
243
- return writers_schema.fullname == readers_schema.fullname &&
244
- writers_schema.size == readers_schema.size
245
- when :enum
246
- return writers_schema.fullname == readers_schema.fullname
247
- when :map
248
- return writers_schema.values.type == readers_schema.values.type
249
- when :array
250
- return writers_schema.items.type == readers_schema.items.type
251
- end
252
- end
253
-
254
- # Handle schema promotion
255
- if w_type == :int && [:long, :float, :double].include?(r_type)
256
- return true
257
- elsif w_type == :long && [:float, :double].include?(r_type)
258
- return true
259
- elsif w_type == :float && r_type == :double
260
- return true
261
- end
262
-
263
- return false
240
+ Avro::SchemaCompatibility.match_schemas(writers_schema, readers_schema)
264
241
  end
265
242
 
266
243
  attr_accessor :writers_schema, :readers_schema
@@ -293,7 +270,7 @@ module Avro
293
270
 
294
271
  # function dispatch for reading data based on type of writer's
295
272
  # schema
296
- case writers_schema.type_sym
273
+ datum = case writers_schema.type_sym
297
274
  when :null; decoder.read_null
298
275
  when :boolean; decoder.read_boolean
299
276
  when :string; decoder.read_string
@@ -311,6 +288,8 @@ module Avro
311
288
  else
312
289
  raise AvroError, "Cannot read unknown schema type: #{writers_schema.type}"
313
290
  end
291
+
292
+ readers_schema.type_adapter.decode(datum)
314
293
  end
315
294
 
316
295
  def read_fixed(writers_schema, readers_schema, decoder)
@@ -336,7 +315,7 @@ module Avro
336
315
  while block_count != 0
337
316
  if block_count < 0
338
317
  block_count = -block_count
339
- block_size = decoder.read_long
318
+ _block_size = decoder.read_long
340
319
  end
341
320
  block_count.times do
342
321
  read_items << read_data(writers_schema.items,
@@ -355,7 +334,7 @@ module Avro
355
334
  while block_count != 0
356
335
  if block_count < 0
357
336
  block_count = -block_count
358
- block_size = decoder.read_long
337
+ _block_size = decoder.read_long
359
338
  end
360
339
  block_count.times do
361
340
  key = decoder.read_string
@@ -393,11 +372,11 @@ module Avro
393
372
  writers_fields_hash = writers_schema.fields_hash
394
373
  readers_fields_hash.each do |field_name, field|
395
374
  unless writers_fields_hash.has_key? field_name
396
- if !field.default.nil?
375
+ if field.default?
397
376
  field_val = read_default_value(field.type, field.default)
398
377
  read_record[field.name] = field_val
399
378
  else
400
- # FIXME(jmhodges) another 'unset' here
379
+ raise AvroError, "Missing data for #{field.type} with no default"
401
380
  end
402
381
  end
403
382
  end
@@ -407,10 +386,6 @@ module Avro
407
386
  end
408
387
 
409
388
  def read_default_value(field_schema, default_value)
410
- if default_value == :no_default
411
- raise AvroError, "Missing data for #{field_schema} with no default"
412
- end
413
-
414
389
  # Basically a JSON Decoder?
415
390
  case field_schema.type_sym
416
391
  when :null
@@ -524,7 +499,7 @@ module Avro
524
499
  if block_count < 0
525
500
  decoder.skip(decoder.read_long)
526
501
  else
527
- block_count.times &blk
502
+ block_count.times(&blk)
528
503
  end
529
504
  block_count = decoder.read_long
530
505
  end
@@ -542,8 +517,10 @@ module Avro
542
517
  write_data(writers_schema, datum, encoder)
543
518
  end
544
519
 
545
- def write_data(writers_schema, datum, encoder)
546
- unless Schema.validate(writers_schema, datum)
520
+ def write_data(writers_schema, logical_datum, encoder)
521
+ datum = writers_schema.type_adapter.encode(logical_datum)
522
+
523
+ unless Schema.validate(writers_schema, datum, { recursive: false, encoded: true })
547
524
  raise AvroTypeError.new(writers_schema, datum)
548
525
  end
549
526
 
@@ -578,6 +555,7 @@ module Avro
578
555
  end
579
556
 
580
557
  def write_array(writers_schema, datum, encoder)
558
+ raise AvroTypeError.new(writers_schema, datum) unless datum.is_a?(Array)
581
559
  if datum.size > 0
582
560
  encoder.write_long(datum.size)
583
561
  datum.each do |item|
@@ -588,6 +566,7 @@ module Avro
588
566
  end
589
567
 
590
568
  def write_map(writers_schema, datum, encoder)
569
+ raise AvroTypeError.new(writers_schema, datum) unless datum.is_a?(Hash)
591
570
  if datum.size > 0
592
571
  encoder.write_long(datum.size)
593
572
  datum.each do |k,v|
@@ -610,6 +589,7 @@ module Avro
610
589
  end
611
590
 
612
591
  def write_record(writers_schema, datum, encoder)
592
+ raise AvroTypeError.new(writers_schema, datum) unless datum.is_a?(Hash)
613
593
  writers_schema.fields.each do |field|
614
594
  write_data(field.type, datum[field.name], encoder)
615
595
  end
@@ -5,9 +5,9 @@
5
5
  # to you under the Apache License, Version 2.0 (the
6
6
  # "License"); you may not use this file except in compliance
7
7
  # with the License. You may obtain a copy of the License at
8
- #
8
+ #
9
9
  # http://www.apache.org/licenses/LICENSE-2.0
10
- #
10
+ #
11
11
  # Unless required by applicable law or agreed to in writing, software
12
12
  # distributed under the License is distributed on an "AS IS" BASIS,
13
13
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -74,10 +74,10 @@ module Avro::IPC
74
74
 
75
75
  class ConnectionClosedException < Avro::AvroError; end
76
76
 
77
+ # Base class for the client side of a protocol interaction.
77
78
  class Requestor
78
- """Base class for the client side of a protocol interaction."""
79
- attr_reader :local_protocol, :transport
80
- attr_accessor :remote_protocol, :remote_hash, :send_protocol
79
+ attr_reader :local_protocol, :transport, :remote_protocol, :remote_hash
80
+ attr_accessor :send_protocol
81
81
 
82
82
  def initialize(local_protocol, transport)
83
83
  @local_protocol = local_protocol
@@ -193,9 +193,9 @@ module Avro::IPC
193
193
  # * a one-byte error flag boolean, followed by either:
194
194
  # * if the error flag is false,
195
195
  # the message response, serialized per the message's response schema.
196
- # * if the error flag is true,
196
+ # * if the error flag is true,
197
197
  # the error, serialized per the message's error union schema.
198
- response_metadata = META_READER.read(decoder)
198
+ _response_metadata = META_READER.read(decoder)
199
199
 
200
200
  # remote response schema
201
201
  remote_message_schema = remote_protocol.messages[message_name]
@@ -257,7 +257,7 @@ module Avro::IPC
257
257
  end
258
258
 
259
259
  # read request using remote protocol
260
- request_metadata = META_READER.read(buffer_decoder)
260
+ _request_metadata = META_READER.read(buffer_decoder)
261
261
  remote_message_name = buffer_decoder.read_string
262
262
 
263
263
  # get remote and local request schemas so we can do
@@ -0,0 +1,90 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Licensed to the Apache Software Foundation (ASF) under one
3
+ # or more contributor license agreements. See the NOTICE file
4
+ # distributed with this work for additional information
5
+ # regarding copyright ownership. The ASF licenses this file
6
+ # to you under the Apache License, Version 2.0 (the
7
+ # "License"); you may not use this file except in compliance
8
+ # with the License. You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+ require 'date'
19
+
20
+ module Avro
21
+ module LogicalTypes
22
+ module IntDate
23
+ EPOCH_START = Date.new(1970, 1, 1)
24
+
25
+ def self.encode(date)
26
+ return date.to_i if date.is_a?(Numeric)
27
+
28
+ (date - EPOCH_START).to_i
29
+ end
30
+
31
+ def self.decode(int)
32
+ EPOCH_START + int
33
+ end
34
+ end
35
+
36
+ module TimestampMillis
37
+ def self.encode(value)
38
+ return value.to_i if value.is_a?(Numeric)
39
+
40
+ time = value.to_time
41
+ time.to_i * 1000 + time.usec / 1000
42
+ end
43
+
44
+ def self.decode(int)
45
+ s, ms = int / 1000, int % 1000
46
+ Time.at(s, ms * 1000).utc
47
+ end
48
+ end
49
+
50
+ module TimestampMicros
51
+ def self.encode(value)
52
+ return value.to_i if value.is_a?(Numeric)
53
+
54
+ time = value.to_time
55
+ time.to_i * 1000_000 + time.usec
56
+ end
57
+
58
+ def self.decode(int)
59
+ s, us = int / 1000_000, int % 1000_000
60
+ Time.at(s, us).utc
61
+ end
62
+ end
63
+
64
+ module Identity
65
+ def self.encode(datum)
66
+ datum
67
+ end
68
+
69
+ def self.decode(datum)
70
+ datum
71
+ end
72
+ end
73
+
74
+ TYPES = {
75
+ "int" => {
76
+ "date" => IntDate
77
+ },
78
+ "long" => {
79
+ "timestamp-millis" => TimestampMillis,
80
+ "timestamp-micros" => TimestampMicros
81
+ },
82
+ }.freeze
83
+
84
+ def self.type_adapter(type, logical_type)
85
+ return unless logical_type
86
+
87
+ TYPES.fetch(type, {}.freeze).fetch(logical_type, Identity)
88
+ end
89
+ end
90
+ end