capnp 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/capnpc-ruby +15 -0
- data/lib/capnp/generator/generator.rb +568 -0
- data/lib/capnp/generator/schema.capnp.rb +1327 -0
- data/lib/capnp/generator.rb +4 -0
- data/lib/capnp/runtime/buffer/buffer.rb +62 -0
- data/lib/capnp/runtime/buffer/io_buffer.rb +85 -0
- data/lib/capnp/runtime/buffer/sliceable_buffer.rb +16 -0
- data/lib/capnp/runtime/buffer/string_buffer.rb +88 -0
- data/lib/capnp/runtime/list/buffer/data.rb +19 -0
- data/lib/capnp/runtime/list/buffer/list.rb +100 -0
- data/lib/capnp/runtime/list/buffer/numeric.rb +103 -0
- data/lib/capnp/runtime/list/buffer/string.rb +19 -0
- data/lib/capnp/runtime/list/buffer/struct.rb +47 -0
- data/lib/capnp/runtime/list/list.rb +42 -0
- data/lib/capnp/runtime/list/object/list.rb +26 -0
- data/lib/capnp/runtime/list/object/string.rb +26 -0
- data/lib/capnp/runtime/list/string.rb +22 -0
- data/lib/capnp/runtime/message/flat_message.rb +21 -0
- data/lib/capnp/runtime/message/message.rb +62 -0
- data/lib/capnp/runtime/message/stream_message.rb +50 -0
- data/lib/capnp/runtime/reference.rb +100 -0
- data/lib/capnp/runtime/segment.rb +89 -0
- data/lib/capnp/runtime/struct.rb +159 -0
- data/lib/capnp/runtime.rb +3 -0
- data/lib/capnp/version.rb +5 -0
- data/lib/capnp.rb +14 -0
- metadata +200 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 71ef797c99d9b126f104b4d4ea36c776ca8dd870d6fc721314710e8e74b7f133
|
4
|
+
data.tar.gz: 96cba73c43bd3d9656f88c21a9c7b745bac8cdeb66cee4ba84ad90ba63bdf573
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 284eefe7ac8c07d0eb9c390d2efdeef4d26464a095b13d87aa568c8da35c5ffb228d2a33e078769fe526861782de066281ca5d6405e30d63657a64416fd3b805
|
7
|
+
data.tar.gz: 9726a5775467dd15e6d2455de023d5d7743a59c6475564b63e3903edb130161883bc6cdc3825820fa969db93477b3ec90502909c52168cfad94fb679eb635ffd
|
data/bin/capnpc-ruby
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# typed: strict
|
3
|
+
|
4
|
+
require "capnp"
|
5
|
+
require "capnp/generator"
|
6
|
+
|
7
|
+
begin
|
8
|
+
buffer = Capnp::IOBuffer.new(IO::Buffer.for(STDIN.read))
|
9
|
+
message = Capnp::StreamMessage.new(buffer)
|
10
|
+
generator = Capnp::Generator.new(message.root)
|
11
|
+
generator.generate
|
12
|
+
rescue => e
|
13
|
+
warn "#{e.class}: #{e.message}"
|
14
|
+
e.backtrace.to_a.reject { |line| line.include?("/gems/sorbet-runtime-") }.each { |line| warn(line) }
|
15
|
+
end
|
@@ -0,0 +1,568 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
3
|
+
require "sorbet-runtime"
|
4
|
+
require "capnp"
|
5
|
+
require_relative "schema.capnp"
|
6
|
+
|
7
|
+
class Capnp::Generator
|
8
|
+
extend T::Sig
|
9
|
+
|
10
|
+
sig { params(request_ref: Capnp::Reference).void }
|
11
|
+
def initialize(request_ref)
|
12
|
+
request = Schema::CodeGeneratorRequest.from_pointer(request_ref)
|
13
|
+
raise "Invalid CodeGeneratorRequest" if request.nil?
|
14
|
+
|
15
|
+
nodes = request.nodes
|
16
|
+
raise "No nodes found" if nodes.nil?
|
17
|
+
|
18
|
+
requested_files = request.requested_files
|
19
|
+
raise "No requested files found" if requested_files.nil?
|
20
|
+
|
21
|
+
requested_file_ids = requested_files.map(&:id)
|
22
|
+
|
23
|
+
# Build a hash of nodes by id and gather all requested file nodes
|
24
|
+
@nodes_by_id = T.let({}, T::Hash[Integer, Schema::Node])
|
25
|
+
@files = T.let([], T::Array[Schema::Node])
|
26
|
+
nodes.each do |node|
|
27
|
+
@nodes_by_id[node.id] = node
|
28
|
+
if node.is_file? && requested_file_ids.include?(node.id)
|
29
|
+
@files << node
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Gather all nodes that will become classes
|
34
|
+
@node_to_class_path = T.let({}, T::Hash[Integer, T::Array[String]])
|
35
|
+
@files.each do |file|
|
36
|
+
name = file_to_module_name(file)
|
37
|
+
@node_to_class_path.merge!(find_classes(file, [name]))
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
sig { params(node: Schema::Node, path: T::Array[String]).returns(T::Hash[Integer, T::Array[String]]) }
|
42
|
+
def find_classes(node, path)
|
43
|
+
# Skip constants and annotations
|
44
|
+
return {} if node.is_const? || node.is_annotation?
|
45
|
+
|
46
|
+
result = T.let({node.id => path}, T::Hash[Integer, T::Array[String]])
|
47
|
+
|
48
|
+
nested_nodes = node.nested_nodes
|
49
|
+
return result if nested_nodes.nil?
|
50
|
+
|
51
|
+
# Recurse into nested nodes
|
52
|
+
nested_nodes.each do |nestednode|
|
53
|
+
name = nestednode.name&.to_s
|
54
|
+
raise "Node without a name" if name.nil?
|
55
|
+
|
56
|
+
new_path = path + [name]
|
57
|
+
result.merge!(find_classes(@nodes_by_id.fetch(nestednode.id), new_path))
|
58
|
+
end
|
59
|
+
|
60
|
+
result
|
61
|
+
end
|
62
|
+
|
63
|
+
# Convert from camelCase to CapitalCase
|
64
|
+
sig { params(name: String).returns(String) }
|
65
|
+
def class_name(name) = "#{name[0]&.upcase}#{name[1..]}"
|
66
|
+
|
67
|
+
# Convert from camelCase to snake_case
|
68
|
+
sig { params(name: String).returns(String) }
|
69
|
+
def method_name(name) = name.gsub(/([^A-Z])([A-Z]+)/, '\1_\2').downcase
|
70
|
+
|
71
|
+
# Convert from camelCase to SCREAMING_SNAKE_CASE
|
72
|
+
sig { params(name: String).returns(String) }
|
73
|
+
def const_name(name) = name.gsub(/([^A-Z])([A-Z]+)/, '\1_\2').upcase
|
74
|
+
|
75
|
+
sig { params(file: Schema::Node).returns(String) }
|
76
|
+
def file_to_module_name(file) = class_name(file.display_name&.to_s&.split("/")&.last&.sub(".capnp", "") || "")
|
77
|
+
|
78
|
+
sig { void }
|
79
|
+
def generate
|
80
|
+
@files.each do |file|
|
81
|
+
nested_nodes = file.nested_nodes
|
82
|
+
next "" if nested_nodes.nil?
|
83
|
+
|
84
|
+
nested_nodes_code = nested_nodes.flat_map do |nestednode|
|
85
|
+
name = nestednode.name&.to_s
|
86
|
+
raise "Node without a name" if name.nil?
|
87
|
+
node = @nodes_by_id[nestednode.id]
|
88
|
+
raise "Node not found" if node.nil?
|
89
|
+
process_node(name, node)
|
90
|
+
end
|
91
|
+
|
92
|
+
code = [
|
93
|
+
"# typed: strict",
|
94
|
+
"",
|
95
|
+
'require "capnp"',
|
96
|
+
'require "sorbet-runtime"',
|
97
|
+
"module #{file_to_module_name(file)}",
|
98
|
+
*nested_nodes_code.map { " #{_1}" }[1..],
|
99
|
+
"end",
|
100
|
+
""
|
101
|
+
].map(&:rstrip).join("\n")
|
102
|
+
|
103
|
+
# TODO: Use RedquestedFile.filename
|
104
|
+
path = "#{file.display_name&.to_s}.rb"
|
105
|
+
File.write(path, code)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
sig { params(name: String, node: Schema::Node).returns(T::Array[String]) }
|
110
|
+
def process_node(name, node)
|
111
|
+
which_val = node.which?
|
112
|
+
case which_val
|
113
|
+
when Schema::Node::Which::Struct
|
114
|
+
process_struct(name, node)
|
115
|
+
when Schema::Node::Which::Enum
|
116
|
+
process_enum(name, node)
|
117
|
+
when Schema::Node::Which::Interface
|
118
|
+
warn "Ignoring interface node"
|
119
|
+
[]
|
120
|
+
when Schema::Node::Which::Const
|
121
|
+
value = node.const.value
|
122
|
+
raise "Const without a value" if value.nil?
|
123
|
+
["#{const_name(name)} = #{process_value(value)}"]
|
124
|
+
when Schema::Node::Which::Annotation
|
125
|
+
warn "Ignoring annotation node"
|
126
|
+
[]
|
127
|
+
when Schema::Node::Which::File
|
128
|
+
raise "Unexpected file node"
|
129
|
+
else
|
130
|
+
T.absurd(which_val)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
sig { params(name: String, node: Schema::Node).returns(T::Array[String]) }
|
135
|
+
def process_struct(name, node)
|
136
|
+
raise "Generic structs are not supported" if node.is_generic
|
137
|
+
|
138
|
+
fields = node.struct.fields
|
139
|
+
raise "No fields found" if fields.nil?
|
140
|
+
|
141
|
+
field_code = fields.sort_by(&:code_order).flat_map do |field|
|
142
|
+
process_field(field)
|
143
|
+
end
|
144
|
+
|
145
|
+
nested_node_code = node.nested_nodes&.flat_map do |nestednode|
|
146
|
+
nested_node_name = nestednode.name&.to_s
|
147
|
+
raise "Node without a name" if nested_node_name.nil?
|
148
|
+
nested_node = @nodes_by_id.fetch(nestednode.id)
|
149
|
+
|
150
|
+
process_node(nested_node_name, nested_node)
|
151
|
+
end
|
152
|
+
nested_node_code ||= []
|
153
|
+
|
154
|
+
name = class_name(name)
|
155
|
+
|
156
|
+
list_class_code = if node.struct.is_group
|
157
|
+
[]
|
158
|
+
else
|
159
|
+
[
|
160
|
+
"",
|
161
|
+
" class List < Capnp::StructList",
|
162
|
+
" Elem = type_member { {fixed: #{name}} }",
|
163
|
+
" sig { override.returns(T.class_of(#{name})) }",
|
164
|
+
" def element_class = #{name}",
|
165
|
+
" end"
|
166
|
+
]
|
167
|
+
end
|
168
|
+
|
169
|
+
# Create Which enum class for unions
|
170
|
+
which_code = if node.struct.discriminant_count.zero?
|
171
|
+
[]
|
172
|
+
else
|
173
|
+
discriminant_offset = node.struct.discriminant_offset * 2
|
174
|
+
enumerants = fields
|
175
|
+
.reject { _1.discriminant_value == Schema::Field::NO_DISCRIMINANT }
|
176
|
+
.sort_by(&:discriminant_value)
|
177
|
+
.map { _1.name&.to_s || "" }
|
178
|
+
[
|
179
|
+
"sig { returns(Which) }",
|
180
|
+
"def which? = Which.from_integer(read_u16(#{discriminant_offset}, 0))",
|
181
|
+
*process_enumeration("Which", enumerants)
|
182
|
+
]
|
183
|
+
end
|
184
|
+
|
185
|
+
# Create to_obj method
|
186
|
+
to_obj_code = create_struct_to_obj(fields)
|
187
|
+
|
188
|
+
[
|
189
|
+
"",
|
190
|
+
"class #{name} < Capnp::Struct",
|
191
|
+
*field_code.map { " #{_1}" },
|
192
|
+
*nested_node_code.map { " #{_1}" },
|
193
|
+
*list_class_code,
|
194
|
+
*which_code.map { " #{_1}" },
|
195
|
+
*to_obj_code.map { " #{_1}" },
|
196
|
+
"end"
|
197
|
+
]
|
198
|
+
end
|
199
|
+
|
200
|
+
sig { params(fields: T::Enumerable[Schema::Field]).returns(T::Array[[String, String]]) }
|
201
|
+
def create_struct_to_obj_assignments(fields)
|
202
|
+
fields.map do |field|
|
203
|
+
name = field.name&.to_s
|
204
|
+
raise "Field without a name" if name.nil?
|
205
|
+
|
206
|
+
mname = method_name(name)
|
207
|
+
|
208
|
+
assignment = if field.is_group?
|
209
|
+
# Group "fields" are treated as nested structs
|
210
|
+
"res[#{mname.inspect}] = #{mname}.to_obj"
|
211
|
+
else
|
212
|
+
# Normal (non-group) fields
|
213
|
+
type = field.slot.type
|
214
|
+
raise "Field without a type" if type.nil?
|
215
|
+
|
216
|
+
case type.which?
|
217
|
+
when Schema::Type::Which::Text, Schema::Type::Which::Data, Schema::Type::Which::List, Schema::Type::Which::Struct
|
218
|
+
"res[#{mname.inspect}] = #{mname}&.to_obj"
|
219
|
+
when Schema::Type::Which::Interface, Schema::Type::Which::AnyPointer
|
220
|
+
warn "Interfaces and AnyPointers cannot be converted to objects"
|
221
|
+
"res[#{mname.inspect}] = #{mname}"
|
222
|
+
else
|
223
|
+
"res[#{mname.inspect}] = #{mname}"
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
[name, assignment]
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
sig { params(fields: T::Enumerable[Schema::Field]).returns(T::Array[String]) }
|
232
|
+
def create_struct_to_obj(fields)
|
233
|
+
# Split up union and non-union fields
|
234
|
+
normal, union = fields
|
235
|
+
.sort_by(&:code_order)
|
236
|
+
.partition { _1.discriminant_value == Schema::Field::NO_DISCRIMINANT }
|
237
|
+
|
238
|
+
# Process normal fields
|
239
|
+
assignments = create_struct_to_obj_assignments(normal).map { " #{_2}" }
|
240
|
+
|
241
|
+
# Process union fields with a case statement
|
242
|
+
union_assignments = if union.empty?
|
243
|
+
[]
|
244
|
+
else
|
245
|
+
whens = create_struct_to_obj_assignments(union).map do |name, assignment|
|
246
|
+
" when Which::#{class_name(name)} then #{assignment}"
|
247
|
+
end
|
248
|
+
[
|
249
|
+
" case which?",
|
250
|
+
*whens,
|
251
|
+
" end"
|
252
|
+
]
|
253
|
+
end
|
254
|
+
|
255
|
+
[
|
256
|
+
"sig { override.returns(Object) }",
|
257
|
+
"def to_obj",
|
258
|
+
" res = {}",
|
259
|
+
*assignments,
|
260
|
+
*union_assignments,
|
261
|
+
" res",
|
262
|
+
"end"
|
263
|
+
]
|
264
|
+
end
|
265
|
+
|
266
|
+
sig { params(field: Schema::Field).returns(T::Array[String]) }
|
267
|
+
def process_field(field)
|
268
|
+
# TODO: Check union discriminant values
|
269
|
+
warn "Ignoring annotations" unless field.annotations&.length.to_i.zero?
|
270
|
+
|
271
|
+
name = field.name&.to_s
|
272
|
+
raise "Field without a name" if name.nil?
|
273
|
+
|
274
|
+
mname = method_name(name)
|
275
|
+
|
276
|
+
getter_def = if field.is_group?
|
277
|
+
group_node = @nodes_by_id.fetch(field.group.type_id)
|
278
|
+
class_name = "Group#{class_name(name)}"
|
279
|
+
group_class_code = process_struct(class_name, group_node)
|
280
|
+
[
|
281
|
+
"sig { returns(#{class_name}) }",
|
282
|
+
"def #{mname} = #{class_name}.new(@data, @data_size, @pointers, @pointers_size)",
|
283
|
+
*group_class_code
|
284
|
+
]
|
285
|
+
else
|
286
|
+
type = field.slot.type
|
287
|
+
raise "Field without a type" if type.nil?
|
288
|
+
|
289
|
+
default_variable = "DEFAULT_#{const_name(name)}"
|
290
|
+
|
291
|
+
which_type = type.which?
|
292
|
+
case which_type
|
293
|
+
when Schema::Type::Which::Void
|
294
|
+
[
|
295
|
+
"sig { returns(NilClass) }",
|
296
|
+
"def #{mname} = nil"
|
297
|
+
]
|
298
|
+
when Schema::Type::Which::Bool
|
299
|
+
default_value = field.slot.default_value&.bool ? "0xFF" : "0x00"
|
300
|
+
offset = field.slot.offset / 8
|
301
|
+
mask = (1 << (field.slot.offset % 8)).to_s(16)
|
302
|
+
[
|
303
|
+
"#{default_variable} = #{field.slot.default_value&.bool == true}",
|
304
|
+
"sig { returns(T::Boolean) }",
|
305
|
+
"def #{mname} = (read_u8(#{offset}, #{default_value}) & 0x#{mask}) != 0"
|
306
|
+
]
|
307
|
+
when Schema::Type::Which::Int8
|
308
|
+
default_value = field.slot.default_value&.int8 || 0
|
309
|
+
[
|
310
|
+
"#{default_variable} = #{default_value}",
|
311
|
+
"sig { returns(Integer) }",
|
312
|
+
"def #{mname} = read_s8(#{field.slot.offset}, #{default_value})"
|
313
|
+
]
|
314
|
+
when Schema::Type::Which::Int16
|
315
|
+
default_value = field.slot.default_value&.int16 || 0
|
316
|
+
offset = field.slot.offset * 2
|
317
|
+
[
|
318
|
+
"#{default_variable} = #{default_value}",
|
319
|
+
"sig { returns(Integer) }",
|
320
|
+
"def #{mname} = read_s16(#{offset}, #{default_value})"
|
321
|
+
]
|
322
|
+
when Schema::Type::Which::Int32
|
323
|
+
default_value = field.slot.default_value&.int32 || 0
|
324
|
+
offset = field.slot.offset * 4
|
325
|
+
[
|
326
|
+
"#{default_variable} = #{default_value}",
|
327
|
+
"sig { returns(Integer) }",
|
328
|
+
"def #{mname} = read_s32(#{offset}, #{default_value})"
|
329
|
+
]
|
330
|
+
when Schema::Type::Which::Int64
|
331
|
+
default_value = field.slot.default_value&.int64 || 0
|
332
|
+
offset = field.slot.offset * 8
|
333
|
+
[
|
334
|
+
"#{default_variable} = #{default_value}",
|
335
|
+
"sig { returns(Integer) }",
|
336
|
+
"def #{mname} = read_s64(#{offset}, #{default_value})"
|
337
|
+
]
|
338
|
+
when Schema::Type::Which::Uint8
|
339
|
+
default_value = field.slot.default_value&.uint8 || 0
|
340
|
+
[
|
341
|
+
"#{default_variable} = #{default_value}",
|
342
|
+
"sig { returns(Integer) }",
|
343
|
+
"def #{mname} = read_u8(#{field.slot.offset}, #{default_value})"
|
344
|
+
]
|
345
|
+
when Schema::Type::Which::Uint16
|
346
|
+
default_value = field.slot.default_value&.uint16 || 0
|
347
|
+
offset = field.slot.offset * 2
|
348
|
+
[
|
349
|
+
"#{default_variable} = #{default_value}",
|
350
|
+
"sig { returns(Integer) }",
|
351
|
+
"def #{mname} = read_u16(#{offset}, #{default_value})"
|
352
|
+
]
|
353
|
+
when Schema::Type::Which::Uint32
|
354
|
+
default_value = field.slot.default_value&.uint32 || 0
|
355
|
+
offset = field.slot.offset * 4
|
356
|
+
[
|
357
|
+
"#{default_variable} = #{default_value}",
|
358
|
+
"sig { returns(Integer) }",
|
359
|
+
"def #{mname} = read_u32(#{offset}, #{default_value})"
|
360
|
+
]
|
361
|
+
when Schema::Type::Which::Uint64
|
362
|
+
default_value = field.slot.default_value&.uint64 || 0
|
363
|
+
offset = field.slot.offset * 8
|
364
|
+
[
|
365
|
+
"#{default_variable} = #{default_value}",
|
366
|
+
"sig { returns(Integer) }",
|
367
|
+
"def #{mname} = read_u64(#{offset}, #{default_value})"
|
368
|
+
]
|
369
|
+
when Schema::Type::Which::Float32
|
370
|
+
default_value = field.slot.default_value&.float32 || 0.0
|
371
|
+
offset = field.slot.offset * 4
|
372
|
+
[
|
373
|
+
"#{default_variable} = #{default_value}",
|
374
|
+
"sig { returns(Float) }",
|
375
|
+
"def #{mname} = read_f32(#{offset}, #{default_value})"
|
376
|
+
]
|
377
|
+
when Schema::Type::Which::Float64
|
378
|
+
default_value = field.slot.default_value&.float64 || 0.0
|
379
|
+
offset = field.slot.offset * 8
|
380
|
+
[
|
381
|
+
"#{default_variable} = #{default_value}",
|
382
|
+
"sig { returns(Float) }",
|
383
|
+
"def #{mname} = read_f64(#{offset}, #{default_value})"
|
384
|
+
]
|
385
|
+
when Schema::Type::Which::Text
|
386
|
+
default_value = field.slot.default_value&.text&.to_s.inspect
|
387
|
+
apply_default = field.slot.had_explicit_default ? " || Capnp::ObjectString.new(#{default_variable})" : ""
|
388
|
+
[
|
389
|
+
"#{default_variable} = #{default_value}",
|
390
|
+
"sig { returns(T.nilable(Capnp::String)) }",
|
391
|
+
"def #{mname} = Capnp::BufferString.from_pointer(read_pointer(#{field.slot.offset}))#{apply_default}"
|
392
|
+
]
|
393
|
+
when Schema::Type::Which::Data
|
394
|
+
default_value = field.slot.default_value&.data&.value.inspect
|
395
|
+
apply_default = field.slot.had_explicit_default ? " || #{default_variable}" : ""
|
396
|
+
[
|
397
|
+
"#{default_variable} = #{default_value}",
|
398
|
+
"sig { returns(T.nilable(Capnp::Data)) }",
|
399
|
+
"def #{mname} = Capnp::Data.from_pointer(read_pointer(#{field.slot.offset}))#{apply_default}"
|
400
|
+
]
|
401
|
+
when Schema::Type::Which::List
|
402
|
+
raise "List default values not supported" if field.slot.had_explicit_default
|
403
|
+
element_class = type.list.element_type
|
404
|
+
raise "List without an element type" if element_class.nil?
|
405
|
+
which_element_type = element_class.which?
|
406
|
+
case which_element_type
|
407
|
+
when Schema::Type::Which::Void
|
408
|
+
raise "Void list elements not supported"
|
409
|
+
when Schema::Type::Which::Bool
|
410
|
+
raise "Bool list elements not supported"
|
411
|
+
when Schema::Type::Which::Int8, Schema::Type::Which::Int16, Schema::Type::Which::Int32, Schema::Type::Which::Int64
|
412
|
+
list_class = "Capnp::SignedIntegerList"
|
413
|
+
element_class = "Integer"
|
414
|
+
when Schema::Type::Which::Uint8, Schema::Type::Which::Uint16, Schema::Type::Which::Uint32, Schema::Type::Which::Uint64
|
415
|
+
list_class = "Capnp::UnsignedIntegerList"
|
416
|
+
element_class = "Integer"
|
417
|
+
when Schema::Type::Which::Float32, Schema::Type::Which::Float64
|
418
|
+
list_class = "Capnp::FloatList"
|
419
|
+
element_class = "Float"
|
420
|
+
when Schema::Type::Which::Text
|
421
|
+
raise "Text list elements not supported"
|
422
|
+
when Schema::Type::Which::Data
|
423
|
+
raise "Data list elements not supported"
|
424
|
+
when Schema::Type::Which::List
|
425
|
+
raise "List list elements not supported"
|
426
|
+
when Schema::Type::Which::Enum
|
427
|
+
raise "Enum list elements not supported"
|
428
|
+
when Schema::Type::Which::Struct
|
429
|
+
raise "List[Struct] default values not supported" if field.slot.had_explicit_default
|
430
|
+
element_class = @node_to_class_path.fetch(element_class.struct.type_id).join("::")
|
431
|
+
list_class = "#{element_class}::List"
|
432
|
+
when Schema::Type::Which::Interface
|
433
|
+
raise "Interface list elements not supported"
|
434
|
+
when Schema::Type::Which::AnyPointer
|
435
|
+
raise "AnyPointer list elements not supported"
|
436
|
+
else
|
437
|
+
T.absurd(which_element_type)
|
438
|
+
end
|
439
|
+
|
440
|
+
[
|
441
|
+
"sig { returns(T.nilable(Capnp::List[#{element_class}])) }",
|
442
|
+
"def #{mname} = #{list_class}.from_pointer(read_pointer(#{field.slot.offset}))"
|
443
|
+
]
|
444
|
+
when Schema::Type::Which::Enum
|
445
|
+
enumerants = @nodes_by_id.fetch(type.enum.type_id).enum.enumerants
|
446
|
+
raise "No enumerants found" if enumerants.nil?
|
447
|
+
|
448
|
+
default_num = field.slot.default_value&.enum || 0
|
449
|
+
default_value = class_name(enumerants[default_num]&.name&.to_s || "")
|
450
|
+
|
451
|
+
offset = field.slot.offset * 2
|
452
|
+
class_path = @node_to_class_path.fetch(type.enum.type_id).join("::")
|
453
|
+
[
|
454
|
+
# TODO: This doesn't work if the enum class is declared after this field
|
455
|
+
"# #{default_variable} = #{class_path}::#{default_value}",
|
456
|
+
"sig { returns(#{class_path}) }",
|
457
|
+
"def #{mname} = #{class_path}.from_integer(read_u16(#{offset}, #{default_num}))"
|
458
|
+
]
|
459
|
+
when Schema::Type::Which::Struct
|
460
|
+
raise "Struct default values not supported" if field.slot.had_explicit_default
|
461
|
+
class_path = @node_to_class_path.fetch(type.struct.type_id).join("::")
|
462
|
+
[
|
463
|
+
"sig { returns(T.nilable(#{class_path})) }",
|
464
|
+
"def #{mname} = #{class_path}.from_pointer(read_pointer(#{field.slot.offset}))"
|
465
|
+
]
|
466
|
+
when Schema::Type::Which::Interface
|
467
|
+
raise "Interface fields not supported"
|
468
|
+
when Schema::Type::Which::AnyPointer
|
469
|
+
raise "Only unconstrained AnyPointers are supported" unless type.any_pointer.is_unconstrained?
|
470
|
+
[
|
471
|
+
"sig { returns(Capnp::Reference) }",
|
472
|
+
"def #{mname} = read_pointer(#{field.slot.offset})"
|
473
|
+
]
|
474
|
+
else
|
475
|
+
T.absurd(which_type)
|
476
|
+
end
|
477
|
+
end
|
478
|
+
|
479
|
+
# Add type checking methods for union fields
|
480
|
+
if field.discriminant_value != Schema::Field::NO_DISCRIMINANT
|
481
|
+
getter_def += [
|
482
|
+
"sig { returns(T::Boolean) }",
|
483
|
+
"def is_#{mname}? = which? == Which::#{class_name(name)}"
|
484
|
+
]
|
485
|
+
end
|
486
|
+
|
487
|
+
getter_def
|
488
|
+
end
|
489
|
+
|
490
|
+
sig { params(name: String, node: Schema::Node).returns(T::Array[String]) }
|
491
|
+
def process_enum(name, node)
|
492
|
+
raise "Nested nodes not supported in enum" unless node.nested_nodes&.length.to_i.zero?
|
493
|
+
raise "Generic structs are not supported" if node.is_generic
|
494
|
+
|
495
|
+
enumerants = node.enum.enumerants
|
496
|
+
raise "No enumerants found" if enumerants.nil?
|
497
|
+
|
498
|
+
# Enumerants are ordered by their numeric value
|
499
|
+
enums = enumerants.map do |enumerant|
|
500
|
+
warn "Ignoring annotations" unless enumerant.annotations&.length.to_i.zero?
|
501
|
+
|
502
|
+
enumerant_name = enumerant.name&.to_s
|
503
|
+
raise "Enumerant without a name" if enumerant_name.nil?
|
504
|
+
enumerant_name
|
505
|
+
end
|
506
|
+
|
507
|
+
process_enumeration(name, enums)
|
508
|
+
end
|
509
|
+
|
510
|
+
sig { params(name: String, enumerants: T::Array[String]).returns(T::Array[String]) }
|
511
|
+
def process_enumeration(name, enumerants)
|
512
|
+
definitions = T.let([], T::Array[String])
|
513
|
+
from_int = T.let([], T::Array[String])
|
514
|
+
|
515
|
+
# Enumerants are ordered by their numeric value
|
516
|
+
enumerants.each_with_index do |enumerant_name, ix|
|
517
|
+
ename = class_name(enumerant_name)
|
518
|
+
definitions << " #{ename} = new(#{enumerant_name.inspect})"
|
519
|
+
from_int << " when #{ix} then #{ename}"
|
520
|
+
end
|
521
|
+
|
522
|
+
# TODO: Define an Capnp::Enum class
|
523
|
+
class_name = class_name(name)
|
524
|
+
[
|
525
|
+
"",
|
526
|
+
"class #{class_name} < T::Enum",
|
527
|
+
" extend T::Sig",
|
528
|
+
" enums do",
|
529
|
+
*definitions,
|
530
|
+
" end",
|
531
|
+
" sig { params(value: Integer).returns(#{class_name}) }",
|
532
|
+
" def self.from_integer(value)",
|
533
|
+
" case value",
|
534
|
+
*from_int,
|
535
|
+
" else raise \"Unknown #{name} value: \#{value}\"",
|
536
|
+
" end",
|
537
|
+
" end",
|
538
|
+
"end"
|
539
|
+
]
|
540
|
+
end
|
541
|
+
|
542
|
+
sig { params(value: Schema::Value).returns(T.any(String, T::Boolean, Numeric, NilClass)) }
|
543
|
+
def process_value(value)
|
544
|
+
which_value = value.which?
|
545
|
+
case which_value
|
546
|
+
when Schema::Value::Which::Void then nil
|
547
|
+
when Schema::Value::Which::Bool then value.bool
|
548
|
+
when Schema::Value::Which::Int8 then value.int8
|
549
|
+
when Schema::Value::Which::Int16 then value.int16
|
550
|
+
when Schema::Value::Which::Int32 then value.int32
|
551
|
+
when Schema::Value::Which::Int64 then value.int64
|
552
|
+
when Schema::Value::Which::Uint8 then value.uint8
|
553
|
+
when Schema::Value::Which::Uint16 then value.uint16
|
554
|
+
when Schema::Value::Which::Uint32 then value.uint32
|
555
|
+
when Schema::Value::Which::Uint64 then value.uint64
|
556
|
+
when Schema::Value::Which::Float32 then value.float32
|
557
|
+
when Schema::Value::Which::Float64 then value.float64
|
558
|
+
when Schema::Value::Which::Text then value.text&.to_s.inspect
|
559
|
+
when Schema::Value::Which::Data then value.data&.value.inspect
|
560
|
+
when Schema::Value::Which::List then raise "List values not supported"
|
561
|
+
when Schema::Value::Which::Enum then value.enum # TODO: Convert to enum class
|
562
|
+
when Schema::Value::Which::Struct then raise "Struct values not supported"
|
563
|
+
when Schema::Value::Which::Interface then nil
|
564
|
+
when Schema::Value::Which::AnyPointer then raise "AnyPointer values not supported"
|
565
|
+
else T.absurd(which_value)
|
566
|
+
end
|
567
|
+
end
|
568
|
+
end
|