rbs_protobuf 1.1.0 → 1.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 36689cc81f238ec33c07f1485ab4ff50f73fe365c1e84fcb925d34b0159859d3
4
- data.tar.gz: a149120654ab2387721383d723bf3c54923385a66feff3a755ebce1056bf014c
3
+ metadata.gz: bcaf6201c92331c28b9fa7d1791711def79ccb4803d81692a220b60d08e640a6
4
+ data.tar.gz: 93651dffc7e86d657f5ed5298b3ab43042a7e663fd07a502b25fe968ede6cef2
5
5
  SHA512:
6
- metadata.gz: f2bc3a51198e70d9617edf6df9c40e9fbfbac8061d3b849d6cf2ec21b839bd379bea6bf94d5a63dc134af173c773338f7b937b53597906e1bb3e85dd6c965c3c
7
- data.tar.gz: 349c294046f6c7665dad74a0461311bf4c7edb58d9c8f804bd70b6c6ed11442d295086a59b9990465d478195c87c96e54180237e8fdd1a73c202e48ffde73b60
6
+ metadata.gz: '09835d51dd74b278a376dc721bbde2527f6e501596dd5239b29a0da5aa96af69cfc08831e2dafc7b6ac67491ef3e5c35b2ec2dca536fbc66864291b3a2109e6a'
7
+ data.tar.gz: 42b4c2d53a767f7b8732f8d4ee7a74d724ba334280cf006326a1ebfcd5f1dc4031d44404d149ee63e8c66f46da5dee2f519eaa6539d7789fb8b9b5e5a9dfb863
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 1.2.0 (2023-09-06)
4
+
5
+ * Add `RBS_PROTOBUF_CONCAT_LEVEL` option ([#38](https://github.com/square/rbs_protobuf/pull/38))
6
+ * Generate rpc method types and interfaces ([#37](https://github.com/square/rbs_protobuf/pull/37))
7
+ * Generate `record` type of each proto message ([#36](https://github.com/square/rbs_protobuf/pull/36))
8
+ * Add filter ([#34](https://github.com/square/rbs_protobuf/pull/34))
9
+
3
10
  ## 1.1.0 (2023-08-16)
4
11
 
5
12
  * Support optional fields in proto3 ([\#32](https://github.com/square/rbs_protobuf/pull/32))
data/Gemfile.lock CHANGED
@@ -1,14 +1,14 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rbs_protobuf (1.1.0)
4
+ rbs_protobuf (1.2.0)
5
5
  activesupport (>= 4.0)
6
6
  rbs (>= 2.2.0)
7
7
 
8
8
  GEM
9
9
  remote: https://rubygems.org/
10
10
  specs:
11
- activesupport (7.0.7)
11
+ activesupport (7.0.7.2)
12
12
  concurrent-ruby (~> 1.0, >= 1.0.2)
13
13
  i18n (>= 1.6, < 2)
14
14
  minitest (>= 5.1)
data/README.md CHANGED
@@ -82,21 +82,6 @@ Run `protoc` with `--rbs_out` option.
82
82
 
83
83
  You may need `bundle exec protoc ...` to let bundler set up PATH.
84
84
 
85
- ## Type checking
86
-
87
- To type check the output, you need to configure your tools to import [gem_rbs_collection](https://github.com/ruby/gem_rbs_collection) with `rbs collection` command.
88
-
89
- ```yaml
90
- # Add the dependency in rbs_collection.yaml
91
- gems:
92
- - name: rbs_protobuf
93
- ```
94
-
95
- We assume that you don't type check the generated `.pb.rb` code.
96
- If you want to type check them, you need the definition of `Google::Protobuf`, which can be generated from [`descriptor.proto`](https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/descriptor.proto).
97
-
98
- Note: `rbs_protobuf` generates RBS files assuming some types added in [this PR](https://github.com/ruby/gem_rbs_collection/pull/145). Make sure you are using one of the newer versions of `rbs_gem_collection` definitions.
99
-
100
85
  ### Options
101
86
 
102
87
  * `RBS_PROTOBUF_BACKEND` specifies the Ruby code generator gem. Supported value is `protobuf`. (We will add `google-protobuf` for `google-protobuf` gem.)
@@ -104,6 +89,20 @@ Note: `rbs_protobuf` generates RBS files assuming some types added in [this PR](
104
89
  * `RBS_PROTOBUF_NO_NESTED_NAMESPACE` is to make the RBS declarations flat.
105
90
  * `RBS_PROTOBUF_EXTENSION` specifies what to do for extensions.
106
91
  * `RBS_PROTOBUF_ACCEPT_NIL_ATTR_WRITER` is to allow passing `nil` to required fields.
92
+ * `RBS_PROTOBUF_FILTERS` contains filter Ruby script paths separated by `File::PATH_SEPARATOR`
93
+ * `RBS_PROTOBUF_CONCAT_LEVEL` contains the number of dir levels that groups generated RBS files to concat
94
+
95
+ ## Type checking
96
+
97
+ To type check the output, make sure your type checker configuration loads type definitions of `protobuf` gem.
98
+
99
+ ```ruby
100
+ # Declare in Gemfile and load it with rbs-collection
101
+ gem 'protobuf'
102
+ ```
103
+
104
+ We assume that you don't type check the generated `.pb.rb` code.
105
+ If you want to type check them, you need the definition of `Google::Protobuf`, which can be generated from [`descriptor.proto`](https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/descriptor.proto).
107
106
 
108
107
  ## Supported features
109
108
 
@@ -118,7 +117,7 @@ Note: `rbs_protobuf` generates RBS files assuming some types added in [this PR](
118
117
  | Services | Only generates classes |
119
118
  | Oneof | No support in `protobuf` gem |
120
119
 
121
- ### Extensions
120
+ ## Extensions
122
121
 
123
122
  Adding extensions may cause problems if the name of new attribute conflicts.
124
123
 
@@ -144,6 +143,27 @@ You can control the behavior with `RBS_PROTOBUF_EXTENSION` environment variable.
144
143
  * Any value else: Generates RBS for extensions.
145
144
  * undefined: Ignores extensions but print messages to ask you to specify a value.
146
145
 
146
+ ## Filters
147
+
148
+ You can apply filters that modifies generated RBS files.
149
+
150
+ A filter is a proc object with type of `^(String rbs_name, String rbs_content, untyped proto_file) -> [String, String]`:
151
+ It receives the file name of RBS file, the content of RBS file, the source protobuf object, and returns a pair of RBS file name and content.
152
+
153
+ ```ruby
154
+ # example_fitler.rb: It adds a warning comment at the top of the RBS content.
155
+ ->(rbs_name, rbs_content, _proto_file) {
156
+ [
157
+ rbs_name,
158
+ "# Don't modify this file. This is generated with rbs_protobuf.\n\n" + rbs_content
159
+ ]
160
+ }
161
+ ```
162
+
163
+ You can apply filters by setting `RBS_PROTOBUF_FILTERS` environment variable.
164
+
165
+ $ RBS_PROTOBUF_BACKEND=protobuf RBS_PROTOBUF_FILTERS=example_filter.rb protoc ...
166
+
147
167
  ## Development
148
168
 
149
169
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test example:typecheck` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
data/bin/setup CHANGED
@@ -9,13 +9,4 @@ echo ">>> Checking if protoc is available... 👀"
9
9
  protoc --version
10
10
 
11
11
  echo ">>> Setting up gem_rbs_collection... 💎"
12
- REPODIR=$(cd $(dirname $0)/..; pwd)/tmp/rbs/gem_rbs_collection
13
- if [ -e $REPODIR ]; then
14
- echo " ${REPODIR} exists"
15
- else
16
- echo " Cloning git repo into ${REPODIR}..."
17
- mkdir -p $REPODIR/..
18
- git clone https://github.com/ruby/gem_rbs_collection.git ${REPODIR}
19
- fi
20
-
21
12
  bundle exec rbs collection install
data/exe/protoc-gen-rbs CHANGED
@@ -13,6 +13,20 @@ unless backend
13
13
  exit 1
14
14
  end
15
15
 
16
+ filters = []
17
+
18
+ if string = ENV["RBS_PROTOBUF_FILTERS"]
19
+ string.split(File::PATH_SEPARATOR).each do |file|
20
+ filter = Module.new.instance_eval(File.read(file), file)
21
+
22
+ if filter.respond_to?(:call)
23
+ filters << filter
24
+ else
25
+ raise "A filter `#{file}` returns `#{filter.inspect}`, but a proc object of type `^(String rbs_name, String rbs_content, untyped proto_file) -> [String, String]` is expected"
26
+ end
27
+ end
28
+ end
29
+
16
30
  input = Google::Protobuf::Compiler::CodeGeneratorRequest.decode(STDIN.read)
17
31
 
18
32
  translator = case backend
@@ -34,6 +48,7 @@ translator = case backend
34
48
 
35
49
  RBSProtobuf::Translator::ProtobufGem.new(
36
50
  input,
51
+ filters,
37
52
  upcase_enum: upcase_enum,
38
53
  nested_namespace: !no_nested_namespace,
39
54
  extension: extension,
@@ -43,6 +58,9 @@ translator = case backend
43
58
  raise NotImplementedError
44
59
  end
45
60
 
61
+ if level = ENV["RBS_PROTOBUF_CONCAT_LEVEL"]
62
+ translator.rbs_concat_level = level.to_i
63
+ end
46
64
  translator.generate_rbs!
47
65
 
48
66
  response = translator.response
@@ -46,6 +46,7 @@ module RBSProtobuf
46
46
  end
47
47
 
48
48
  def union_type(type, *types)
49
+ types = types.compact
49
50
  if types.empty?
50
51
  type
51
52
  else
@@ -3,10 +3,19 @@ module RBSProtobuf
3
3
  class Base
4
4
  FieldDescriptorProto = Google::Protobuf::FieldDescriptorProto
5
5
 
6
- attr_reader :input
6
+ attr_reader :input, :filters
7
7
 
8
- def initialize(input)
8
+ attr_accessor :rbs_concat_level
9
+
10
+ def initialize(input, filters = [])
9
11
  @input = input
12
+ @filters = filters
13
+ end
14
+
15
+ def apply_filter(rbs_name, rbs_content, proto_files)
16
+ filters.inject([rbs_name, rbs_content]) do |(rbs_name, rbs_content), filter| #$ [String, String]
17
+ filter[rbs_name, rbs_content, proto_files] or return
18
+ end
10
19
  end
11
20
 
12
21
  def factory
@@ -18,14 +27,61 @@ module RBSProtobuf
18
27
  end
19
28
 
20
29
  def generate_rbs!
30
+ rbs_decls = {} #: Hash[Pathname, [Array[RBS::AST::Declarations::t], untyped]]
31
+
21
32
  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
- )
33
+ path = Pathname(file.name).sub_ext(".rbs")
34
+ decls = rbs_content(file)
35
+ rbs_decls[path] = [decls, file]
36
+ end
37
+
38
+ rbs_contents = {} #: Hash[Pathname, [String, Array[untyped]]]
39
+
40
+ if (level = rbs_concat_level)&.nonzero?
41
+ groups = rbs_decls.each_key.group_by do |path|
42
+ path.sub_ext("").to_s.split(File::SEPARATOR).take(level).join(File::SEPARATOR)
43
+ end
44
+
45
+ groups.each do |prefix, paths|
46
+ path = Pathname(prefix).sub_ext(".rbs")
47
+
48
+ decls = [] #: Array[RBS::AST::Declarations::t]
49
+ files = [] #: Array[untyped]
50
+
51
+ paths.each do |path|
52
+ ds, file = rbs_decls.fetch(path)
53
+ decls.concat(ds)
54
+ files.push(file)
55
+ end
56
+
57
+ rbs_contents[path] = [
58
+ format_rbs(decls: decls),
59
+ files
60
+ ]
61
+ end
62
+ else
63
+ rbs_decls.each do |path, (decls, file)|
64
+ content = format_rbs(decls: decls)
65
+ rbs_contents[path] = [content, [file]]
66
+ end
67
+ end
68
+
69
+ rbs_contents.each do |path, (content, files)|
70
+ if (name, content = apply_filter(path.to_s, content, files))
71
+ response.file << Google::Protobuf::Compiler::CodeGeneratorResponse::File.new(
72
+ name: name,
73
+ content: content
74
+ )
75
+ end
26
76
  end
27
77
  end
28
78
 
79
+ def format_rbs(dirs: [], decls: [])
80
+ StringIO.new.tap do |io|
81
+ RBS::Writer.new(out: io).write(dirs + decls)
82
+ end.string
83
+ end
84
+
29
85
  def rbs_name(proto_name)
30
86
  dirname = File.dirname(proto_name)
31
87
  basename = File.basename(proto_name, File.extname(proto_name))
@@ -120,6 +176,15 @@ module RBSProtobuf
120
176
  )
121
177
  )
122
178
  end
179
+
180
+ def optional_type(type)
181
+ case type
182
+ when RBS::Types::Optional
183
+ type
184
+ else
185
+ RBS::Types::Optional.new(type: type, location: nil)
186
+ end
187
+ end
123
188
  end
124
189
  end
125
190
  end
@@ -19,8 +19,8 @@ module RBSProtobuf
19
19
 
20
20
  attr_reader :accept_nil_writer
21
21
 
22
- def initialize(input, upcase_enum:, nested_namespace:, extension:, accept_nil_writer:, stderr: STDERR)
23
- super(input)
22
+ def initialize(input, filters=[], upcase_enum:, nested_namespace:, extension:, accept_nil_writer:, stderr: STDERR)
23
+ super(input, filters)
24
24
  @upcase_enum = upcase_enum
25
25
  @nested_namespace = nested_namespace
26
26
  @extension = extension
@@ -49,7 +49,7 @@ module RBSProtobuf
49
49
  end
50
50
 
51
51
  def rbs_content(file)
52
- decls = []
52
+ decls = [] #: Array[RBS::AST::Declarations::t]
53
53
 
54
54
  source_code_info = file.source_code_info
55
55
 
@@ -127,9 +127,7 @@ module RBSProtobuf
127
127
  end
128
128
  end
129
129
 
130
- StringIO.new.tap do |io|
131
- RBS::Writer.new(out: io).write(decls)
132
- end.string
130
+ decls
133
131
  end
134
132
 
135
133
  def message_to_decl(message, prefix:, message_path:, source_code_info:, path:)
@@ -200,7 +198,17 @@ module RBSProtobuf
200
198
  ),
201
199
  annotations: []
202
200
  ),
203
- ],
201
+ unless field_types.empty?
202
+ RBS::AST::Members::MethodDefinition::Overload.new(
203
+ method_type: factory.method_type(
204
+ type: factory.function().update(
205
+ required_positionals: [factory.param(factory.alias_type("record"))]
206
+ )
207
+ ),
208
+ annotations: []
209
+ )
210
+ end,
211
+ ].compact,
204
212
  annotations: [],
205
213
  comment: nil,
206
214
  location: nil,
@@ -352,7 +360,11 @@ module RBSProtobuf
352
360
  class_decl.members << RBS::AST::Declarations::TypeAlias.new(
353
361
  name: TypeName("init"),
354
362
  type_params: [],
355
- type: factory.union_type(class_instance_type, TO_PROTO[]),
363
+ type: factory.union_type(
364
+ class_instance_type,
365
+ TO_PROTO[],
366
+ field_types.empty? ? nil : factory.alias_type("record")
367
+ ),
356
368
  annotations: [],
357
369
  comment: RBS::AST::Comment.new(string: "The type of `#initialize` parameter.", location: nil),
358
370
  location: nil
@@ -403,6 +415,18 @@ module RBSProtobuf
403
415
  comment: nil,
404
416
  location: nil
405
417
  )
418
+
419
+ class_decl.members << RBS::AST::Declarations::TypeAlias.new(
420
+ name: TypeName("record"),
421
+ type_params: [],
422
+ type: RBS::Types::Record.new(
423
+ fields: field_types.transform_values {|_, _, type| optional_type(type) },
424
+ location: nil
425
+ ),
426
+ annotations: [],
427
+ location: nil,
428
+ comment: nil
429
+ )
406
430
  end
407
431
  end
408
432
 
@@ -896,11 +920,81 @@ module RBSProtobuf
896
920
  def service_to_decl(service, prefix:, source_code_info:, path:)
897
921
  service_name = ActiveSupport::Inflector.camelize(service.name)
898
922
 
923
+ members = [] #: Array[RBS::AST::Declarations::Class::member]
924
+
925
+ service.method.each do |method|
926
+ method_name = ActiveSupport::Inflector.underscore(method.name).to_sym #: Symbol
927
+
928
+ interface_name = "_#{ActiveSupport::Inflector.camelize(method.name)}Method"
929
+
930
+ members << RBS::AST::Declarations::Interface.new(
931
+ name: factory.type_name(interface_name),
932
+ type_params: [],
933
+ members: [
934
+ RBS::AST::Members::MethodDefinition.new(
935
+ name: :request,
936
+ kind: :instance,
937
+ overloads: [
938
+ RBS::AST::Members::MethodDefinition::Overload.new(
939
+ method_type: factory.method_type(type: factory.function(message_type(method.input_type))),
940
+ annotations: []
941
+ )
942
+ ],
943
+ annotations: [],
944
+ location: nil,
945
+ comment: nil,
946
+ overloading: false,
947
+ visibility: nil
948
+ ),
949
+ RBS::AST::Members::MethodDefinition.new(
950
+ name: :respond_with,
951
+ kind: :instance,
952
+ overloads: [
953
+ RBS::AST::Members::MethodDefinition::Overload.new(
954
+ method_type: factory.method_type(
955
+ type: factory.function().update(
956
+ required_positionals: [
957
+ factory.param(message_init_type(message_type(method.output_type)))
958
+ ]
959
+ )
960
+ ),
961
+ annotations: []
962
+ )
963
+ ],
964
+ annotations: [],
965
+ location: nil,
966
+ comment: nil,
967
+ overloading: false,
968
+ visibility: nil
969
+ )
970
+ ],
971
+ annotations: [],
972
+ location: nil,
973
+ comment: nil
974
+ )
975
+
976
+ members << RBS::AST::Members::MethodDefinition.new(
977
+ name: method_name,
978
+ kind: :instance,
979
+ overloads: [
980
+ RBS::AST::Members::MethodDefinition::Overload.new(
981
+ method_type: factory.method_type(type: factory.function()),
982
+ annotations: []
983
+ )
984
+ ],
985
+ annotations: [],
986
+ location: nil,
987
+ comment: nil,
988
+ overloading: false,
989
+ visibility: nil
990
+ )
991
+ end
992
+
899
993
  RBS::AST::Declarations::Class.new(
900
994
  name: RBS::TypeName.new(name: service_name.to_sym, namespace: prefix),
901
995
  super_class: service_base_class,
902
996
  type_params: factory.module_type_params(),
903
- members: [],
997
+ members: members,
904
998
  comment: comment_for_path(source_code_info, path, options: nil),
905
999
  location: nil,
906
1000
  annotations: []
@@ -1,3 +1,3 @@
1
1
  module RBSProtobuf
2
- VERSION = "1.1.0"
2
+ VERSION = "1.2.0"
3
3
  end
@@ -2,7 +2,7 @@
2
2
  sources:
3
3
  - type: git
4
4
  name: ruby/gem_rbs_collection
5
- revision: 8a678b2ec20e9d594055f53745399814e3a887dc
5
+ revision: 5666e737d2b27a8425b4e3855c1a38cae54989f7
6
6
  remote: https://github.com/ruby/gem_rbs_collection.git
7
7
  repo_dir: gems
8
8
  path: ".gem_rbs_collection"
@@ -10,7 +10,7 @@ module RBSProtobuf
10
10
 
11
11
  def singleton_type: (String | RBS::TypeName name) -> RBS::Types::ClassSingleton
12
12
 
13
- def union_type: (RBS::Types::t `type`, *RBS::Types::t types) -> RBS::Types::t
13
+ def union_type: (RBS::Types::t `type`, *RBS::Types::t? types) -> RBS::Types::t
14
14
 
15
15
  def nil_type: (?location: RBS::Location[untyped, untyped]?) -> RBS::Types::Bases::Nil
16
16
 
@@ -5,7 +5,15 @@ module RBSProtobuf
5
5
 
6
6
  attr_reader input: untyped
7
7
 
8
- def initialize: (untyped input) -> void
8
+ type filter = ^(String rbs_name, String rbs_content, Array[untyped] proto_files) -> [String, String]?
9
+
10
+ attr_reader filters: Array[filter]
11
+
12
+ attr_accessor rbs_concat_level: Integer?
13
+
14
+ def initialize: (untyped input, ?Array[filter] filters) -> void
15
+
16
+ def apply_filter: (String rbs_name, String rbs_content, Array[untyped] proto_file) -> [String, String]?
9
17
 
10
18
  @factory: RBSFactory
11
19
 
@@ -21,7 +29,9 @@ module RBSProtobuf
21
29
 
22
30
  def rbs_suffix: () -> ::String
23
31
 
24
- def rbs_content: (String file) -> String
32
+ def format_rbs: (?dirs: Array[RBS::AST::Directives::t], ?decls: Array[RBS::AST::Declarations::t]) -> String
33
+
34
+ def rbs_content: (String file) -> Array[RBS::AST::Declarations::t]
25
35
 
26
36
  def comment_for_path: (untyped source_code_info, Array[Integer] path, options: untyped) -> RBS::AST::Comment?
27
37
 
@@ -30,6 +40,8 @@ module RBSProtobuf
30
40
  def base_type: (untyped `type`) -> RBS::Types::t
31
41
 
32
42
  def message_type: (String) -> RBS::Types::ClassInstance
43
+
44
+ def optional_type: (RBS::Types::t) -> RBS::Types::Optional
33
45
  end
34
46
  end
35
47
  end
@@ -28,6 +28,7 @@ module RBSProtobuf
28
28
 
29
29
  def initialize: (
30
30
  untyped input,
31
+ ?Array[Base::filter] filters,
31
32
  upcase_enum: bool,
32
33
  nested_namespace: bool,
33
34
  extension: bool | :print | nil,
@@ -63,7 +64,7 @@ module RBSProtobuf
63
64
  # The entry point.
64
65
  # Generate RBS declarations from the `file` and returns the string representation of the declarations.
65
66
  #
66
- def rbs_content: (untyped file) -> String
67
+ def rbs_content: (untyped file) -> Array[RBS::AST::Declarations::t]
67
68
 
68
69
  # Returns the class declaration for given message.
69
70
  #
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rbs_protobuf
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Soutaro Matsumoto
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-08-16 00:00:00.000000000 Z
11
+ date: 2023-09-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rbs