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,217 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Protostar
4
+ module Schema
5
+ # A built `.proto` file
6
+ class File < Descriptor
7
+ # @return [String] the package, "" when none
8
+ attr_reader :package
9
+ # @return [Symbol] :proto2, :proto3 or :editions
10
+ attr_reader :syntax
11
+ # @return [Symbol, Integer, nil] the Edition enum name for editions files
12
+ # (an Integer only for an edition this model has no name for)
13
+ attr_reader :edition
14
+ # @return [Array<String>] imported file names, in declaration order
15
+ attr_reader :dependencies
16
+ # @return [Array<Integer>] indexes into {#dependencies} of `import public`
17
+ attr_reader :public_dependencies
18
+ # @return [Array<Integer>] indexes into {#dependencies} of `import weak`
19
+ attr_reader :weak_dependencies
20
+ # @return [Array<String>] `import option` file names (edition 2024)
21
+ attr_reader :option_dependencies
22
+ # @return [Array<File>] linked {#dependencies}, same order
23
+ attr_reader :dependency_files
24
+ # @return [Array<Message>] top-level messages
25
+ attr_reader :messages
26
+ # @return [Array<Enum>] top-level enums
27
+ attr_reader :enums
28
+ # @return [Array<Service>]
29
+ attr_reader :services
30
+ # @return [Array<Field>] top-level extensions
31
+ attr_reader :extensions
32
+ # @return [SourceInfo, nil]
33
+ attr_reader :source_info
34
+ # @return [Array<Message>] every message, depth first in declaration order
35
+ attr_reader :all_messages
36
+ # @return [Array<Enum>] every enum, top level first, then by message in {#all_messages} order
37
+ attr_reader :all_enums
38
+ # @return [Array<Field>] every extension, top level first, then by message in {#all_messages} order
39
+ attr_reader :all_extensions
40
+ # @return [Hash{String => Descriptor}] every symbol this file defines, by full name
41
+ attr_reader :symbols
42
+ # @return [Array<File>] this file, its imports and whatever they re-export
43
+ # through `import public`: the files whose symbols are visible here
44
+ attr_reader :visible_files
45
+
46
+ # @api private
47
+ def initialize(name:, package:, syntax:, edition:, proto_syntax:)
48
+ @name = name
49
+ @full_name = name
50
+ @index = 0
51
+ @package = package
52
+ @syntax = syntax
53
+ @edition = edition
54
+ @proto_syntax = proto_syntax
55
+ @dependencies = Freezer::EMPTY_ARRAY
56
+ @public_dependencies = Freezer::EMPTY_ARRAY
57
+ @weak_dependencies = Freezer::EMPTY_ARRAY
58
+ @option_dependencies = Freezer::EMPTY_ARRAY
59
+ @dependency_files = Freezer::EMPTY_ARRAY
60
+ @messages = @enums = @services = @extensions = Freezer::EMPTY_ARRAY
61
+ @all_messages = @all_enums = @all_extensions = Freezer::EMPTY_ARRAY
62
+ @symbols = {}
63
+ @visible_files = [self]
64
+ end
65
+
66
+ # @api private
67
+ def __dependencies(dependencies:, public_dependencies:, weak_dependencies:, option_dependencies:, dependency_files:)
68
+ @dependencies = dependencies
69
+ @public_dependencies = public_dependencies
70
+ @weak_dependencies = weak_dependencies
71
+ @option_dependencies = option_dependencies
72
+ @dependency_files = dependency_files
73
+ @visible_files = compute_visible_files
74
+ self
75
+ end
76
+
77
+ # @api private
78
+ # @return [String, nil] the `syntax` string the FileDescriptorProto carries
79
+ attr_reader :proto_syntax
80
+
81
+ # @return [File] itself, so every descriptor answers `#file`
82
+ def file
83
+ self
84
+ end
85
+
86
+ # @return [Integer] the edition number features resolve against
87
+ # (EDITION_PROTO2 or EDITION_PROTO3 for the legacy syntaxes)
88
+ def edition_number
89
+ case @syntax
90
+ when :proto2 then Features::EDITIONS[:EDITION_PROTO2]
91
+ when :proto3 then Features::EDITIONS[:EDITION_PROTO3]
92
+ else Features.edition_number(@edition)
93
+ end
94
+ end
95
+
96
+ # @param full_name [String] without a leading dot
97
+ # @return [Message, nil] a message defined in this file
98
+ def find_message(full_name)
99
+ found = @symbols[full_name]
100
+ found if found.is_a?(Message)
101
+ end
102
+
103
+ # @return [Enum, nil] an enum defined in this file
104
+ def find_enum(full_name)
105
+ found = @symbols[full_name]
106
+ found if found.is_a?(Enum)
107
+ end
108
+
109
+ # @return [Service, nil] a service defined in this file
110
+ def find_service(full_name)
111
+ found = @symbols[full_name]
112
+ found if found.is_a?(Service)
113
+ end
114
+
115
+ # @return [Field, nil] an extension defined in this file
116
+ def find_extension(full_name)
117
+ found = @symbols[full_name]
118
+ found if found.is_a?(Field) && found.extension?
119
+ end
120
+
121
+ # @return [Descriptor, nil] any symbol defined in this file: message, field,
122
+ # oneof, enum, enum value, service or method
123
+ def find_symbol(full_name)
124
+ @symbols[full_name]
125
+ end
126
+
127
+ def inspect
128
+ "#<#{self.class.name} #{@name}>"
129
+ end
130
+ alias_method :to_s, :inspect
131
+
132
+ # This file as a `google.protobuf.FileDescriptorProto` of the bootstrap
133
+ # classes, as protoc's `FileDescriptor::CopyTo` writes it
134
+ # @param source_info [Boolean] include `source_code_info` when the file has it
135
+ # @param default_json_names [Boolean] false leaves out every `json_name` equal to the one
136
+ # derived from the field name, as protoc's Ruby generator embeds descriptors
137
+ # @return [Protostar::Google::Protobuf::FileDescriptorProto]
138
+ def to_proto(source_info: true, default_json_names: true)
139
+ Proto::Writer.file(self, source_info: source_info, json_names: default_json_names)
140
+ end
141
+
142
+ # @param (see #to_proto)
143
+ # @return [String] the serialized FileDescriptorProto, byte for byte what protoc writes
144
+ def serialize(source_info: true, default_json_names: true)
145
+ bytes = to_proto(source_info: false, default_json_names: default_json_names).encode
146
+ return bytes unless source_info && @source_info
147
+ Proto::Writer.with_source_code_info(bytes, @source_info)
148
+ end
149
+
150
+ # Builds a file from a `google.protobuf.FileDescriptorProto` message (or
151
+ # its bytes), as a plugin receives them. Extension bytes in options are
152
+ # decoded by the extensions the pool (or the file itself) defines;
153
+ # others stay {Options#unknown_fields}.
154
+ # @param proto [Protostar::Google::Protobuf::FileDescriptorProto, String]
155
+ # @param pool [Pool] where dependencies and extensions are looked up; the file is not added
156
+ # @return [File]
157
+ def self.from_proto(proto, pool:)
158
+ proto = Proto.types::FileDescriptorProto.decode(proto) if proto.is_a?(String)
159
+ Proto::Reader.new(proto, pool).call
160
+ end
161
+
162
+ # Starts describing a file. The draft creates the real descriptors as it
163
+ # goes; {Draft::File#resolve} links them and {Draft::File#freeze!} hands
164
+ # back the finished, shareable file.
165
+ #
166
+ # file = Schema::File.draft("shop.proto", package: "shop", syntax: :proto3)
167
+ # file.message("Item") do |m|
168
+ # m.field "name", 1, :string
169
+ # m.field "price_cents", 2, :int64, optional: true
170
+ # m.map "stock", 3, key: :string, value: :int32
171
+ # end
172
+ # file.resolve(pool)
173
+ # file.freeze! # => #<Protostar::Schema::File shop.proto>
174
+ #
175
+ # @param name [String] the file name as imported
176
+ # @param package [String, nil]
177
+ # @param syntax [Symbol, nil] :proto2, :proto3 or :editions; nil is proto2, and
178
+ # unlike an explicit :proto2 leaves `syntax` out of the FileDescriptorProto, as protoc does
179
+ # @param edition [Symbol, Integer, nil] e.g. :EDITION_2023; implies :editions
180
+ # @param dependencies [Array<String>] imported file names
181
+ # @param public_dependencies [Array<String, Integer>] names or indexes of `import public` files
182
+ # @param weak_dependencies [Array<String, Integer>]
183
+ # @param option_dependencies [Array<String>] `import option` file names
184
+ # @param options [Hash, nil] the file's options
185
+ # @return [Draft::File]
186
+ def self.draft(name, package: nil, syntax: nil, edition: nil, dependencies: [], public_dependencies: [],
187
+ weak_dependencies: [], option_dependencies: [], options: nil)
188
+ Draft::File.new(name, package: package, syntax: syntax, edition: edition, dependencies: dependencies,
189
+ public_dependencies: public_dependencies, weak_dependencies: weak_dependencies,
190
+ option_dependencies: option_dependencies, options: options)
191
+ end
192
+
193
+ private
194
+
195
+ # protoc's rule: direct imports, plus the public imports of those, recursively
196
+ def compute_visible_files
197
+ seen = {self => true}
198
+ queue = []
199
+ @dependency_files.each do |dep|
200
+ next if seen[dep]
201
+ seen[dep] = true
202
+ queue << dep
203
+ end
204
+ until queue.empty?
205
+ dep = queue.shift
206
+ dep.public_dependencies.each do |i|
207
+ pub = dep.dependency_files[i]
208
+ next if pub.nil? || seen[pub]
209
+ seen[pub] = true
210
+ queue << pub
211
+ end
212
+ end
213
+ seen.keys
214
+ end
215
+ end
216
+ end
217
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Protostar
4
+ module Schema
5
+ # Deep-copies plain data (Hashes, Arrays, Strings, scalars) into frozen,
6
+ # deduplicated form so that building never freezes the caller's input.
7
+ # @api private
8
+ module Freezer
9
+ EMPTY_HASH = {}.freeze
10
+ EMPTY_ARRAY = [].freeze
11
+
12
+ # Freezes a descriptor graph before `Ractor.make_shareable` sees it.
13
+ # make_shareable on a graph full of back-references (every field points
14
+ # at its message, which points at all its fields) re-checks the graph
15
+ # from each back-reference and goes quadratic: about 1.4s for
16
+ # unittest.proto. Freezing everything first lets it confirm the graph in
17
+ # one pass. Arrays and Hashes are always descended into, since they may
18
+ # be frozen while their members are not; other frozen objects are
19
+ # finished descriptors of dependencies or immutable values.
20
+ def self.deep_freeze(root)
21
+ stack = [root]
22
+ containers = {}.compare_by_identity
23
+ until stack.empty?
24
+ obj = stack.pop
25
+ case obj
26
+ when Array
27
+ next if containers.key?(obj)
28
+ containers[obj] = true
29
+ stack.concat(obj)
30
+ when Hash
31
+ next if containers.key?(obj)
32
+ containers[obj] = true
33
+ obj.each { |k, v| stack << k << v }
34
+ else
35
+ next if obj.frozen?
36
+ obj.instance_variables.each { |ivar| stack << obj.instance_variable_get(ivar) }
37
+ end
38
+ obj.freeze
39
+ end
40
+ root
41
+ end
42
+
43
+ def self.call(value)
44
+ case value
45
+ when Hash
46
+ return EMPTY_HASH if value.empty?
47
+ value.each_with_object({}) { |(k, v), out| out[call(k)] = call(v) }.freeze
48
+ when Array
49
+ return EMPTY_ARRAY if value.empty?
50
+ value.map { |v| call(v) }.freeze
51
+ when String
52
+ -value
53
+ when Symbol, Integer, Float, true, false, nil
54
+ value
55
+ else
56
+ raise Error, "cannot store a #{value.class} in a descriptor: #{value.inspect}"
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Protostar
4
+ module Schema
5
+ # A message type
6
+ class Message < Descriptor
7
+ # @return [File]
8
+ attr_reader :file
9
+ # @return [Message, nil] the enclosing message for a nested type
10
+ attr_reader :containing_message
11
+ # @return [Array<Field>] declaration order, oneof members included, extensions excluded
12
+ attr_reader :fields
13
+ # @return [Hash{Integer => Field}]
14
+ attr_reader :fields_by_number
15
+ # @return [Hash{String => Field}]
16
+ attr_reader :fields_by_name
17
+ # @return [Hash{String => Field}]
18
+ attr_reader :fields_by_json_name
19
+ # @return [Array<Oneof>] all oneofs, synthetic ones included
20
+ attr_reader :oneofs
21
+ # @return [Array<Oneof>] oneofs that are not synthetic (proto3 `optional`)
22
+ attr_reader :real_oneofs
23
+ # @return [Array<Message>]
24
+ attr_reader :nested_messages
25
+ # @return [Array<Enum>]
26
+ attr_reader :nested_enums
27
+ # @return [Array<Field>] extensions declared inside this message
28
+ attr_reader :extensions
29
+ # @return [Array<ExtensionRange>]
30
+ attr_reader :extension_range_descriptors
31
+ # @return [Array<Range>] inclusive extension number ranges
32
+ attr_reader :extension_ranges
33
+ # @return [Array<Range>] inclusive reserved number ranges
34
+ attr_reader :reserved_ranges
35
+ # @return [Array<String>]
36
+ attr_reader :reserved_names
37
+ # @return [Symbol, nil] declared `visibility`: :local or :export (edition 2024), nil when not declared
38
+ attr_reader :visibility
39
+
40
+ # @api private
41
+ def initialize(name:, full_name:, file:, containing_message:, index:)
42
+ @name = name
43
+ @full_name = full_name
44
+ @file = file
45
+ @containing_message = containing_message
46
+ @index = index
47
+ @reserved_ranges = Freezer::EMPTY_ARRAY
48
+ @reserved_names = Freezer::EMPTY_ARRAY
49
+ @visibility = nil
50
+ end
51
+
52
+ # @api private
53
+ def __members(fields:, oneofs:, nested_messages:, nested_enums:, extensions:, extension_range_descriptors:)
54
+ @fields = fields
55
+ @oneofs = oneofs
56
+ @real_oneofs = oneofs.reject(&:synthetic?).freeze
57
+ @nested_messages = nested_messages
58
+ @nested_enums = nested_enums
59
+ @extensions = extensions
60
+ @extension_range_descriptors = extension_range_descriptors
61
+ @extension_ranges = extension_range_descriptors.map(&:range).freeze
62
+ @fields_by_number = {}
63
+ @fields_by_name = {}
64
+ @fields_by_json_name = {}
65
+ fields.each do |f|
66
+ @fields_by_number[f.number] ||= f
67
+ @fields_by_name[f.name] ||= f
68
+ @fields_by_json_name[f.json_name] ||= f
69
+ end
70
+ @fields_by_number.freeze
71
+ @fields_by_name.freeze
72
+ @fields_by_json_name.freeze
73
+ self
74
+ end
75
+
76
+ # @param key [Integer, String, Symbol] a field number or name
77
+ # @return [Field, nil]
78
+ def field(key)
79
+ key.is_a?(Integer) ? @fields_by_number[key] : @fields_by_name[key.to_s]
80
+ end
81
+
82
+ # @return [Boolean] whether the number falls in an extension range
83
+ def extension_number?(number)
84
+ @extension_ranges.any? { |r| r.cover?(number) }
85
+ end
86
+
87
+ # @return [Boolean] a synthesized `map<K, V>` entry type
88
+ def map_entry?
89
+ options.map_entry?
90
+ end
91
+
92
+ # @return [Boolean]
93
+ def message_set_wire_format?
94
+ options.message_set_wire_format?
95
+ end
96
+
97
+ # @param default_json_names [Boolean] see {File#to_proto}
98
+ # @return [Protostar::Google::Protobuf::DescriptorProto]
99
+ def to_proto(default_json_names: true)
100
+ Proto::Writer.message(self, default_json_names)
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Protostar
4
+ module Schema
5
+ # A oneof, real or synthesized for a proto3 `optional` field
6
+ class Oneof < Descriptor
7
+ # @return [Message]
8
+ attr_reader :containing_message
9
+ # @return [Array<Field>] members in declaration order
10
+ attr_reader :fields
11
+
12
+ # @api private
13
+ def initialize(name:, full_name:, containing_message:, index:)
14
+ @name = name
15
+ @full_name = full_name
16
+ @containing_message = containing_message
17
+ @index = index
18
+ @fields = []
19
+ @synthetic = false
20
+ end
21
+
22
+ # @return [File]
23
+ def file
24
+ @containing_message.file
25
+ end
26
+
27
+ # @return [Boolean] whether protoc synthesized it for a proto3 `optional` field
28
+ def synthetic?
29
+ @synthetic
30
+ end
31
+
32
+ # @return [Protostar::Google::Protobuf::OneofDescriptorProto]
33
+ def to_proto
34
+ Proto::Writer.oneof(self)
35
+ end
36
+ end
37
+ end
38
+ end