hn_api_ruby 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/hn_api_ruby/hn_api.rb +854 -0
- data/hn_api_ruby/libhn_api.dylib +0 -0
- data/hn_api_ruby/test.rb +10 -0
- metadata +45 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 7f1bbfe1d9c841e0bd586243032197e48878db8495d76ed5f58fe6e18abdd857
|
4
|
+
data.tar.gz: 9ccc4522030f04cbfc517ae6ba54dd7c486686926f997e4019fac7d43b7fff4d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f1157b553e0de5ed3fb76b997de222ebd5b6d42169f3b08da8a328a96c5adfdf220701779a74ce110763356e27a95b338744dbc459c5c018cbc0ef0ed735da91
|
7
|
+
data.tar.gz: 1e429cca606fbf9d0d1cbd27f795fe23c6316030dc659b78c9b2f040375274276bb55fb093abebb47da01dac3884fdd9267bee617e1f5e1bc4f6b2fc716052d3
|
@@ -0,0 +1,854 @@
|
|
1
|
+
# This file was autogenerated by some hot garbage in the `uniffi` crate.
|
2
|
+
# Trust me, you don't want to mess with it!
|
3
|
+
|
4
|
+
# Common helper code.
|
5
|
+
#
|
6
|
+
# Ideally this would live in a separate .rb file where it can be unittested etc
|
7
|
+
# in isolation, and perhaps even published as a re-useable package.
|
8
|
+
#
|
9
|
+
# However, it's important that the detils of how this helper code works (e.g. the
|
10
|
+
# way that different builtin types are passed across the FFI) exactly match what's
|
11
|
+
# expected by the rust code on the other side of the interface. In practice right
|
12
|
+
# now that means coming from the exact some version of `uniffi` that was used to
|
13
|
+
# compile the rust component. The easiest way to ensure this is to bundle the Ruby
|
14
|
+
# helpers directly inline like we're doing here.
|
15
|
+
|
16
|
+
require 'ffi'
|
17
|
+
|
18
|
+
module HnApi
|
19
|
+
class RustBuffer < FFI::Struct
|
20
|
+
layout :capacity, :int32,
|
21
|
+
:len, :int32,
|
22
|
+
:data, :pointer
|
23
|
+
|
24
|
+
def self.alloc(size)
|
25
|
+
return HnApi.rust_call(:ffi_hn_api_d6e5_rustbuffer_alloc, size)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.reserve(rbuf, additional)
|
29
|
+
return HnApi.rust_call(:ffi_hn_api_d6e5_rustbuffer_reserve, rbuf, additional)
|
30
|
+
end
|
31
|
+
|
32
|
+
def free
|
33
|
+
HnApi.rust_call(:ffi_hn_api_d6e5_rustbuffer_free, self)
|
34
|
+
end
|
35
|
+
|
36
|
+
def capacity
|
37
|
+
self[:capacity]
|
38
|
+
end
|
39
|
+
|
40
|
+
def len
|
41
|
+
self[:len]
|
42
|
+
end
|
43
|
+
|
44
|
+
def len=(value)
|
45
|
+
self[:len] = value
|
46
|
+
end
|
47
|
+
|
48
|
+
def data
|
49
|
+
self[:data]
|
50
|
+
end
|
51
|
+
|
52
|
+
def to_s
|
53
|
+
"RustBuffer(capacity=#{capacity}, len=#{len}, data=#{data.read_bytes len})"
|
54
|
+
end
|
55
|
+
|
56
|
+
# The allocated buffer will be automatically freed if an error occurs, ensuring that
|
57
|
+
# we don't accidentally leak it.
|
58
|
+
def self.allocWithBuilder
|
59
|
+
builder = RustBufferBuilder.new
|
60
|
+
|
61
|
+
begin
|
62
|
+
yield builder
|
63
|
+
rescue => e
|
64
|
+
builder.discard
|
65
|
+
raise e
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# The RustBuffer will be freed once the context-manager exits, ensuring that we don't
|
70
|
+
# leak it even if an error occurs.
|
71
|
+
def consumeWithStream
|
72
|
+
stream = RustBufferStream.new self
|
73
|
+
|
74
|
+
yield stream
|
75
|
+
|
76
|
+
raise RuntimeError, 'junk data left in buffer after consuming' if stream.remaining != 0
|
77
|
+
ensure
|
78
|
+
free
|
79
|
+
end# The primitive String type.
|
80
|
+
|
81
|
+
def self.allocFromString(value)
|
82
|
+
RustBuffer.allocWithBuilder do |builder|
|
83
|
+
builder.write value.encode('utf-8')
|
84
|
+
return builder.finalize
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def consumeIntoString
|
89
|
+
consumeWithStream do |stream|
|
90
|
+
return stream.read(stream.remaining).force_encoding(Encoding::UTF_8)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# The Record type Story.
|
95
|
+
|
96
|
+
def self.alloc_from_TypeStory(v)
|
97
|
+
RustBuffer.allocWithBuilder do |builder|
|
98
|
+
builder.write_TypeStory(v)
|
99
|
+
return builder.finalize
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def consumeIntoTypeStory
|
104
|
+
consumeWithStream do |stream|
|
105
|
+
return stream.readTypeStory
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# The Record type User.
|
110
|
+
|
111
|
+
def self.alloc_from_TypeUser(v)
|
112
|
+
RustBuffer.allocWithBuilder do |builder|
|
113
|
+
builder.write_TypeUser(v)
|
114
|
+
return builder.finalize
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def consumeIntoTypeUser
|
119
|
+
consumeWithStream do |stream|
|
120
|
+
return stream.readTypeUser
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# The Optional<T> type for u32.
|
125
|
+
|
126
|
+
def self.alloc_from_Optionalu32(v)
|
127
|
+
RustBuffer.allocWithBuilder do |builder|
|
128
|
+
builder.write_Optionalu32(v)
|
129
|
+
return builder.finalize()
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def consumeIntoOptionalu32
|
134
|
+
consumeWithStream do |stream|
|
135
|
+
return stream.readOptionalu32
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# The Optional<T> type for string.
|
140
|
+
|
141
|
+
def self.alloc_from_Optionalstring(v)
|
142
|
+
RustBuffer.allocWithBuilder do |builder|
|
143
|
+
builder.write_Optionalstring(v)
|
144
|
+
return builder.finalize()
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def consumeIntoOptionalstring
|
149
|
+
consumeWithStream do |stream|
|
150
|
+
return stream.readOptionalstring
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
# The Optional<T> type for TypeStory.
|
155
|
+
|
156
|
+
def self.alloc_from_OptionalTypeStory(v)
|
157
|
+
RustBuffer.allocWithBuilder do |builder|
|
158
|
+
builder.write_OptionalTypeStory(v)
|
159
|
+
return builder.finalize()
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def consumeIntoOptionalTypeStory
|
164
|
+
consumeWithStream do |stream|
|
165
|
+
return stream.readOptionalTypeStory
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
# The Optional<T> type for TypeUser.
|
170
|
+
|
171
|
+
def self.alloc_from_OptionalTypeUser(v)
|
172
|
+
RustBuffer.allocWithBuilder do |builder|
|
173
|
+
builder.write_OptionalTypeUser(v)
|
174
|
+
return builder.finalize()
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def consumeIntoOptionalTypeUser
|
179
|
+
consumeWithStream do |stream|
|
180
|
+
return stream.readOptionalTypeUser
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
# The Optional<T> type for Sequenceu32.
|
185
|
+
|
186
|
+
def self.alloc_from_OptionalSequenceu32(v)
|
187
|
+
RustBuffer.allocWithBuilder do |builder|
|
188
|
+
builder.write_OptionalSequenceu32(v)
|
189
|
+
return builder.finalize()
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def consumeIntoOptionalSequenceu32
|
194
|
+
consumeWithStream do |stream|
|
195
|
+
return stream.readOptionalSequenceu32
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
# The Sequence<T> type for u32.
|
200
|
+
|
201
|
+
def self.alloc_from_Sequenceu32(v)
|
202
|
+
RustBuffer.allocWithBuilder do |builder|
|
203
|
+
builder.write_Sequenceu32(v)
|
204
|
+
return builder.finalize()
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
def consumeIntoSequenceu32
|
209
|
+
consumeWithStream do |stream|
|
210
|
+
return stream.readSequenceu32
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
|
215
|
+
end
|
216
|
+
|
217
|
+
module UniFFILib
|
218
|
+
class ForeignBytes < FFI::Struct
|
219
|
+
layout :len, :int32,
|
220
|
+
:data, :pointer
|
221
|
+
|
222
|
+
def len
|
223
|
+
self[:len]
|
224
|
+
end
|
225
|
+
|
226
|
+
def data
|
227
|
+
self[:data]
|
228
|
+
end
|
229
|
+
|
230
|
+
def to_s
|
231
|
+
"ForeignBytes(len=#{len}, data=#{data.read_bytes(len)})"
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
private_constant :UniFFILib
|
237
|
+
|
238
|
+
# Helper for structured reading of values from a RustBuffer.
|
239
|
+
class RustBufferStream
|
240
|
+
|
241
|
+
def initialize(rbuf)
|
242
|
+
@rbuf = rbuf
|
243
|
+
@offset = 0
|
244
|
+
end
|
245
|
+
|
246
|
+
def remaining
|
247
|
+
@rbuf.len - @offset
|
248
|
+
end
|
249
|
+
|
250
|
+
def read(size)
|
251
|
+
raise InternalError, 'read past end of rust buffer' if @offset + size > @rbuf.len
|
252
|
+
|
253
|
+
data = @rbuf.data.get_bytes @offset, size
|
254
|
+
|
255
|
+
@offset += size
|
256
|
+
|
257
|
+
data
|
258
|
+
end
|
259
|
+
|
260
|
+
def readU32
|
261
|
+
unpack_from 4, 'L>'
|
262
|
+
end
|
263
|
+
|
264
|
+
def readU64
|
265
|
+
unpack_from 8, 'Q>'
|
266
|
+
end
|
267
|
+
|
268
|
+
def readString
|
269
|
+
size = unpack_from 4, 'l>'
|
270
|
+
|
271
|
+
raise InternalError, 'Unexpected negative string length' if size.negative?
|
272
|
+
|
273
|
+
read(size).force_encoding(Encoding::UTF_8)
|
274
|
+
end
|
275
|
+
|
276
|
+
# The Object type HnClient.
|
277
|
+
|
278
|
+
def readTypeHnClient
|
279
|
+
pointer = FFI::Pointer.new unpack_from 8, 'Q>'
|
280
|
+
return HnClient._uniffi_allocate(pointer)
|
281
|
+
end
|
282
|
+
|
283
|
+
# The Record type Story.
|
284
|
+
|
285
|
+
def readTypeStory
|
286
|
+
Story.new(
|
287
|
+
readU32,
|
288
|
+
readU32,
|
289
|
+
readString,
|
290
|
+
readOptionalSequenceu32,
|
291
|
+
readU32,
|
292
|
+
readString,
|
293
|
+
readOptionalstring,
|
294
|
+
readOptionalstring,
|
295
|
+
readU64
|
296
|
+
)
|
297
|
+
end
|
298
|
+
|
299
|
+
# The Record type User.
|
300
|
+
|
301
|
+
def readTypeUser
|
302
|
+
User.new(
|
303
|
+
readString,
|
304
|
+
readU64,
|
305
|
+
readU32,
|
306
|
+
readOptionalu32,
|
307
|
+
readOptionalstring,
|
308
|
+
readSequenceu32
|
309
|
+
)
|
310
|
+
end
|
311
|
+
|
312
|
+
# The Optional<T> type for u32.
|
313
|
+
|
314
|
+
def readOptionalu32
|
315
|
+
flag = unpack_from 1, 'c'
|
316
|
+
|
317
|
+
if flag == 0
|
318
|
+
return nil
|
319
|
+
elsif flag == 1
|
320
|
+
return readU32
|
321
|
+
else
|
322
|
+
raise InternalError, 'Unexpected flag byte for Optionalu32'
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
# The Optional<T> type for string.
|
327
|
+
|
328
|
+
def readOptionalstring
|
329
|
+
flag = unpack_from 1, 'c'
|
330
|
+
|
331
|
+
if flag == 0
|
332
|
+
return nil
|
333
|
+
elsif flag == 1
|
334
|
+
return readString
|
335
|
+
else
|
336
|
+
raise InternalError, 'Unexpected flag byte for Optionalstring'
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
# The Optional<T> type for TypeStory.
|
341
|
+
|
342
|
+
def readOptionalTypeStory
|
343
|
+
flag = unpack_from 1, 'c'
|
344
|
+
|
345
|
+
if flag == 0
|
346
|
+
return nil
|
347
|
+
elsif flag == 1
|
348
|
+
return readTypeStory
|
349
|
+
else
|
350
|
+
raise InternalError, 'Unexpected flag byte for OptionalTypeStory'
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
# The Optional<T> type for TypeUser.
|
355
|
+
|
356
|
+
def readOptionalTypeUser
|
357
|
+
flag = unpack_from 1, 'c'
|
358
|
+
|
359
|
+
if flag == 0
|
360
|
+
return nil
|
361
|
+
elsif flag == 1
|
362
|
+
return readTypeUser
|
363
|
+
else
|
364
|
+
raise InternalError, 'Unexpected flag byte for OptionalTypeUser'
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
# The Optional<T> type for Sequenceu32.
|
369
|
+
|
370
|
+
def readOptionalSequenceu32
|
371
|
+
flag = unpack_from 1, 'c'
|
372
|
+
|
373
|
+
if flag == 0
|
374
|
+
return nil
|
375
|
+
elsif flag == 1
|
376
|
+
return readSequenceu32
|
377
|
+
else
|
378
|
+
raise InternalError, 'Unexpected flag byte for OptionalSequenceu32'
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
# The Sequence<T> type for u32.
|
383
|
+
|
384
|
+
def readSequenceu32
|
385
|
+
count = unpack_from 4, 'l>'
|
386
|
+
|
387
|
+
raise InternalError, 'Unexpected negative sequence length' if count.negative?
|
388
|
+
|
389
|
+
items = []
|
390
|
+
|
391
|
+
count.times do
|
392
|
+
items.append readU32
|
393
|
+
end
|
394
|
+
|
395
|
+
items
|
396
|
+
end
|
397
|
+
|
398
|
+
|
399
|
+
|
400
|
+
def unpack_from(size, format)
|
401
|
+
raise InternalError, 'read past end of rust buffer' if @offset + size > @rbuf.len
|
402
|
+
|
403
|
+
value = @rbuf.data.get_bytes(@offset, size).unpack format
|
404
|
+
|
405
|
+
@offset += size
|
406
|
+
|
407
|
+
# TODO: verify this
|
408
|
+
raise 'more than one element!!!' if value.size > 1
|
409
|
+
|
410
|
+
value[0]
|
411
|
+
end
|
412
|
+
end
|
413
|
+
|
414
|
+
private_constant :RustBufferStream
|
415
|
+
|
416
|
+
# Helper for structured writing of values into a RustBuffer.
|
417
|
+
class RustBufferBuilder
|
418
|
+
def initialize
|
419
|
+
@rust_buf = RustBuffer.alloc 16
|
420
|
+
@rust_buf.len = 0
|
421
|
+
end
|
422
|
+
|
423
|
+
def finalize
|
424
|
+
rbuf = @rust_buf
|
425
|
+
|
426
|
+
@rust_buf = nil
|
427
|
+
|
428
|
+
rbuf
|
429
|
+
end
|
430
|
+
|
431
|
+
def discard
|
432
|
+
return if @rust_buf.nil?
|
433
|
+
|
434
|
+
rbuf = finalize
|
435
|
+
rbuf.free
|
436
|
+
end
|
437
|
+
|
438
|
+
def write(value)
|
439
|
+
reserve(value.bytes.size) do
|
440
|
+
@rust_buf.data.put_array_of_char @rust_buf.len, value.bytes
|
441
|
+
end
|
442
|
+
end
|
443
|
+
|
444
|
+
def write_U32(v)
|
445
|
+
pack_into(4, 'L>', v)
|
446
|
+
end
|
447
|
+
|
448
|
+
def write_U64(v)
|
449
|
+
pack_into(8, 'Q>', v)
|
450
|
+
end
|
451
|
+
|
452
|
+
def write_String(v)
|
453
|
+
v = v.to_s
|
454
|
+
pack_into 4, 'l>', v.bytes.size
|
455
|
+
write v
|
456
|
+
end
|
457
|
+
|
458
|
+
# The Object type HnClient.
|
459
|
+
|
460
|
+
def write_TypeHnClient(obj)
|
461
|
+
pointer = HnClient._uniffi_lower obj
|
462
|
+
pack_into(8, 'Q>', pointer.address)
|
463
|
+
end
|
464
|
+
|
465
|
+
# The Record type Story.
|
466
|
+
|
467
|
+
def write_TypeStory(v)
|
468
|
+
self.write_U32(v.id)
|
469
|
+
self.write_U32(v.descendants)
|
470
|
+
self.write_String(v.by)
|
471
|
+
self.write_OptionalSequenceu32(v.kids)
|
472
|
+
self.write_U32(v.score)
|
473
|
+
self.write_String(v.title)
|
474
|
+
self.write_Optionalstring(v.url)
|
475
|
+
self.write_Optionalstring(v.text)
|
476
|
+
self.write_U64(v.time)
|
477
|
+
end
|
478
|
+
|
479
|
+
# The Record type User.
|
480
|
+
|
481
|
+
def write_TypeUser(v)
|
482
|
+
self.write_String(v.id)
|
483
|
+
self.write_U64(v.created)
|
484
|
+
self.write_U32(v.karma)
|
485
|
+
self.write_Optionalu32(v.delay)
|
486
|
+
self.write_Optionalstring(v.about)
|
487
|
+
self.write_Sequenceu32(v.submitted)
|
488
|
+
end
|
489
|
+
|
490
|
+
# The Optional<T> type for u32.
|
491
|
+
|
492
|
+
def write_Optionalu32(v)
|
493
|
+
if v.nil?
|
494
|
+
pack_into(1, 'c', 0)
|
495
|
+
else
|
496
|
+
pack_into(1, 'c', 1)
|
497
|
+
self.write_U32(v)
|
498
|
+
end
|
499
|
+
end
|
500
|
+
|
501
|
+
# The Optional<T> type for string.
|
502
|
+
|
503
|
+
def write_Optionalstring(v)
|
504
|
+
if v.nil?
|
505
|
+
pack_into(1, 'c', 0)
|
506
|
+
else
|
507
|
+
pack_into(1, 'c', 1)
|
508
|
+
self.write_String(v)
|
509
|
+
end
|
510
|
+
end
|
511
|
+
|
512
|
+
# The Optional<T> type for TypeStory.
|
513
|
+
|
514
|
+
def write_OptionalTypeStory(v)
|
515
|
+
if v.nil?
|
516
|
+
pack_into(1, 'c', 0)
|
517
|
+
else
|
518
|
+
pack_into(1, 'c', 1)
|
519
|
+
self.write_TypeStory(v)
|
520
|
+
end
|
521
|
+
end
|
522
|
+
|
523
|
+
# The Optional<T> type for TypeUser.
|
524
|
+
|
525
|
+
def write_OptionalTypeUser(v)
|
526
|
+
if v.nil?
|
527
|
+
pack_into(1, 'c', 0)
|
528
|
+
else
|
529
|
+
pack_into(1, 'c', 1)
|
530
|
+
self.write_TypeUser(v)
|
531
|
+
end
|
532
|
+
end
|
533
|
+
|
534
|
+
# The Optional<T> type for Sequenceu32.
|
535
|
+
|
536
|
+
def write_OptionalSequenceu32(v)
|
537
|
+
if v.nil?
|
538
|
+
pack_into(1, 'c', 0)
|
539
|
+
else
|
540
|
+
pack_into(1, 'c', 1)
|
541
|
+
self.write_Sequenceu32(v)
|
542
|
+
end
|
543
|
+
end
|
544
|
+
|
545
|
+
# The Sequence<T> type for u32.
|
546
|
+
|
547
|
+
def write_Sequenceu32(items)
|
548
|
+
pack_into(4, 'l>', items.size)
|
549
|
+
|
550
|
+
items.each do |item|
|
551
|
+
self.write_U32(item)
|
552
|
+
end
|
553
|
+
end
|
554
|
+
|
555
|
+
|
556
|
+
|
557
|
+
private
|
558
|
+
|
559
|
+
def reserve(num_bytes)
|
560
|
+
if @rust_buf.len + num_bytes > @rust_buf.capacity
|
561
|
+
@rust_buf = RustBuffer.reserve(@rust_buf, num_bytes)
|
562
|
+
end
|
563
|
+
|
564
|
+
yield
|
565
|
+
|
566
|
+
@rust_buf.len += num_bytes
|
567
|
+
end
|
568
|
+
|
569
|
+
def pack_into(size, format, value)
|
570
|
+
reserve(size) do
|
571
|
+
@rust_buf.data.put_array_of_char @rust_buf.len, [value].pack(format).bytes
|
572
|
+
end
|
573
|
+
end
|
574
|
+
end
|
575
|
+
|
576
|
+
private_constant :RustBufferBuilder
|
577
|
+
|
578
|
+
# Error definitions
|
579
|
+
class RustCallStatus < FFI::Struct
|
580
|
+
layout :code, :int8,
|
581
|
+
:error_buf, RustBuffer
|
582
|
+
|
583
|
+
def code
|
584
|
+
self[:code]
|
585
|
+
end
|
586
|
+
|
587
|
+
def error_buf
|
588
|
+
self[:error_buf]
|
589
|
+
end
|
590
|
+
|
591
|
+
def to_s
|
592
|
+
"RustCallStatus(code=#{self[:code]})"
|
593
|
+
end
|
594
|
+
end
|
595
|
+
|
596
|
+
# These match the values from the uniffi::rustcalls module
|
597
|
+
CALL_SUCCESS = 0
|
598
|
+
CALL_ERROR = 1
|
599
|
+
CALL_PANIC = 2
|
600
|
+
|
601
|
+
# Map error modules to the RustBuffer method name that reads them
|
602
|
+
ERROR_MODULE_TO_READER_METHOD = {
|
603
|
+
}
|
604
|
+
|
605
|
+
private_constant :ERROR_MODULE_TO_READER_METHOD, :CALL_SUCCESS, :CALL_ERROR, :CALL_PANIC,
|
606
|
+
:RustCallStatus
|
607
|
+
|
608
|
+
def self.consume_buffer_into_error(error_module, rust_buffer)
|
609
|
+
rust_buffer.consumeWithStream do |stream|
|
610
|
+
reader_method = ERROR_MODULE_TO_READER_METHOD[error_module]
|
611
|
+
return stream.send(reader_method)
|
612
|
+
end
|
613
|
+
end
|
614
|
+
|
615
|
+
class InternalError < StandardError
|
616
|
+
end
|
617
|
+
|
618
|
+
def self.rust_call(fn_name, *args)
|
619
|
+
# Call a rust function
|
620
|
+
rust_call_with_error(nil, fn_name, *args)
|
621
|
+
end
|
622
|
+
|
623
|
+
def self.rust_call_with_error(error_module, fn_name, *args)
|
624
|
+
# Call a rust function and handle errors
|
625
|
+
#
|
626
|
+
# Use this when the rust function returns a Result<>. error_module must be the error_module that corresponds to that Result.
|
627
|
+
|
628
|
+
|
629
|
+
# Note: RustCallStatus.new zeroes out the struct, which is exactly what we
|
630
|
+
# want to pass to Rust (code=0, error_buf=RustBuffer(len=0, capacity=0,
|
631
|
+
# data=NULL))
|
632
|
+
status = RustCallStatus.new
|
633
|
+
args << status
|
634
|
+
|
635
|
+
result = UniFFILib.public_send(fn_name, *args)
|
636
|
+
|
637
|
+
case status.code
|
638
|
+
when CALL_SUCCESS
|
639
|
+
result
|
640
|
+
when CALL_ERROR
|
641
|
+
if error_module.nil?
|
642
|
+
status.error_buf.free
|
643
|
+
raise InternalError, "CALL_ERROR with no error_module set"
|
644
|
+
else
|
645
|
+
raise consume_buffer_into_error(error_module, status.error_buf)
|
646
|
+
end
|
647
|
+
when CALL_PANIC
|
648
|
+
# When the rust code sees a panic, it tries to construct a RustBuffer
|
649
|
+
# with the message. But if that code panics, then it just sends back
|
650
|
+
# an empty buffer.
|
651
|
+
if status.error_buf.len > 0
|
652
|
+
raise InternalError, status.error_buf.consumeIntoString()
|
653
|
+
else
|
654
|
+
raise InternalError, "Rust panic"
|
655
|
+
end
|
656
|
+
else
|
657
|
+
raise InternalError, "Unknown call status: #{status.code}"
|
658
|
+
end
|
659
|
+
end
|
660
|
+
|
661
|
+
private_class_method :consume_buffer_into_error
|
662
|
+
|
663
|
+
# This is how we find and load the dynamic library provided by the component.
|
664
|
+
# For now we just look it up by name.
|
665
|
+
module UniFFILib
|
666
|
+
extend FFI::Library
|
667
|
+
|
668
|
+
|
669
|
+
ffi_lib 'hn_api'
|
670
|
+
|
671
|
+
|
672
|
+
attach_function :ffi_hn_api_d6e5_HnClient_object_free,
|
673
|
+
[:pointer, RustCallStatus.by_ref],
|
674
|
+
:void
|
675
|
+
attach_function :hn_api_d6e5_HnClient_new,
|
676
|
+
[RustCallStatus.by_ref],
|
677
|
+
:pointer
|
678
|
+
attach_function :hn_api_d6e5_HnClient_get_user,
|
679
|
+
[:pointer, RustBuffer.by_value, RustCallStatus.by_ref],
|
680
|
+
RustBuffer.by_value
|
681
|
+
attach_function :hn_api_d6e5_HnClient_get_new_stories,
|
682
|
+
[:pointer, RustCallStatus.by_ref],
|
683
|
+
RustBuffer.by_value
|
684
|
+
attach_function :hn_api_d6e5_HnClient_get_story,
|
685
|
+
[:pointer, :uint32, RustCallStatus.by_ref],
|
686
|
+
RustBuffer.by_value
|
687
|
+
attach_function :ffi_hn_api_d6e5_rustbuffer_alloc,
|
688
|
+
[:int32, RustCallStatus.by_ref],
|
689
|
+
RustBuffer.by_value
|
690
|
+
attach_function :ffi_hn_api_d6e5_rustbuffer_from_bytes,
|
691
|
+
[ForeignBytes, RustCallStatus.by_ref],
|
692
|
+
RustBuffer.by_value
|
693
|
+
attach_function :ffi_hn_api_d6e5_rustbuffer_free,
|
694
|
+
[RustBuffer.by_value, RustCallStatus.by_ref],
|
695
|
+
:void
|
696
|
+
attach_function :ffi_hn_api_d6e5_rustbuffer_reserve,
|
697
|
+
[RustBuffer.by_value, :int32, RustCallStatus.by_ref],
|
698
|
+
RustBuffer.by_value
|
699
|
+
|
700
|
+
end
|
701
|
+
|
702
|
+
# Public interface members begin here.
|
703
|
+
|
704
|
+
|
705
|
+
# Record type Story
|
706
|
+
class Story
|
707
|
+
attr_reader :id, :descendants, :by, :kids, :score, :title, :url, :text, :time
|
708
|
+
|
709
|
+
def initialize(id, descendants, by, kids, score, title, url, text, time)
|
710
|
+
@id = id
|
711
|
+
@descendants = descendants
|
712
|
+
@by = by
|
713
|
+
@kids = kids
|
714
|
+
@score = score
|
715
|
+
@title = title
|
716
|
+
@url = url
|
717
|
+
@text = text
|
718
|
+
@time = time
|
719
|
+
end
|
720
|
+
|
721
|
+
def ==(other)
|
722
|
+
if @id != other.id
|
723
|
+
return false
|
724
|
+
end
|
725
|
+
if @descendants != other.descendants
|
726
|
+
return false
|
727
|
+
end
|
728
|
+
if @by != other.by
|
729
|
+
return false
|
730
|
+
end
|
731
|
+
if @kids != other.kids
|
732
|
+
return false
|
733
|
+
end
|
734
|
+
if @score != other.score
|
735
|
+
return false
|
736
|
+
end
|
737
|
+
if @title != other.title
|
738
|
+
return false
|
739
|
+
end
|
740
|
+
if @url != other.url
|
741
|
+
return false
|
742
|
+
end
|
743
|
+
if @text != other.text
|
744
|
+
return false
|
745
|
+
end
|
746
|
+
if @time != other.time
|
747
|
+
return false
|
748
|
+
end
|
749
|
+
|
750
|
+
true
|
751
|
+
end
|
752
|
+
end
|
753
|
+
|
754
|
+
# Record type User
|
755
|
+
class User
|
756
|
+
attr_reader :id, :created, :karma, :delay, :about, :submitted
|
757
|
+
|
758
|
+
def initialize(id, created, karma, delay, about, submitted)
|
759
|
+
@id = id
|
760
|
+
@created = created
|
761
|
+
@karma = karma
|
762
|
+
@delay = delay
|
763
|
+
@about = about
|
764
|
+
@submitted = submitted
|
765
|
+
end
|
766
|
+
|
767
|
+
def ==(other)
|
768
|
+
if @id != other.id
|
769
|
+
return false
|
770
|
+
end
|
771
|
+
if @created != other.created
|
772
|
+
return false
|
773
|
+
end
|
774
|
+
if @karma != other.karma
|
775
|
+
return false
|
776
|
+
end
|
777
|
+
if @delay != other.delay
|
778
|
+
return false
|
779
|
+
end
|
780
|
+
if @about != other.about
|
781
|
+
return false
|
782
|
+
end
|
783
|
+
if @submitted != other.submitted
|
784
|
+
return false
|
785
|
+
end
|
786
|
+
|
787
|
+
true
|
788
|
+
end
|
789
|
+
end
|
790
|
+
|
791
|
+
|
792
|
+
|
793
|
+
|
794
|
+
|
795
|
+
class HnClient
|
796
|
+
|
797
|
+
# A private helper for initializing instances of the class from a raw pointer,
|
798
|
+
# bypassing any initialization logic and ensuring they are GC'd properly.
|
799
|
+
def self._uniffi_allocate(pointer)
|
800
|
+
pointer.autorelease = false
|
801
|
+
inst = allocate
|
802
|
+
inst.instance_variable_set :@pointer, pointer
|
803
|
+
ObjectSpace.define_finalizer(inst, _uniffi_define_finalizer_by_pointer(pointer, inst.object_id))
|
804
|
+
return inst
|
805
|
+
end
|
806
|
+
|
807
|
+
# A private helper for registering an object finalizer.
|
808
|
+
# N.B. it's important that this does not capture a reference
|
809
|
+
# to the actual instance, only its underlying pointer.
|
810
|
+
def self._uniffi_define_finalizer_by_pointer(pointer, object_id)
|
811
|
+
Proc.new do |_id|
|
812
|
+
HnApi.rust_call(
|
813
|
+
:ffi_hn_api_d6e5_HnClient_object_free,
|
814
|
+
pointer
|
815
|
+
)
|
816
|
+
end
|
817
|
+
end
|
818
|
+
|
819
|
+
# A private helper for lowering instances into a raw pointer.
|
820
|
+
# This does an explicit typecheck, because accidentally lowering a different type of
|
821
|
+
# object in a place where this type is expected, could lead to memory unsafety.
|
822
|
+
def self._uniffi_lower(inst)
|
823
|
+
if not inst.is_a? self
|
824
|
+
raise TypeError.new "Expected a HnClient instance, got #{inst}"
|
825
|
+
end
|
826
|
+
return inst.instance_variable_get :@pointer
|
827
|
+
end
|
828
|
+
def initialize()
|
829
|
+
pointer = HnApi.rust_call(:hn_api_d6e5_HnClient_new,)
|
830
|
+
@pointer = pointer
|
831
|
+
ObjectSpace.define_finalizer(self, self.class._uniffi_define_finalizer_by_pointer(pointer, self.object_id))
|
832
|
+
end
|
833
|
+
|
834
|
+
|
835
|
+
|
836
|
+
def get_user(username)
|
837
|
+
username = username.to_s
|
838
|
+
result = HnApi.rust_call(:hn_api_d6e5_HnClient_get_user,@pointer,RustBuffer.allocFromString(username))
|
839
|
+
return result.consumeIntoOptionalTypeUser
|
840
|
+
end
|
841
|
+
def get_new_stories()
|
842
|
+
result = HnApi.rust_call(:hn_api_d6e5_HnClient_get_new_stories,@pointer,)
|
843
|
+
return result.consumeIntoSequenceu32
|
844
|
+
end
|
845
|
+
def get_story(id)
|
846
|
+
id = id.to_i
|
847
|
+
result = HnApi.rust_call(:hn_api_d6e5_HnClient_get_story,@pointer,id)
|
848
|
+
return result.consumeIntoOptionalTypeStory
|
849
|
+
end
|
850
|
+
|
851
|
+
end
|
852
|
+
|
853
|
+
end
|
854
|
+
|
Binary file
|
data/hn_api_ruby/test.rb
ADDED
metadata
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hn_api_ruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Autogenerated
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-05-13 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: ''
|
14
|
+
email: pmkelly4444@gmail.com
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- hn_api_ruby/hn_api.rb
|
20
|
+
- hn_api_ruby/libhn_api.dylib
|
21
|
+
- hn_api_ruby/test.rb
|
22
|
+
homepage: https://rubygems.org/gems/hn_api_ruby
|
23
|
+
licenses:
|
24
|
+
- MIT
|
25
|
+
metadata: {}
|
26
|
+
post_install_message:
|
27
|
+
rdoc_options: []
|
28
|
+
require_paths:
|
29
|
+
- lib
|
30
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0'
|
35
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
requirements: []
|
41
|
+
rubygems_version: 3.4.10
|
42
|
+
signing_key:
|
43
|
+
specification_version: 4
|
44
|
+
summary: Hacker News API Client
|
45
|
+
test_files: []
|