rbs_protobuf 1.1.0 → 1.2.0

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