avrocadabra 0.0.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +6 -0
- data/Cargo.lock +923 -0
- data/Cargo.toml +8 -0
- data/LICENSE.txt +21 -0
- data/README.md +131 -0
- data/docs/avro_turf.md +61 -0
- data/docs/licenses/crates/quad-rand-0.2.3/DECLARED-LICENSE.txt +32 -0
- data/docs/licenses/gcc-16.2.0/COPYING +340 -0
- data/docs/licenses/gcc-16.2.0/COPYING.RUNTIME +73 -0
- data/docs/licenses/mingw-w64-14.0.0/AUTHORS +73 -0
- data/docs/licenses/mingw-w64-14.0.0/COPYING +43 -0
- data/docs/licenses/rust-1.99.0/COMPILER-BUILTINS.txt +275 -0
- data/docs/licenses/rust-1.99.0/COPYRIGHT-library.html +29357 -0
- data/docs/licenses/rust-1.99.0/LIBM.txt +258 -0
- data/docs/licenses/rust-1.99.0/LLVM-LIBUNWIND.txt +311 -0
- data/docs/licenses/rust-1.99.0/RUST-LICENSE-APACHE.txt +176 -0
- data/docs/licenses/rust-1.99.0/RUST-LICENSE-MIT.txt +25 -0
- data/docs/licenses/rust-1.99.0/STDLIB-BACKTRACE-MIT.txt +25 -0
- data/docs/licenses/rust-1.99.0/STDLIB-PORTABLE-SIMD-MIT.txt +19 -0
- data/docs/licenses/rust-1.99.0/STDLIB-STDARCH-MIT.txt +25 -0
- data/docs/licenses/rust-1.99.0/licenses/Apache-2.0.txt +73 -0
- data/docs/licenses/rust-1.99.0/licenses/BSD-2-Clause.txt +9 -0
- data/docs/licenses/rust-1.99.0/licenses/CC-BY-SA-4.0.txt +427 -0
- data/docs/licenses/rust-1.99.0/licenses/GCC-exception-3.1.txt +30 -0
- data/docs/licenses/rust-1.99.0/licenses/GPL-2.0-only.txt +133 -0
- data/docs/licenses/rust-1.99.0/licenses/GPL-3.0-or-later.txt +202 -0
- data/docs/licenses/rust-1.99.0/licenses/ISC.txt +7 -0
- data/docs/licenses/rust-1.99.0/licenses/LLVM-exception.txt +15 -0
- data/docs/licenses/rust-1.99.0/licenses/MIT.txt +9 -0
- data/docs/licenses/rust-1.99.0/licenses/NCSA.txt +28 -0
- data/docs/licenses/rust-1.99.0/licenses/OFL-1.1.txt +43 -0
- data/docs/licenses/rust-1.99.0/licenses/Unicode-3.0.txt +39 -0
- data/docs/releasing.md +71 -0
- data/docs/third_party.md +5922 -0
- data/ext/avrocadabra/Cargo.toml +29 -0
- data/ext/avrocadabra/build.rs +4 -0
- data/ext/avrocadabra/extconf.rb +9 -0
- data/ext/avrocadabra/src/big_decimal.rs +107 -0
- data/ext/avrocadabra/src/callback.rs +106 -0
- data/ext/avrocadabra/src/convert.rs +617 -0
- data/ext/avrocadabra/src/decode_value.rs +473 -0
- data/ext/avrocadabra/src/guard.rs +680 -0
- data/ext/avrocadabra/src/lib.rs +670 -0
- data/ext/avrocadabra/src/mapping.rs +63 -0
- data/ext/avrocadabra/src/memory.rs +194 -0
- data/ext/avrocadabra/src/prepare.rs +819 -0
- data/ext/avrocadabra/src/resolution.rs +1932 -0
- data/ext/avrocadabra/src/schema_state.rs +60 -0
- data/ext/avrocadabra/src/validation.rs +174 -0
- data/ext/avrocadabra/src/wire.rs +269 -0
- data/lib/avrocadabra/avro_turf/cache.rb +28 -0
- data/lib/avrocadabra/avro_turf/codec.rb +69 -0
- data/lib/avrocadabra/avro_turf/datum_reader.rb +17 -0
- data/lib/avrocadabra/avro_turf/datum_writer.rb +14 -0
- data/lib/avrocadabra/avro_turf/mapping.rb +48 -0
- data/lib/avrocadabra/avro_turf/messaging.rb +12 -0
- data/lib/avrocadabra/avro_turf/ractor_support.rb +154 -0
- data/lib/avrocadabra/avro_turf/routing.rb +15 -0
- data/lib/avrocadabra/avro_turf/schema_state.rb +70 -0
- data/lib/avrocadabra/avro_turf/validation.rb +33 -0
- data/lib/avrocadabra/avro_turf.rb +25 -0
- data/lib/avrocadabra/logical.rb +71 -0
- data/lib/avrocadabra/schema.rb +48 -0
- data/lib/avrocadabra/version.rb +5 -0
- data/lib/avrocadabra.rb +34 -0
- metadata +183 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Avrocadabra
|
|
4
|
+
module AvroTurf
|
|
5
|
+
module RactorSupport
|
|
6
|
+
JSON_ERROR = ::MultiJson::ParseError
|
|
7
|
+
EXCON_DEFAULTS = Ractor.make_shareable(::Excon::DEFAULTS, copy: true)
|
|
8
|
+
LOGICAL_TYPES = Ractor.make_shareable(::Avro::LogicalTypes::TYPES, copy: true)
|
|
9
|
+
REQUEST_KEYS = Ractor.make_shareable(::Excon::VALID_REQUEST_KEYS, copy: true)
|
|
10
|
+
CONNECTION_KEYS = Ractor.make_shareable(::Excon::VALID_CONNECTION_KEYS, copy: true)
|
|
11
|
+
STATUS_ERRORS = Ractor.make_shareable(::Excon::Error.status_errors, copy: true)
|
|
12
|
+
|
|
13
|
+
module LogicalTypes
|
|
14
|
+
def type_adapter(type, logical_type, schema = nil)
|
|
15
|
+
return super if Ractor.main?
|
|
16
|
+
return unless logical_type
|
|
17
|
+
|
|
18
|
+
adapter = LOGICAL_TYPES.fetch(type, {}).fetch(logical_type, ::Avro::LogicalTypes::Identity)
|
|
19
|
+
adapter.is_a?(Class) ? adapter.new(schema) : adapter
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
module StatusErrors
|
|
24
|
+
def status_errors
|
|
25
|
+
return super if Ractor.main?
|
|
26
|
+
|
|
27
|
+
Ractor.store_if_absent(:avrocadabra_status_errors) { STATUS_ERRORS.transform_values(&:dup) }
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
module ConnectionKeys
|
|
32
|
+
def valid_request_keys(middlewares)
|
|
33
|
+
return super if Ractor.main?
|
|
34
|
+
|
|
35
|
+
valid_middleware_keys(middlewares) + REQUEST_KEYS
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def validate_params(validation, params, middlewares)
|
|
41
|
+
return super if Ractor.main?
|
|
42
|
+
|
|
43
|
+
keys = case validation
|
|
44
|
+
when :connection then valid_middleware_keys(middlewares) + CONNECTION_KEYS
|
|
45
|
+
when :request then valid_request_keys(middlewares)
|
|
46
|
+
else raise ArgumentError, "Invalid validation type '#{validation}'"
|
|
47
|
+
end
|
|
48
|
+
if validation == :connection && params[:omit_default_port] != true
|
|
49
|
+
::Excon.display_warning("The `omit_default_port` connection option is deprecated, " \
|
|
50
|
+
"please use `include_default_port` instead.")
|
|
51
|
+
end
|
|
52
|
+
invalid = params.keys - keys
|
|
53
|
+
return if invalid.empty?
|
|
54
|
+
|
|
55
|
+
::Excon.display_warning("Invalid Excon #{validation} keys: #{invalid.map(&:inspect).join(", ")}")
|
|
56
|
+
return unless validation == :request
|
|
57
|
+
|
|
58
|
+
warn_deprecated_keys(invalid)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def warn_deprecated_keys(invalid)
|
|
62
|
+
messages = (invalid & ::Excon::DEPRECATED_VALID_REQUEST_KEYS.keys).map do |key|
|
|
63
|
+
"#{key}: #{::Excon::DEPRECATED_VALID_REQUEST_KEYS[key]}"
|
|
64
|
+
end
|
|
65
|
+
::Excon.display_warning("The following request keys are only valid with the associated middleware: " \
|
|
66
|
+
"#{messages.join(", ")}")
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
module Parsing
|
|
71
|
+
def parse(json)
|
|
72
|
+
return super if Ractor.main?
|
|
73
|
+
|
|
74
|
+
real_parse(RactorSupport.parse_json(json), {})
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
module Serialization
|
|
79
|
+
def to_s
|
|
80
|
+
return super if Ractor.main?
|
|
81
|
+
|
|
82
|
+
JSON.generate(to_avro)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
module Configuration
|
|
87
|
+
def disable_enum_symbol_validation
|
|
88
|
+
return super if Ractor.main?
|
|
89
|
+
|
|
90
|
+
@disable_enum_symbol_validation || ENV.fetch("AVRO_DISABLE_ENUM_SYMBOL_VALIDATION", "") != ""
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def disable_field_default_validation
|
|
94
|
+
return super if Ractor.main?
|
|
95
|
+
|
|
96
|
+
@disable_field_default_validation || ENV.fetch("AVRO_DISABLE_FIELD_DEFAULT_VALIDATION", "") != ""
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def disable_schema_name_validation
|
|
100
|
+
return super if Ractor.main?
|
|
101
|
+
|
|
102
|
+
@disable_schema_name_validation || ENV.fetch("AVRO_DISABLE_SCHEMA_NAME_VALIDATION", "") != ""
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
module ExconState
|
|
107
|
+
def defaults
|
|
108
|
+
return super if Ractor.main?
|
|
109
|
+
|
|
110
|
+
Ractor.store_if_absent(:avrocadabra_excon_defaults) { RactorSupport.excon_defaults }
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def defaults=(value)
|
|
114
|
+
if Ractor.main?
|
|
115
|
+
super
|
|
116
|
+
else
|
|
117
|
+
Ractor[:avrocadabra_excon_defaults] = value || RactorSupport.excon_defaults
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
class << self
|
|
123
|
+
def excon_defaults
|
|
124
|
+
EXCON_DEFAULTS.transform_values do |value|
|
|
125
|
+
value.is_a?(Hash) || value.is_a?(Array) ? value.dup : value
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def prepare
|
|
130
|
+
%i[PRIMITIVE_TYPES NAMED_TYPES VALID_TYPES PRIMITIVE_TYPES_SYM NAMED_TYPES_SYM VALID_TYPES_SYM].each do |name|
|
|
131
|
+
Ractor.make_shareable(::Avro::Schema.const_get(name))
|
|
132
|
+
end
|
|
133
|
+
Ractor.make_shareable(::Avro::LogicalTypes::IntDate::EPOCH_START)
|
|
134
|
+
::Avro::LogicalTypes.singleton_class.prepend(LogicalTypes)
|
|
135
|
+
::Excon::Error.singleton_class.prepend(StatusErrors)
|
|
136
|
+
::Excon::Connection.prepend(ConnectionKeys)
|
|
137
|
+
::Avro::Schema.singleton_class.prepend(Parsing)
|
|
138
|
+
::Avro::Schema.prepend(Serialization)
|
|
139
|
+
::Avro.singleton_class.prepend(Configuration)
|
|
140
|
+
::Excon.singleton_class.prepend(ExconState)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def parse_json(json)
|
|
144
|
+
json = json.read if json.respond_to?(:read)
|
|
145
|
+
return if json.nil? || json.strip.empty?
|
|
146
|
+
|
|
147
|
+
JSON.parse(json)
|
|
148
|
+
rescue JSON::ParserError => e
|
|
149
|
+
raise JSON_ERROR.build(e, json)
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Avrocadabra
|
|
4
|
+
module AvroTurf
|
|
5
|
+
module Routing
|
|
6
|
+
def encode(...)
|
|
7
|
+
AvroTurf.with_codecs(@avrocadabra_codecs) { super }
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def decode_message(...)
|
|
11
|
+
AvroTurf.with_codecs(@avrocadabra_codecs) { super }
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Avrocadabra
|
|
4
|
+
module AvroTurf
|
|
5
|
+
class SchemaState
|
|
6
|
+
FIELD_ATTRIBUTES = %i[name type default default? aliases].freeze
|
|
7
|
+
|
|
8
|
+
attr_reader :schemas
|
|
9
|
+
|
|
10
|
+
def initialize(source)
|
|
11
|
+
@schemas = Set.new.compare_by_identity
|
|
12
|
+
@attributes = []
|
|
13
|
+
@containers = []
|
|
14
|
+
visit(source, Set.new.compare_by_identity)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def current?
|
|
18
|
+
NativeSchema.unchanged?(@attributes, @containers)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def visit(schema, observed)
|
|
24
|
+
return if @schemas.include?(schema)
|
|
25
|
+
|
|
26
|
+
@schemas.add(schema)
|
|
27
|
+
%i[type_sym logical_type type_adapter name namespace aliases precision scale size symbols
|
|
28
|
+
default].each do |attribute|
|
|
29
|
+
observe(schema, attribute, observed) if schema.respond_to?(attribute)
|
|
30
|
+
end
|
|
31
|
+
case schema.type_sym
|
|
32
|
+
when :record, :error
|
|
33
|
+
observe(schema, :fields, observed).each do |field|
|
|
34
|
+
FIELD_ATTRIBUTES.each { observe(field, it, observed) }
|
|
35
|
+
visit(field.type, observed)
|
|
36
|
+
end
|
|
37
|
+
when :array then visit(observe(schema, :items, observed), observed)
|
|
38
|
+
when :map then visit(observe(schema, :values, observed), observed)
|
|
39
|
+
when :union then observe(schema, :schemas, observed).each { visit(it, observed) }
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def observe(object, attribute, observed)
|
|
44
|
+
value = object.public_send(attribute)
|
|
45
|
+
@attributes.push(object, attribute, value)
|
|
46
|
+
watch(value, observed)
|
|
47
|
+
value
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def watch(value, observed)
|
|
51
|
+
return if observed.include?(value)
|
|
52
|
+
|
|
53
|
+
observed.add(value)
|
|
54
|
+
case value
|
|
55
|
+
when String
|
|
56
|
+
@containers.push(value, value.dup.freeze) unless value.frozen?
|
|
57
|
+
when Array
|
|
58
|
+
@containers.push(value, value.dup.freeze) unless value.frozen?
|
|
59
|
+
value.each { watch(it, observed) }
|
|
60
|
+
when Hash
|
|
61
|
+
@containers.push(value, value.to_a.flatten(1).freeze) unless value.frozen?
|
|
62
|
+
value.each do |key, child|
|
|
63
|
+
watch(key, observed)
|
|
64
|
+
watch(child, observed)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Avrocadabra
|
|
4
|
+
module AvroTurf
|
|
5
|
+
module Validation
|
|
6
|
+
def validate!(schema, value, options = ::Avro::SchemaValidator::DEFAULT_VALIDATION_OPTIONS)
|
|
7
|
+
if Thread.current[:avrocadabra_codecs] && !options.key?(:avrocadabra_budget)
|
|
8
|
+
options = options.merge(avrocadabra_budget: [Schema::MAX_ITEMS, Schema::MAX_DEPTH + 1])
|
|
9
|
+
end
|
|
10
|
+
super
|
|
11
|
+
rescue EncodeError => e
|
|
12
|
+
raise ::Avro::IO::AvroTypeError.new(schema, value), cause: e
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def validate_recursive(schema, value, path, result, options)
|
|
18
|
+
budget = options[:avrocadabra_budget]
|
|
19
|
+
return super unless budget
|
|
20
|
+
|
|
21
|
+
depth = schema.type_sym == :union ? 0 : 1
|
|
22
|
+
budget[0] -= 1
|
|
23
|
+
budget[1] -= depth
|
|
24
|
+
raise EncodeError, "#{path}: union search exceeds maximum item count" if budget[0].negative?
|
|
25
|
+
raise EncodeError, "#{path}: union search exceeds maximum depth" if budget[1].negative?
|
|
26
|
+
|
|
27
|
+
super
|
|
28
|
+
ensure
|
|
29
|
+
budget[1] += depth if budget
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "avrocadabra"
|
|
4
|
+
require "avro_turf/messaging"
|
|
5
|
+
|
|
6
|
+
module Avrocadabra
|
|
7
|
+
module AvroTurf
|
|
8
|
+
RactorSupport.prepare
|
|
9
|
+
|
|
10
|
+
class << self
|
|
11
|
+
def with_codecs(cache)
|
|
12
|
+
previous = Thread.current[:avrocadabra_codecs]
|
|
13
|
+
Thread.current[:avrocadabra_codecs] = cache
|
|
14
|
+
yield
|
|
15
|
+
ensure
|
|
16
|
+
Thread.current[:avrocadabra_codecs] = previous
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
::AvroTurf::Messaging.prepend(Routing)
|
|
21
|
+
::Avro::IO::DatumWriter.prepend(DatumWriter)
|
|
22
|
+
::Avro::IO::DatumReader.prepend(DatumReader)
|
|
23
|
+
::Avro::SchemaValidator.singleton_class.prepend(Validation)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Avrocadabra
|
|
4
|
+
module Logical
|
|
5
|
+
EPOCH_JD = 2_440_588
|
|
6
|
+
|
|
7
|
+
class << self
|
|
8
|
+
def decimal_unscaled(value, precision, scale)
|
|
9
|
+
sign, digits, _base, exponent = decimal_input(value).split
|
|
10
|
+
return "0" if digits == "0"
|
|
11
|
+
|
|
12
|
+
shift = exponent - digits.length + scale
|
|
13
|
+
raise EncodeError, "decimal has excess scale (maximum #{scale})" if shift.negative?
|
|
14
|
+
raise EncodeError, "decimal exceeds precision #{precision}" if digits.length + shift > precision
|
|
15
|
+
|
|
16
|
+
"#{"-" if sign.negative?}#{digits}#{"0" * shift}"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def decimal_value(unscaled, scale)
|
|
20
|
+
value = BigDecimal("#{unscaled}e#{-scale}")
|
|
21
|
+
unless value.finite? && (!value.zero? || unscaled == "0")
|
|
22
|
+
raise DecodeError, "decimal exponent exceeds Ruby BigDecimal range"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
value
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def big_decimal_parts(value)
|
|
29
|
+
sign, digits, _base, exponent = decimal_input(value).split
|
|
30
|
+
return ["0", 0] if digits == "0"
|
|
31
|
+
|
|
32
|
+
[sign.negative? ? "-#{digits}" : digits, digits.length - exponent]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def date_days(value)
|
|
36
|
+
return value.to_i if value.is_a?(Numeric)
|
|
37
|
+
raise EncodeError, "date requires Date or Numeric" unless value.is_a?(Date)
|
|
38
|
+
|
|
39
|
+
value.jd - EPOCH_JD
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def date_value(days)
|
|
43
|
+
Date.jd(EPOCH_JD + days, Date::GREGORIAN)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def timestamp_ticks(value, units)
|
|
47
|
+
return value.to_i if value.is_a?(Numeric)
|
|
48
|
+
raise EncodeError, "timestamp requires Time, Date or Numeric" unless value.respond_to?(:to_time)
|
|
49
|
+
|
|
50
|
+
(value.to_time.to_r * units).floor
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def timestamp_value(ticks, units)
|
|
54
|
+
Time.at(Rational(ticks, units)).utc
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def decimal_input(value)
|
|
60
|
+
unless value.is_a?(BigDecimal) || value.is_a?(Integer) || value.is_a?(Float)
|
|
61
|
+
raise EncodeError, "decimal requires BigDecimal, Integer or Float"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
value = value.to_d
|
|
65
|
+
raise EncodeError, "decimal must be finite" unless value.finite?
|
|
66
|
+
|
|
67
|
+
value
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Avrocadabra
|
|
4
|
+
class Schema
|
|
5
|
+
MAX_DEPTH = 64
|
|
6
|
+
MAX_ITEMS = 1_000_000
|
|
7
|
+
|
|
8
|
+
def initialize(schema, references: [], max_depth: MAX_DEPTH, max_bytes: 16 * 1024 * 1024, max_items: MAX_ITEMS)
|
|
9
|
+
raise SchemaError, "references must be an Array of schemas" unless references.is_a?(Array)
|
|
10
|
+
|
|
11
|
+
@native = NativeSchema.new(
|
|
12
|
+
schema_json(schema), references.map { schema_json(it) }, max_depth, max_bytes, max_items
|
|
13
|
+
)
|
|
14
|
+
Ractor.make_shareable(self)
|
|
15
|
+
rescue JSON::JSONError, TypeError, ArgumentError, RangeError => e
|
|
16
|
+
raise SchemaError, e.message
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def encode(data, release_gvl: false)
|
|
20
|
+
@native.encode(data, release_gvl, nil)
|
|
21
|
+
rescue TypeError, ArgumentError, RangeError, EncodingError => e
|
|
22
|
+
raise EncodeError, e.message
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def decode(bytes, reader_schema: nil, release_gvl: false, tagged_unions: false)
|
|
26
|
+
unless reader_schema.nil? || reader_schema.is_a?(Schema)
|
|
27
|
+
raise DecodeError, "reader_schema must be an Avrocadabra::Schema"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
@native.decode(bytes, reader_schema&.native, release_gvl, tagged_unions, nil)
|
|
31
|
+
rescue TypeError, ArgumentError, RangeError, EncodingError => e
|
|
32
|
+
raise DecodeError, e.message
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
protected
|
|
36
|
+
|
|
37
|
+
attr_reader :native
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def schema_json(schema)
|
|
42
|
+
return schema if schema.is_a?(String)
|
|
43
|
+
raise SchemaError, "schema must be JSON, a Hash, or an Array" unless schema.is_a?(Hash) || schema.is_a?(Array)
|
|
44
|
+
|
|
45
|
+
JSON.generate(schema, max_nesting: 128)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
data/lib/avrocadabra.rb
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "bigdecimal"
|
|
5
|
+
require "bigdecimal/util"
|
|
6
|
+
require "date"
|
|
7
|
+
require "stringio"
|
|
8
|
+
require "zeitwerk"
|
|
9
|
+
|
|
10
|
+
loader = Zeitwerk::Loader.for_gem
|
|
11
|
+
loader.inflector.inflect("avro_turf" => "AvroTurf")
|
|
12
|
+
loader.do_not_eager_load("#{__dir__}/avrocadabra/avro_turf.rb", "#{__dir__}/avrocadabra/avro_turf")
|
|
13
|
+
loader.setup
|
|
14
|
+
|
|
15
|
+
module Avrocadabra
|
|
16
|
+
class Error < StandardError; end
|
|
17
|
+
class SchemaError < Error; end
|
|
18
|
+
class EncodeError < Error; end
|
|
19
|
+
class DecodeError < Error; end
|
|
20
|
+
class ResolutionError < DecodeError; end
|
|
21
|
+
|
|
22
|
+
Union = Data.define(:branch, :value)
|
|
23
|
+
Duration = Data.define(:months, :days, :milliseconds)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
begin
|
|
27
|
+
require "avrocadabra/#{RUBY_VERSION.split(".").first(2).join(".")}/avrocadabra"
|
|
28
|
+
rescue LoadError => e
|
|
29
|
+
raise unless e.path == "avrocadabra/#{RUBY_VERSION.split(".").first(2).join(".")}/avrocadabra"
|
|
30
|
+
|
|
31
|
+
require "avrocadabra/avrocadabra"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
loader.eager_load
|
metadata
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: avrocadabra
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Alexander Grebennik
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: bigdecimal
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '3.1'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '3.1'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: date
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '3.4'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '3.4'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: json
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '2.9'
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '2.9'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: rb_sys
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '0.9'
|
|
61
|
+
type: :runtime
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '0.9'
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: zeitwerk
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - ">="
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '2.8'
|
|
75
|
+
type: :runtime
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - ">="
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '2.8'
|
|
82
|
+
description: Reusable Avro schemas, raw datum encoding and decoding, and schema resolution
|
|
83
|
+
through Apache Avro
|
|
84
|
+
email:
|
|
85
|
+
- slbug@users.noreply.github.com
|
|
86
|
+
- sl.bug.sl@gmail.com
|
|
87
|
+
executables: []
|
|
88
|
+
extensions:
|
|
89
|
+
- ext/avrocadabra/extconf.rb
|
|
90
|
+
extra_rdoc_files: []
|
|
91
|
+
files:
|
|
92
|
+
- CHANGELOG.md
|
|
93
|
+
- Cargo.lock
|
|
94
|
+
- Cargo.toml
|
|
95
|
+
- LICENSE.txt
|
|
96
|
+
- README.md
|
|
97
|
+
- docs/avro_turf.md
|
|
98
|
+
- docs/licenses/crates/quad-rand-0.2.3/DECLARED-LICENSE.txt
|
|
99
|
+
- docs/licenses/gcc-16.2.0/COPYING
|
|
100
|
+
- docs/licenses/gcc-16.2.0/COPYING.RUNTIME
|
|
101
|
+
- docs/licenses/mingw-w64-14.0.0/AUTHORS
|
|
102
|
+
- docs/licenses/mingw-w64-14.0.0/COPYING
|
|
103
|
+
- docs/licenses/rust-1.99.0/COMPILER-BUILTINS.txt
|
|
104
|
+
- docs/licenses/rust-1.99.0/COPYRIGHT-library.html
|
|
105
|
+
- docs/licenses/rust-1.99.0/LIBM.txt
|
|
106
|
+
- docs/licenses/rust-1.99.0/LLVM-LIBUNWIND.txt
|
|
107
|
+
- docs/licenses/rust-1.99.0/RUST-LICENSE-APACHE.txt
|
|
108
|
+
- docs/licenses/rust-1.99.0/RUST-LICENSE-MIT.txt
|
|
109
|
+
- docs/licenses/rust-1.99.0/STDLIB-BACKTRACE-MIT.txt
|
|
110
|
+
- docs/licenses/rust-1.99.0/STDLIB-PORTABLE-SIMD-MIT.txt
|
|
111
|
+
- docs/licenses/rust-1.99.0/STDLIB-STDARCH-MIT.txt
|
|
112
|
+
- docs/licenses/rust-1.99.0/licenses/Apache-2.0.txt
|
|
113
|
+
- docs/licenses/rust-1.99.0/licenses/BSD-2-Clause.txt
|
|
114
|
+
- docs/licenses/rust-1.99.0/licenses/CC-BY-SA-4.0.txt
|
|
115
|
+
- docs/licenses/rust-1.99.0/licenses/GCC-exception-3.1.txt
|
|
116
|
+
- docs/licenses/rust-1.99.0/licenses/GPL-2.0-only.txt
|
|
117
|
+
- docs/licenses/rust-1.99.0/licenses/GPL-3.0-or-later.txt
|
|
118
|
+
- docs/licenses/rust-1.99.0/licenses/ISC.txt
|
|
119
|
+
- docs/licenses/rust-1.99.0/licenses/LLVM-exception.txt
|
|
120
|
+
- docs/licenses/rust-1.99.0/licenses/MIT.txt
|
|
121
|
+
- docs/licenses/rust-1.99.0/licenses/NCSA.txt
|
|
122
|
+
- docs/licenses/rust-1.99.0/licenses/OFL-1.1.txt
|
|
123
|
+
- docs/licenses/rust-1.99.0/licenses/Unicode-3.0.txt
|
|
124
|
+
- docs/releasing.md
|
|
125
|
+
- docs/third_party.md
|
|
126
|
+
- ext/avrocadabra/Cargo.toml
|
|
127
|
+
- ext/avrocadabra/build.rs
|
|
128
|
+
- ext/avrocadabra/extconf.rb
|
|
129
|
+
- ext/avrocadabra/src/big_decimal.rs
|
|
130
|
+
- ext/avrocadabra/src/callback.rs
|
|
131
|
+
- ext/avrocadabra/src/convert.rs
|
|
132
|
+
- ext/avrocadabra/src/decode_value.rs
|
|
133
|
+
- ext/avrocadabra/src/guard.rs
|
|
134
|
+
- ext/avrocadabra/src/lib.rs
|
|
135
|
+
- ext/avrocadabra/src/mapping.rs
|
|
136
|
+
- ext/avrocadabra/src/memory.rs
|
|
137
|
+
- ext/avrocadabra/src/prepare.rs
|
|
138
|
+
- ext/avrocadabra/src/resolution.rs
|
|
139
|
+
- ext/avrocadabra/src/schema_state.rs
|
|
140
|
+
- ext/avrocadabra/src/validation.rs
|
|
141
|
+
- ext/avrocadabra/src/wire.rs
|
|
142
|
+
- lib/avrocadabra.rb
|
|
143
|
+
- lib/avrocadabra/avro_turf.rb
|
|
144
|
+
- lib/avrocadabra/avro_turf/cache.rb
|
|
145
|
+
- lib/avrocadabra/avro_turf/codec.rb
|
|
146
|
+
- lib/avrocadabra/avro_turf/datum_reader.rb
|
|
147
|
+
- lib/avrocadabra/avro_turf/datum_writer.rb
|
|
148
|
+
- lib/avrocadabra/avro_turf/mapping.rb
|
|
149
|
+
- lib/avrocadabra/avro_turf/messaging.rb
|
|
150
|
+
- lib/avrocadabra/avro_turf/ractor_support.rb
|
|
151
|
+
- lib/avrocadabra/avro_turf/routing.rb
|
|
152
|
+
- lib/avrocadabra/avro_turf/schema_state.rb
|
|
153
|
+
- lib/avrocadabra/avro_turf/validation.rb
|
|
154
|
+
- lib/avrocadabra/logical.rb
|
|
155
|
+
- lib/avrocadabra/schema.rb
|
|
156
|
+
- lib/avrocadabra/version.rb
|
|
157
|
+
homepage: https://github.com/slbug/avrocadabra
|
|
158
|
+
licenses:
|
|
159
|
+
- MIT
|
|
160
|
+
metadata:
|
|
161
|
+
homepage_uri: https://github.com/slbug/avrocadabra
|
|
162
|
+
source_code_uri: https://github.com/slbug/avrocadabra/tree/main
|
|
163
|
+
changelog_uri: https://github.com/slbug/avrocadabra/blob/main/CHANGELOG.md
|
|
164
|
+
rubygems_mfa_required: 'true'
|
|
165
|
+
cargo_crate_name: avrocadabra
|
|
166
|
+
rdoc_options: []
|
|
167
|
+
require_paths:
|
|
168
|
+
- lib
|
|
169
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
170
|
+
requirements:
|
|
171
|
+
- - ">="
|
|
172
|
+
- !ruby/object:Gem::Version
|
|
173
|
+
version: '4.0'
|
|
174
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
175
|
+
requirements:
|
|
176
|
+
- - ">="
|
|
177
|
+
- !ruby/object:Gem::Version
|
|
178
|
+
version: '0'
|
|
179
|
+
requirements: []
|
|
180
|
+
rubygems_version: 4.0.21
|
|
181
|
+
specification_version: 4
|
|
182
|
+
summary: Avro binary encoding and decoding with a Rust native extension
|
|
183
|
+
test_files: []
|