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.
@@ -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