protod 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.
- checksums.yaml +7 -0
- data/.bundle/config +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +585 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +21 -0
- data/Gemfile.lock +165 -0
- data/LICENSE.txt +21 -0
- data/README.md +37 -0
- data/Rakefile +12 -0
- data/lib/generators/protod/gruf_generator.rb +43 -0
- data/lib/generators/protod/task_generator.rb +23 -0
- data/lib/protod/configuration.rb +78 -0
- data/lib/protod/interpreter/active_record.rb +161 -0
- data/lib/protod/interpreter/builtin.rb +328 -0
- data/lib/protod/interpreter/rpc.rb +206 -0
- data/lib/protod/interpreter.rb +180 -0
- data/lib/protod/proto/builder.rb +70 -0
- data/lib/protod/proto/features.rb +32 -0
- data/lib/protod/proto/field.rb +88 -0
- data/lib/protod/proto/message.rb +95 -0
- data/lib/protod/proto/oneof.rb +46 -0
- data/lib/protod/proto/package.rb +120 -0
- data/lib/protod/proto/part.rb +191 -0
- data/lib/protod/proto/procedure.rb +42 -0
- data/lib/protod/proto/service.rb +42 -0
- data/lib/protod/protocol_buffers.rb +46 -0
- data/lib/protod/rake_task.rb +91 -0
- data/lib/protod/rpc/handler.rb +114 -0
- data/lib/protod/rpc/request.rb +68 -0
- data/lib/protod/rpc/response.rb +68 -0
- data/lib/protod/ruby_ident.rb +49 -0
- data/lib/protod/version.rb +5 -0
- data/lib/protod.rb +103 -0
- data/sig/protod.rbs +4 -0
- metadata +136 -0
@@ -0,0 +1,328 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Protod
|
4
|
+
class Interpreter
|
5
|
+
class Builtin
|
6
|
+
def self.setup!
|
7
|
+
google_protobuf = Protod.find_or_register_package('google.protobuf')
|
8
|
+
google_type = Protod.find_or_register_package('google.type', url: 'https://github.com/googleapis/googleapis.git')
|
9
|
+
|
10
|
+
Interpreter.register_for('RBS::Types::Bases::Any', parent: google_protobuf, path: 'google/protobuf/any.proto', force: false, ignore: true) do
|
11
|
+
def proto_ident
|
12
|
+
'Any'
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_pb_from(rb)
|
16
|
+
i = Protod::Interpreter.find_by(rb.class)
|
17
|
+
|
18
|
+
raise NotImplementedError, "Not found the interpreter for #{rb.class.name}. You can define a interpreter using Protod::Interpreter.register_for" unless i
|
19
|
+
|
20
|
+
value = if i.pb_const
|
21
|
+
pb = i.to_pb_from(rb)
|
22
|
+
|
23
|
+
pb.protod__object_id = rb.object_id.to_s if pb.respond_to?(:protod__object_id)
|
24
|
+
|
25
|
+
i.pb_const.encode(pb)
|
26
|
+
elsif i.proto_ident
|
27
|
+
pb = i.to_pb_from(rb)
|
28
|
+
|
29
|
+
case i.proto_ident
|
30
|
+
when 'bool'
|
31
|
+
# ref: https://github.com/ruby-protobuf/protobuf/blob/b866fc5667226d0582f328ab24c648e578c5a380/lib/protobuf/field/bool_field.rb#L46
|
32
|
+
[pb ? 1 : 0].pack('C')
|
33
|
+
when 'double'
|
34
|
+
# ref: https://github.com/ruby-protobuf/protobuf/blob/b866fc5667226d0582f328ab24c648e578c5a380/lib/protobuf/field/double_field.rb#L16
|
35
|
+
[pb].pack('E')
|
36
|
+
when 'sint64', 'sint32'
|
37
|
+
# ref: https://github.com/ruby-protobuf/protobuf/blob/b866fc5667226d0582f328ab24c648e578c5a380/lib/protobuf/field/signed_integer_field.rb#L20-L24
|
38
|
+
if pb >= 0
|
39
|
+
varint_encode(pb << 1)
|
40
|
+
else
|
41
|
+
varint_encode(~(pb << 1))
|
42
|
+
end
|
43
|
+
when 'uint64'
|
44
|
+
# ref: https://github.com/ruby-protobuf/protobuf/blob/b866fc5667226d0582f328ab24c648e578c5a380/lib/protobuf/field/varint_field.rb#L68
|
45
|
+
varint_encode(pb)
|
46
|
+
when 'string', 'bytes'
|
47
|
+
# ref: https://github.com/ruby-protobuf/protobuf/blob/b866fc5667226d0582f328ab24c648e578c5a380/lib/protobuf/field/string_field.rb#L37-L43
|
48
|
+
v = "" + pb
|
49
|
+
if i.proto_ident == 'string' && v.encoding != ::Encoding::UTF_8
|
50
|
+
v.encode!(::Encoding::UTF_8, :invalid => :replace, :undef => :replace, :replace => "")
|
51
|
+
end
|
52
|
+
v.force_encoding(::Encoding::BINARY)
|
53
|
+
else
|
54
|
+
raise NotImplementedError, "Unsupported #{i.proto_ident} on #{__method__} for #{const.name}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
Google::Protobuf::Any.new(type_url: "/#{i.proto_full_ident}", value: value)
|
59
|
+
end
|
60
|
+
|
61
|
+
def to_rb_from(pb)
|
62
|
+
i = Protod::Interpreter.find_by_proto(pb.type_url.split('/').last)
|
63
|
+
|
64
|
+
raise NotImplementedError, "Not found the interpreter for #{pb.type_url}. You can define a interpreter using Protod::Interpreter.register_for" unless i
|
65
|
+
|
66
|
+
value = if i.pb_const
|
67
|
+
i.pb_const.decode(pb.value)
|
68
|
+
elsif i.proto_ident
|
69
|
+
case i.proto_ident
|
70
|
+
when 'bool'
|
71
|
+
pb.value.unpack('C').first == 1
|
72
|
+
when 'double'
|
73
|
+
pb.value.unpack('E').first
|
74
|
+
when 'sint64', 'sint32'
|
75
|
+
v = varint_decode(StringIO.new(pb.value))
|
76
|
+
|
77
|
+
(v & 1).zero? ? v >> 1 : ~v >> 1
|
78
|
+
when 'uint64'
|
79
|
+
varint_decode(StringIO.new(pb.value))
|
80
|
+
when 'string', 'bytes'
|
81
|
+
pb.value.dup.force_encoding(i.proto_ident == 'string' ? ::Encoding::UTF_8 : ::Encoding::BINARY)
|
82
|
+
else
|
83
|
+
raise NotImplementedError, "Unsupported #{i.proto_ident} on #{__method__} for #{const.name}"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
i.to_rb_from(value)
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
# ref: https://github.com/ruby-protobuf/protobuf/blob/b866fc5667226d0582f328ab24c648e578c5a380/lib/protobuf/varint_pure.rb#L10-L29
|
93
|
+
|
94
|
+
def varint_encode(value)
|
95
|
+
bytes = []
|
96
|
+
until value < 128
|
97
|
+
bytes << (0x80 | (value & 0x7f))
|
98
|
+
value >>= 7
|
99
|
+
end
|
100
|
+
(bytes << value).pack('C*')
|
101
|
+
end
|
102
|
+
|
103
|
+
def varint_decode(stream)
|
104
|
+
value = index = 0
|
105
|
+
begin
|
106
|
+
byte = stream.readbyte
|
107
|
+
value |= (byte & 0x7f) << (7 * index)
|
108
|
+
index += 1
|
109
|
+
end while (byte & 0x80).nonzero?
|
110
|
+
value
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
Interpreter.register_for(*%w[RBS::Types::Bases::Void RBS::Types::Bases::Nil], force: false, ignore: true) do
|
115
|
+
def proto_ident
|
116
|
+
nil
|
117
|
+
end
|
118
|
+
|
119
|
+
def to_pb_from(rb)
|
120
|
+
nil
|
121
|
+
end
|
122
|
+
|
123
|
+
def to_rb_from(pb)
|
124
|
+
nil
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
Interpreter.register_for('TrueClass', 'FalseClass', 'RBS::Types::Bases::Bool', force: false, ignore: true) do
|
129
|
+
def proto_ident
|
130
|
+
'bool'
|
131
|
+
end
|
132
|
+
|
133
|
+
def to_pb_from(rb)
|
134
|
+
rb ? true : false
|
135
|
+
end
|
136
|
+
|
137
|
+
def to_rb_from(pb)
|
138
|
+
pb
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
Interpreter.register_for('Numeric', force: false, ignore: true) do
|
143
|
+
def proto_ident
|
144
|
+
'double'
|
145
|
+
end
|
146
|
+
|
147
|
+
def to_pb_from(rb)
|
148
|
+
rb.to_f
|
149
|
+
end
|
150
|
+
|
151
|
+
def to_rb_from(pb)
|
152
|
+
pb
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
Interpreter.register_for('Integer', force: false, ignore: true) do
|
157
|
+
def proto_ident
|
158
|
+
'sint64'
|
159
|
+
end
|
160
|
+
|
161
|
+
def to_pb_from(rb)
|
162
|
+
rb.to_i
|
163
|
+
end
|
164
|
+
|
165
|
+
def to_rb_from(pb)
|
166
|
+
pb
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
Interpreter.register_for('Fixnum', force: false, ignore: true) do
|
171
|
+
def proto_ident
|
172
|
+
'sint32'
|
173
|
+
end
|
174
|
+
|
175
|
+
def to_pb_from(rb)
|
176
|
+
rb.to_i
|
177
|
+
end
|
178
|
+
|
179
|
+
def to_rb_from(pb)
|
180
|
+
pb
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
Interpreter.register_for('String', force: false, ignore: true) do
|
185
|
+
def proto_ident
|
186
|
+
'string'
|
187
|
+
end
|
188
|
+
|
189
|
+
def to_pb_from(rb)
|
190
|
+
rb.to_s
|
191
|
+
end
|
192
|
+
|
193
|
+
def to_rb_from(pb)
|
194
|
+
pb
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
Interpreter.register_for('BigDecimal', parent: google_type, path: 'google/type/decimal.proto', force: false, ignore: true) do
|
199
|
+
def proto_ident
|
200
|
+
'Decimal'
|
201
|
+
end
|
202
|
+
|
203
|
+
def to_pb_from(rb)
|
204
|
+
Google::Type::Decimal.new(value: rb.to_s)
|
205
|
+
end
|
206
|
+
|
207
|
+
def to_rb_from(pb)
|
208
|
+
BigDecimal(pb.value)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
Interpreter.register_for('Date', parent: google_type, path: 'google/type/date.proto', force: false, ignore: true) do
|
213
|
+
def proto_ident
|
214
|
+
'Date'
|
215
|
+
end
|
216
|
+
|
217
|
+
def to_pb_from(rb)
|
218
|
+
Google::Type::Date.new(year: rb.year, month: rb.month, day: rb.day)
|
219
|
+
end
|
220
|
+
|
221
|
+
def to_rb_from(pb)
|
222
|
+
::Date.new(pb.year, pb.month, pb.day)
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
Interpreter.register_for('Time', 'DateTime', 'ActiveSupport::TimeWithZone', parent: google_protobuf, path: 'google/protobuf/timestamp.proto', force: false, ignore: true) do
|
227
|
+
def proto_ident
|
228
|
+
'Timestamp'
|
229
|
+
end
|
230
|
+
|
231
|
+
def to_pb_from(rb)
|
232
|
+
Google::Protobuf::Timestamp.new(seconds: rb.to_i, nanos: rb.nsec)
|
233
|
+
end
|
234
|
+
|
235
|
+
def to_rb_from(pb)
|
236
|
+
::Time.zone.at(pb.seconds, pb.nanos, :nanosecond)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
Interpreter.register_for('Protod::Types::Binary', force: false, ignore: true) do
|
241
|
+
def proto_ident
|
242
|
+
'bytes'
|
243
|
+
end
|
244
|
+
|
245
|
+
def to_pb_from(rb)
|
246
|
+
rb
|
247
|
+
end
|
248
|
+
|
249
|
+
def to_rb_from(pb)
|
250
|
+
pb
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
Interpreter.register_for('Protod::Types::UnsignedInteger', force: false, ignore: true) do
|
255
|
+
def proto_ident
|
256
|
+
'uint64'
|
257
|
+
end
|
258
|
+
|
259
|
+
def to_pb_from(rb)
|
260
|
+
rb.to_i
|
261
|
+
end
|
262
|
+
|
263
|
+
def to_rb_from(pb)
|
264
|
+
pb
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
Interpreter.register_for('Array', force: false, ignore: true) do
|
269
|
+
def proto_message
|
270
|
+
Protod::Proto::Message.new(
|
271
|
+
ident: proto_ident,
|
272
|
+
fields: [Protod::Proto::Field.build_from('RBS::Types::Bases::Any', ident: 'values', optional: true, repeated: true)]
|
273
|
+
)
|
274
|
+
end
|
275
|
+
|
276
|
+
def to_pb_from(rb)
|
277
|
+
i = Protod::Interpreter.find_by('RBS::Types::Bases::Any')
|
278
|
+
|
279
|
+
pb_const.new(values: rb.map { i.to_pb_from(_1) })
|
280
|
+
end
|
281
|
+
|
282
|
+
def to_rb_from(pb)
|
283
|
+
i = Protod::Interpreter.find_by('RBS::Types::Bases::Any')
|
284
|
+
|
285
|
+
pb.values.map { i.to_rb_from(_1) }
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
Interpreter.register_for('Hash', parent: google_protobuf, path: 'google/protobuf/struct.proto', force: false, ignore: true) do
|
290
|
+
def proto_ident
|
291
|
+
'Struct'
|
292
|
+
end
|
293
|
+
|
294
|
+
def to_pb_from(rb)
|
295
|
+
Google::Protobuf::Struct.from_hash(rb)
|
296
|
+
end
|
297
|
+
|
298
|
+
def to_rb_from(pb)
|
299
|
+
pb.to_h
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
Interpreter.register_for(*['Data', 'Struct'].select(&:safe_constantize), force: false, ignore: true) do
|
304
|
+
def proto_message
|
305
|
+
Protod::Proto::Message.new(
|
306
|
+
ident: proto_ident,
|
307
|
+
fields: const.memebers.map do |name|
|
308
|
+
Protod::Proto::Field.build_from('RBS::Types::Bases::Any', ident: name, optional: true)
|
309
|
+
end
|
310
|
+
)
|
311
|
+
end
|
312
|
+
|
313
|
+
def to_pb_from(rb)
|
314
|
+
attributes = const.members.map { [_1, rb.public_send(_1)] }.to_h
|
315
|
+
|
316
|
+
pb_const.new(**attributes)
|
317
|
+
end
|
318
|
+
|
319
|
+
def to_rb_from(pb)
|
320
|
+
attributes = const.members.map { [_1, pb.public_send(_1)] }.to_h
|
321
|
+
|
322
|
+
const.new(**attributes)
|
323
|
+
end
|
324
|
+
end
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
@@ -0,0 +1,206 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Protod
|
4
|
+
class Interpreter
|
5
|
+
class Rpc
|
6
|
+
def self.setup!
|
7
|
+
Interpreter.register_for('Protod::Rpc::Request::Base', force: false, ignore: true) do
|
8
|
+
def proto_ident
|
9
|
+
"Request"
|
10
|
+
end
|
11
|
+
|
12
|
+
def proto_message
|
13
|
+
m = Protod.rbs_method_type_for(const.ruby_ident)
|
14
|
+
|
15
|
+
fields = [
|
16
|
+
if const.ruby_ident.singleton
|
17
|
+
nil
|
18
|
+
else
|
19
|
+
Protod::Proto::Field.build_from(const.ruby_ident.const_name, ident: const.ruby_ident.const_name)
|
20
|
+
end,
|
21
|
+
*m.type.required_positionals.map do |arg|
|
22
|
+
raise ArgumentError, "Unsupported non-named argument : #{const.ruby_ident}" unless arg.name
|
23
|
+
|
24
|
+
Protod::Proto::Field.build_from_rbs(arg.type, on: const.ruby_ident, ident: arg.name)
|
25
|
+
end,
|
26
|
+
*m.type.optional_positionals.map do |arg|
|
27
|
+
raise ArgumentError, "Unsupported non-named argument : #{const.ruby_ident}" unless arg.name
|
28
|
+
|
29
|
+
Protod::Proto::Field.build_from_rbs(arg.type, on: const.ruby_ident, ident: arg.name, required: false, optional: true)
|
30
|
+
end,
|
31
|
+
m.type.rest_positionals&.then do |arg|
|
32
|
+
raise ArgumentError, "Unsupported non-named argument : #{const.ruby_ident}" unless arg.name
|
33
|
+
|
34
|
+
Protod::Proto::Field.build_from_rbs(arg.type, on: const.ruby_ident, ident: arg.name, as_rest: true, required: false, repeated: true)
|
35
|
+
end,
|
36
|
+
*m.type.required_keywords.map do |name, arg|
|
37
|
+
Protod::Proto::Field.build_from_rbs(arg.type, on: const.ruby_ident, ident: name, as_keyword: true)
|
38
|
+
end,
|
39
|
+
*m.type.optional_keywords.map do |name, arg|
|
40
|
+
Protod::Proto::Field.build_from_rbs(arg.type, on: const.ruby_ident, ident: name, as_keyword: true, required: false, optional: true)
|
41
|
+
end,
|
42
|
+
m.type.rest_keywords&.then do |arg|
|
43
|
+
# Not supporting for now because it's complicated and I can't understand the specification of ruby and rbs about this completely
|
44
|
+
raise ArgumentError, "Unsupported rest keywords argument : #{const.ruby_ident}"
|
45
|
+
|
46
|
+
raise ArgumentError, "Unsupported non-named argument : #{const.ruby_ident}" unless arg.name
|
47
|
+
|
48
|
+
Protod::Proto::Field.build_from_rbs(arg.type, on: const.ruby_ident, ident: arg.name, as_keyword: true, as_rest: true, required: false, optional: true)
|
49
|
+
end
|
50
|
+
].compact
|
51
|
+
|
52
|
+
Protod::Proto::Message.new(ident: proto_ident, fields: fields)
|
53
|
+
end
|
54
|
+
|
55
|
+
def to_rb_from(pb)
|
56
|
+
args = []
|
57
|
+
kwargs = {}
|
58
|
+
receiver_id = nil
|
59
|
+
|
60
|
+
proto_message.fields.each.with_index do |f, i|
|
61
|
+
next if Protod::Proto.omits_field?(pb, f.ident) && f.required.!
|
62
|
+
|
63
|
+
f_pb = pb.public_send(f.ident)
|
64
|
+
|
65
|
+
receiver_id = f_pb.protod__object_id if i == 0 && f_pb.respond_to?(:protod__object_id)
|
66
|
+
|
67
|
+
arg = if f.repeated
|
68
|
+
f_pb&.map { f.interpreter.to_rb_from(_1) }
|
69
|
+
else
|
70
|
+
f.interpreter.to_rb_from(f_pb)
|
71
|
+
end
|
72
|
+
|
73
|
+
if f.as_keyword
|
74
|
+
kwargs[f.ident.to_sym] = arg
|
75
|
+
elsif f.as_rest
|
76
|
+
args = [*args, *arg]
|
77
|
+
else
|
78
|
+
args.push(arg)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
receiver = if const.ruby_ident.singleton
|
83
|
+
const.ruby_ident.const_name.constantize
|
84
|
+
else
|
85
|
+
args.shift
|
86
|
+
end
|
87
|
+
|
88
|
+
Protod::Rpc::Handler::RequestPacket.new(receiver_id: receiver_id, receiver: receiver, args: args, kwargs: kwargs)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
Interpreter.register_for('Protod::Rpc::Response::Base', force: false, ignore: true) do
|
93
|
+
def proto_ident
|
94
|
+
"Response"
|
95
|
+
end
|
96
|
+
|
97
|
+
def proto_message
|
98
|
+
m = Protod.rbs_method_type_for(const.ruby_ident)
|
99
|
+
|
100
|
+
fields = if m.type.return_type
|
101
|
+
[Protod::Proto::Field.build_from_rbs(m.type.return_type, on: const.ruby_ident, ident: 'value')]
|
102
|
+
else
|
103
|
+
[]
|
104
|
+
end
|
105
|
+
|
106
|
+
Protod::Proto::Message.new(ident: proto_ident, fields: fields)
|
107
|
+
end
|
108
|
+
|
109
|
+
def to_pb_from(rb)
|
110
|
+
return unless rb
|
111
|
+
|
112
|
+
f = proto_message.find('value', by: :ident, as: 'Protod::Proto::Field')
|
113
|
+
|
114
|
+
return unless f
|
115
|
+
|
116
|
+
pb_maker = ->(v) do
|
117
|
+
f.interpreter.to_pb_from(v).tap do
|
118
|
+
_1.protod__object_id = v.object_id.to_s if _1.respond_to?(:protod__object_id)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
pb = f.repeated ? rb.map { pb_maker.call(_1) } : pb_maker.call(rb)
|
123
|
+
|
124
|
+
pb_const.new(value: pb)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
Interpreter.register_for('Protod::Rpc::Request::Receiver::Base', force: false, ignore: true) do
|
129
|
+
def proto_ident
|
130
|
+
"Request"
|
131
|
+
end
|
132
|
+
|
133
|
+
def proto_message
|
134
|
+
f = Protod::Proto::Oneof.new(ident: Protod::Rpc::Request::Receiver::ONEOF_NAME)
|
135
|
+
|
136
|
+
[
|
137
|
+
*proto_fields,
|
138
|
+
*const.ruby_ident.const_name.constantize.ancestors.drop(1).filter_map do |c|
|
139
|
+
r = Protod::Rpc::Request.find_by(c.name) if c.name
|
140
|
+
Interpreter.find_by(r) if r
|
141
|
+
end.flat_map(&:proto_fields)
|
142
|
+
].each { f.find_or_push(_1, by: :ident, into: :fields) }
|
143
|
+
|
144
|
+
Protod::Proto::Message.new(ident: proto_ident, fields: [f])
|
145
|
+
end
|
146
|
+
|
147
|
+
def to_rb_from(pb)
|
148
|
+
procedure = pb.public_send(Protod::Rpc::Request::Receiver::ONEOF_NAME)
|
149
|
+
|
150
|
+
raise Protod::Rpc::Handler::InvalidArgument, "Not set procedure" unless procedure
|
151
|
+
|
152
|
+
f = proto_message
|
153
|
+
.find(Protod::Rpc::Request::Receiver::ONEOF_NAME, by: :ident, as: 'Protod::Proto::Oneof')
|
154
|
+
.find(procedure, by: :ident, as: 'Protod::Proto::Field')
|
155
|
+
|
156
|
+
raise Protod::Rpc::Handler::InvalidArgument, "Not found acceptable procedure : #{procedure}" unless f
|
157
|
+
|
158
|
+
f.interpreter.to_rb_from(pb.public_send(procedure)).tap { _1.procedure = procedure }
|
159
|
+
end
|
160
|
+
|
161
|
+
def proto_fields
|
162
|
+
const.procedures.map do |ruby_ident|
|
163
|
+
Protod::Proto::Field.build_from(Protod::Rpc::Request.find_by(ruby_ident), ident: ruby_ident.method_name)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
Interpreter.register_for('Protod::Rpc::Response::Receiver::Base', force: false, ignore: true) do
|
169
|
+
def proto_ident
|
170
|
+
"Response"
|
171
|
+
end
|
172
|
+
|
173
|
+
def proto_message
|
174
|
+
f = Protod::Proto::Oneof.new(ident: Protod::Rpc::Response::Receiver::ONEOF_NAME)
|
175
|
+
|
176
|
+
[
|
177
|
+
*proto_fields,
|
178
|
+
*const.ruby_ident.const_name.constantize.ancestors.drop(1).filter_map do |c|
|
179
|
+
r = Protod::Rpc::Response.find_by(c.name) if c.name
|
180
|
+
Interpreter.find_by(r) if r
|
181
|
+
end.flat_map(&:proto_fields)
|
182
|
+
].each { f.find_or_push(_1, by: :ident, into: :fields) }
|
183
|
+
|
184
|
+
Protod::Proto::Message.new(ident: proto_ident, fields: [f])
|
185
|
+
end
|
186
|
+
|
187
|
+
def to_pb_from(packet)
|
188
|
+
f = proto_message
|
189
|
+
.find(Protod::Rpc::Response::Receiver::ONEOF_NAME, by: :ident, as: 'Protod::Proto::Oneof')
|
190
|
+
.find(packet.procedure, by: :ident, as: 'Protod::Proto::Field')
|
191
|
+
|
192
|
+
pb = f.interpreter.to_pb_from(packet.object)
|
193
|
+
|
194
|
+
pb_const.new(packet.procedure.to_sym => pb)
|
195
|
+
end
|
196
|
+
|
197
|
+
def proto_fields
|
198
|
+
const.procedures.map do |ruby_ident|
|
199
|
+
Protod::Proto::Field.build_from(Protod::Rpc::Response.find_by(ruby_ident), ident: ruby_ident.method_name)
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
@@ -0,0 +1,180 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Protod
|
4
|
+
class Interpreter
|
5
|
+
class << self
|
6
|
+
def register_for(*const_names, with: nil, parent: nil, path: nil, force: true, ignore: false, &body)
|
7
|
+
base = if with
|
8
|
+
find_by(with)&.class or raise ArgumentError, "Not found the interpreter for #{with}"
|
9
|
+
else
|
10
|
+
Base
|
11
|
+
end
|
12
|
+
|
13
|
+
Class.new(base, &body).tap do |interpreter_const|
|
14
|
+
const_names.each do
|
15
|
+
c = Protod::RubyIdent.absolute_of(_1).safe_constantize
|
16
|
+
|
17
|
+
raise ArgumentError, "Not found to constantize for #{_1}" unless c
|
18
|
+
|
19
|
+
next if map.key?(c) && ignore
|
20
|
+
|
21
|
+
raise ArgumentError, "Interpreter already regsitered for #{c.name}" if map.key?(c) && !force
|
22
|
+
|
23
|
+
map[c] = { const: interpreter_const, parent: parent, path: path }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def find_by(const_or_name, with_register_from_ancestor: false)
|
29
|
+
const_name = const_or_name.is_a?(::String) ? Protod::RubyIdent.absolute_of(const_or_name) : nil
|
30
|
+
const = const_or_name.is_a?(::String) ? const_or_name.safe_constantize : const_or_name
|
31
|
+
entryables = const.respond_to?(:ancestors) ? const.ancestors : [const]
|
32
|
+
entry_const = entryables.compact.find { map.key?(_1) }
|
33
|
+
entry = entry_const && map.fetch(entry_const)
|
34
|
+
|
35
|
+
return unless entry
|
36
|
+
|
37
|
+
unless entry.is_a?(Base)
|
38
|
+
entry = map[entry_const] = entry.fetch(:const).new(entry_const, **entry.except(:const)).tap do
|
39
|
+
_1.extend SkipNilAbility
|
40
|
+
_1.extend ProtoMessageCacheable
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
return entry unless with_register_from_ancestor
|
45
|
+
|
46
|
+
unless map[const].is_a?(Base)
|
47
|
+
map[const] = entry.class.new(const, parent: entry.parent, path: entry.path).tap do
|
48
|
+
_1.extend SkipNilAbility
|
49
|
+
_1.extend ProtoMessageCacheable
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
map.fetch(const)
|
54
|
+
end
|
55
|
+
|
56
|
+
def find_by_proto(full_ident)
|
57
|
+
raise NotImplementedError, "You need to call Protod::Interpreter.setup_reverse_lookup! before" unless @reverse_map
|
58
|
+
|
59
|
+
@reverse_map[full_ident]
|
60
|
+
end
|
61
|
+
|
62
|
+
def keys
|
63
|
+
map.keys
|
64
|
+
end
|
65
|
+
|
66
|
+
def reverse_keys
|
67
|
+
@reverse_map&.keys
|
68
|
+
end
|
69
|
+
|
70
|
+
def clear!
|
71
|
+
@map = nil
|
72
|
+
@reverse_map = nil
|
73
|
+
end
|
74
|
+
|
75
|
+
def setup_reverse_lookup!
|
76
|
+
@reverse_map = keys.map { find_by(_1) }.uniq.map { [_1.proto_full_ident, _1] }.to_h
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def map
|
82
|
+
@map ||= {}
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
module SkipNilAbility
|
87
|
+
def to_pb_from(rb)
|
88
|
+
return if rb.nil?
|
89
|
+
super
|
90
|
+
end
|
91
|
+
|
92
|
+
def to_rb_from(pb)
|
93
|
+
return if pb.nil?
|
94
|
+
super
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
module ProtoMessageCacheable
|
99
|
+
def proto_message
|
100
|
+
if @_proto_message.nil?
|
101
|
+
@_proto_message = super&.tap do |m|
|
102
|
+
next if const.ancestors.any? { _1.in?([Protod::Rpc::Request::Base, Protod::Rpc::Response::Base]) }
|
103
|
+
|
104
|
+
m.fields.unshift(Protod::Proto::Field.build_from('::String', ident: 'protod__object_id', optional: true))
|
105
|
+
end
|
106
|
+
|
107
|
+
@_proto_message = false unless @_proto_message
|
108
|
+
end
|
109
|
+
|
110
|
+
@_proto_message ? @_proto_message : nil
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
class Base
|
115
|
+
attr_reader :const, :parent, :path
|
116
|
+
|
117
|
+
def initialize(const, parent: nil, path: nil)
|
118
|
+
@const = const
|
119
|
+
@parent = parent
|
120
|
+
@path = path
|
121
|
+
end
|
122
|
+
|
123
|
+
def ==(other)
|
124
|
+
const == other.const && parent == other.parent && path == other.path
|
125
|
+
end
|
126
|
+
|
127
|
+
def bindable?
|
128
|
+
return false unless proto_message
|
129
|
+
return false if bound?
|
130
|
+
true
|
131
|
+
end
|
132
|
+
|
133
|
+
def bound?
|
134
|
+
return false unless proto_message
|
135
|
+
|
136
|
+
parent.nil?.!
|
137
|
+
end
|
138
|
+
|
139
|
+
def set_parent(v)
|
140
|
+
raise ArgumentError, "Can't set parent #{v.full_ident} for #{proto_ident} as #{const.name} : already set #{parent.full_ident}" unless parent.nil?
|
141
|
+
@parent = v
|
142
|
+
end
|
143
|
+
|
144
|
+
def package
|
145
|
+
return parent if parent.is_a?(Protod::Proto::Package)
|
146
|
+
|
147
|
+
parent&.ancestor_as(Protod::Proto::Package)
|
148
|
+
end
|
149
|
+
|
150
|
+
def proto_path
|
151
|
+
@path || package&.proto_path
|
152
|
+
end
|
153
|
+
|
154
|
+
def proto_full_ident
|
155
|
+
[parent&.full_ident, proto_ident].compact.join('.').presence
|
156
|
+
end
|
157
|
+
|
158
|
+
def proto_ident
|
159
|
+
Protod::Proto::Ident.build_from(const.name)
|
160
|
+
end
|
161
|
+
|
162
|
+
def proto_message
|
163
|
+
nil
|
164
|
+
end
|
165
|
+
|
166
|
+
def pb_const
|
167
|
+
proto_message&.pb_const ||
|
168
|
+
Google::Protobuf::DescriptorPool.generated_pool.lookup(proto_full_ident)&.msgclass
|
169
|
+
end
|
170
|
+
|
171
|
+
def to_pb_from(rb)
|
172
|
+
raise NotImplementedError, "You need to implement #{__method__} for #{const.name}"
|
173
|
+
end
|
174
|
+
|
175
|
+
def to_rb_from(pb)
|
176
|
+
raise NotImplementedError, "You need to implement #{__method__} for #{const.name}"
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|