proofmode 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,208 @@
1
+ # Foreign callback support for ProofModeCallbacks.
2
+ #
3
+ # The UniFFI Ruby backend does not generate vtable callback infrastructure
4
+ # for traits with `#[uniffi::export(with_foreign)]`. This file adds that
5
+ # support so Ruby code can implement ProofModeCallbacks and pass instances
6
+ # to generate_proof / check_files.
7
+ #
8
+ # Usage:
9
+ # require "proofmode"
10
+ # require "proofmode_callbacks"
11
+ #
12
+ # Then subclass Proofmode::ProofModeCallbacks and override the 7 methods.
13
+
14
+ require "proofmode"
15
+
16
+ module Proofmode
17
+ # Thread-safe handle map for storing foreign (Ruby) callback objects.
18
+ class CallbackHandleMap
19
+ def initialize
20
+ @mutex = Mutex.new
21
+ @map = {}
22
+ @next_handle = 1
23
+ end
24
+
25
+ def insert(obj)
26
+ @mutex.synchronize do
27
+ handle = (@next_handle << 1) | 1 # odd handles = foreign
28
+ @next_handle += 1
29
+ @map[handle] = obj
30
+ handle
31
+ end
32
+ end
33
+
34
+ def get(handle)
35
+ @mutex.synchronize do
36
+ @map.fetch(handle) { raise "CallbackHandleMap: unknown handle #{handle}" }
37
+ end
38
+ end
39
+
40
+ def remove(handle)
41
+ @mutex.synchronize do
42
+ @map.delete(handle)
43
+ end
44
+ end
45
+
46
+ def clone_handle(handle)
47
+ @mutex.synchronize do
48
+ obj = @map.fetch(handle) { raise "CallbackHandleMap: unknown handle #{handle}" }
49
+ new_handle = (@next_handle << 1) | 1
50
+ @next_handle += 1
51
+ @map[new_handle] = obj
52
+ new_handle
53
+ end
54
+ end
55
+ end
56
+
57
+ CALLBACK_HANDLE_MAP = CallbackHandleMap.new
58
+
59
+ # Access the private UniFFILib module using const_get
60
+ _uniffi_lib = const_get(:UniFFILib)
61
+
62
+ # Define vtable callback functions using FFI::Function.
63
+ # Each function receives a handle, arguments as RustBuffers, an out_return
64
+ # pointer, and a call_status pointer.
65
+
66
+ _vtable_free = FFI::Function.new(:void, [:uint64]) do |handle|
67
+ CALLBACK_HANDLE_MAP.remove(handle)
68
+ rescue => e
69
+ $stderr.puts "proofmode vtable free error: #{e}"
70
+ end
71
+
72
+ _vtable_clone = FFI::Function.new(:uint64, [:uint64]) do |handle|
73
+ CALLBACK_HANDLE_MAP.clone_handle(handle)
74
+ rescue => e
75
+ $stderr.puts "proofmode vtable clone error: #{e}"
76
+ 0
77
+ end
78
+
79
+ _vtable_get_location = FFI::Function.new(:void, [:uint64, :pointer, :pointer]) do |handle, out_return, _call_status|
80
+ obj = CALLBACK_HANDLE_MAP.get(handle)
81
+ result = obj.get_location
82
+ buf = RustBuffer.alloc_from_OptionalTypeLocationData(result)
83
+ # Copy RustBuffer struct bytes into the out_return pointer
84
+ out_return.put_uint64(0, buf[:capacity])
85
+ out_return.put_uint64(8, buf[:len])
86
+ out_return.put_pointer(16, buf[:data])
87
+ rescue => e
88
+ $stderr.puts "proofmode vtable get_location error: #{e}\n#{e.backtrace.first(5).join("\n")}"
89
+ end
90
+
91
+ _vtable_get_device_info = FFI::Function.new(:void, [:uint64, :pointer, :pointer]) do |handle, out_return, _call_status|
92
+ obj = CALLBACK_HANDLE_MAP.get(handle)
93
+ result = obj.get_device_info
94
+ buf = RustBuffer.alloc_from_OptionalTypeDeviceData(result)
95
+ out_return.put_uint64(0, buf[:capacity])
96
+ out_return.put_uint64(8, buf[:len])
97
+ out_return.put_pointer(16, buf[:data])
98
+ rescue => e
99
+ $stderr.puts "proofmode vtable get_device_info error: #{e}\n#{e.backtrace.first(5).join("\n")}"
100
+ end
101
+
102
+ _vtable_get_network_info = FFI::Function.new(:void, [:uint64, :pointer, :pointer]) do |handle, out_return, _call_status|
103
+ obj = CALLBACK_HANDLE_MAP.get(handle)
104
+ result = obj.get_network_info
105
+ buf = RustBuffer.alloc_from_OptionalTypeNetworkData(result)
106
+ out_return.put_uint64(0, buf[:capacity])
107
+ out_return.put_uint64(8, buf[:len])
108
+ out_return.put_pointer(16, buf[:data])
109
+ rescue => e
110
+ $stderr.puts "proofmode vtable get_network_info error: #{e}\n#{e.backtrace.first(5).join("\n")}"
111
+ end
112
+
113
+ # save_data: (handle, hash_buf, filename_buf, data_buf, out_return_i8_ptr, call_status_ptr)
114
+ # The RustBuffer args are passed by value. In Ruby FFI, callback functions
115
+ # receive by-value structs as pointers that we need to cast.
116
+ _vtable_save_data = FFI::Function.new(:void, [:uint64, RustBuffer.by_value, RustBuffer.by_value, RustBuffer.by_value, :pointer, :pointer]) do |handle, hash_buf, filename_buf, data_buf, out_return, _call_status|
117
+ obj = CALLBACK_HANDLE_MAP.get(handle)
118
+ hash_str = hash_buf.consumeIntoString
119
+ filename_str = filename_buf.consumeIntoString
120
+ data_bytes = data_buf.consumeIntoBytes
121
+ result = obj.save_data(hash_str, filename_str, data_bytes)
122
+ out_return.put_int8(0, result ? 1 : 0)
123
+ rescue => e
124
+ $stderr.puts "proofmode vtable save_data error: #{e}\n#{e.backtrace.first(5).join("\n")}"
125
+ end
126
+
127
+ _vtable_save_text = FFI::Function.new(:void, [:uint64, RustBuffer.by_value, RustBuffer.by_value, RustBuffer.by_value, :pointer, :pointer]) do |handle, hash_buf, filename_buf, text_buf, out_return, _call_status|
128
+ obj = CALLBACK_HANDLE_MAP.get(handle)
129
+ hash_str = hash_buf.consumeIntoString
130
+ filename_str = filename_buf.consumeIntoString
131
+ text_str = text_buf.consumeIntoString
132
+ result = obj.save_text(hash_str, filename_str, text_str)
133
+ out_return.put_int8(0, result ? 1 : 0)
134
+ rescue => e
135
+ $stderr.puts "proofmode vtable save_text error: #{e}\n#{e.backtrace.first(5).join("\n")}"
136
+ end
137
+
138
+ _vtable_sign_data = FFI::Function.new(:void, [:uint64, RustBuffer.by_value, :pointer, :pointer]) do |handle, data_buf, out_return, _call_status|
139
+ obj = CALLBACK_HANDLE_MAP.get(handle)
140
+ data_bytes = data_buf.consumeIntoBytes
141
+ result = obj.sign_data(data_bytes)
142
+ buf = RustBuffer.alloc_from_Optionalbytes(result)
143
+ out_return.put_uint64(0, buf[:capacity])
144
+ out_return.put_uint64(8, buf[:len])
145
+ out_return.put_pointer(16, buf[:data])
146
+ rescue => e
147
+ $stderr.puts "proofmode vtable sign_data error: #{e}\n#{e.backtrace.first(5).join("\n")}"
148
+ end
149
+
150
+ _vtable_report_progress = FFI::Function.new(:void, [:uint64, RustBuffer.by_value, :pointer, :pointer]) do |handle, message_buf, _out_return, _call_status|
151
+ obj = CALLBACK_HANDLE_MAP.get(handle)
152
+ message_str = message_buf.consumeIntoString
153
+ obj.report_progress(message_str)
154
+ rescue => e
155
+ $stderr.puts "proofmode vtable report_progress error: #{e}\n#{e.backtrace.first(5).join("\n")}"
156
+ end
157
+
158
+ # Build the vtable as an array of function pointers.
159
+ # Layout order: free, clone, get_location, get_device_info, get_network_info,
160
+ # save_data, save_text, sign_data, report_progress
161
+ vtable_fns = [
162
+ _vtable_free,
163
+ _vtable_clone,
164
+ _vtable_get_location,
165
+ _vtable_get_device_info,
166
+ _vtable_get_network_info,
167
+ _vtable_save_data,
168
+ _vtable_save_text,
169
+ _vtable_sign_data,
170
+ _vtable_report_progress,
171
+ ]
172
+
173
+ # Keep references alive to prevent GC
174
+ VTABLE_REFS = vtable_fns.freeze
175
+
176
+ # Allocate persistent memory for the vtable (array of function pointers)
177
+ VTABLE_PTR = FFI::MemoryPointer.new(:pointer, vtable_fns.length, true)
178
+ vtable_fns.each_with_index do |fn, i|
179
+ VTABLE_PTR.put_pointer(i * FFI::Pointer.size, fn)
180
+ end
181
+
182
+ # Register the vtable with Rust
183
+ _rust_call_status = const_get(:RustCallStatus).new
184
+ _uniffi_lib.uniffi_proofmode_fn_init_callback_vtable_proofmodecallbacks(
185
+ VTABLE_PTR, _rust_call_status
186
+ )
187
+
188
+ # Patch ProofModeCallbacks to support foreign (Ruby) implementations
189
+ class ProofModeCallbacks
190
+ class << self
191
+ def uniffi_check_lower(inst)
192
+ unless inst.is_a?(self)
193
+ raise TypeError, "Expected a ProofModeCallbacks instance, got #{inst}"
194
+ end
195
+ end
196
+
197
+ def uniffi_lower(inst)
198
+ if inst.instance_variable_defined?(:@handle)
199
+ # Rust-created object
200
+ inst.uniffi_clone_handle
201
+ else
202
+ # Ruby-created (foreign) object
203
+ CALLBACK_HANDLE_MAP.insert(inst)
204
+ end
205
+ end
206
+ end
207
+ end
208
+ end
data/proofmode.gemspec ADDED
@@ -0,0 +1,22 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "proofmode"
3
+ s.version = "0.8.0"
4
+ s.summary = "Ruby bindings for ProofMode"
5
+ s.description = "Capture, share, and preserve verifiable photos and videos using ProofMode"
6
+ s.authors = ["Guardian Project"]
7
+ s.email = "support@guardianproject.info"
8
+ s.homepage = "https://proofmode.org"
9
+ s.license = "Apache-2.0"
10
+
11
+ s.files = Dir["lib/**/*"] + ["proofmode.gemspec"]
12
+ s.require_paths = ["lib"]
13
+
14
+ s.required_ruby_version = ">= 2.7.0"
15
+
16
+ s.add_dependency "ffi", "~> 1.15"
17
+
18
+ s.metadata = {
19
+ "source_code_uri" => "https://gitlab.com/guardianproject/proofmode/proofmode-rust",
20
+ "bug_tracker_uri" => "https://gitlab.com/guardianproject/proofmode/proofmode-rust/-/issues"
21
+ }
22
+ end
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: proofmode
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.8.0
5
+ platform: ruby
6
+ authors:
7
+ - Guardian Project
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-02-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ffi
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.15'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.15'
27
+ description: Capture, share, and preserve verifiable photos and videos using ProofMode
28
+ email: support@guardianproject.info
29
+ executables: []
30
+ extensions: []
31
+ extra_rdoc_files: []
32
+ files:
33
+ - lib/libproofmode.so
34
+ - lib/proofmode.rb
35
+ - lib/proofmode_callbacks.rb
36
+ - proofmode.gemspec
37
+ homepage: https://proofmode.org
38
+ licenses:
39
+ - Apache-2.0
40
+ metadata:
41
+ source_code_uri: https://gitlab.com/guardianproject/proofmode/proofmode-rust
42
+ bug_tracker_uri: https://gitlab.com/guardianproject/proofmode/proofmode-rust/-/issues
43
+ post_install_message:
44
+ rdoc_options: []
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: 2.7.0
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ requirements: []
58
+ rubygems_version: 3.4.19
59
+ signing_key:
60
+ specification_version: 4
61
+ summary: Ruby bindings for ProofMode
62
+ test_files: []