capnp 0.0.1

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