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,363 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Protostar
4
+ class Compiler
5
+ # protoc's OptionInterpreter. Each `option` statement becomes an entry
6
+ # `[field, value]` (`value` is itself a list of entries for message
7
+ # fields), accumulated per element the way protoc accumulates unknown
8
+ # fields, so "already set" checks and the merging of `(a).b = 1` with
9
+ # `(a).c = 2` behave as in protoc. The entries are then folded into a
10
+ # {Schema::MessageValue}: options message fields by name, extensions by
11
+ # their {Schema::Field}.
12
+ #
13
+ # Option types are read from descriptors: those in the pool, and for the
14
+ # file being linked its draft, linked but not yet given options, so
15
+ # options declared and used in the same file (descriptor.proto's own
16
+ # included) resolve the same way.
17
+ #
18
+ # @api private
19
+ class OptionInterpreter
20
+ INT32_RANGE = (-2**31..2**31 - 1)
21
+ INT64_RANGE = (-2**63..2**63 - 1)
22
+ UINT32_MAX = 2**32 - 1
23
+ UINT64_MAX = 2**64 - 1
24
+
25
+ # @return [Hash{Array<Integer> => Array<Integer>}] uninterpreted_option paths to option paths
26
+ attr_reader :interpreted_paths
27
+
28
+ # @param linker [Linker]
29
+ # @param pool [Schema::Pool]
30
+ # @param fallback [#call, nil] the bundled descriptor.proto's message for a name
31
+ def initialize(linker, pool, fallback)
32
+ @linker = linker
33
+ @table = linker.table
34
+ @pool = pool
35
+ @fallback = fallback
36
+ @interpreted_paths = {}
37
+ @repeated_counts = Hash.new(0)
38
+ end
39
+
40
+ # @param pending [Array<Linker::Pending>]
41
+ # @return [Hash{AST::Node => Schema::MessageValue}] the options of every element that declared options
42
+ def run(pending)
43
+ @local = @linker.draft.descriptor
44
+ state = pending.map { |p| [p, [], []] }
45
+ state.each { |p, first, _| interpret(p, first, skip_extensions: true) }
46
+ if block_given?
47
+ features = {}.compare_by_identity
48
+ state.each do |p, first, _|
49
+ folded = fold(first)
50
+ features[p.node] = folded[:features] if folded.key?(:features)
51
+ end
52
+ yield features
53
+ end
54
+ state.each { |p, first, second| interpret(p, second, skip_extensions: false, earlier: first) }
55
+ result = {}.compare_by_identity
56
+ state.each do |p, first, second|
57
+ result[p.node] = fold(first + second, p.options.known)
58
+ end
59
+ result
60
+ end
61
+
62
+ # A Schema descriptor for a symbol table entry
63
+ def schema_of(symbol)
64
+ symbol&.object
65
+ end
66
+
67
+ # The options message to interpret against, protoc's choice: the pool's
68
+ # (or this file's) when present, else the one compiled into protoc
69
+ def options_type(name)
70
+ found = @table.find_not_enforcing_deps(name)
71
+ message = schema_of(found) if found&.kind == :message
72
+ return message if message
73
+ builtin = @fallback&.call(name)
74
+ @builtin_file = builtin&.file
75
+ builtin
76
+ end
77
+
78
+ private
79
+
80
+ def interpret(pending, entries, skip_extensions:, earlier: nil)
81
+ @pending = pending
82
+ pending.options.uninterpreted.each_with_index do |option, i|
83
+ src_path = pending.path + [999, i]
84
+ break unless interpret_single(option, entries, src_path, skip_extensions)
85
+ end
86
+ end
87
+
88
+ def name_error(option, message)
89
+ @linker.error(option, :option_name, message)
90
+ false
91
+ end
92
+
93
+ def value_error(option, message)
94
+ @linker.error(option, :option_value, message)
95
+ false
96
+ end
97
+
98
+ def interpret_single(option, entries, src_path, skip_extensions)
99
+ if option.name.empty?
100
+ return true if skip_extensions
101
+ return name_error(option, "Option must have a name.")
102
+ end
103
+ if option.name[0][0] == "uninterpreted_option"
104
+ return true if skip_extensions
105
+ return name_error(option, "Option must not use reserved name \"uninterpreted_option\".")
106
+ end
107
+ return true if skip_extensions == option.name[0][1]
108
+
109
+ descriptor = options_type(Linker::OPTIONS_TYPES.fetch(@pending.kind))
110
+ field = nil
111
+ intermediate = []
112
+ debug = +""
113
+ dest_path = @pending.path.dup
114
+ option.name.each_with_index do |(part, extension), i|
115
+ debug << "." unless debug.empty?
116
+ unresolved = nil
117
+ if extension
118
+ debug << "(#{part})"
119
+ symbol = @table.lookup(part, @pending.name_scope)
120
+ unresolved = @table.unresolved_name
121
+ field = (symbol&.kind == :field) ? schema_of(symbol) : nil
122
+ else
123
+ debug << part
124
+ field = descriptor.fields_by_name[part]
125
+ end
126
+
127
+ if field.nil?
128
+ if unresolved
129
+ return name_error(option, "Option \"#{debug}\" is resolved to \"(#{unresolved})\", which is not defined. " \
130
+ "The innermost scope is searched first in name resolution. Consider using a leading '.'(i.e., " \
131
+ "\"(.#{debug[1..]}\") to start from the outermost scope.")
132
+ end
133
+ return name_error(option, "Option \"#{debug}\" unknown. Ensure that your proto definition file imports " \
134
+ "the proto which defines the option (i.e. via import option after edition 2024).")
135
+ end
136
+ holder = field.extension? ? field.extendee : field.containing_message
137
+ if holder.full_name != descriptor.full_name
138
+ return name_error(option, "Option field \"#{debug}\" is not a field or extension of message " \
139
+ "\"#{descriptor.name}\".")
140
+ end
141
+ dest_path << field.number
142
+ if field.file.equal?(@local) && option.name[0] == ["features", false]
143
+ return name_error(option, "Feature \"#{debug}\" can't be used in the same file it's defined in.")
144
+ end
145
+ next unless i < option.name.size - 1
146
+ unless field.type == :message || field.type == :group
147
+ return name_error(option, "Option \"#{debug}\" is an atomic type, not a message.")
148
+ end
149
+ if field.repeated?
150
+ return name_error(option, "Option field \"#{debug}\" is a repeated message. Repeated message options " \
151
+ "must be initialized using an aggregate value.")
152
+ end
153
+ intermediate << field
154
+ descriptor = field.message_type
155
+ end
156
+
157
+ return false if !field.repeated? && already_set?(intermediate, field, debug, entries, option)
158
+
159
+ value = option_value(field, option)
160
+ return false if value.equal?(FAILED)
161
+ entry = [field, value]
162
+ intermediate.reverse_each { |outer| entry = [outer, [entry]] }
163
+ entries << entry
164
+
165
+ if field.repeated?
166
+ key = dest_path.dup.freeze
167
+ index = @repeated_counts[key]
168
+ @repeated_counts[key] += 1
169
+ dest_path << index
170
+ end
171
+ @interpreted_paths[src_path] = dest_path
172
+ true
173
+ end
174
+
175
+ FAILED = Object.new.freeze
176
+ private_constant :FAILED
177
+
178
+ # ExamineIfOptionIsSet
179
+ def already_set?(intermediate, field, debug, entries, option)
180
+ if intermediate.empty?
181
+ if entries.any? { |f, _| f.number == field.number }
182
+ name_error(option, "Option \"#{debug}\" was already set.")
183
+ return true
184
+ end
185
+ return false
186
+ end
187
+ first, *rest = intermediate
188
+ entries.each do |f, value|
189
+ next unless f.number == first.number && value.is_a?(Array)
190
+ return true if already_set?(rest, field, debug, value, option)
191
+ end
192
+ false
193
+ end
194
+
195
+ def option_value(field, option)
196
+ kind = option.value_kind
197
+ value = option.value
198
+ name = field.full_name
199
+ case field.type
200
+ when :int32, :sint32, :sfixed32
201
+ int_option(option, kind, value, INT32_RANGE, "int32", name)
202
+ when :int64, :sint64, :sfixed64
203
+ if kind == :positive_int_value
204
+ if value > INT64_RANGE.end
205
+ value_error(option, out_of_range(INT64_RANGE, "int64", name))
206
+ return FAILED
207
+ end
208
+ value
209
+ elsif kind == :negative_int_value
210
+ value
211
+ else
212
+ value_error(option, must_be_int(INT64_RANGE, "int64", name))
213
+ FAILED
214
+ end
215
+ when :uint32, :fixed32
216
+ if kind == :positive_int_value
217
+ if value > UINT32_MAX
218
+ value_error(option, out_of_range(0..UINT32_MAX, "uint32", name))
219
+ return FAILED
220
+ end
221
+ value
222
+ else
223
+ value_error(option, must_be_int(0..UINT32_MAX, "uint32", name))
224
+ FAILED
225
+ end
226
+ when :uint64, :fixed64
227
+ return value if kind == :positive_int_value
228
+ value_error(option, must_be_int(0..UINT64_MAX, "uint64", name))
229
+ FAILED
230
+ when :float, :double
231
+ number = case kind
232
+ when :double_value, :positive_int_value, :negative_int_value then value.to_f
233
+ when :identifier_value
234
+ if value == "inf"
235
+ Float::INFINITY
236
+ elsif value == "nan"
237
+ Float::NAN
238
+ end
239
+ end
240
+ if number.nil?
241
+ value_error(option, "Value must be number for #{field.type} option \"#{name}\".")
242
+ return FAILED
243
+ end
244
+ (field.type == :float) ? Schema::DefaultValue.to_float32(number) : number
245
+ when :bool
246
+ unless kind == :identifier_value
247
+ value_error(option, "Value must be identifier for boolean option \"#{name}\".")
248
+ return FAILED
249
+ end
250
+ case value
251
+ when "true" then true
252
+ when "false" then false
253
+ else
254
+ value_error(option, "Value must be \"true\" or \"false\" for boolean option \"#{name}\".")
255
+ FAILED
256
+ end
257
+ when :enum
258
+ unless kind == :identifier_value
259
+ value_error(option, "Value must be identifier for enum-valued option \"#{name}\".")
260
+ return FAILED
261
+ end
262
+ enum_option(field, value, option)
263
+ when :string, :bytes
264
+ unless kind == :string_value
265
+ value_error(option, "Value must be quoted string for string option \"#{name}\".")
266
+ return FAILED
267
+ end
268
+ (field.type == :string) ? value.dup.force_encoding(Encoding::UTF_8) : value.b
269
+ when :message, :group
270
+ unless kind == :aggregate_value
271
+ value_error(option, "Option \"#{name}\" is a message. To set the entire message, use syntax like " \
272
+ "\"#{field.name} = { <proto text format> }\". To set fields within it, use syntax like " \
273
+ "\"#{field.name}.foo = value\".")
274
+ return FAILED
275
+ end
276
+ parser = TextFormat.new(value, field.message_type, self, @table)
277
+ entries = parser.parse
278
+ unless entries
279
+ value_error(option, "Error while parsing option value for \"#{field.name}\": #{parser.errors.join("; ")}")
280
+ return FAILED
281
+ end
282
+ entries
283
+ end
284
+ end
285
+
286
+ def int_option(option, kind, value, range, type, name)
287
+ if kind == :positive_int_value || kind == :negative_int_value
288
+ return value if range.cover?(value)
289
+ value_error(option, out_of_range(range, type, name))
290
+ else
291
+ value_error(option, must_be_int(range, type, name))
292
+ end
293
+ FAILED
294
+ end
295
+
296
+ def out_of_range(range, type, name)
297
+ "Value out of range, #{range.begin} to #{range.end}, for #{type} option \"#{name}\"."
298
+ end
299
+
300
+ def must_be_int(range, type, name)
301
+ "Value must be integer, from #{range.begin} to #{range.end}, for #{type} option \"#{name}\"."
302
+ end
303
+
304
+ def enum_option(field, value_name, option)
305
+ enum = field.enum_type
306
+ found = nil
307
+ if @builtin_file && enum.file.equal?(@builtin_file)
308
+ found = enum.value(value_name)
309
+ else
310
+ scope = enum.full_name[0, enum.full_name.size - enum.name.size]
311
+ symbol = @table.find_not_enforcing_deps(scope + value_name)
312
+ if symbol&.kind == :enum_value
313
+ candidate = schema_of(symbol)
314
+ if candidate.enum.full_name != enum.full_name
315
+ value_error(option, "Enum type \"#{enum.full_name}\" has no value named \"#{value_name}\" for option " \
316
+ "\"#{field.full_name}\". This appears to be a value from a sibling type.")
317
+ return FAILED
318
+ end
319
+ found = candidate
320
+ end
321
+ end
322
+ unless found
323
+ value_error(option, "Enum type \"#{enum.full_name}\" has no value named \"#{value_name}\" for option " \
324
+ "\"#{field.full_name}\".")
325
+ return FAILED
326
+ end
327
+ enum.values_by_number.fetch(found.number).name.to_sym
328
+ end
329
+
330
+ public
331
+
332
+ # Folds entries into a value the way parsing their serialized form
333
+ # would: repeated fields append, message fields merge, scalars take the
334
+ # last value
335
+ # @param known [Hash{Symbol => Object}] fields set before any entry (the parser's `map_entry`)
336
+ # @return [Schema::MessageValue]
337
+ def fold(entries, known = nil)
338
+ merged = {}.compare_by_identity
339
+ entries.each do |field, value|
340
+ if (field.type == :message || field.type == :group) && !field.repeated?
341
+ (merged[field] ||= []).concat(value)
342
+ else
343
+ merged[field] ||= []
344
+ merged[field] << value
345
+ end
346
+ end
347
+ fields = known ? known.dup : {}
348
+ extensions = {}
349
+ merged.each do |field, values|
350
+ target, key = field.extension? ? [extensions, field] : [fields, field.name.to_sym]
351
+ target[key] = if field.type == :message || field.type == :group
352
+ field.repeated? ? values.map { |v| fold(v) } : fold(values)
353
+ elsif field.repeated?
354
+ values
355
+ else
356
+ values.last
357
+ end
358
+ end
359
+ Schema::MessageValue.new(fields, extensions)
360
+ end
361
+ end
362
+ end
363
+ end