gmeow-gts 1.0.0.rc.1
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/README.md +125 -0
- data/lib/gmeow/gts.rb +323 -0
- metadata +64 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: f2e169a98f542d68a7bf37cae39f22769ee76e54c5950fcfe93e5507d3bc5bb5
|
|
4
|
+
data.tar.gz: 6691d4c9a30319c16ce9c6430b06fef67dc9b7af81a813cbff909c1df4b5b180
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: dc1c389118859615b4f72f60f6da0def15b3a31e994ed3f94238eda9508105c93d68e7daab30acc7f1602a11d8194dcbb875dbf23eb646477f2b8c8fc3cfde20
|
|
7
|
+
data.tar.gz: 5a647fabae6d3a5dac905454d241634d6c40296b318708ddc9c14ab664c8d50b491d8b9645d24242049a39e33af563cd33218842dbccf044458f100c55095602
|
data/README.md
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# GTS Ruby C ABI Wrapper
|
|
2
|
+
|
|
3
|
+
<!-- SPDX-FileCopyrightText: 2026 Blackcat Informatics Inc. <paudley@blackcatinformatics.ca> -->
|
|
4
|
+
<!-- SPDX-License-Identifier: MIT OR Apache-2.0 -->
|
|
5
|
+
|
|
6
|
+
`gmeow-gts` is a thin Ruby FFI wrapper over the Rust-backed `libgts` C ABI. It
|
|
7
|
+
does not parse, fold, write, or verify GTS archives in Ruby; all GTS semantics
|
|
8
|
+
come from the checked-in C ABI in `rust/capi/include/gts.h`.
|
|
9
|
+
|
|
10
|
+
## Requirements
|
|
11
|
+
|
|
12
|
+
- Ruby 3.1 or newer.
|
|
13
|
+
- The `ffi` gem.
|
|
14
|
+
- A built `libgts` shared library from `rust/capi`.
|
|
15
|
+
|
|
16
|
+
Build the shared library from the repository root:
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
cargo build --manifest-path rust/capi/Cargo.toml
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Point the wrapper at the library with `GTS_LIBGTS`:
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
export GTS_LIBGTS="$PWD/rust/capi/target/debug/libgts.so"
|
|
26
|
+
ruby -I ruby/lib ruby/tests/smoke.rb \
|
|
27
|
+
vectors/01-minimal.gts vectors/04-damaged-frame.gts vectors/28-empty-file.gts
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
On macOS use `libgts.dylib`; on Windows use `gts.dll`. If `GTS_LIBGTS` is not
|
|
31
|
+
set, the wrapper asks the platform dynamic loader for the default library name.
|
|
32
|
+
|
|
33
|
+
## RubyGems Installation
|
|
34
|
+
|
|
35
|
+
The published `gmeow-gts` gem is source-only. It ships the Ruby FFI wrapper and
|
|
36
|
+
declares the `ffi` dependency, but it does not vendor `libgts`.
|
|
37
|
+
|
|
38
|
+
```sh
|
|
39
|
+
gem install gmeow-gts
|
|
40
|
+
export GTS_LIBGTS=/path/to/libgts.so
|
|
41
|
+
ruby -rgmeow/gts -e 'puts Gmeow::Gts.load.version'
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
On macOS set `GTS_LIBGTS` to the `.dylib`; on Windows set it to `gts.dll`.
|
|
45
|
+
|
|
46
|
+
## API Shape
|
|
47
|
+
|
|
48
|
+
```ruby
|
|
49
|
+
require "gmeow/gts"
|
|
50
|
+
|
|
51
|
+
gts = Gmeow::Gts.load
|
|
52
|
+
input = File.binread("vectors/01-minimal.gts")
|
|
53
|
+
|
|
54
|
+
metadata = gts.build_metadata_json
|
|
55
|
+
capabilities = gts.capabilities_json
|
|
56
|
+
folded = gts.read_json(input)
|
|
57
|
+
verified = gts.verify_json(input)
|
|
58
|
+
nquads = gts.to_nquads(input)
|
|
59
|
+
round_trip = gts.from_nquads(nquads)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Files-profile helpers use Ruby strings for binary GTS payloads and ordinary path
|
|
63
|
+
strings for directories:
|
|
64
|
+
|
|
65
|
+
```ruby
|
|
66
|
+
packed = gts.files_pack(["/path/to/tree"])
|
|
67
|
+
diff = gts.files_diff_json(packed, "/path/to/tree")
|
|
68
|
+
report = gts.files_unpack(packed, "/tmp/unpacked")
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
These path strings are forwarded to `libgts` as NUL-terminated UTF-8 C strings.
|
|
72
|
+
On Windows this covers only paths representable by that contract, not every
|
|
73
|
+
native wide-character filesystem path. Future wide-character C ABI entry points
|
|
74
|
+
would be additive symbols under the compatibility policy.
|
|
75
|
+
|
|
76
|
+
## Ownership And Errors
|
|
77
|
+
|
|
78
|
+
The wrapper copies every returned `gts_buffer` into a Ruby string and then calls
|
|
79
|
+
`gts_buffer_free`. If a C ABI call returns an error handle, the wrapper copies
|
|
80
|
+
the stable error code and message, releases the handle with `gts_error_free`,
|
|
81
|
+
and raises `Gmeow::Gts::Error`.
|
|
82
|
+
|
|
83
|
+
Callers never receive raw FFI pointers, buffer capacities, or Rust-owned memory.
|
|
84
|
+
Ruby strings are binary-safe and are used for both GTS byte streams and UTF-8
|
|
85
|
+
JSON/N-Quads text.
|
|
86
|
+
|
|
87
|
+
Raised errors expose:
|
|
88
|
+
|
|
89
|
+
- `operation`: C ABI operation name.
|
|
90
|
+
- `status`: integer C ABI status code.
|
|
91
|
+
- `status_name`: symbolic C ABI status name.
|
|
92
|
+
- `code`: stable C ABI error code.
|
|
93
|
+
- `detail`: human-readable diagnostic detail.
|
|
94
|
+
|
|
95
|
+
```ruby
|
|
96
|
+
begin
|
|
97
|
+
gts.from_nquads("<https://example/s> <https://example/p> .\n")
|
|
98
|
+
rescue Gmeow::Gts::Error => error
|
|
99
|
+
warn "#{error.status_name} #{error.code}: #{error.detail}"
|
|
100
|
+
end
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Threading And ABI Stability
|
|
104
|
+
|
|
105
|
+
`libgts` operations are reentrant. Each call owns its output buffer and error
|
|
106
|
+
handle independently, and the Ruby wrapper frees them before returning or
|
|
107
|
+
throwing. Do not share raw FFI values between Ruby runtimes.
|
|
108
|
+
|
|
109
|
+
The wrapper targets `GTS_ABI_VERSION` 1. Check `gts.abi_version` and
|
|
110
|
+
`gts.capabilities_json` when loading a system-provided `libgts`.
|
|
111
|
+
|
|
112
|
+
## Validation
|
|
113
|
+
|
|
114
|
+
Run the smoke test from the repository root:
|
|
115
|
+
|
|
116
|
+
```sh
|
|
117
|
+
bash ruby/scripts/smoke.sh
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
The script builds `libgts`, validates the gemspec, and exercises ABI metadata,
|
|
121
|
+
capabilities, read/fold, verify, N-Quads export/import, structured errors, and
|
|
122
|
+
files-profile pack/diff/unpack. It runs the smoke once from the checkout and
|
|
123
|
+
once through an installed local gem with the checkout removed from Ruby's load
|
|
124
|
+
path. If local Ruby with the `ffi` gem is missing, it uses the pinned fallback
|
|
125
|
+
image defined in `ruby/Dockerfile`.
|
data/lib/gmeow/gts.rb
ADDED
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2026 Blackcat Informatics Inc. <paudley@blackcatinformatics.ca>
|
|
2
|
+
# SPDX-License-Identifier: MIT OR Apache-2.0
|
|
3
|
+
|
|
4
|
+
require "ffi"
|
|
5
|
+
require "rbconfig"
|
|
6
|
+
require "thread"
|
|
7
|
+
|
|
8
|
+
module Gmeow
|
|
9
|
+
module Gts
|
|
10
|
+
VERSION = "1.0.0.rc.1"
|
|
11
|
+
ABI_VERSION = 1
|
|
12
|
+
UINT32_MAX = 0xffff_ffff
|
|
13
|
+
|
|
14
|
+
module Status
|
|
15
|
+
OK = 0
|
|
16
|
+
INVALID_ARGUMENT = 1
|
|
17
|
+
IO = 2
|
|
18
|
+
PARSE = 3
|
|
19
|
+
DIAGNOSTIC = 4
|
|
20
|
+
INTERNAL = 5
|
|
21
|
+
PANIC = 6
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
module UnpackFlags
|
|
25
|
+
NONE = 0
|
|
26
|
+
INCLUDE_SUPPRESSED = 1 << 0
|
|
27
|
+
ALLOW_SYMLINKS = 1 << 1
|
|
28
|
+
ALLOW_SPECIAL = 1 << 2
|
|
29
|
+
SAME_OWNER = 1 << 3
|
|
30
|
+
PRESERVE_SETID = 1 << 4
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
STATUS_NAMES = {
|
|
34
|
+
Status::OK => "OK",
|
|
35
|
+
Status::INVALID_ARGUMENT => "INVALID_ARGUMENT",
|
|
36
|
+
Status::IO => "IO",
|
|
37
|
+
Status::PARSE => "PARSE",
|
|
38
|
+
Status::DIAGNOSTIC => "DIAGNOSTIC",
|
|
39
|
+
Status::INTERNAL => "INTERNAL",
|
|
40
|
+
Status::PANIC => "PANIC"
|
|
41
|
+
}.freeze
|
|
42
|
+
|
|
43
|
+
class Error < StandardError
|
|
44
|
+
attr_reader :operation, :status, :status_name, :code, :detail
|
|
45
|
+
|
|
46
|
+
def initialize(operation:, status:, code:, detail:)
|
|
47
|
+
@operation = operation
|
|
48
|
+
@status = status
|
|
49
|
+
@status_name = Gts.status_name(status)
|
|
50
|
+
@code = code
|
|
51
|
+
@detail = detail
|
|
52
|
+
super(build_message)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def build_message
|
|
58
|
+
message = "#{operation} failed with #{status_name}"
|
|
59
|
+
message = "#{message} (#{code})" unless code.empty?
|
|
60
|
+
message = "#{message}: #{detail}" unless detail.empty?
|
|
61
|
+
message
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
class Buffer < FFI::Struct
|
|
66
|
+
layout :data, :pointer,
|
|
67
|
+
:len, :size_t,
|
|
68
|
+
:capacity, :size_t
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
module Native
|
|
72
|
+
@modules = {}
|
|
73
|
+
@mutex = Mutex.new
|
|
74
|
+
|
|
75
|
+
def self.bind(library)
|
|
76
|
+
key = library.to_s
|
|
77
|
+
@mutex.synchronize do
|
|
78
|
+
@modules[key] ||= build(key)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def self.build(library)
|
|
83
|
+
Module.new do
|
|
84
|
+
extend FFI::Library
|
|
85
|
+
|
|
86
|
+
ffi_lib library
|
|
87
|
+
|
|
88
|
+
attach_function :gts_abi_version, [], :uint32
|
|
89
|
+
attach_function :gts_version, [], :pointer
|
|
90
|
+
|
|
91
|
+
attach_function :gts_buffer_free, [:pointer], :void
|
|
92
|
+
attach_function :gts_error_free, [:pointer], :void
|
|
93
|
+
attach_function :gts_error_code, [:pointer], :pointer
|
|
94
|
+
attach_function :gts_error_message, [:pointer], :pointer
|
|
95
|
+
|
|
96
|
+
attach_function :gts_build_metadata_json, [:pointer, :pointer], :int
|
|
97
|
+
attach_function :gts_capabilities_json, [:pointer, :pointer], :int
|
|
98
|
+
attach_function :gts_read_json, [:pointer, :size_t, :pointer, :pointer], :int
|
|
99
|
+
attach_function :gts_verify_json, [:pointer, :size_t, :pointer, :pointer], :int
|
|
100
|
+
attach_function :gts_to_nquads, [:pointer, :size_t, :pointer, :pointer], :int
|
|
101
|
+
attach_function :gts_from_nquads, [:pointer, :size_t, :pointer, :pointer], :int
|
|
102
|
+
attach_function :gts_files_pack, [:pointer, :size_t, :pointer, :pointer], :int
|
|
103
|
+
attach_function :gts_files_unpack,
|
|
104
|
+
[:pointer, :size_t, :pointer, :uint32, :pointer, :pointer],
|
|
105
|
+
:int
|
|
106
|
+
attach_function :gts_files_diff_json,
|
|
107
|
+
[:pointer, :size_t, :pointer, :pointer, :pointer],
|
|
108
|
+
:int
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
private_class_method :build
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
class Library
|
|
116
|
+
def initialize(library = nil)
|
|
117
|
+
@native = Native.bind(library || Gts.default_library)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def abi_version
|
|
121
|
+
@native.gts_abi_version
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def version
|
|
125
|
+
copy_c_string(@native.gts_version)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def build_metadata_json
|
|
129
|
+
call_buffer("gts_build_metadata_json") do |out, error|
|
|
130
|
+
@native.gts_build_metadata_json(out, error)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def capabilities_json
|
|
135
|
+
call_buffer("gts_capabilities_json") do |out, error|
|
|
136
|
+
@native.gts_capabilities_json(out, error)
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def read_json(data)
|
|
141
|
+
with_bytes(data, "data") do |pointer, length|
|
|
142
|
+
call_buffer("gts_read_json") do |out, error|
|
|
143
|
+
@native.gts_read_json(pointer, length, out, error)
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def verify_json(data)
|
|
149
|
+
with_bytes(data, "data") do |pointer, length|
|
|
150
|
+
call_buffer("gts_verify_json") do |out, error|
|
|
151
|
+
@native.gts_verify_json(pointer, length, out, error)
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def to_nquads(data)
|
|
157
|
+
with_bytes(data, "data") do |pointer, length|
|
|
158
|
+
call_buffer("gts_to_nquads") do |out, error|
|
|
159
|
+
@native.gts_to_nquads(pointer, length, out, error)
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def from_nquads(text)
|
|
165
|
+
with_bytes(text, "text") do |pointer, length|
|
|
166
|
+
call_buffer("gts_from_nquads") do |out, error|
|
|
167
|
+
@native.gts_from_nquads(pointer, length, out, error)
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def files_pack(paths)
|
|
173
|
+
path_pointers, keepalive = native_path_list(paths)
|
|
174
|
+
call_buffer("gts_files_pack") do |out, error|
|
|
175
|
+
@native.gts_files_pack(path_pointers, keepalive.length, out, error)
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def files_unpack(data, destination, flags = UnpackFlags::NONE)
|
|
180
|
+
destination_pointer = native_string(destination, "destination")
|
|
181
|
+
unpack_flags = checked_uint32(flags, "flags")
|
|
182
|
+
|
|
183
|
+
with_bytes(data, "data") do |pointer, length|
|
|
184
|
+
call_buffer("gts_files_unpack") do |out, error|
|
|
185
|
+
@native.gts_files_unpack(pointer, length, destination_pointer, unpack_flags, out, error)
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def files_diff_json(data, directory)
|
|
191
|
+
directory_pointer = native_string(directory, "directory")
|
|
192
|
+
|
|
193
|
+
with_bytes(data, "data") do |pointer, length|
|
|
194
|
+
call_buffer("gts_files_diff_json") do |out, error|
|
|
195
|
+
@native.gts_files_diff_json(pointer, length, directory_pointer, out, error)
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
private
|
|
201
|
+
|
|
202
|
+
def call_buffer(operation)
|
|
203
|
+
out = new_buffer
|
|
204
|
+
error = FFI::MemoryPointer.new(:pointer)
|
|
205
|
+
error.write_pointer(FFI::Pointer::NULL)
|
|
206
|
+
|
|
207
|
+
begin
|
|
208
|
+
status = yield(out.to_ptr, error)
|
|
209
|
+
err_ptr = error.read_pointer
|
|
210
|
+
if status != Status::OK
|
|
211
|
+
raise build_error(operation, status, err_ptr)
|
|
212
|
+
end
|
|
213
|
+
unless err_ptr.null?
|
|
214
|
+
raise build_error(operation, Status::INTERNAL, err_ptr)
|
|
215
|
+
end
|
|
216
|
+
copy_buffer(out)
|
|
217
|
+
ensure
|
|
218
|
+
@native.gts_buffer_free(out.to_ptr)
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def new_buffer
|
|
223
|
+
buffer = Buffer.new
|
|
224
|
+
buffer[:data] = FFI::Pointer::NULL
|
|
225
|
+
buffer[:len] = 0
|
|
226
|
+
buffer[:capacity] = 0
|
|
227
|
+
buffer
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def build_error(operation, status, error)
|
|
231
|
+
code = ""
|
|
232
|
+
detail = ""
|
|
233
|
+
|
|
234
|
+
unless error.null?
|
|
235
|
+
begin
|
|
236
|
+
code = copy_c_string(@native.gts_error_code(error))
|
|
237
|
+
detail = copy_c_string(@native.gts_error_message(error))
|
|
238
|
+
ensure
|
|
239
|
+
@native.gts_error_free(error)
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
Error.new(operation: operation, status: status, code: code, detail: detail)
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def copy_buffer(buffer)
|
|
247
|
+
length = buffer[:len]
|
|
248
|
+
return "" if length.zero?
|
|
249
|
+
|
|
250
|
+
data = buffer[:data]
|
|
251
|
+
raise RuntimeError, "C ABI returned a null data pointer with non-zero length." if data.null?
|
|
252
|
+
|
|
253
|
+
data.read_string_length(length)
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def copy_c_string(pointer)
|
|
257
|
+
return "" if pointer.nil? || pointer.null?
|
|
258
|
+
|
|
259
|
+
pointer.read_string
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def with_bytes(value, name)
|
|
263
|
+
raise TypeError, "#{name} must be a String" unless value.is_a?(String)
|
|
264
|
+
|
|
265
|
+
length = value.bytesize
|
|
266
|
+
pointer = FFI::MemoryPointer.new(:uint8, [length, 1].max)
|
|
267
|
+
pointer.put_bytes(0, value) unless length.zero?
|
|
268
|
+
yield(pointer, length)
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def native_string(value, name)
|
|
272
|
+
raise TypeError, "#{name} must be a String" unless value.is_a?(String)
|
|
273
|
+
raise ArgumentError, "#{name} must not contain NUL bytes" if value.include?("\0")
|
|
274
|
+
|
|
275
|
+
FFI::MemoryPointer.from_string(value)
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
def native_path_list(paths)
|
|
279
|
+
raise TypeError, "paths must be an Array" unless paths.is_a?(Array)
|
|
280
|
+
raise ArgumentError, "paths must not be empty" if paths.empty?
|
|
281
|
+
|
|
282
|
+
pointer_array = FFI::MemoryPointer.new(:pointer, paths.length)
|
|
283
|
+
keepalive = paths.each_with_index.map do |path, index|
|
|
284
|
+
pointer = native_string(path, "paths[#{index}]")
|
|
285
|
+
pointer_array.put_pointer(index * FFI.type_size(:pointer), pointer)
|
|
286
|
+
pointer
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
[pointer_array, keepalive]
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
def checked_uint32(value, name)
|
|
293
|
+
raise TypeError, "#{name} must be an Integer" unless value.is_a?(Integer)
|
|
294
|
+
raise RangeError, "#{name} must be an unsigned 32-bit integer" if value.negative? || value > UINT32_MAX
|
|
295
|
+
|
|
296
|
+
value
|
|
297
|
+
end
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
def self.default_library
|
|
301
|
+
from_env = ENV.fetch("GTS_LIBGTS", nil)
|
|
302
|
+
return from_env unless from_env.nil? || from_env.empty?
|
|
303
|
+
|
|
304
|
+
host_os = RbConfig::CONFIG.fetch("host_os", "")
|
|
305
|
+
case host_os
|
|
306
|
+
when /darwin/i
|
|
307
|
+
"libgts.dylib"
|
|
308
|
+
when /mswin|mingw|cygwin/i
|
|
309
|
+
"gts.dll"
|
|
310
|
+
else
|
|
311
|
+
"libgts.so"
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
def self.load(library = nil)
|
|
316
|
+
Library.new(library)
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def self.status_name(status)
|
|
320
|
+
STATUS_NAMES.fetch(status, "UNKNOWN")
|
|
321
|
+
end
|
|
322
|
+
end
|
|
323
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: gmeow-gts
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0.rc.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Blackcat Informatics
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: ffi
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '1.17'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '1.17'
|
|
26
|
+
description: A source-only Ruby FFI wrapper over the Rust-backed libgts C ABI. The
|
|
27
|
+
gem expects libgts to be provided by the host at runtime.
|
|
28
|
+
email:
|
|
29
|
+
- paudley@blackcatinformatics.ca
|
|
30
|
+
executables: []
|
|
31
|
+
extensions: []
|
|
32
|
+
extra_rdoc_files: []
|
|
33
|
+
files:
|
|
34
|
+
- README.md
|
|
35
|
+
- lib/gmeow/gts.rb
|
|
36
|
+
homepage: https://blackcatinformatics.ca/projects/gts
|
|
37
|
+
licenses:
|
|
38
|
+
- MIT
|
|
39
|
+
- Apache-2.0
|
|
40
|
+
metadata:
|
|
41
|
+
allowed_push_host: https://rubygems.org
|
|
42
|
+
changelog_uri: https://github.com/Blackcat-Informatics/gmeow-gts/blob/main/CHANGELOG.md
|
|
43
|
+
homepage_uri: https://blackcatinformatics.ca/projects/gts
|
|
44
|
+
rubygems_mfa_required: 'true'
|
|
45
|
+
source_code_uri: https://github.com/Blackcat-Informatics/gmeow-gts
|
|
46
|
+
bug_tracker_uri: https://github.com/Blackcat-Informatics/gmeow-gts/issues
|
|
47
|
+
rdoc_options: []
|
|
48
|
+
require_paths:
|
|
49
|
+
- lib
|
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '3.1'
|
|
55
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
56
|
+
requirements:
|
|
57
|
+
- - ">="
|
|
58
|
+
- !ruby/object:Gem::Version
|
|
59
|
+
version: '0'
|
|
60
|
+
requirements: []
|
|
61
|
+
rubygems_version: 3.6.9
|
|
62
|
+
specification_version: 4
|
|
63
|
+
summary: Ruby FFI wrapper for the source-only GTS C ABI.
|
|
64
|
+
test_files: []
|