protostar 0.1.0.beta1
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 +60 -0
- data/LICENSE.txt +21 -0
- data/README.md +647 -0
- data/THIRD_PARTY_NOTICES.md +61 -0
- data/exe/protoc-gen-protostar +7 -0
- data/exe/protostar +7 -0
- data/lib/protostar/builder/generator.rb +71 -0
- data/lib/protostar/builder/sink.rb +35 -0
- data/lib/protostar/builder.rb +965 -0
- data/lib/protostar/cli.rb +267 -0
- data/lib/protostar/codec/decode_generator.rb +199 -0
- data/lib/protostar/codec/decoder.rb +296 -0
- data/lib/protostar/codec/encode_generator.rb +224 -0
- data/lib/protostar/codec/encoder.rb +121 -0
- data/lib/protostar/codec/sizer.rb +91 -0
- data/lib/protostar/codec.rb +15 -0
- data/lib/protostar/compiler/ast.rb +193 -0
- data/lib/protostar/compiler/emitters/descriptor_set.rb +57 -0
- data/lib/protostar/compiler/emitters/google_ruby.rb +103 -0
- data/lib/protostar/compiler/emitters/grpc.rb +175 -0
- data/lib/protostar/compiler/emitters/protostar_rbs.rb +252 -0
- data/lib/protostar/compiler/emitters/rbs.rb +394 -0
- data/lib/protostar/compiler/emitters/ruby.rb +503 -0
- data/lib/protostar/compiler/emitters.rb +268 -0
- data/lib/protostar/compiler/include/LICENSE +32 -0
- data/lib/protostar/compiler/include/google/protobuf/any.proto +106 -0
- data/lib/protostar/compiler/include/google/protobuf/api.proto +229 -0
- data/lib/protostar/compiler/include/google/protobuf/compiler/plugin.proto +180 -0
- data/lib/protostar/compiler/include/google/protobuf/cpp_features.proto +87 -0
- data/lib/protostar/compiler/include/google/protobuf/descriptor.proto +1532 -0
- data/lib/protostar/compiler/include/google/protobuf/duration.proto +115 -0
- data/lib/protostar/compiler/include/google/protobuf/empty.proto +51 -0
- data/lib/protostar/compiler/include/google/protobuf/field_mask.proto +243 -0
- data/lib/protostar/compiler/include/google/protobuf/java_features.proto +132 -0
- data/lib/protostar/compiler/include/google/protobuf/source_context.proto +48 -0
- data/lib/protostar/compiler/include/google/protobuf/struct.proto +111 -0
- data/lib/protostar/compiler/include/google/protobuf/timestamp.proto +145 -0
- data/lib/protostar/compiler/include/google/protobuf/type.proto +217 -0
- data/lib/protostar/compiler/include/google/protobuf/wrappers.proto +157 -0
- data/lib/protostar/compiler/lexer.rb +659 -0
- data/lib/protostar/compiler/linker.rb +894 -0
- data/lib/protostar/compiler/option_interpreter.rb +363 -0
- data/lib/protostar/compiler/parser.rb +1696 -0
- data/lib/protostar/compiler/retention.rb +104 -0
- data/lib/protostar/compiler/run.rb +184 -0
- data/lib/protostar/compiler/source_info.rb +161 -0
- data/lib/protostar/compiler/symbol_table.rb +223 -0
- data/lib/protostar/compiler/text_format.rb +340 -0
- data/lib/protostar/compiler/validator.rb +944 -0
- data/lib/protostar/compiler.rb +304 -0
- data/lib/protostar/diagnostics/codes.rb +1307 -0
- data/lib/protostar/diagnostics/formats.rb +64 -0
- data/lib/protostar/diagnostics/renderer.rb +240 -0
- data/lib/protostar/diagnostics/source.rb +153 -0
- data/lib/protostar/diagnostics.rb +117 -0
- data/lib/protostar/dsl.rb +367 -0
- data/lib/protostar/enum.rb +70 -0
- data/lib/protostar/errors.rb +23 -0
- data/lib/protostar/google/protobuf/any_pb.rb +90 -0
- data/lib/protostar/google/protobuf/api_pb.rb +199 -0
- data/lib/protostar/google/protobuf/compiler/plugin_pb.rb +168 -0
- data/lib/protostar/google/protobuf/descriptor_pb.rb +1239 -0
- data/lib/protostar/google/protobuf/duration_pb.rb +99 -0
- data/lib/protostar/google/protobuf/empty_pb.rb +36 -0
- data/lib/protostar/google/protobuf/field_mask_pb.rb +228 -0
- data/lib/protostar/google/protobuf/source_context_pb.rb +33 -0
- data/lib/protostar/google/protobuf/struct_pb.rb +91 -0
- data/lib/protostar/google/protobuf/timestamp_pb.rb +129 -0
- data/lib/protostar/google/protobuf/type_pb.rb +202 -0
- data/lib/protostar/google/protobuf/wrappers_pb.rb +125 -0
- data/lib/protostar/google_protobuf_shim.rb +24 -0
- data/lib/protostar/json/encoder.rb +340 -0
- data/lib/protostar/json/errors.rb +34 -0
- data/lib/protostar/json/parser.rb +437 -0
- data/lib/protostar/json/path.rb +39 -0
- data/lib/protostar/json.rb +95 -0
- data/lib/protostar/lint/command.rb +197 -0
- data/lib/protostar/lint/config.rb +98 -0
- data/lib/protostar/lint/context.rb +126 -0
- data/lib/protostar/lint/directives.rb +56 -0
- data/lib/protostar/lint/fixer.rb +165 -0
- data/lib/protostar/lint/naming.rb +54 -0
- data/lib/protostar/lint/rule.rb +120 -0
- data/lib/protostar/lint/rules/comment_enum.rb +31 -0
- data/lib/protostar/lint/rules/comment_enum_value.rb +32 -0
- data/lib/protostar/lint/rules/comment_field.rb +33 -0
- data/lib/protostar/lint/rules/comment_message.rb +31 -0
- data/lib/protostar/lint/rules/comment_rpc.rb +31 -0
- data/lib/protostar/lint/rules/comment_service.rb +31 -0
- data/lib/protostar/lint/rules/edition_recommended.rb +31 -0
- data/lib/protostar/lint/rules/enum_first_value_zero.rb +34 -0
- data/lib/protostar/lint/rules/enum_pascal_case.rb +25 -0
- data/lib/protostar/lint/rules/enum_value_prefix.rb +38 -0
- data/lib/protostar/lint/rules/enum_value_upper_snake_case.rb +28 -0
- data/lib/protostar/lint/rules/enum_zero_value_suffix.rb +41 -0
- data/lib/protostar/lint/rules/field_lower_snake_case.rb +34 -0
- data/lib/protostar/lint/rules/field_not_required.rb +39 -0
- data/lib/protostar/lint/rules/field_number_reuse_risk.rb +45 -0
- data/lib/protostar/lint/rules/field_numbers_hot_range.rb +44 -0
- data/lib/protostar/lint/rules/file_lower_snake_case.rb +27 -0
- data/lib/protostar/lint/rules/import_no_public.rb +29 -0
- data/lib/protostar/lint/rules/import_no_weak.rb +28 -0
- data/lib/protostar/lint/rules/import_used.rb +33 -0
- data/lib/protostar/lint/rules/message_pascal_case.rb +27 -0
- data/lib/protostar/lint/rules/no_deprecated_features.rb +50 -0
- data/lib/protostar/lint/rules/no_groups.rb +34 -0
- data/lib/protostar/lint/rules/oneof_lower_snake_case.rb +27 -0
- data/lib/protostar/lint/rules/package_defined.rb +28 -0
- data/lib/protostar/lint/rules/package_directory_match.rb +33 -0
- data/lib/protostar/lint/rules/package_lower_snake_case.rb +28 -0
- data/lib/protostar/lint/rules/package_same_directory.rb +39 -0
- data/lib/protostar/lint/rules/package_version_suffix.rb +31 -0
- data/lib/protostar/lint/rules/reserved_names_with_numbers.rb +44 -0
- data/lib/protostar/lint/rules/rpc_pascal_case.rb +25 -0
- data/lib/protostar/lint/rules/rpc_request_response_unique.rb +40 -0
- data/lib/protostar/lint/rules/rpc_request_standard_name.rb +32 -0
- data/lib/protostar/lint/rules/rpc_response_standard_name.rb +31 -0
- data/lib/protostar/lint/rules/service_pascal_case.rb +25 -0
- data/lib/protostar/lint/rules/service_suffix.rb +28 -0
- data/lib/protostar/lint/rules/syntax_specified.rb +35 -0
- data/lib/protostar/lint/runner.rb +135 -0
- data/lib/protostar/lint.rb +122 -0
- data/lib/protostar/message/accessors.rb +86 -0
- data/lib/protostar/message/coercion.rb +171 -0
- data/lib/protostar/message/field_info.rb +145 -0
- data/lib/protostar/message/reflection.rb +149 -0
- data/lib/protostar/message.rb +572 -0
- data/lib/protostar/plugin.rb +94 -0
- data/lib/protostar/schema/default_value.rb +113 -0
- data/lib/protostar/schema/descriptor.rb +41 -0
- data/lib/protostar/schema/draft/copy.rb +88 -0
- data/lib/protostar/schema/draft/resolution.rb +177 -0
- data/lib/protostar/schema/draft.rb +779 -0
- data/lib/protostar/schema/enum.rb +106 -0
- data/lib/protostar/schema/extension_range.rb +37 -0
- data/lib/protostar/schema/features.rb +153 -0
- data/lib/protostar/schema/field.rb +201 -0
- data/lib/protostar/schema/file.rb +217 -0
- data/lib/protostar/schema/freezer.rb +61 -0
- data/lib/protostar/schema/message.rb +104 -0
- data/lib/protostar/schema/oneof.rb +38 -0
- data/lib/protostar/schema/options.rb +367 -0
- data/lib/protostar/schema/pool.rb +179 -0
- data/lib/protostar/schema/proto.rb +576 -0
- data/lib/protostar/schema/resolver.rb +118 -0
- data/lib/protostar/schema/service.rb +89 -0
- data/lib/protostar/schema/source_info.rb +53 -0
- data/lib/protostar/schema.rb +65 -0
- data/lib/protostar/short_names/any.rb +7 -0
- data/lib/protostar/short_names/duration.rb +7 -0
- data/lib/protostar/short_names/empty.rb +7 -0
- data/lib/protostar/short_names/field_mask.rb +7 -0
- data/lib/protostar/short_names/struct.rb +9 -0
- data/lib/protostar/short_names/timestamp.rb +7 -0
- data/lib/protostar/short_names/wrappers.rb +15 -0
- data/lib/protostar/text/extensions.rb +114 -0
- data/lib/protostar/text/parser.rb +439 -0
- data/lib/protostar/text/printer.rb +304 -0
- data/lib/protostar/text/tokenizer.rb +116 -0
- data/lib/protostar/text.rb +67 -0
- data/lib/protostar/types.rb +152 -0
- data/lib/protostar/unknown_field.rb +58 -0
- data/lib/protostar/version.rb +5 -0
- data/lib/protostar/view/generator.rb +298 -0
- data/lib/protostar/view/map.rb +194 -0
- data/lib/protostar/view/repeated.rb +143 -0
- data/lib/protostar/view/scan.rb +183 -0
- data/lib/protostar/view.rb +283 -0
- data/lib/protostar/well_known/any.rb +52 -0
- data/lib/protostar/well_known/duration.rb +53 -0
- data/lib/protostar/well_known/list_value.rb +63 -0
- data/lib/protostar/well_known/struct.rb +55 -0
- data/lib/protostar/well_known/timestamp.rb +64 -0
- data/lib/protostar/well_known/value.rb +60 -0
- data/lib/protostar/well_known.rb +38 -0
- data/lib/protostar/wire/io_source.rb +40 -0
- data/lib/protostar/wire/reader.rb +254 -0
- data/lib/protostar/wire/writer.rb +201 -0
- data/lib/protostar/wire.rb +96 -0
- data/lib/protostar.rb +95 -0
- data/shims/google-protobuf/lib/google/protobuf/any_pb.rb +17 -0
- data/shims/google-protobuf/lib/google/protobuf/api_pb.rb +22 -0
- data/shims/google-protobuf/lib/google/protobuf/compiler/plugin_pb.rb +25 -0
- data/shims/google-protobuf/lib/google/protobuf/descriptor_pb.rb +72 -0
- data/shims/google-protobuf/lib/google/protobuf/descriptor_pool.rb +164 -0
- data/shims/google-protobuf/lib/google/protobuf/descriptors.rb +553 -0
- data/shims/google-protobuf/lib/google/protobuf/duration_pb.rb +17 -0
- data/shims/google-protobuf/lib/google/protobuf/empty_pb.rb +17 -0
- data/shims/google-protobuf/lib/google/protobuf/field_mask_pb.rb +17 -0
- data/shims/google-protobuf/lib/google/protobuf/internal/convert.rb +180 -0
- data/shims/google-protobuf/lib/google/protobuf/internal/extensions.rb +154 -0
- data/shims/google-protobuf/lib/google/protobuf/internal/json.rb +104 -0
- data/shims/google-protobuf/lib/google/protobuf/internal/well_known.rb +140 -0
- data/shims/google-protobuf/lib/google/protobuf/map.rb +216 -0
- data/shims/google-protobuf/lib/google/protobuf/message.rb +544 -0
- data/shims/google-protobuf/lib/google/protobuf/message_exts.rb +41 -0
- data/shims/google-protobuf/lib/google/protobuf/plugin_pb.rb +4 -0
- data/shims/google-protobuf/lib/google/protobuf/repeated_field.rb +366 -0
- data/shims/google-protobuf/lib/google/protobuf/source_context_pb.rb +17 -0
- data/shims/google-protobuf/lib/google/protobuf/struct_pb.rb +20 -0
- data/shims/google-protobuf/lib/google/protobuf/timestamp_pb.rb +17 -0
- data/shims/google-protobuf/lib/google/protobuf/type_pb.rb +27 -0
- data/shims/google-protobuf/lib/google/protobuf/well_known_types.rb +37 -0
- data/shims/google-protobuf/lib/google/protobuf/wrappers_pb.rb +25 -0
- data/shims/google-protobuf/lib/google/protobuf.rb +107 -0
- data/shims/google-protobuf/lib/google/protobuf_ffi.rb +5 -0
- data/shims/google-protobuf/lib/google/protobuf_native.rb +5 -0
- data/sig/protostar/google/protobuf/any_pb.rbs +77 -0
- data/sig/protostar/google/protobuf/api_pb.rbs +242 -0
- data/sig/protostar/google/protobuf/compiler/plugin_pb.rbs +264 -0
- data/sig/protostar/google/protobuf/descriptor_pb.rbs +2220 -0
- data/sig/protostar/google/protobuf/duration_pb.rbs +82 -0
- data/sig/protostar/google/protobuf/empty_pb.rbs +18 -0
- data/sig/protostar/google/protobuf/field_mask_pb.rbs +170 -0
- data/sig/protostar/google/protobuf/source_context_pb.rbs +24 -0
- data/sig/protostar/google/protobuf/struct_pb.rbs +135 -0
- data/sig/protostar/google/protobuf/timestamp_pb.rbs +102 -0
- data/sig/protostar/google/protobuf/type_pb.rbs +304 -0
- data/sig/protostar/google/protobuf/wrappers_pb.rbs +169 -0
- data/sig/protostar/lint.rbs +216 -0
- data/sig/protostar.rbs +326 -0
- metadata +366 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Google
|
|
4
|
+
module Protobuf
|
|
5
|
+
module Internal
|
|
6
|
+
# Checks and converts Ruby values on their way into a field, a
|
|
7
|
+
# RepeatedField or a Map, with google-protobuf's rules and error
|
|
8
|
+
# messages, into what protostar stores: Integers, Floats (single
|
|
9
|
+
# precision for float), true/false, frozen Strings, enum Symbols (or
|
|
10
|
+
# Integers for numbers without a name) and message instances.
|
|
11
|
+
# @api private
|
|
12
|
+
module Convert
|
|
13
|
+
INTEGER_TYPES = {
|
|
14
|
+
int32: [32, false], sint32: [32, false], sfixed32: [32, false],
|
|
15
|
+
int64: [64, false], sint64: [64, false], sfixed64: [64, false],
|
|
16
|
+
uint32: [32, true], fixed32: [32, true],
|
|
17
|
+
uint64: [64, true], fixed64: [64, true]
|
|
18
|
+
}.freeze
|
|
19
|
+
|
|
20
|
+
FIXNUM = -(2**62)...(2**62)
|
|
21
|
+
LONG = -(2**63)...(2**63)
|
|
22
|
+
|
|
23
|
+
UTF_8 = Encoding::UTF_8
|
|
24
|
+
BINARY = Encoding::BINARY
|
|
25
|
+
TIMESTAMP = "google.protobuf.Timestamp"
|
|
26
|
+
DURATION = "google.protobuf.Duration"
|
|
27
|
+
|
|
28
|
+
module_function
|
|
29
|
+
|
|
30
|
+
# @param value [Object]
|
|
31
|
+
# @param type [Symbol] the field type as FieldDescriptor#type gives it
|
|
32
|
+
# @param sub [Descriptor, EnumDescriptor, nil] for message and enum types
|
|
33
|
+
# @param name [String] the field name, for error messages
|
|
34
|
+
# @return [Object] what protostar stores
|
|
35
|
+
def to_store(value, type, sub, name)
|
|
36
|
+
case type
|
|
37
|
+
when :double
|
|
38
|
+
raise TypeError, "Expected number type for double field '#{name}' (given #{value.class})." unless number?(value)
|
|
39
|
+
value.to_f
|
|
40
|
+
when :float
|
|
41
|
+
raise TypeError, "Expected number type for float field '#{name}' (given #{value.class})." unless number?(value)
|
|
42
|
+
Protostar::Message::Coercion.float32(value.to_f)
|
|
43
|
+
when :bool
|
|
44
|
+
return value if value == true || value == false
|
|
45
|
+
raise TypeError, "Invalid argument for boolean field '#{name}' (given #{value.class})."
|
|
46
|
+
when :string then string(value, name)
|
|
47
|
+
when :bytes then bytes(value, name)
|
|
48
|
+
when :enum then enum(value, sub, name)
|
|
49
|
+
when :message, :group then message(value, sub, name)
|
|
50
|
+
else integer(value, type, name)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def number?(value)
|
|
55
|
+
value.is_a?(Integer) || value.is_a?(Float)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def string(value, name)
|
|
59
|
+
if value.is_a?(Symbol)
|
|
60
|
+
value = value.to_s
|
|
61
|
+
elsif !value.is_a?(String)
|
|
62
|
+
raise TypeError, "Invalid argument for string field '#{name}' (given #{value.class})."
|
|
63
|
+
end
|
|
64
|
+
if value.encoding != UTF_8
|
|
65
|
+
value = value.encode(UTF_8)
|
|
66
|
+
elsif !value.valid_encoding?
|
|
67
|
+
raise Encoding::InvalidByteSequenceError, "String is invalid UTF-8"
|
|
68
|
+
end
|
|
69
|
+
value.frozen? ? value : value.dup.freeze
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def bytes(value, name)
|
|
73
|
+
raise TypeError, "Invalid argument for bytes field '#{name}' (given #{value.class})." unless value.instance_of?(String)
|
|
74
|
+
value = value.encode(BINARY) if value.encoding != BINARY
|
|
75
|
+
value.frozen? ? value : value.dup.freeze
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def integer(value, type, name)
|
|
79
|
+
given = value
|
|
80
|
+
if value.is_a?(Float)
|
|
81
|
+
if value.nan? || (value.finite? && value.floor != value)
|
|
82
|
+
raise RangeError, "Non-integral floating point value assigned to integer field '#{name}' (given Float)."
|
|
83
|
+
end
|
|
84
|
+
raise RangeError, out_of_range(given, type) if value.infinite?
|
|
85
|
+
value = value.to_i
|
|
86
|
+
elsif !value.is_a?(Integer)
|
|
87
|
+
raise TypeError, "Expected number type for integral field '#{name}' (given #{value.class})."
|
|
88
|
+
end
|
|
89
|
+
bits, unsigned = INTEGER_TYPES.fetch(type)
|
|
90
|
+
if unsigned
|
|
91
|
+
raise RangeError, "Assigning negative value to unsigned integer field '#{name}' (given #{given.class})." if value < 0
|
|
92
|
+
return value if value.bit_length <= bits
|
|
93
|
+
elsif value.bit_length < bits
|
|
94
|
+
return value
|
|
95
|
+
end
|
|
96
|
+
raise RangeError, out_of_range(given, type)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# The message Ruby's NUM2INT and friends give, which the gem lets through
|
|
100
|
+
def out_of_range(given, type)
|
|
101
|
+
bits, unsigned = INTEGER_TYPES.fetch(type)
|
|
102
|
+
if given.is_a?(Float)
|
|
103
|
+
if bits == 64 || !LONG.cover?(given)
|
|
104
|
+
target = if bits == 32 then "integer"
|
|
105
|
+
else
|
|
106
|
+
unsigned ? "unsigned long long" : "long long"
|
|
107
|
+
end
|
|
108
|
+
text = if given.infinite? then (given > 0) ? "Inf" : "-Inf"
|
|
109
|
+
else format("%-.10g", given)
|
|
110
|
+
end
|
|
111
|
+
return "float #{text} out of range of #{target}"
|
|
112
|
+
end
|
|
113
|
+
given = given.to_i
|
|
114
|
+
end
|
|
115
|
+
if bits == 32 && FIXNUM.cover?(given)
|
|
116
|
+
"integer #{given} too #{(given < 0) ? "small" : "big"} to convert to '#{unsigned ? "unsigned int" : "int"}'"
|
|
117
|
+
elsif bits == 32
|
|
118
|
+
"bignum too big to convert into '#{unsigned ? "unsigned long" : "long"}'"
|
|
119
|
+
else
|
|
120
|
+
"bignum too big to convert into '#{unsigned ? "unsigned long long" : "long long"}'"
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def enum(value, sub, name)
|
|
125
|
+
case value
|
|
126
|
+
when Integer, Float
|
|
127
|
+
number = integer(value, :int32, name)
|
|
128
|
+
sub.__names[number] || number
|
|
129
|
+
when String, Symbol
|
|
130
|
+
number = sub.__numbers[value.to_sym]
|
|
131
|
+
raise RangeError, "Unknown symbol value for enum field '#{name}'." unless number
|
|
132
|
+
sub.__names.fetch(number)
|
|
133
|
+
else
|
|
134
|
+
raise TypeError, "Expected number or symbol type for enum field '#{name}'."
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def message(value, sub, name)
|
|
139
|
+
klass = sub.msgclass
|
|
140
|
+
return value if value.instance_of?(klass)
|
|
141
|
+
raise TypeError, "nil message not allowed here." if value.nil?
|
|
142
|
+
case sub.name
|
|
143
|
+
when TIMESTAMP
|
|
144
|
+
return klass.new(seconds: value.to_i, nanos: value.nsec) if value.is_a?(::Time)
|
|
145
|
+
when DURATION
|
|
146
|
+
if value.is_a?(Numeric)
|
|
147
|
+
seconds = value.to_i
|
|
148
|
+
return klass.new(seconds: seconds, nanos: ((value.to_f - seconds) * 1_000_000_000).round)
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
raise TypeError, "Invalid type #{value.class} to assign to submessage field '#{name}'."
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# A field's value from a Hash given to `new`: Hashes become messages, here and nested
|
|
155
|
+
# @return [Object] what protostar stores
|
|
156
|
+
def initial(value, type, sub, name)
|
|
157
|
+
if (type == :message || type == :group) && value.is_a?(Hash)
|
|
158
|
+
sub.msgclass.new(value)
|
|
159
|
+
else
|
|
160
|
+
to_store(value, type, sub, name)
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# A stored value as the gem hands it out: enum values keep their Symbol
|
|
165
|
+
# or Integer, everything else is what is stored
|
|
166
|
+
def zero(type, sub)
|
|
167
|
+
case type
|
|
168
|
+
when :double, :float then 0.0
|
|
169
|
+
when :bool then false
|
|
170
|
+
when :string then ""
|
|
171
|
+
when :bytes then "".b.freeze
|
|
172
|
+
when :enum then sub.__names[0] || 0
|
|
173
|
+
when :message, :group then nil
|
|
174
|
+
else 0
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Google
|
|
4
|
+
module Protobuf
|
|
5
|
+
module Internal
|
|
6
|
+
# Extension values live in a message's unknown fields, as protostar
|
|
7
|
+
# keeps them; these read them out and write them back by the
|
|
8
|
+
# extension's FieldDescriptor.
|
|
9
|
+
# @api private
|
|
10
|
+
module Extensions
|
|
11
|
+
ITEM = 1 # MessageSet item group
|
|
12
|
+
TYPE_ID = 2
|
|
13
|
+
MESSAGE = 3
|
|
14
|
+
|
|
15
|
+
module_function
|
|
16
|
+
|
|
17
|
+
# @return [Array] the extension's values on the message, as stored
|
|
18
|
+
def read(msg, fd)
|
|
19
|
+
info = info(fd)
|
|
20
|
+
if message_set?(msg)
|
|
21
|
+
items(msg).filter_map { |type_id, bytes| info.message_class.decode(bytes) if type_id == fd.number }
|
|
22
|
+
else
|
|
23
|
+
found = msg.unknown_fields.select { |u| u.number == fd.number }
|
|
24
|
+
codec(fd).read(info, found) || []
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Replaces the extension's values on the message
|
|
29
|
+
def write(msg, fd, values)
|
|
30
|
+
info = info(fd)
|
|
31
|
+
kept = msg.unknown_fields.reject { |u| holds?(msg, u, fd) }
|
|
32
|
+
added = if message_set?(msg)
|
|
33
|
+
values.map { |v| item(fd.number, v.encode) }
|
|
34
|
+
else
|
|
35
|
+
codec(fd).write(info, values)
|
|
36
|
+
end
|
|
37
|
+
msg.__greplace_unknown(kept + added)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def holds?(msg, unknown, fd)
|
|
41
|
+
if message_set?(msg)
|
|
42
|
+
unknown.number == ITEM && unknown.wire_type == Protostar::Wire::START_GROUP &&
|
|
43
|
+
item_type_id(unknown.bytes) == fd.number
|
|
44
|
+
else
|
|
45
|
+
unknown.number == fd.number
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def message_set?(msg)
|
|
50
|
+
msg.class.descriptor.__schema.message_set_wire_format?
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def info(fd)
|
|
54
|
+
codec(fd).info(fd.__schema) || raise(TypeError, "extension #{fd.name} has no class for its type")
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def codec(fd)
|
|
58
|
+
fd.descriptor_pool.__extension_codec
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# @return [Array<Array(Integer, String)>] each MessageSet item's type id and message bytes
|
|
62
|
+
def items(msg)
|
|
63
|
+
msg.unknown_fields.filter_map do |u|
|
|
64
|
+
next unless u.number == ITEM && u.wire_type == Protostar::Wire::START_GROUP
|
|
65
|
+
type_id = message = nil
|
|
66
|
+
r = Protostar::Wire::Reader.new(u.bytes)
|
|
67
|
+
until r.eof?
|
|
68
|
+
number, wire_type = r.read_tag
|
|
69
|
+
break if wire_type == Protostar::Wire::END_GROUP
|
|
70
|
+
if number == TYPE_ID && wire_type == Protostar::Wire::VARINT
|
|
71
|
+
type_id = r.read_varint
|
|
72
|
+
elsif number == MESSAGE && wire_type == Protostar::Wire::LENGTH_DELIMITED
|
|
73
|
+
message = r.read_bytes
|
|
74
|
+
else
|
|
75
|
+
r.skip_field(number, wire_type)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
[type_id, message] if type_id && message
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def item_type_id(bytes)
|
|
83
|
+
r = Protostar::Wire::Reader.new(bytes)
|
|
84
|
+
until r.eof?
|
|
85
|
+
number, wire_type = r.read_tag
|
|
86
|
+
return nil if wire_type == Protostar::Wire::END_GROUP
|
|
87
|
+
return r.read_varint if number == TYPE_ID && wire_type == Protostar::Wire::VARINT
|
|
88
|
+
r.skip_field(number, wire_type)
|
|
89
|
+
end
|
|
90
|
+
nil
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def item(type_id, bytes)
|
|
94
|
+
w = Protostar::Wire::Writer.new
|
|
95
|
+
w.write_tag(TYPE_ID, Protostar::Wire::VARINT)
|
|
96
|
+
w.write_varint(type_id)
|
|
97
|
+
w.write_tag(MESSAGE, Protostar::Wire::LENGTH_DELIMITED)
|
|
98
|
+
w.write_bytes(bytes)
|
|
99
|
+
w.write_tag(ITEM, Protostar::Wire::END_GROUP)
|
|
100
|
+
Protostar::UnknownField.new(ITEM, Protostar::Wire::START_GROUP, w.string.b.freeze)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
module MessageMethods
|
|
105
|
+
# @api private
|
|
106
|
+
def __greplace_unknown(fields)
|
|
107
|
+
@unknown = fields.empty? ? nil : fields.freeze
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
private
|
|
111
|
+
|
|
112
|
+
def __gext_get(fd)
|
|
113
|
+
values = Extensions.read(self, fd)
|
|
114
|
+
if fd.repeated?
|
|
115
|
+
RepeatedField.__for_field(fd, values)
|
|
116
|
+
elsif values.empty?
|
|
117
|
+
fd.default
|
|
118
|
+
else
|
|
119
|
+
values.last
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def __gext_has?(fd)
|
|
124
|
+
!Extensions.read(self, fd).empty?
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def __gext_set(fd, value)
|
|
128
|
+
values = if fd.repeated?
|
|
129
|
+
raise TypeError, "Expected repeated field array" unless value.is_a?(RepeatedField)
|
|
130
|
+
value.to_a
|
|
131
|
+
elsif value.nil?
|
|
132
|
+
[]
|
|
133
|
+
else
|
|
134
|
+
[Convert.to_store(value, fd.type, fd.subtype, fd.name)]
|
|
135
|
+
end
|
|
136
|
+
Extensions.write(self, fd, values)
|
|
137
|
+
value
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def __gext_clear(fd)
|
|
141
|
+
Extensions.write(self, fd, [])
|
|
142
|
+
nil
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
class DescriptorPool
|
|
148
|
+
# @api private
|
|
149
|
+
def __extension_codec
|
|
150
|
+
@extension_codec ||= Protostar::Text::Extensions.new(@registry, @schema_pool)
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module Google
|
|
6
|
+
module Protobuf
|
|
7
|
+
module Internal
|
|
8
|
+
# JSON through protostar's encoder and parser, with the gem's options,
|
|
9
|
+
# errors, and extensions of the top-level message written as
|
|
10
|
+
# `"[full.name]": value`.
|
|
11
|
+
# @api private
|
|
12
|
+
module Json
|
|
13
|
+
INT64 = %i[int64 sint64 sfixed64 uint64 fixed64].to_h { |t| [t, true] }.freeze
|
|
14
|
+
|
|
15
|
+
module_function
|
|
16
|
+
|
|
17
|
+
def encode(message, options)
|
|
18
|
+
json = Protostar::JSON.encode(
|
|
19
|
+
message,
|
|
20
|
+
preserve_proto_field_names: options[:preserve_proto_fieldnames] ? true : false,
|
|
21
|
+
emit_defaults: options[:emit_defaults] ? true : false,
|
|
22
|
+
format_enums_as_integers: options[:format_enums_as_integers] ? true : false,
|
|
23
|
+
registry: message.class.__gpool.__registry
|
|
24
|
+
)
|
|
25
|
+
json = with_extensions(message, json, options) unless message.unknown_fields.empty?
|
|
26
|
+
json.force_encoding(Encoding::UTF_8)
|
|
27
|
+
rescue Protostar::JSON::Error, Protostar::Error => e
|
|
28
|
+
raise ParseError, "Error occurred during encoding: #{e.message}"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def decode(klass, json, options)
|
|
32
|
+
extensions = nil
|
|
33
|
+
if json.include?("\"[")
|
|
34
|
+
tree = ::JSON.parse(json)
|
|
35
|
+
if tree.is_a?(Hash)
|
|
36
|
+
extensions, rest = tree.partition { |k, _| k.start_with?("[") && k.end_with?("]") }
|
|
37
|
+
json = ::JSON.generate(rest.to_h) unless extensions.empty?
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
message = Protostar::JSON.decode(
|
|
41
|
+
klass, json,
|
|
42
|
+
ignore_unknown_fields: options[:ignore_unknown_fields] ? true : false,
|
|
43
|
+
registry: klass.__gpool.__registry
|
|
44
|
+
)
|
|
45
|
+
extensions&.each { |key, value| set_extension(klass, message, key[1..-2], value, options) }
|
|
46
|
+
message
|
|
47
|
+
rescue Protostar::JSON::Error, Protostar::Error, ::JSON::ParserError => e
|
|
48
|
+
# upb's wording, which callers match on
|
|
49
|
+
reason = e.message.sub(/\A\S+ has no field "(.*)"\z/) { "No such field: #{$1}" }
|
|
50
|
+
raise ParseError, "Error occurred during parsing: #{reason}"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def set_extension(klass, message, name, value, options)
|
|
54
|
+
fd = klass.__gpool.lookup(name)
|
|
55
|
+
unless fd.is_a?(FieldDescriptor) && fd.__owner.equal?(klass.descriptor)
|
|
56
|
+
return if options[:ignore_unknown_fields]
|
|
57
|
+
raise ParseError, "Error occurred during parsing: No such field: [#{name}]"
|
|
58
|
+
end
|
|
59
|
+
if fd.repeated?
|
|
60
|
+
list = RepeatedField.__for_field(fd, [])
|
|
61
|
+
value.each { |v| list << from_json(fd, v) }
|
|
62
|
+
fd.set(message, list)
|
|
63
|
+
else
|
|
64
|
+
fd.set(message, from_json(fd, value))
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def from_json(fd, value)
|
|
69
|
+
case fd.type
|
|
70
|
+
when :message, :group then fd.subtype.msgclass.decode_json(::JSON.generate(value))
|
|
71
|
+
when :bytes then value.tr("-_", "+/").unpack1("m")
|
|
72
|
+
when :float, :double then value.is_a?(String) ? Float(value) : value
|
|
73
|
+
when :enum, :string, :bool then value
|
|
74
|
+
else value.is_a?(String) ? Integer(value, 10) : value
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def to_json(fd, value, options)
|
|
79
|
+
case fd.type
|
|
80
|
+
when :message, :group then fd.subtype.msgclass.encode_json(value, options)
|
|
81
|
+
when :bytes then ::JSON.generate([value].pack("m0"))
|
|
82
|
+
when :enum
|
|
83
|
+
options[:format_enums_as_integers] ? fd.subtype.__numbers.fetch(value, value).to_s : ::JSON.generate(value.to_s)
|
|
84
|
+
else
|
|
85
|
+
INT64.key?(fd.type) ? ::JSON.generate(value.to_s) : ::JSON.generate(value)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def with_extensions(message, json, options)
|
|
90
|
+
pool = message.class.__gpool
|
|
91
|
+
parts = pool.__schema_pool.extensions_of(message.class.descriptor.__schema).filter_map do |ext|
|
|
92
|
+
fd = pool.__wrap(ext)
|
|
93
|
+
values = Extensions.read(message, fd)
|
|
94
|
+
next if values.empty?
|
|
95
|
+
value = fd.repeated? ? "[#{values.map { |v| to_json(fd, v, options) }.join(",")}]" : to_json(fd, values.last, options)
|
|
96
|
+
"#{::JSON.generate("[#{ext.full_name}]")}:#{value}"
|
|
97
|
+
end
|
|
98
|
+
return json if parts.empty?
|
|
99
|
+
(json == "{}") ? "{#{parts.join(",")}}" : "#{json[0..-2]},#{parts.join(",")}}"
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Google
|
|
4
|
+
module Protobuf
|
|
5
|
+
module Internal
|
|
6
|
+
# The Ruby helpers of google.protobuf.Struct, Value and ListValue as
|
|
7
|
+
# the gem's well_known_types.rb defines them. {MessageClass.setup}
|
|
8
|
+
# includes them after {MessageMethods}, so their `[]`, `[]=` and `to_h`
|
|
9
|
+
# win over the message methods of the same names, whether or not
|
|
10
|
+
# well_known_types is required.
|
|
11
|
+
# @api private
|
|
12
|
+
module WellKnown
|
|
13
|
+
module Value
|
|
14
|
+
module ClassMethods
|
|
15
|
+
# @return [Value] a new Value holding the Ruby value
|
|
16
|
+
def from_ruby(value)
|
|
17
|
+
new.from_ruby(value)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# @param recursive [Boolean] convert a Struct or ListValue to a Hash or Array as well
|
|
22
|
+
# @return [Object]
|
|
23
|
+
# @raise [UnexpectedStructType] when no kind is set
|
|
24
|
+
def to_ruby(recursive = false)
|
|
25
|
+
case kind
|
|
26
|
+
when :struct_value then recursive ? struct_value.to_h : struct_value
|
|
27
|
+
when :list_value then recursive ? list_value.to_a : list_value
|
|
28
|
+
when :null_value then nil
|
|
29
|
+
when :number_value then number_value
|
|
30
|
+
when :string_value then string_value
|
|
31
|
+
when :bool_value then bool_value
|
|
32
|
+
else raise UnexpectedStructType
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Sets the kind matching a Ruby value
|
|
37
|
+
# @return [self]
|
|
38
|
+
# @raise [UnexpectedStructType]
|
|
39
|
+
def from_ruby(value)
|
|
40
|
+
by_name = self.class.__by_name
|
|
41
|
+
struct = by_name.fetch("struct_value").message_class
|
|
42
|
+
list = by_name.fetch("list_value").message_class
|
|
43
|
+
case value
|
|
44
|
+
when NilClass then self.null_value = :NULL_VALUE
|
|
45
|
+
when Numeric then self.number_value = value
|
|
46
|
+
when String then self.string_value = value
|
|
47
|
+
when TrueClass then self.bool_value = true
|
|
48
|
+
when FalseClass then self.bool_value = false
|
|
49
|
+
when struct then self.struct_value = value
|
|
50
|
+
when Hash then self.struct_value = struct.from_hash(value)
|
|
51
|
+
when list then self.list_value = value
|
|
52
|
+
when Array then self.list_value = list.from_a(value)
|
|
53
|
+
else raise UnexpectedStructType
|
|
54
|
+
end
|
|
55
|
+
self
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
module Struct
|
|
60
|
+
module ClassMethods
|
|
61
|
+
# @return [Struct]
|
|
62
|
+
def from_hash(hash)
|
|
63
|
+
struct = new
|
|
64
|
+
hash.each { |key, value| struct[key] = value }
|
|
65
|
+
struct
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# @return [Object, nil] the value as Ruby; nil for a missing key
|
|
70
|
+
def [](key)
|
|
71
|
+
fields[key].to_ruby
|
|
72
|
+
rescue NoMethodError
|
|
73
|
+
nil
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# @raise [UnexpectedStructType] for a key that is not a String or a value Struct cannot hold
|
|
77
|
+
def []=(key, value)
|
|
78
|
+
raise UnexpectedStructType, "Struct keys must be strings." unless key.is_a?(String)
|
|
79
|
+
fields[key] ||= self.class.__by_name.fetch("fields").value.message_class.new
|
|
80
|
+
fields[key].from_ruby(value)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# @return [Hash] with nested Structs and ListValues converted too
|
|
84
|
+
def to_h
|
|
85
|
+
fields.each_with_object({}) { |(key, value), h| h[key] = value.to_ruby(true) }
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def has_key?(key)
|
|
89
|
+
fields.has_key?(key)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
module ListValue
|
|
94
|
+
include Enumerable
|
|
95
|
+
|
|
96
|
+
module ClassMethods
|
|
97
|
+
# @return [ListValue]
|
|
98
|
+
def from_a(array)
|
|
99
|
+
list = new
|
|
100
|
+
array.each { |v| list << v }
|
|
101
|
+
list
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def length
|
|
106
|
+
values.length
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def [](index)
|
|
110
|
+
values[index].to_ruby
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def []=(index, value)
|
|
114
|
+
values[index].from_ruby(value)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def <<(value)
|
|
118
|
+
values << self.class.__by_name.fetch("values").message_class.new.from_ruby(value)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def each
|
|
122
|
+
values.each { |v| yield v.to_ruby }
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# @return [Array] with nested Structs and ListValues converted too
|
|
126
|
+
def to_a
|
|
127
|
+
values.map { |v| v.to_ruby(true) }
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Full name to the module {MessageClass.setup} mixes in
|
|
132
|
+
MIXINS = {
|
|
133
|
+
"google.protobuf.Struct" => Struct,
|
|
134
|
+
"google.protobuf.Value" => Value,
|
|
135
|
+
"google.protobuf.ListValue" => ListValue
|
|
136
|
+
}.freeze
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|