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,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