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,544 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Google
|
|
4
|
+
module Protobuf
|
|
5
|
+
module Internal
|
|
6
|
+
# Turns a class protostar's registry made into a google-protobuf
|
|
7
|
+
# message class: the gem's class and instance methods are mixed in,
|
|
8
|
+
# and the field accessors are defined in a module prepended to the class.
|
|
9
|
+
# @api private
|
|
10
|
+
module MessageClass
|
|
11
|
+
module_function
|
|
12
|
+
|
|
13
|
+
# @param klass [Class] a subclass of Protostar::Message
|
|
14
|
+
# @param descriptor [Descriptor]
|
|
15
|
+
def setup(klass, descriptor)
|
|
16
|
+
descriptor.__msgclass = klass
|
|
17
|
+
plan = Array.new(klass.__fields.size)
|
|
18
|
+
klass.__fields.each { |info| plan[info.index] = descriptor.lookup(info.name) }
|
|
19
|
+
oneofs = []
|
|
20
|
+
descriptor.each_oneof { |o| oneofs << o }
|
|
21
|
+
klass.instance_variable_set(:@__gdescriptor, descriptor)
|
|
22
|
+
klass.instance_variable_set(:@__gplan, plan.freeze)
|
|
23
|
+
klass.instance_variable_set(:@__gnames, plan.to_h { |fd| [fd.name, fd] }.freeze)
|
|
24
|
+
klass.instance_variable_set(:@__goneof_setters, oneofs.to_h { |o| [:"#{o.name}=", true] }.freeze)
|
|
25
|
+
klass.instance_variable_set(:@__gnested, klass.__fields.select { |i| i.map ? i.value.message? : i.message? }.freeze)
|
|
26
|
+
klass.include(MessageExts)
|
|
27
|
+
klass.include(MessageMethods)
|
|
28
|
+
klass.extend(MessageExts::ClassMethods)
|
|
29
|
+
klass.extend(ClassMethods)
|
|
30
|
+
accessors = Module.new
|
|
31
|
+
taken = reserved.dup
|
|
32
|
+
plan.each { |fd| define_field(accessors, fd, taken) }
|
|
33
|
+
oneofs.each { |o| define_oneof(accessors, o, taken) }
|
|
34
|
+
klass.prepend(accessors)
|
|
35
|
+
if (well_known = WellKnown::MIXINS[descriptor.name])
|
|
36
|
+
klass.include(well_known)
|
|
37
|
+
klass.extend(well_known::ClassMethods)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Methods a message already has; a field named like one is reached through [] and []=
|
|
42
|
+
def reserved
|
|
43
|
+
@reserved ||= (Object.public_instance_methods + Protostar::Message.public_instance_methods +
|
|
44
|
+
MessageExts.public_instance_methods + MessageMethods.public_instance_methods + %i[initialize])
|
|
45
|
+
.to_h { |m| [m, true] }.freeze
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def define_field(mod, fd, taken)
|
|
49
|
+
info = fd.__info
|
|
50
|
+
i = info.index
|
|
51
|
+
name = fd.name
|
|
52
|
+
claim = ->(method) { !taken[method] && (taken[method] = true) }
|
|
53
|
+
if claim.call(name.to_sym)
|
|
54
|
+
if info.map
|
|
55
|
+
mod.define_method(name) { __gmap(fd) }
|
|
56
|
+
elsif info.repeated
|
|
57
|
+
mod.define_method(name) { __grepeated(fd) }
|
|
58
|
+
elsif info.presence && !info.message?
|
|
59
|
+
default = info.default
|
|
60
|
+
mod.define_method(name) do
|
|
61
|
+
v = @values[i]
|
|
62
|
+
v.nil? ? default : v
|
|
63
|
+
end
|
|
64
|
+
else
|
|
65
|
+
mod.define_method(name) { @values[i] }
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
mod.define_method(:"#{name}=") { |v| __gset(fd, v) } if claim.call(:"#{name}=")
|
|
69
|
+
mod.define_method(:"clear_#{name}") { __gclear(fd) } if claim.call(:"clear_#{name}")
|
|
70
|
+
mod.define_method(:"has_#{name}?") { !@values[i].nil? } if info.presence && claim.call(:"has_#{name}?")
|
|
71
|
+
if fd.type == :enum && claim.call(:"#{name}_const")
|
|
72
|
+
if info.repeated
|
|
73
|
+
mod.define_method(:"#{name}_const") { __grepeated(fd).map { |v| fd.subtype.__numbers.fetch(v, v) } }
|
|
74
|
+
else
|
|
75
|
+
mod.define_method(:"#{name}_const") do
|
|
76
|
+
v = @values[i]
|
|
77
|
+
v = info.default if v.nil?
|
|
78
|
+
fd.subtype.__numbers.fetch(v, v)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
return unless !info.repeated && fd.wrapper?
|
|
83
|
+
mod.define_method(:"#{name}_as_value") { @values[i]&.value } if claim.call(:"#{name}_as_value")
|
|
84
|
+
if claim.call(:"#{name}_as_value=")
|
|
85
|
+
mod.define_method(:"#{name}_as_value=") do |v|
|
|
86
|
+
if v.nil?
|
|
87
|
+
__gclear(fd)
|
|
88
|
+
else
|
|
89
|
+
sub = fd.subtype
|
|
90
|
+
value_fd = sub.lookup("value")
|
|
91
|
+
__gset(fd, sub.msgclass.new(value: Convert.to_store(v, value_fd.type, nil, name)))
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def define_oneof(mod, oneof, taken)
|
|
98
|
+
name = oneof.name
|
|
99
|
+
fields = oneof.map { |fd| [fd.__info.index, fd.name.to_sym] }.freeze
|
|
100
|
+
if !taken[name.to_sym] && (taken[name.to_sym] = true)
|
|
101
|
+
mod.define_method(name) do
|
|
102
|
+
fields.each { |slot, member| return member unless @values[slot].nil? }
|
|
103
|
+
nil
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
if !taken[:"clear_#{name}"] && (taken[:"clear_#{name}"] = true)
|
|
107
|
+
mod.define_method(:"clear_#{name}") do
|
|
108
|
+
__check_gfrozen
|
|
109
|
+
fields.each { |slot, _| @values[slot] = nil }
|
|
110
|
+
nil
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
if !taken[:"has_#{name}?"] && (taken[:"has_#{name}?"] = true)
|
|
114
|
+
mod.define_method(:"has_#{name}?") { fields.any? { |slot, _| !@values[slot].nil? } }
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Class methods of every message class
|
|
120
|
+
# @api private
|
|
121
|
+
module ClassMethods
|
|
122
|
+
MAX_INPUT = 0x7FFF_FFFF
|
|
123
|
+
|
|
124
|
+
# @return [Descriptor]
|
|
125
|
+
def descriptor
|
|
126
|
+
@__gdescriptor
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# @api private
|
|
130
|
+
attr_reader :__gplan, :__gnames, :__goneof_setters, :__gnested
|
|
131
|
+
|
|
132
|
+
# @param data [String] protobuf wire format
|
|
133
|
+
# @param options [Hash] `recursion_limit:` how deep messages may nest (default 100)
|
|
134
|
+
# @return [Message]
|
|
135
|
+
# @raise [ParseError]
|
|
136
|
+
def decode(data, options = {})
|
|
137
|
+
raise ArgumentError, "Expected hash arguments." unless options.is_a?(Hash)
|
|
138
|
+
raise ArgumentError, "Expected string for binary protobuf data." unless data.is_a?(String)
|
|
139
|
+
# upb refuses input of 2 GiB or more, the most the wire format can describe
|
|
140
|
+
raise ParseError, "Error occurred during parsing" if data.bytesize > MAX_INPUT
|
|
141
|
+
limit = options[:recursion_limit]
|
|
142
|
+
limit = limit.is_a?(Numeric) ? limit.to_i : Protostar::Codec::Decoder::RECURSION_LIMIT
|
|
143
|
+
Protostar::Codec::Decoder.decode(self, data, recursion_limit: limit)
|
|
144
|
+
rescue Protostar::DecodeError
|
|
145
|
+
raise ParseError, "Error occurred during parsing"
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# @param message [Message] an instance of this class
|
|
149
|
+
# @param options [Hash] `recursion_limit:` how deep messages may nest (default 100)
|
|
150
|
+
# @return [String] binary, frozen
|
|
151
|
+
def encode(message, options = {})
|
|
152
|
+
raise ArgumentError, "Message of wrong type." unless message.is_a?(self)
|
|
153
|
+
raise ArgumentError, "Expected hash arguments." unless options.is_a?(Hash)
|
|
154
|
+
limit = options[:recursion_limit]
|
|
155
|
+
limit = limit.is_a?(Numeric) ? limit.to_i : Protostar::Codec::Decoder::RECURSION_LIMIT
|
|
156
|
+
raise "Exceeded maximum depth (possibly cycle)" if message.__gdepth(limit + 1) > limit
|
|
157
|
+
Protostar::Codec::Encoder.encode(message)
|
|
158
|
+
rescue Protostar::EncodeError => e
|
|
159
|
+
raise TypeError, e.message
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# @param data [String] JSON
|
|
163
|
+
# @param options [Hash] `ignore_unknown_fields:`
|
|
164
|
+
# @return [Message]
|
|
165
|
+
# @raise [ParseError]
|
|
166
|
+
def decode_json(data, options = {})
|
|
167
|
+
raise ArgumentError, "Expected hash arguments." unless options.is_a?(Hash)
|
|
168
|
+
raise ArgumentError, "Expected string for JSON data." unless data.is_a?(String)
|
|
169
|
+
raise "Cannot parse a wrapper directly" if descriptor.send(:wrapper?)
|
|
170
|
+
# upb reads empty input as an empty message
|
|
171
|
+
return new if data.empty?
|
|
172
|
+
Internal::Json.decode(self, data, options)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# @param message [Message] an instance of this class
|
|
176
|
+
# @param options [Hash] `preserve_proto_fieldnames:`, `emit_defaults:`, `format_enums_as_integers:`
|
|
177
|
+
# @return [String] UTF-8, frozen
|
|
178
|
+
def encode_json(message, options = {})
|
|
179
|
+
Internal::Json.encode(message, Internal.options_hash(options))
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# @api private
|
|
183
|
+
def __gpool
|
|
184
|
+
@__gdescriptor.descriptor_pool
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# The options Hash the gem's methods take, from a Hash or anything with to_h
|
|
189
|
+
# @api private
|
|
190
|
+
def self.options_hash(options)
|
|
191
|
+
return options if options.is_a?(Hash)
|
|
192
|
+
return options.to_h if options.respond_to?(:to_h)
|
|
193
|
+
raise ArgumentError, "Expected hash arguments."
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# Instance methods of every message class
|
|
197
|
+
# @api private
|
|
198
|
+
module MessageMethods
|
|
199
|
+
NOTHING = Object.new.freeze
|
|
200
|
+
private_constant :NOTHING
|
|
201
|
+
|
|
202
|
+
# @param initial_value [Hash, nil] field names (Strings or Symbols) to values;
|
|
203
|
+
# Hashes become messages, Arrays repeated fields, Hashes map fields
|
|
204
|
+
# @raise [ArgumentError] for a name that is not a field
|
|
205
|
+
def initialize(initial_value = NOTHING)
|
|
206
|
+
super()
|
|
207
|
+
return if NOTHING.equal?(initial_value)
|
|
208
|
+
unless initial_value.is_a?(Hash)
|
|
209
|
+
raise ArgumentError, "Expected hash arguments or message, not #{initial_value.class}"
|
|
210
|
+
end
|
|
211
|
+
names = self.class.__gnames
|
|
212
|
+
initial_value.each do |key, value|
|
|
213
|
+
unless key.is_a?(String) || key.is_a?(Symbol)
|
|
214
|
+
raise ArgumentError, "Expected string or symbols as hash keys when initializing proto from hash."
|
|
215
|
+
end
|
|
216
|
+
fd = names[key.to_s]
|
|
217
|
+
raise ArgumentError, "Unknown field name '#{key}' in initialization map entry." unless fd
|
|
218
|
+
__ginit(fd, value) unless value.nil?
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# @param name [String]
|
|
223
|
+
# @return [Object, nil] the field's value; nil when there is no such field
|
|
224
|
+
def [](name)
|
|
225
|
+
raise ::TypeError, "wrong argument type #{name.class} (expected String)" unless name.is_a?(String)
|
|
226
|
+
fd = self.class.__gnames[name]
|
|
227
|
+
fd && __gget(fd)
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
# @param name [String]
|
|
231
|
+
# @raise [ArgumentError] when there is no such field
|
|
232
|
+
def []=(name, value)
|
|
233
|
+
raise ::TypeError, "wrong argument type #{name.class} (expected String)" unless name.is_a?(String)
|
|
234
|
+
fd = self.class.__gnames[name]
|
|
235
|
+
raise ArgumentError, "Unknown field: #{name}" unless fd
|
|
236
|
+
__gset(fd, value)
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
# @return [Hash] Symbol keys; fields with presence when set, others
|
|
240
|
+
# when not at their default; nested messages as Hashes
|
|
241
|
+
def to_h
|
|
242
|
+
__to_h
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
def inspect
|
|
246
|
+
parts = []
|
|
247
|
+
values = @values
|
|
248
|
+
self.class.__gplan.each do |fd|
|
|
249
|
+
info = fd.__info
|
|
250
|
+
v = values[info.index]
|
|
251
|
+
if info.map
|
|
252
|
+
entries = (v || {}).map { |k, e| "#{k.inspect}=>#{e.inspect}" }
|
|
253
|
+
parts << "#{fd.name}: {#{entries.join(", ")}}"
|
|
254
|
+
elsif info.repeated
|
|
255
|
+
parts << "#{fd.name}: [#{(v || []).map(&:inspect).join(", ")}]"
|
|
256
|
+
elsif info.presence
|
|
257
|
+
parts << "#{fd.name}: #{v.inspect}" unless v.nil?
|
|
258
|
+
else
|
|
259
|
+
parts << "#{fd.name}: #{v.inspect}"
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
"<#{self.class.name || Module.instance_method(:to_s).bind_call(self.class)}: #{parts.join(", ")}>"
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
def to_s
|
|
266
|
+
inspect
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
def pretty_print(q)
|
|
270
|
+
q.text(inspect)
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
# Same class and the same field values, extensions included. The gem
|
|
274
|
+
# compares encodings, so -0.0 differs from 0.0
|
|
275
|
+
def ==(other)
|
|
276
|
+
return true if equal?(other)
|
|
277
|
+
return false unless other.instance_of?(self.class)
|
|
278
|
+
super && __gextensions == other.__gextensions && __gsame_zeros?(other)
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def eql?(other)
|
|
282
|
+
self == other
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
def hash
|
|
286
|
+
super
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
# A shallow copy, as the gem's: nested messages, repeated fields and
|
|
290
|
+
# maps are shared with the original, and a copy of a frozen message
|
|
291
|
+
# is frozen
|
|
292
|
+
def dup
|
|
293
|
+
copy = self.class.allocate
|
|
294
|
+
copy.__gcopy_from(self)
|
|
295
|
+
copy.freeze if frozen?
|
|
296
|
+
copy
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
def clone
|
|
300
|
+
dup
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
# Freezes the message and everything in it
|
|
304
|
+
# @return [self]
|
|
305
|
+
def freeze
|
|
306
|
+
@__gwrappers ||= {} unless frozen?
|
|
307
|
+
super
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
# @api private
|
|
311
|
+
def __gcopy_from(source)
|
|
312
|
+
@values = source.__values.dup
|
|
313
|
+
@unknown = source.unknown_fields.empty? ? nil : source.unknown_fields.dup.freeze
|
|
314
|
+
@__gwrappers = nil
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
# @api private
|
|
318
|
+
# @return [Message] a copy with every nested message copied too
|
|
319
|
+
def __gdeep_copy
|
|
320
|
+
copy = self.class.allocate
|
|
321
|
+
copy.__gdeep_from(self)
|
|
322
|
+
copy
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
# @api private
|
|
326
|
+
def __gdeep_from(source)
|
|
327
|
+
@values = source.__values.map { |v| Internal.deep_copy_value(v) }
|
|
328
|
+
@unknown = source.unknown_fields.empty? ? nil : source.unknown_fields.dup.freeze
|
|
329
|
+
@__gwrappers = nil
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
# @api private
|
|
333
|
+
# @param fd [FieldDescriptor]
|
|
334
|
+
def __gget(fd)
|
|
335
|
+
return __gext_get(fd) if fd.__extension?
|
|
336
|
+
info = fd.__info
|
|
337
|
+
if info.map
|
|
338
|
+
__gmap(fd)
|
|
339
|
+
elsif info.repeated
|
|
340
|
+
__grepeated(fd)
|
|
341
|
+
else
|
|
342
|
+
v = @values[info.index]
|
|
343
|
+
(v.nil? && !info.message?) ? info.default : v
|
|
344
|
+
end
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
# @api private
|
|
348
|
+
def __ghas?(fd)
|
|
349
|
+
return __gext_has?(fd) if fd.__extension?
|
|
350
|
+
!@values[fd.__info.index].nil?
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
# @api private
|
|
354
|
+
def __gset(fd, value)
|
|
355
|
+
__check_gfrozen
|
|
356
|
+
return __gext_set(fd, value) if fd.__extension?
|
|
357
|
+
info = fd.__info
|
|
358
|
+
i = info.index
|
|
359
|
+
if info.map
|
|
360
|
+
raise TypeError, "Expected Map instance" unless value.is_a?(Map)
|
|
361
|
+
value.__check_field(fd)
|
|
362
|
+
@values[i] = value.__hash
|
|
363
|
+
(@__gwrappers ||= {})[i] = value
|
|
364
|
+
elsif info.repeated
|
|
365
|
+
raise TypeError, "Expected repeated field array" unless value.is_a?(RepeatedField)
|
|
366
|
+
value.__check_field(fd)
|
|
367
|
+
@values[i] = value.__array
|
|
368
|
+
(@__gwrappers ||= {})[i] = value
|
|
369
|
+
elsif value.nil? && (info.message? || info.siblings)
|
|
370
|
+
@values[i] = nil
|
|
371
|
+
else
|
|
372
|
+
v = Convert.to_store(value, fd.type, fd.subtype, fd.name)
|
|
373
|
+
info.siblings&.each { |s| @values[s] = nil }
|
|
374
|
+
@values[i] = v
|
|
375
|
+
end
|
|
376
|
+
value
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
# @api private
|
|
380
|
+
def __gclear(fd)
|
|
381
|
+
__check_gfrozen
|
|
382
|
+
return __gext_clear(fd) if fd.__extension?
|
|
383
|
+
info = fd.__info
|
|
384
|
+
@values[info.index] = (info.presence || info.repeated || info.map) ? nil : info.default
|
|
385
|
+
nil
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
# @api private
|
|
389
|
+
# @return [RepeatedField] the field's, the same object while the field holds the same Array
|
|
390
|
+
def __grepeated(fd)
|
|
391
|
+
__gcontainer(fd, RepeatedField, [])
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
# @api private
|
|
395
|
+
# @return [Map] the field's, the same object while the field holds the same Hash
|
|
396
|
+
def __gmap(fd)
|
|
397
|
+
__gcontainer(fd, Map, {})
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
# The wrapper of a repeated or map field's storage, kept per slot so
|
|
401
|
+
# that reading the field twice gives the same object; a frozen
|
|
402
|
+
# message's wrappers are frozen
|
|
403
|
+
def __gcontainer(fd, kind, empty)
|
|
404
|
+
i = fd.__info.index
|
|
405
|
+
storage = @values[i]
|
|
406
|
+
cache = @__gwrappers
|
|
407
|
+
wrapper = cache&.[](i)
|
|
408
|
+
frozen = frozen?
|
|
409
|
+
return wrapper if wrapper && (storage.nil? ? frozen : wrapper.__storage.equal?(storage))
|
|
410
|
+
if storage.nil?
|
|
411
|
+
storage = frozen ? empty.freeze : (@values[i] = empty)
|
|
412
|
+
end
|
|
413
|
+
wrapper = kind.__for_field(fd, storage)
|
|
414
|
+
wrapper.freeze if frozen
|
|
415
|
+
if cache
|
|
416
|
+
cache[i] = wrapper
|
|
417
|
+
elsif !frozen
|
|
418
|
+
@__gwrappers = {i => wrapper}
|
|
419
|
+
end
|
|
420
|
+
wrapper
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
# @api private
|
|
424
|
+
# @return [Integer] how deeply messages nest under this one, counting it; stops at `limit`
|
|
425
|
+
def __gdepth(limit)
|
|
426
|
+
nested = self.class.__gnested
|
|
427
|
+
return 1 if nested.empty?
|
|
428
|
+
return limit if limit <= 1
|
|
429
|
+
deepest = 0
|
|
430
|
+
nested.each do |info|
|
|
431
|
+
v = @values[info.index]
|
|
432
|
+
next if v.nil?
|
|
433
|
+
if info.map || info.repeated
|
|
434
|
+
(info.map ? v.each_value : v).each do |m|
|
|
435
|
+
d = m.__gdepth(limit - 1)
|
|
436
|
+
deepest = d if d > deepest
|
|
437
|
+
return limit if deepest + 1 >= limit
|
|
438
|
+
end
|
|
439
|
+
else
|
|
440
|
+
d = v.__gdepth(limit - 1)
|
|
441
|
+
deepest = d if d > deepest
|
|
442
|
+
return limit if deepest + 1 >= limit
|
|
443
|
+
end
|
|
444
|
+
end
|
|
445
|
+
deepest + 1
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
# @api private
|
|
449
|
+
# @return [Array<UnknownField>] the unknown fields holding extensions, for comparison
|
|
450
|
+
def __gextensions
|
|
451
|
+
descriptor = self.class.descriptor.__schema
|
|
452
|
+
return Protostar::Message::EMPTY_ARRAY if descriptor.extension_ranges.empty?
|
|
453
|
+
unknown_fields.sort_by(&:number).map(&:to_wire)
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
private
|
|
457
|
+
|
|
458
|
+
FLOATS = [Protostar::Message::FieldInfo::DOUBLE, Protostar::Message::FieldInfo::FLOAT].freeze
|
|
459
|
+
private_constant :FLOATS
|
|
460
|
+
|
|
461
|
+
# Whether every float field holding a zero holds the same zero as other's
|
|
462
|
+
def __gsame_zeros?(other)
|
|
463
|
+
theirs = other.__values
|
|
464
|
+
self.class.__fields.each do |info|
|
|
465
|
+
kind = info.map ? info.value.kind : info.kind
|
|
466
|
+
next unless FLOATS.include?(kind)
|
|
467
|
+
a = @values[info.index]
|
|
468
|
+
b = theirs[info.index]
|
|
469
|
+
pairs = if info.map then (a || {}).map { |k, v| [v, b[k]] }
|
|
470
|
+
elsif info.repeated then (a || []).zip(b || [])
|
|
471
|
+
else [[a, b]]
|
|
472
|
+
end
|
|
473
|
+
pairs.each do |x, y|
|
|
474
|
+
return false if x.is_a?(Float) && y.is_a?(Float) && x.zero? && (1.0 / x).negative? != (1.0 / y).negative?
|
|
475
|
+
end
|
|
476
|
+
end
|
|
477
|
+
true
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
def initialize_copy(source)
|
|
481
|
+
super
|
|
482
|
+
@__gwrappers = nil
|
|
483
|
+
end
|
|
484
|
+
|
|
485
|
+
def __check_gfrozen
|
|
486
|
+
raise FrozenError.new("can't modify frozen #{self.class}: #{inspect}", receiver: self) if frozen?
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
def __ginit(fd, value)
|
|
490
|
+
info = fd.__info
|
|
491
|
+
i = info.index
|
|
492
|
+
if info.map
|
|
493
|
+
unless value.is_a?(Hash)
|
|
494
|
+
raise ArgumentError, "Expected Hash object as initializer value for map field '#{fd.name}' (given #{value.class})."
|
|
495
|
+
end
|
|
496
|
+
entry = fd.subtype
|
|
497
|
+
key_fd = entry.lookup("key")
|
|
498
|
+
value_fd = entry.lookup("value")
|
|
499
|
+
hash = {}
|
|
500
|
+
value.each do |k, v|
|
|
501
|
+
hash[Convert.to_store(k, key_fd.type, nil, "")] = Convert.initial(v, value_fd.type, value_fd.subtype, "")
|
|
502
|
+
end
|
|
503
|
+
@values[i] = hash
|
|
504
|
+
elsif info.repeated
|
|
505
|
+
unless value.is_a?(Array)
|
|
506
|
+
raise ArgumentError, "Expected array as initializer value for repeated field '#{fd.name}' (given #{value.class})."
|
|
507
|
+
end
|
|
508
|
+
type = fd.type
|
|
509
|
+
sub = fd.subtype
|
|
510
|
+
name = fd.name
|
|
511
|
+
@values[i] = value.map { |v| Convert.initial(v, type, sub, name) }
|
|
512
|
+
elsif value.is_a?(Hash) && info.message?
|
|
513
|
+
info.siblings&.each { |s| @values[s] = nil }
|
|
514
|
+
@values[i] = fd.subtype.msgclass.new(value)
|
|
515
|
+
else
|
|
516
|
+
__gset(fd, value)
|
|
517
|
+
end
|
|
518
|
+
end
|
|
519
|
+
|
|
520
|
+
def method_missing(name, *args)
|
|
521
|
+
if self.class.__goneof_setters.key?(name)
|
|
522
|
+
raise RuntimeError, "Oneof accessors are read-only." # standard:disable Style/RedundantException
|
|
523
|
+
end
|
|
524
|
+
super
|
|
525
|
+
end
|
|
526
|
+
|
|
527
|
+
def respond_to_missing?(name, include_private = false)
|
|
528
|
+
return false if self.class.__goneof_setters.key?(name)
|
|
529
|
+
super
|
|
530
|
+
end
|
|
531
|
+
end
|
|
532
|
+
|
|
533
|
+
# @api private
|
|
534
|
+
def self.deep_copy_value(v)
|
|
535
|
+
case v
|
|
536
|
+
when Protostar::Message then v.__gdeep_copy
|
|
537
|
+
when Array then v.map { |e| e.is_a?(Protostar::Message) ? e.__gdeep_copy : e }
|
|
538
|
+
when Hash then v.transform_values { |e| e.is_a?(Protostar::Message) ? e.__gdeep_copy : e }
|
|
539
|
+
else v
|
|
540
|
+
end
|
|
541
|
+
end
|
|
542
|
+
end
|
|
543
|
+
end
|
|
544
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Google
|
|
4
|
+
module Protobuf
|
|
5
|
+
# Included into every message class. Libraries extend it (and
|
|
6
|
+
# {ClassMethods}) to give all messages more methods, as with the gem.
|
|
7
|
+
module MessageExts
|
|
8
|
+
def self.included(klass)
|
|
9
|
+
klass.extend(ClassMethods)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Extended by every message class
|
|
13
|
+
module ClassMethods
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# @param options [Hash] see the class's encode_json
|
|
17
|
+
# @return [String]
|
|
18
|
+
def to_json(options = {})
|
|
19
|
+
self.class.encode_json(self, options)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# @param options [Hash] see the class's encode
|
|
23
|
+
# @return [String] binary
|
|
24
|
+
def to_proto(options = {})
|
|
25
|
+
self.class.encode(self, options)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def to_hash
|
|
29
|
+
to_h
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# What the gem's message classes inherit from. Classes here inherit from
|
|
34
|
+
# Protostar::Message instead and include MessageExts directly.
|
|
35
|
+
class AbstractMessage
|
|
36
|
+
include MessageExts
|
|
37
|
+
extend MessageExts::ClassMethods
|
|
38
|
+
end
|
|
39
|
+
private_constant :AbstractMessage
|
|
40
|
+
end
|
|
41
|
+
end
|