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.
Files changed (223) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +60 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +647 -0
  5. data/THIRD_PARTY_NOTICES.md +61 -0
  6. data/exe/protoc-gen-protostar +7 -0
  7. data/exe/protostar +7 -0
  8. data/lib/protostar/builder/generator.rb +71 -0
  9. data/lib/protostar/builder/sink.rb +35 -0
  10. data/lib/protostar/builder.rb +965 -0
  11. data/lib/protostar/cli.rb +267 -0
  12. data/lib/protostar/codec/decode_generator.rb +199 -0
  13. data/lib/protostar/codec/decoder.rb +296 -0
  14. data/lib/protostar/codec/encode_generator.rb +224 -0
  15. data/lib/protostar/codec/encoder.rb +121 -0
  16. data/lib/protostar/codec/sizer.rb +91 -0
  17. data/lib/protostar/codec.rb +15 -0
  18. data/lib/protostar/compiler/ast.rb +193 -0
  19. data/lib/protostar/compiler/emitters/descriptor_set.rb +57 -0
  20. data/lib/protostar/compiler/emitters/google_ruby.rb +103 -0
  21. data/lib/protostar/compiler/emitters/grpc.rb +175 -0
  22. data/lib/protostar/compiler/emitters/protostar_rbs.rb +252 -0
  23. data/lib/protostar/compiler/emitters/rbs.rb +394 -0
  24. data/lib/protostar/compiler/emitters/ruby.rb +503 -0
  25. data/lib/protostar/compiler/emitters.rb +268 -0
  26. data/lib/protostar/compiler/include/LICENSE +32 -0
  27. data/lib/protostar/compiler/include/google/protobuf/any.proto +106 -0
  28. data/lib/protostar/compiler/include/google/protobuf/api.proto +229 -0
  29. data/lib/protostar/compiler/include/google/protobuf/compiler/plugin.proto +180 -0
  30. data/lib/protostar/compiler/include/google/protobuf/cpp_features.proto +87 -0
  31. data/lib/protostar/compiler/include/google/protobuf/descriptor.proto +1532 -0
  32. data/lib/protostar/compiler/include/google/protobuf/duration.proto +115 -0
  33. data/lib/protostar/compiler/include/google/protobuf/empty.proto +51 -0
  34. data/lib/protostar/compiler/include/google/protobuf/field_mask.proto +243 -0
  35. data/lib/protostar/compiler/include/google/protobuf/java_features.proto +132 -0
  36. data/lib/protostar/compiler/include/google/protobuf/source_context.proto +48 -0
  37. data/lib/protostar/compiler/include/google/protobuf/struct.proto +111 -0
  38. data/lib/protostar/compiler/include/google/protobuf/timestamp.proto +145 -0
  39. data/lib/protostar/compiler/include/google/protobuf/type.proto +217 -0
  40. data/lib/protostar/compiler/include/google/protobuf/wrappers.proto +157 -0
  41. data/lib/protostar/compiler/lexer.rb +659 -0
  42. data/lib/protostar/compiler/linker.rb +894 -0
  43. data/lib/protostar/compiler/option_interpreter.rb +363 -0
  44. data/lib/protostar/compiler/parser.rb +1696 -0
  45. data/lib/protostar/compiler/retention.rb +104 -0
  46. data/lib/protostar/compiler/run.rb +184 -0
  47. data/lib/protostar/compiler/source_info.rb +161 -0
  48. data/lib/protostar/compiler/symbol_table.rb +223 -0
  49. data/lib/protostar/compiler/text_format.rb +340 -0
  50. data/lib/protostar/compiler/validator.rb +944 -0
  51. data/lib/protostar/compiler.rb +304 -0
  52. data/lib/protostar/diagnostics/codes.rb +1307 -0
  53. data/lib/protostar/diagnostics/formats.rb +64 -0
  54. data/lib/protostar/diagnostics/renderer.rb +240 -0
  55. data/lib/protostar/diagnostics/source.rb +153 -0
  56. data/lib/protostar/diagnostics.rb +117 -0
  57. data/lib/protostar/dsl.rb +367 -0
  58. data/lib/protostar/enum.rb +70 -0
  59. data/lib/protostar/errors.rb +23 -0
  60. data/lib/protostar/google/protobuf/any_pb.rb +90 -0
  61. data/lib/protostar/google/protobuf/api_pb.rb +199 -0
  62. data/lib/protostar/google/protobuf/compiler/plugin_pb.rb +168 -0
  63. data/lib/protostar/google/protobuf/descriptor_pb.rb +1239 -0
  64. data/lib/protostar/google/protobuf/duration_pb.rb +99 -0
  65. data/lib/protostar/google/protobuf/empty_pb.rb +36 -0
  66. data/lib/protostar/google/protobuf/field_mask_pb.rb +228 -0
  67. data/lib/protostar/google/protobuf/source_context_pb.rb +33 -0
  68. data/lib/protostar/google/protobuf/struct_pb.rb +91 -0
  69. data/lib/protostar/google/protobuf/timestamp_pb.rb +129 -0
  70. data/lib/protostar/google/protobuf/type_pb.rb +202 -0
  71. data/lib/protostar/google/protobuf/wrappers_pb.rb +125 -0
  72. data/lib/protostar/google_protobuf_shim.rb +24 -0
  73. data/lib/protostar/json/encoder.rb +340 -0
  74. data/lib/protostar/json/errors.rb +34 -0
  75. data/lib/protostar/json/parser.rb +437 -0
  76. data/lib/protostar/json/path.rb +39 -0
  77. data/lib/protostar/json.rb +95 -0
  78. data/lib/protostar/lint/command.rb +197 -0
  79. data/lib/protostar/lint/config.rb +98 -0
  80. data/lib/protostar/lint/context.rb +126 -0
  81. data/lib/protostar/lint/directives.rb +56 -0
  82. data/lib/protostar/lint/fixer.rb +165 -0
  83. data/lib/protostar/lint/naming.rb +54 -0
  84. data/lib/protostar/lint/rule.rb +120 -0
  85. data/lib/protostar/lint/rules/comment_enum.rb +31 -0
  86. data/lib/protostar/lint/rules/comment_enum_value.rb +32 -0
  87. data/lib/protostar/lint/rules/comment_field.rb +33 -0
  88. data/lib/protostar/lint/rules/comment_message.rb +31 -0
  89. data/lib/protostar/lint/rules/comment_rpc.rb +31 -0
  90. data/lib/protostar/lint/rules/comment_service.rb +31 -0
  91. data/lib/protostar/lint/rules/edition_recommended.rb +31 -0
  92. data/lib/protostar/lint/rules/enum_first_value_zero.rb +34 -0
  93. data/lib/protostar/lint/rules/enum_pascal_case.rb +25 -0
  94. data/lib/protostar/lint/rules/enum_value_prefix.rb +38 -0
  95. data/lib/protostar/lint/rules/enum_value_upper_snake_case.rb +28 -0
  96. data/lib/protostar/lint/rules/enum_zero_value_suffix.rb +41 -0
  97. data/lib/protostar/lint/rules/field_lower_snake_case.rb +34 -0
  98. data/lib/protostar/lint/rules/field_not_required.rb +39 -0
  99. data/lib/protostar/lint/rules/field_number_reuse_risk.rb +45 -0
  100. data/lib/protostar/lint/rules/field_numbers_hot_range.rb +44 -0
  101. data/lib/protostar/lint/rules/file_lower_snake_case.rb +27 -0
  102. data/lib/protostar/lint/rules/import_no_public.rb +29 -0
  103. data/lib/protostar/lint/rules/import_no_weak.rb +28 -0
  104. data/lib/protostar/lint/rules/import_used.rb +33 -0
  105. data/lib/protostar/lint/rules/message_pascal_case.rb +27 -0
  106. data/lib/protostar/lint/rules/no_deprecated_features.rb +50 -0
  107. data/lib/protostar/lint/rules/no_groups.rb +34 -0
  108. data/lib/protostar/lint/rules/oneof_lower_snake_case.rb +27 -0
  109. data/lib/protostar/lint/rules/package_defined.rb +28 -0
  110. data/lib/protostar/lint/rules/package_directory_match.rb +33 -0
  111. data/lib/protostar/lint/rules/package_lower_snake_case.rb +28 -0
  112. data/lib/protostar/lint/rules/package_same_directory.rb +39 -0
  113. data/lib/protostar/lint/rules/package_version_suffix.rb +31 -0
  114. data/lib/protostar/lint/rules/reserved_names_with_numbers.rb +44 -0
  115. data/lib/protostar/lint/rules/rpc_pascal_case.rb +25 -0
  116. data/lib/protostar/lint/rules/rpc_request_response_unique.rb +40 -0
  117. data/lib/protostar/lint/rules/rpc_request_standard_name.rb +32 -0
  118. data/lib/protostar/lint/rules/rpc_response_standard_name.rb +31 -0
  119. data/lib/protostar/lint/rules/service_pascal_case.rb +25 -0
  120. data/lib/protostar/lint/rules/service_suffix.rb +28 -0
  121. data/lib/protostar/lint/rules/syntax_specified.rb +35 -0
  122. data/lib/protostar/lint/runner.rb +135 -0
  123. data/lib/protostar/lint.rb +122 -0
  124. data/lib/protostar/message/accessors.rb +86 -0
  125. data/lib/protostar/message/coercion.rb +171 -0
  126. data/lib/protostar/message/field_info.rb +145 -0
  127. data/lib/protostar/message/reflection.rb +149 -0
  128. data/lib/protostar/message.rb +572 -0
  129. data/lib/protostar/plugin.rb +94 -0
  130. data/lib/protostar/schema/default_value.rb +113 -0
  131. data/lib/protostar/schema/descriptor.rb +41 -0
  132. data/lib/protostar/schema/draft/copy.rb +88 -0
  133. data/lib/protostar/schema/draft/resolution.rb +177 -0
  134. data/lib/protostar/schema/draft.rb +779 -0
  135. data/lib/protostar/schema/enum.rb +106 -0
  136. data/lib/protostar/schema/extension_range.rb +37 -0
  137. data/lib/protostar/schema/features.rb +153 -0
  138. data/lib/protostar/schema/field.rb +201 -0
  139. data/lib/protostar/schema/file.rb +217 -0
  140. data/lib/protostar/schema/freezer.rb +61 -0
  141. data/lib/protostar/schema/message.rb +104 -0
  142. data/lib/protostar/schema/oneof.rb +38 -0
  143. data/lib/protostar/schema/options.rb +367 -0
  144. data/lib/protostar/schema/pool.rb +179 -0
  145. data/lib/protostar/schema/proto.rb +576 -0
  146. data/lib/protostar/schema/resolver.rb +118 -0
  147. data/lib/protostar/schema/service.rb +89 -0
  148. data/lib/protostar/schema/source_info.rb +53 -0
  149. data/lib/protostar/schema.rb +65 -0
  150. data/lib/protostar/short_names/any.rb +7 -0
  151. data/lib/protostar/short_names/duration.rb +7 -0
  152. data/lib/protostar/short_names/empty.rb +7 -0
  153. data/lib/protostar/short_names/field_mask.rb +7 -0
  154. data/lib/protostar/short_names/struct.rb +9 -0
  155. data/lib/protostar/short_names/timestamp.rb +7 -0
  156. data/lib/protostar/short_names/wrappers.rb +15 -0
  157. data/lib/protostar/text/extensions.rb +114 -0
  158. data/lib/protostar/text/parser.rb +439 -0
  159. data/lib/protostar/text/printer.rb +304 -0
  160. data/lib/protostar/text/tokenizer.rb +116 -0
  161. data/lib/protostar/text.rb +67 -0
  162. data/lib/protostar/types.rb +152 -0
  163. data/lib/protostar/unknown_field.rb +58 -0
  164. data/lib/protostar/version.rb +5 -0
  165. data/lib/protostar/view/generator.rb +298 -0
  166. data/lib/protostar/view/map.rb +194 -0
  167. data/lib/protostar/view/repeated.rb +143 -0
  168. data/lib/protostar/view/scan.rb +183 -0
  169. data/lib/protostar/view.rb +283 -0
  170. data/lib/protostar/well_known/any.rb +52 -0
  171. data/lib/protostar/well_known/duration.rb +53 -0
  172. data/lib/protostar/well_known/list_value.rb +63 -0
  173. data/lib/protostar/well_known/struct.rb +55 -0
  174. data/lib/protostar/well_known/timestamp.rb +64 -0
  175. data/lib/protostar/well_known/value.rb +60 -0
  176. data/lib/protostar/well_known.rb +38 -0
  177. data/lib/protostar/wire/io_source.rb +40 -0
  178. data/lib/protostar/wire/reader.rb +254 -0
  179. data/lib/protostar/wire/writer.rb +201 -0
  180. data/lib/protostar/wire.rb +96 -0
  181. data/lib/protostar.rb +95 -0
  182. data/shims/google-protobuf/lib/google/protobuf/any_pb.rb +17 -0
  183. data/shims/google-protobuf/lib/google/protobuf/api_pb.rb +22 -0
  184. data/shims/google-protobuf/lib/google/protobuf/compiler/plugin_pb.rb +25 -0
  185. data/shims/google-protobuf/lib/google/protobuf/descriptor_pb.rb +72 -0
  186. data/shims/google-protobuf/lib/google/protobuf/descriptor_pool.rb +164 -0
  187. data/shims/google-protobuf/lib/google/protobuf/descriptors.rb +553 -0
  188. data/shims/google-protobuf/lib/google/protobuf/duration_pb.rb +17 -0
  189. data/shims/google-protobuf/lib/google/protobuf/empty_pb.rb +17 -0
  190. data/shims/google-protobuf/lib/google/protobuf/field_mask_pb.rb +17 -0
  191. data/shims/google-protobuf/lib/google/protobuf/internal/convert.rb +180 -0
  192. data/shims/google-protobuf/lib/google/protobuf/internal/extensions.rb +154 -0
  193. data/shims/google-protobuf/lib/google/protobuf/internal/json.rb +104 -0
  194. data/shims/google-protobuf/lib/google/protobuf/internal/well_known.rb +140 -0
  195. data/shims/google-protobuf/lib/google/protobuf/map.rb +216 -0
  196. data/shims/google-protobuf/lib/google/protobuf/message.rb +544 -0
  197. data/shims/google-protobuf/lib/google/protobuf/message_exts.rb +41 -0
  198. data/shims/google-protobuf/lib/google/protobuf/plugin_pb.rb +4 -0
  199. data/shims/google-protobuf/lib/google/protobuf/repeated_field.rb +366 -0
  200. data/shims/google-protobuf/lib/google/protobuf/source_context_pb.rb +17 -0
  201. data/shims/google-protobuf/lib/google/protobuf/struct_pb.rb +20 -0
  202. data/shims/google-protobuf/lib/google/protobuf/timestamp_pb.rb +17 -0
  203. data/shims/google-protobuf/lib/google/protobuf/type_pb.rb +27 -0
  204. data/shims/google-protobuf/lib/google/protobuf/well_known_types.rb +37 -0
  205. data/shims/google-protobuf/lib/google/protobuf/wrappers_pb.rb +25 -0
  206. data/shims/google-protobuf/lib/google/protobuf.rb +107 -0
  207. data/shims/google-protobuf/lib/google/protobuf_ffi.rb +5 -0
  208. data/shims/google-protobuf/lib/google/protobuf_native.rb +5 -0
  209. data/sig/protostar/google/protobuf/any_pb.rbs +77 -0
  210. data/sig/protostar/google/protobuf/api_pb.rbs +242 -0
  211. data/sig/protostar/google/protobuf/compiler/plugin_pb.rbs +264 -0
  212. data/sig/protostar/google/protobuf/descriptor_pb.rbs +2220 -0
  213. data/sig/protostar/google/protobuf/duration_pb.rbs +82 -0
  214. data/sig/protostar/google/protobuf/empty_pb.rbs +18 -0
  215. data/sig/protostar/google/protobuf/field_mask_pb.rbs +170 -0
  216. data/sig/protostar/google/protobuf/source_context_pb.rbs +24 -0
  217. data/sig/protostar/google/protobuf/struct_pb.rbs +135 -0
  218. data/sig/protostar/google/protobuf/timestamp_pb.rbs +102 -0
  219. data/sig/protostar/google/protobuf/type_pb.rbs +304 -0
  220. data/sig/protostar/google/protobuf/wrappers_pb.rbs +169 -0
  221. data/sig/protostar/lint.rbs +216 -0
  222. data/sig/protostar.rbs +326 -0
  223. 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
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The gem ships plugin.proto's classes under this name as well
4
+ require "google/protobuf/compiler/plugin_pb"