rbs_protobuf 0.1.0

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.
data/example/b.proto ADDED
@@ -0,0 +1,10 @@
1
+ syntax = "proto2";
2
+
3
+ package rbs_protobuf.example;
4
+
5
+ import 'a.proto';
6
+
7
+ extend rbs_protobuf.example.Project {
8
+ optional string new_name = 100;
9
+ }
10
+
@@ -0,0 +1,32 @@
1
+ require "protobuf"
2
+ require_relative "protobuf-gem/a.pb.rb"
3
+ require_relative "protobuf-gem/b.pb.rb"
4
+
5
+ request = Rbs_protobuf::Example::SearchRequest.decode("")
6
+ request.corpus = :VIDEO
7
+
8
+ request.corpus = Rbs_protobuf::Example::SearchRequest::Corpus::WEB
9
+
10
+ corpus = request.corpus
11
+
12
+ query = request[:query]
13
+
14
+
15
+ req = Rbs_protobuf::Example::SearchRequest.new(corpus: :VIDEO, query: "Hello world")
16
+
17
+
18
+ response = Rbs_protobuf::Example::SearchResponse.new()
19
+
20
+ response.result.each do |result|
21
+
22
+ end
23
+
24
+ response.result.clear
25
+ response.result[0] = nil
26
+
27
+ project = Rbs_protobuf::Example::Project.new
28
+ project.projects["sub project 1"] = Rbs_protobuf::Example::Project.new
29
+
30
+ project = Rbs_protobuf::Example::Project.new
31
+ s = project.new_name
32
+ project.new_name = "Hello world"
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH << File.join(__dir__, "../lib")
4
+
5
+ require "rbs_protobuf"
6
+
7
+ backend = ENV["RBS_PROTOBUF_BACKEND"]
8
+ unless backend
9
+ STDERR.puts "🚨🚨🚨 protoc-gen-rbs requires RBS_PROTOBUF_BACKEND environment variable. 🚨🚨🚨"
10
+ STDERR.puts " Possible options:"
11
+ STDERR.puts " RBS_PROTOBUF_BACKEND=protobuf (for protobuf gem)"
12
+ STDERR.puts " RBS_PROTOBUF_BACKEND=google-protobuf (for google-protobuf gem)"
13
+ exit 1
14
+ end
15
+
16
+ input = Google::Protobuf::Compiler::CodeGeneratorRequest.decode(STDIN.read)
17
+
18
+ translator = case backend
19
+ when "protobuf"
20
+ upcase_enum = ENV.key?("PB_UPCASE_ENUMS")
21
+ no_nested_namespace = ENV.key?("RBS_PROTOBUF_NO_NESTED_NAMESPACE")
22
+
23
+ RBSProtobuf::Translator::ProtobufGem.new(
24
+ input,
25
+ upcase_enum: upcase_enum,
26
+ nested_namespace: !no_nested_namespace
27
+ )
28
+ when "google-protobuf"
29
+ raise NotImplementedError
30
+ end
31
+
32
+ translator.generate_rbs!
33
+
34
+ response = translator.response
35
+ print Google::Protobuf::Compiler::CodeGeneratorResponse.encode(response)
@@ -0,0 +1,19 @@
1
+ require "rbs_protobuf/version"
2
+
3
+ require "logger"
4
+
5
+ require "protobuf/descriptors"
6
+ require "active_support/inflector"
7
+ require "rbs"
8
+
9
+ module RBSProtobuf
10
+ class <<self
11
+ attr_reader :logger
12
+ end
13
+
14
+ @logger = Logger.new(STDERR)
15
+ end
16
+
17
+ require "rbs_protobuf/rbs_factory"
18
+ require "rbs_protobuf/translator/base"
19
+ require "rbs_protobuf/translator/protobuf_gem"
@@ -0,0 +1,149 @@
1
+ module RBSProtobuf
2
+ class RBSFactory
3
+ include RBS
4
+
5
+ def type_name(string)
6
+ absolute = string.start_with?("::")
7
+
8
+ *path, name = string.delete_prefix("::").split("::").map(&:to_sym)
9
+
10
+ TypeName.new(
11
+ name: name,
12
+ namespace: Namespace.new(path: path, absolute: absolute)
13
+ )
14
+ end
15
+
16
+ def namespace(string)
17
+ absolute = string.start_with?("::")
18
+ path = string.delete_prefix("::").split("::").map(&:to_sym)
19
+
20
+ Namespace.new(
21
+ path: path,
22
+ absolute: absolute
23
+ )
24
+ end
25
+
26
+ def instance_type(name, *args)
27
+ type_name = case name
28
+ when TypeName
29
+ name
30
+ else
31
+ type_name(name)
32
+ end
33
+
34
+ Types::ClassInstance.new(name: type_name, args: args, location: nil)
35
+ end
36
+
37
+ def singleton_type(name)
38
+ type_name = case name
39
+ when TypeName
40
+ name
41
+ else
42
+ type_name(name)
43
+ end
44
+
45
+ Types::ClassSingleton.new(name: type_name, location: nil)
46
+ end
47
+
48
+ def union_type(type, *types)
49
+ if types.empty?
50
+ type
51
+ else
52
+ Types::Union.new(
53
+ types: [type] + types,
54
+ location: nil
55
+ )
56
+ end
57
+ end
58
+
59
+ def nil_type(location: nil)
60
+ RBS::Types::Bases::Nil.new(location: location)
61
+ end
62
+
63
+ def bool_type(location: nil)
64
+ RBS::Types::Bases::Bool.new(location: location)
65
+ end
66
+
67
+ def alias_type(name, location: nil)
68
+ type_name = case name
69
+ when TypeName
70
+ name
71
+ else
72
+ type_name(name)
73
+ end
74
+
75
+ Types::Alias.new(name: type_name, location: nil)
76
+ end
77
+
78
+ def function(return_type = Types::Bases::Void.new(location: nil))
79
+ Types::Function.empty(return_type)
80
+ end
81
+
82
+ def param(type, name: nil)
83
+ Types::Function::Param.new(
84
+ type: type,
85
+ name: name
86
+ )
87
+ end
88
+
89
+ def block(function, required: true)
90
+ MethodType::Block.new(
91
+ type: function,
92
+ required: required
93
+ )
94
+ end
95
+
96
+ def untyped(location: nil)
97
+ Types::Bases::Any.new(location: location)
98
+ end
99
+
100
+ def method_type(params: [], type:, block: nil, location: nil)
101
+ MethodType.new(
102
+ type_params: params,
103
+ type: type,
104
+ block: block,
105
+ location: location
106
+ )
107
+ end
108
+
109
+ def literal_type(literal)
110
+ Types::Literal.new(
111
+ literal: literal,
112
+ location: nil
113
+ )
114
+ end
115
+
116
+ def optional_type(type, location: nil)
117
+ Types::Optional.new(
118
+ type: type,
119
+ location: location
120
+ )
121
+ end
122
+
123
+ def type_var(name, location: nil)
124
+ Types::Variable.new(
125
+ name: name,
126
+ location: location
127
+ )
128
+ end
129
+
130
+ def module_type_params(*params)
131
+ params.each.with_object(AST::Declarations::ModuleTypeParams.empty) do |param, type_params|
132
+ type_params.add(AST::Declarations::ModuleTypeParams::TypeParam.new(
133
+ name: param,
134
+ variance: :invariant,
135
+ skip_validation: false
136
+ ))
137
+ end
138
+ end
139
+
140
+ def unwrap_optional(type)
141
+ case type
142
+ when RBS::Types::Optional
143
+ unwrap_optional(type.type)
144
+ else
145
+ type
146
+ end
147
+ end
148
+ end
149
+ end
@@ -0,0 +1,100 @@
1
+ module RBSProtobuf
2
+ module Translator
3
+ class Base
4
+ FieldDescriptorProto = Google::Protobuf::FieldDescriptorProto
5
+
6
+ attr_reader :input
7
+
8
+ def initialize(input)
9
+ @input = input
10
+ end
11
+
12
+ def factory
13
+ @factory ||= RBSFactory.new()
14
+ end
15
+
16
+ def response
17
+ @response ||= Google::Protobuf::Compiler::CodeGeneratorResponse.new
18
+ end
19
+
20
+ def generate_rbs!
21
+ input.proto_file.each do |file|
22
+ response.file << Google::Protobuf::Compiler::CodeGeneratorResponse::File.new(
23
+ name: rbs_name(file.name),
24
+ content: rbs_content(file)
25
+ )
26
+ end
27
+ end
28
+
29
+ def rbs_name(proto_name)
30
+ dirname = File.dirname(proto_name)
31
+ basename = File.basename(proto_name, File.extname(proto_name))
32
+ rbs_name = "#{basename}#{rbs_suffix}.rbs"
33
+
34
+ File.join(dirname, rbs_name)
35
+ end
36
+
37
+ def rbs_suffix
38
+ ""
39
+ end
40
+
41
+ def rbs_content(file)
42
+ raise NotImplementedError
43
+ end
44
+
45
+ def comment_for_path(source_code_info, path)
46
+ loc = source_code_info.location.find {|loc| loc.path == path }
47
+ if loc
48
+ comments = []
49
+ if loc.leading_comments.length > 0
50
+ comments << loc.leading_comments.strip
51
+ end
52
+ if loc.trailing_comments.length > 0
53
+ comments << loc.trailing_comments.strip
54
+ end
55
+ if comments.empty? && !loc.leading_detached_comments.empty?
56
+ comments << loc.leading_detached_comments.join("\n\n").strip
57
+ end
58
+ RBS::AST::Comment.new(
59
+ location: nil,
60
+ string: comments.join("\n\n")
61
+ )
62
+ end
63
+ end
64
+
65
+ def base_type(type)
66
+ case type
67
+ when FieldDescriptorProto::Type::TYPE_STRING,
68
+ FieldDescriptorProto::Type::TYPE_BYTES
69
+ RBS::BuiltinNames::String.instance_type
70
+ when FieldDescriptorProto::Type::TYPE_INT32, FieldDescriptorProto::Type::TYPE_INT64,
71
+ FieldDescriptorProto::Type::TYPE_UINT32, FieldDescriptorProto::Type::TYPE_UINT64,
72
+ FieldDescriptorProto::Type::TYPE_FIXED32, FieldDescriptorProto::Type::TYPE_FIXED64,
73
+ FieldDescriptorProto::Type::TYPE_SINT32, FieldDescriptorProto::Type::TYPE_SINT64,
74
+ FieldDescriptorProto::Type::TYPE_SFIXED32, FieldDescriptorProto::Type::TYPE_SFIXED64
75
+ RBS::BuiltinNames::Integer.instance_type
76
+ when FieldDescriptorProto::Type::TYPE_DOUBLE, FieldDescriptorProto::Type::TYPE_FLOAT
77
+ RBS::BuiltinNames::Float.instance_type
78
+ when FieldDescriptorProto::Type::TYPE_BOOL
79
+ factory.union_type(factory.literal_type(true),
80
+ factory.literal_type(false))
81
+ else
82
+ raise "Unknown base type: #{type}"
83
+ end
84
+ end
85
+
86
+ def message_type(string)
87
+ absolute = string.start_with?(".")
88
+
89
+ *path, name = string.delete_prefix(".").split(".").map {|s| ActiveSupport::Inflector.upcase_first(s).to_sym }
90
+
91
+ factory.instance_type(
92
+ RBS::TypeName.new(
93
+ name: name,
94
+ namespace: RBS::Namespace.new(path: path, absolute: absolute)
95
+ )
96
+ )
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,583 @@
1
+ module RBSProtobuf
2
+ module Translator
3
+ class ProtobufGem < Base
4
+ def initialize(input, upcase_enum:, nested_namespace:)
5
+ super(input)
6
+ @upcase_enum = upcase_enum
7
+ @nested_namespace = nested_namespace
8
+ end
9
+
10
+ def upcase_enum?
11
+ @upcase_enum
12
+ end
13
+
14
+ def nested_namespace?
15
+ @nested_namespace
16
+ end
17
+
18
+ def rbs_content(file)
19
+ decls = []
20
+
21
+ source_code_info = file.source_code_info
22
+
23
+ if file.package && !file.package.empty?
24
+ package_namespace = message_type(file.package).name.to_namespace
25
+ else
26
+ package_namespace = RBS::Namespace.empty
27
+ end
28
+
29
+ prefix = if nested_namespace?
30
+ RBS::Namespace.empty
31
+ else
32
+ package_namespace
33
+ end
34
+
35
+ file.enum_type.each_with_index do |enum, index|
36
+ decls << enum_type_to_decl(enum,
37
+ prefix: prefix,
38
+ source_code_info: source_code_info,
39
+ path: [5, index])
40
+ end
41
+
42
+ file.message_type.each_with_index do |message, index|
43
+ decls << message_to_decl(message,
44
+ prefix: prefix,
45
+ message_path: if package_namespace.empty?
46
+ [message.name.to_sym]
47
+ else
48
+ [file.package.to_sym, message.name.to_sym]
49
+ end,
50
+ source_code_info: source_code_info,
51
+ path: [4, index])
52
+ end
53
+
54
+ file.service.each_with_index do |service, index|
55
+ decls << service_to_decl(service,
56
+ prefix: prefix,
57
+ source_code_info: source_code_info,
58
+ path: [6, index])
59
+ end
60
+
61
+ if nested_namespace?
62
+ package_namespace.path.reverse_each do |name|
63
+ decls = [
64
+ RBS::AST::Declarations::Module.new(
65
+ name: factory.type_name(name.to_s),
66
+ self_types: [],
67
+ type_params: factory.module_type_params,
68
+ location: nil,
69
+ comment: nil,
70
+ annotations: [],
71
+ members: decls
72
+ )
73
+ ]
74
+ end
75
+ end
76
+
77
+ file.extension.group_by(&:extendee).each.with_index do |(name, extensions), index|
78
+ decls.push(*extension_to_decl(name,
79
+ extensions,
80
+ prefix: RBS::Namespace.root,
81
+ source_code_info: source_code_info,
82
+ path: [7, index]))
83
+ end
84
+
85
+ StringIO.new.tap do |io|
86
+ RBS::Writer.new(out: io).write(decls)
87
+ end.string
88
+ end
89
+
90
+ def message_base_class
91
+ RBS::AST::Declarations::Class::Super.new(
92
+ name: RBS::TypeName.new(
93
+ name: :Message,
94
+ namespace: RBS::Namespace.parse("::Protobuf")
95
+ ),
96
+ args: [],
97
+ location: nil
98
+ )
99
+ end
100
+
101
+ def repeated_field_type(type, wtype = type)
102
+ factory.instance_type(
103
+ factory.type_name("::Protobuf::Field::FieldArray"),
104
+ type,
105
+ wtype
106
+ )
107
+ end
108
+
109
+ def message_to_decl(message, prefix:, message_path:, source_code_info:, path:)
110
+ class_name = ActiveSupport::Inflector.upcase_first(message.name)
111
+ decl_namespace = prefix.append(class_name.to_sym)
112
+
113
+ RBS::AST::Declarations::Class.new(
114
+ name: RBS::TypeName.new(name: class_name.to_sym, namespace: prefix),
115
+ super_class: message_base_class,
116
+ type_params: RBS::AST::Declarations::ModuleTypeParams.empty,
117
+ location: nil,
118
+ comment: comment_for_path(source_code_info, path),
119
+ members: [],
120
+ annotations: []
121
+ ).tap do |class_decl|
122
+ maps = {}
123
+
124
+ message.nested_type.each_with_index do |nested_type, index|
125
+ if nested_type.options&.map_entry
126
+ key_field, value_field = nested_type.field.to_a
127
+ map_type_name = ".#{(message_path + [nested_type.name]).join(".")}"
128
+ maps[map_type_name] = [key_field, value_field]
129
+ else
130
+ class_decl.members << message_to_decl(
131
+ nested_type,
132
+ prefix: RBS::Namespace.empty,
133
+ message_path: message_path + [nested_type.name.to_sym],
134
+ source_code_info: source_code_info,
135
+ path: path + [3, index]
136
+ )
137
+ end
138
+ end
139
+
140
+ message.enum_type.each_with_index do |enum, index|
141
+ class_decl.members << enum_type_to_decl(
142
+ enum,
143
+ prefix: RBS::Namespace.empty,
144
+ source_code_info: source_code_info,
145
+ path: path + [4, index]
146
+ )
147
+ end
148
+
149
+ field_read_types = {}
150
+ field_write_types = {}
151
+
152
+ message.field.each_with_index do |field, index|
153
+ field_name = field.name.to_sym
154
+ comment = comment_for_path(source_code_info, path + [2, index])
155
+
156
+ read_type, write_type = field_type(field, maps)
157
+
158
+ field_read_types[field_name] = read_type
159
+ field_write_types[field_name] = write_type
160
+
161
+ if read_type == write_type
162
+ class_decl.members << RBS::AST::Members::AttrAccessor.new(
163
+ name: field_name,
164
+ type: read_type,
165
+ comment: comment,
166
+ location: nil,
167
+ annotations: [],
168
+ ivar_name: false,
169
+ kind: :instance
170
+ )
171
+ else
172
+ class_decl.members << RBS::AST::Members::AttrReader.new(
173
+ name: field_name,
174
+ type: read_type,
175
+ comment: comment,
176
+ location: nil,
177
+ annotations: [],
178
+ ivar_name: false,
179
+ kind: :instance
180
+ )
181
+
182
+ class_decl.members << RBS::AST::Members::AttrWriter.new(
183
+ name: field_name,
184
+ type: write_type,
185
+ comment: comment,
186
+ location: nil,
187
+ annotations: [],
188
+ ivar_name: false,
189
+ kind: :instance
190
+ )
191
+ end
192
+ end
193
+
194
+ class_decl.members << RBS::AST::Members::MethodDefinition.new(
195
+ name: :initialize,
196
+ types: [
197
+ factory.method_type(
198
+ type: factory.function().update(
199
+ optional_keywords: field_write_types.transform_values {|ty|
200
+ factory.param(ty)
201
+ }
202
+ )
203
+ )
204
+ ],
205
+ annotations: [],
206
+ comment: nil,
207
+ location: nil,
208
+ overload: false,
209
+ kind: :instance
210
+ )
211
+
212
+ unless field_read_types.empty?
213
+ class_decl.members << RBS::AST::Members::MethodDefinition.new(
214
+ name: :[],
215
+ types:
216
+ field_read_types.keys.map do |key|
217
+ factory.method_type(
218
+ type: factory.function(field_read_types[key]).update(
219
+ required_positionals: [
220
+ factory.param(factory.literal_type(key))
221
+ ]
222
+ )
223
+ )
224
+ end +
225
+ [
226
+ factory.method_type(
227
+ type: factory.function(factory.untyped).update(
228
+ required_positionals: [
229
+ factory.param(RBS::BuiltinNames::Symbol.instance_type)
230
+ ]
231
+ )
232
+ )
233
+ ],
234
+ annotations: [],
235
+ comment: nil,
236
+ location: nil,
237
+ overload: false,
238
+ kind: :instance
239
+ )
240
+ end
241
+
242
+ unless field_write_types.empty?
243
+ class_decl.members << RBS::AST::Members::MethodDefinition.new(
244
+ name: :[]=,
245
+ types:
246
+ field_write_types.keys.map do |key|
247
+ factory.method_type(
248
+ type: factory.function(field_write_types[key]).update(
249
+ required_positionals: [
250
+ factory.literal_type(key),
251
+ field_write_types[key]
252
+ ].map {|t| factory.param(t) }
253
+ )
254
+ )
255
+ end +
256
+ [
257
+ factory.method_type(
258
+ type: factory.function(factory.untyped).update(
259
+ required_positionals: [
260
+ RBS::BuiltinNames::Symbol.instance_type,
261
+ factory.untyped
262
+ ].map {|t| factory.param(t) }
263
+ )
264
+ )
265
+ ],
266
+ annotations: [],
267
+ comment: nil,
268
+ location: nil,
269
+ overload: false,
270
+ kind: :instance
271
+ )
272
+ end
273
+
274
+ message.field.each do |field|
275
+ if field.type == FieldDescriptorProto::Type::TYPE_BOOL
276
+ class_decl.members << RBS::AST::Members::MethodDefinition.new(
277
+ name: :"#{field.name}?",
278
+ types: [
279
+ factory.method_type(
280
+ type: factory.function(factory.bool_type)
281
+ )
282
+ ],
283
+ annotations: [],
284
+ comment: nil,
285
+ location: nil,
286
+ overload: false,
287
+ kind: :instance
288
+ )
289
+ end
290
+ end
291
+ end
292
+ end
293
+
294
+ def field_type(field, maps)
295
+ case
296
+ when field.type == FieldDescriptorProto::Type::TYPE_MESSAGE
297
+ if maps.key?(field.type_name)
298
+ key_field, value_field = maps[field.type_name]
299
+
300
+ key_type_r, _ = field_type(key_field, maps)
301
+ value_type_r, value_type_w = field_type(value_field, maps)
302
+
303
+ hash_type = factory.instance_type(
304
+ factory.type_name("::Protobuf::Field::FieldHash"),
305
+ key_type_r,
306
+ factory.unwrap_optional(value_type_r),
307
+ factory.unwrap_optional(value_type_w)
308
+ )
309
+
310
+ [
311
+ hash_type,
312
+ hash_type
313
+ ]
314
+ else
315
+ type = message_type(field.type_name)
316
+
317
+ case field.label
318
+ when FieldDescriptorProto::Label::LABEL_OPTIONAL
319
+ type = factory.optional_type(type)
320
+ [type, type]
321
+ when FieldDescriptorProto::Label::LABEL_REPEATED
322
+ type = repeated_field_type(type)
323
+ [type, type]
324
+ else
325
+ [type, factory.optional_type(type)]
326
+ end
327
+ end
328
+ when field.type == FieldDescriptorProto::Type::TYPE_ENUM
329
+ type = message_type(field.type_name)
330
+ enum_namespace = type.name.to_namespace
331
+
332
+ wtype = factory.union_type(
333
+ type,
334
+ factory.alias_type(RBS::TypeName.new(name: :values, namespace: enum_namespace))
335
+ )
336
+
337
+ if field.label == FieldDescriptorProto::Label::LABEL_REPEATED
338
+ type = repeated_field_type(type, wtype)
339
+
340
+ [
341
+ type,
342
+ type
343
+ ]
344
+ else
345
+ [
346
+ type,
347
+ factory.optional_type(wtype)
348
+ ]
349
+ end
350
+ else
351
+ type = base_type(field.type)
352
+
353
+ if field.label == FieldDescriptorProto::Label::LABEL_REPEATED
354
+ type = repeated_field_type(type)
355
+ [type, type]
356
+ else
357
+ [type, factory.optional_type(type)]
358
+ end
359
+ end
360
+ end
361
+
362
+ def enum_base_class
363
+ RBS::AST::Declarations::Class::Super.new(
364
+ name: factory.type_name("::Protobuf::Enum"),
365
+ args: [],
366
+ location: nil
367
+ )
368
+ end
369
+
370
+ def enum_name(name)
371
+ if upcase_enum?
372
+ name.upcase.to_sym
373
+ else
374
+ name.to_sym
375
+ end
376
+ end
377
+
378
+ def enum_type_to_decl(enum_type, prefix:, source_code_info:, path:)
379
+ enum_name = ActiveSupport::Inflector.upcase_first(enum_type.name)
380
+
381
+ RBS::AST::Declarations::Class.new(
382
+ name: RBS::TypeName.new(name: enum_name.to_sym, namespace: prefix),
383
+ super_class: enum_base_class(),
384
+ type_params: factory.module_type_params(),
385
+ members: [],
386
+ comment: comment_for_path(source_code_info, path),
387
+ location: nil,
388
+ annotations: []
389
+ ).tap do |enum_decl|
390
+ names = enum_type.value.map do |v|
391
+ factory.literal_type(enum_name(v.name))
392
+ end
393
+
394
+ strings = enum_type.value.map do |v|
395
+ factory.literal_type(enum_name(v.name).to_s)
396
+ end
397
+
398
+ tags = enum_type.value.map do |v|
399
+ factory.literal_type(v.number)
400
+ end.uniq
401
+
402
+ enum_decl.members << RBS::AST::Declarations::Alias.new(
403
+ name: factory.type_name("names"),
404
+ type: factory.union_type(*names),
405
+ location: nil,
406
+ comment: nil,
407
+ annotations: []
408
+ )
409
+
410
+ enum_decl.members << RBS::AST::Declarations::Alias.new(
411
+ name: factory.type_name("strings"),
412
+ type: factory.union_type(*strings),
413
+ location: nil,
414
+ comment: nil,
415
+ annotations: []
416
+ )
417
+
418
+ enum_decl.members << RBS::AST::Declarations::Alias.new(
419
+ name: factory.type_name("tags"),
420
+ type: factory.union_type(*tags),
421
+ location: nil,
422
+ comment: nil,
423
+ annotations: []
424
+ )
425
+
426
+ enum_decl.members << RBS::AST::Declarations::Alias.new(
427
+ name: factory.type_name("values"),
428
+ type: factory.union_type(
429
+ factory.alias_type("names"),
430
+ factory.alias_type("strings"),
431
+ factory.alias_type("tags")
432
+ ),
433
+ location: nil,
434
+ comment: nil,
435
+ annotations: []
436
+ )
437
+
438
+ enum_decl.members << RBS::AST::Members::AttrReader.new(
439
+ name: :name,
440
+ type: factory.alias_type("names"),
441
+ ivar_name: false,
442
+ annotations: [],
443
+ comment: nil,
444
+ location: nil,
445
+ kind: :instance
446
+ )
447
+
448
+ enum_decl.members << RBS::AST::Members::AttrReader.new(
449
+ name: :tag,
450
+ type: factory.alias_type("tags"),
451
+ ivar_name: false,
452
+ annotations: [],
453
+ comment: nil,
454
+ location: nil,
455
+ kind: :instance
456
+ )
457
+
458
+ enum_type.value.each.with_index do |v, index|
459
+ comment = comment_for_path(source_code_info, path + [2, index])
460
+
461
+ enum_decl.members << RBS::AST::Declarations::Constant.new(
462
+ name: factory.type_name(enum_name(v.name).to_s),
463
+ type: RBS::TypeName.new(name: enum_name.to_sym, namespace: prefix),
464
+ comment: comment,
465
+ location: nil
466
+ )
467
+ end
468
+ end
469
+ end
470
+
471
+ def extension_to_decl(extendee_name, extensions, prefix:, source_code_info:, path:)
472
+ class_name = message_type(extendee_name).name
473
+
474
+ extensions.map do |field|
475
+ field_name = field.name.to_sym
476
+
477
+ RBS::AST::Declarations::Class.new(
478
+ name: class_name,
479
+ super_class: nil,
480
+ type_params: RBS::AST::Declarations::ModuleTypeParams.empty,
481
+ location: nil,
482
+ comment: nil,
483
+ members: [],
484
+ annotations: []
485
+ ).tap do |class_decl|
486
+ read_type, write_type = field_type(field, {})
487
+
488
+ if read_type == write_type
489
+ class_decl.members << RBS::AST::Members::AttrAccessor.new(
490
+ name: field_name,
491
+ type: read_type,
492
+ comment: nil,
493
+ location: nil,
494
+ annotations: [],
495
+ ivar_name: false,
496
+ kind: :instance
497
+ )
498
+ else
499
+ class_decl.members << RBS::AST::Members::AttrReader.new(
500
+ name: field_name,
501
+ type: read_type,
502
+ comment: nil,
503
+ location: nil,
504
+ annotations: [],
505
+ ivar_name: false,
506
+ kind: :instance
507
+ )
508
+
509
+ class_decl.members << RBS::AST::Members::AttrWriter.new(
510
+ name: field_name,
511
+ type: write_type,
512
+ comment: nil,
513
+ location: nil,
514
+ annotations: [],
515
+ ivar_name: false,
516
+ kind: :instance
517
+ )
518
+ end
519
+
520
+ class_decl.members << RBS::AST::Members::MethodDefinition.new(
521
+ name: :[],
522
+ types: [
523
+ factory.method_type(
524
+ type: factory.function(read_type).update(
525
+ required_positionals: [
526
+ factory.param(factory.literal_type(field_name))
527
+ ]
528
+ )
529
+ )
530
+ ],
531
+ annotations: [],
532
+ comment: nil,
533
+ location: nil,
534
+ overload: true,
535
+ kind: :instance
536
+ )
537
+
538
+ class_decl.members << RBS::AST::Members::MethodDefinition.new(
539
+ name: :[]=,
540
+ types: [
541
+ factory.method_type(
542
+ type: factory.function(write_type).update(
543
+ required_positionals: [
544
+ factory.param(factory.literal_type(field_name)),
545
+ factory.param(write_type)
546
+ ]
547
+ )
548
+ )
549
+ ],
550
+ annotations: [],
551
+ comment: nil,
552
+ location: nil,
553
+ overload: true,
554
+ kind: :instance
555
+ )
556
+ end
557
+ end
558
+ end
559
+
560
+ def service_base_class
561
+ RBS::AST::Declarations::Class::Super.new(
562
+ name: factory.type_name("::Protobuf::Rpc::Service"),
563
+ args: [],
564
+ location: nil
565
+ )
566
+ end
567
+
568
+ def service_to_decl(service, prefix:, source_code_info:, path:)
569
+ service_name = ActiveSupport::Inflector.camelize(service.name)
570
+
571
+ RBS::AST::Declarations::Class.new(
572
+ name: RBS::TypeName.new(name: service_name.to_sym, namespace: prefix),
573
+ super_class: service_base_class,
574
+ type_params: factory.module_type_params(),
575
+ members: [],
576
+ comment: comment_for_path(source_code_info, path),
577
+ location: nil,
578
+ annotations: []
579
+ )
580
+ end
581
+ end
582
+ end
583
+ end