rbs_protobuf 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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