1use std::cell::RefCell;
2use std::ffi::{CStr, c_char, c_int};
3
4use heeranjid::{HeerId, RanjId};
5
6thread_local! {
9 static LAST_ERROR: RefCell<String> = const { RefCell::new(String::new()) };
10}
11
12fn set_last_error(msg: impl Into<String>) {
13 LAST_ERROR.with(|e| *e.borrow_mut() = msg.into());
14}
15
16#[unsafe(no_mangle)]
25pub unsafe extern "C" fn heer_last_error(buf: *mut c_char, buf_len: c_int) -> c_int {
26 LAST_ERROR.with(|e| {
27 let msg = e.borrow();
28 if msg.is_empty() {
29 return 0;
30 }
31 if buf.is_null() || buf_len <= 0 {
32 return msg.len() as c_int;
33 }
34 let max = (buf_len as usize) - 1; let copy_len = msg.len().min(max);
36 unsafe {
37 std::ptr::copy_nonoverlapping(msg.as_ptr(), buf as *mut u8, copy_len);
38 *buf.add(copy_len) = 0; }
40 copy_len as c_int
41 })
42}
43
44pub type HeerIdT = i64;
48
49#[unsafe(no_mangle)]
56pub unsafe extern "C" fn heer_id_decode(
57 id: HeerIdT,
58 timestamp_ms: *mut u64,
59 node_id: *mut u16,
60 sequence: *mut u16,
61) -> c_int {
62 match HeerId::from_i64(id) {
63 Ok(hid) => {
64 let parts = hid.into_parts();
65 unsafe {
66 if !timestamp_ms.is_null() {
67 *timestamp_ms = parts.timestamp_ms;
68 }
69 if !node_id.is_null() {
70 *node_id = parts.node_id;
71 }
72 if !sequence.is_null() {
73 *sequence = parts.sequence;
74 }
75 }
76 0
77 }
78 Err(e) => {
79 set_last_error(e.to_string());
80 -1
81 }
82 }
83}
84
85#[unsafe(no_mangle)]
93pub unsafe extern "C" fn heer_id_to_string(id: HeerIdT, buf: *mut c_char, buf_len: c_int) -> c_int {
94 match HeerId::from_i64(id) {
95 Ok(hid) => {
96 let s = hid.to_string();
97 if buf.is_null() || buf_len <= 0 {
98 set_last_error("null buffer");
99 return -1;
100 }
101 let max = (buf_len as usize) - 1;
102 if s.len() > max {
103 set_last_error("buffer too small");
104 return -1;
105 }
106 unsafe {
107 std::ptr::copy_nonoverlapping(s.as_ptr(), buf as *mut u8, s.len());
108 *buf.add(s.len()) = 0;
109 }
110 s.len() as c_int
111 }
112 Err(e) => {
113 set_last_error(e.to_string());
114 -1
115 }
116 }
117}
118
119#[unsafe(no_mangle)]
127pub unsafe extern "C" fn heer_id_from_string(s: *const c_char, out: *mut HeerIdT) -> c_int {
128 if s.is_null() || out.is_null() {
129 set_last_error("null pointer");
130 return -1;
131 }
132 let cstr = unsafe { CStr::from_ptr(s) };
133 let rust_str = match cstr.to_str() {
134 Ok(v) => v,
135 Err(e) => {
136 set_last_error(e.to_string());
137 return -1;
138 }
139 };
140 match rust_str.parse::<HeerId>() {
141 Ok(hid) => {
142 unsafe { *out = hid.as_i64() };
143 0
144 }
145 Err(e) => {
146 set_last_error(e.to_string());
147 -1
148 }
149 }
150}
151
152#[repr(C)]
156pub struct RanjIdT {
157 pub bytes: [u8; 16],
158}
159
160#[unsafe(no_mangle)]
167pub unsafe extern "C" fn ranj_id_decode(
168 id: *const RanjIdT,
169 timestamp_us: *mut u64,
170 node_id: *mut u16,
171 sequence: *mut u16,
172) -> c_int {
173 if id.is_null() {
174 set_last_error("null pointer");
175 return -1;
176 }
177 let bytes = unsafe { &(*id).bytes };
178 let uuid = uuid::Uuid::from_bytes(*bytes);
179 match RanjId::from_uuid(uuid) {
180 Ok(rid) => {
181 unsafe {
182 if !timestamp_us.is_null() {
183 *timestamp_us = rid.timestamp_micros() as u64;
186 }
187 if !node_id.is_null() {
188 *node_id = rid.node_id();
189 }
190 if !sequence.is_null() {
191 *sequence = rid.sequence();
192 }
193 }
194 0
195 }
196 Err(e) => {
197 set_last_error(e.to_string());
198 -1
199 }
200 }
201}
202
203#[unsafe(no_mangle)]
211pub unsafe extern "C" fn ranj_id_to_string(
212 id: *const RanjIdT,
213 buf: *mut c_char,
214 buf_len: c_int,
215) -> c_int {
216 if id.is_null() {
217 set_last_error("null pointer");
218 return -1;
219 }
220 let bytes = unsafe { &(*id).bytes };
221 let uuid = uuid::Uuid::from_bytes(*bytes);
222 match RanjId::from_uuid(uuid) {
223 Ok(rid) => {
224 let s = rid.to_string();
225 if buf.is_null() || buf_len <= 0 {
226 set_last_error("null buffer");
227 return -1;
228 }
229 let max = (buf_len as usize) - 1;
230 if s.len() > max {
231 set_last_error("buffer too small");
232 return -1;
233 }
234 unsafe {
235 std::ptr::copy_nonoverlapping(s.as_ptr(), buf as *mut u8, s.len());
236 *buf.add(s.len()) = 0;
237 }
238 s.len() as c_int
239 }
240 Err(e) => {
241 set_last_error(e.to_string());
242 -1
243 }
244 }
245}
246
247#[unsafe(no_mangle)]
255pub unsafe extern "C" fn ranj_id_from_string(s: *const c_char, out: *mut RanjIdT) -> c_int {
256 if s.is_null() || out.is_null() {
257 set_last_error("null pointer");
258 return -1;
259 }
260 let cstr = unsafe { CStr::from_ptr(s) };
261 let rust_str = match cstr.to_str() {
262 Ok(v) => v,
263 Err(e) => {
264 set_last_error(e.to_string());
265 return -1;
266 }
267 };
268 match rust_str.parse::<RanjId>() {
269 Ok(rid) => {
270 unsafe {
271 (*out).bytes = *rid.as_uuid().as_bytes();
272 }
273 0
274 }
275 Err(e) => {
276 set_last_error(e.to_string());
277 -1
278 }
279 }
280}