1use core::slice;
6
7#[cfg(target_arch = "wasm32")]
12#[link(wasm_import_module = "env")]
13unsafe extern "C" {
14 pub fn state_get(key_ptr: i32, key_len: i32) -> i32;
16 pub fn state_set(key_ptr: i32, key_len: i32, value_ptr: i32, value_len: i32) -> i32;
17 pub fn state_remove(key_ptr: i32, key_len: i32) -> i32;
18
19 pub fn log(level: i32, ptr: i32, len: i32);
21
22 pub fn db_query(query_ptr: i32, query_len: i32, params_ptr: i32, params_len: i32) -> i32;
24 pub fn db_execute(query_ptr: i32, query_len: i32, params_ptr: i32, params_len: i32) -> i32;
25
26 pub fn http_request(
28 method_ptr: i32,
29 method_len: i32,
30 url_ptr: i32,
31 url_len: i32,
32 headers_ptr: i32,
33 headers_len: i32,
34 body_ptr: i32,
35 body_len: i32,
36 ) -> i32;
37
38 pub fn emit_event(event_ptr: i32, event_len: i32, payload_ptr: i32, payload_len: i32) -> i32;
40
41 pub fn get_config(key_ptr: i32, key_len: i32) -> i32;
43
44 pub fn crypto_hash(algorithm: i32, data_ptr: i32, data_len: i32) -> i32;
46 pub fn crypto_random(len: i32) -> i32;
47}
48
49#[cfg(not(target_arch = "wasm32"))]
51pub fn log(level: i32, ptr: i32, len: i32) {
52 let message = unsafe {
53 let slice = slice::from_raw_parts(ptr as *const u8, len as usize);
54 std::str::from_utf8(slice).unwrap_or("<invalid utf8>")
55 };
56 let level_str = match level {
57 0 => "ERROR",
58 1 => "WARN",
59 2 => "INFO",
60 3 => "DEBUG",
61 _ => "TRACE",
62 };
63 eprintln!("[{}] {}", level_str, message);
64}
65
66pub fn allocate_internal(size: i32) -> *mut u8 {
72 let layout = core::alloc::Layout::from_size_align(size as usize, 1).unwrap();
73 unsafe { std::alloc::alloc(layout) }
75}
76
77pub fn deallocate_internal(ptr: *mut u8, size: i32) {
79 if ptr.is_null() {
80 return;
81 }
82 let layout = core::alloc::Layout::from_size_align(size as usize, 1).unwrap();
83 unsafe { std::alloc::dealloc(ptr, layout) }
85}
86
87#[inline]
96pub unsafe fn read_bytes(ptr: *const u8, len: usize) -> Vec<u8> {
97 if ptr.is_null() || len == 0 {
98 return Vec::new();
99 }
100 unsafe { slice::from_raw_parts(ptr, len).to_vec() }
101}
102
103#[inline]
110pub unsafe fn read_length_prefixed(ptr: i32) -> Vec<u8> {
111 if ptr == 0 {
112 return Vec::new();
113 }
114
115 let ptr = ptr as *const u8;
116
117 unsafe {
118 let len_bytes = slice::from_raw_parts(ptr, 4);
120 let len = u32::from_le_bytes([len_bytes[0], len_bytes[1], len_bytes[2], len_bytes[3]]);
121
122 const MAX_LENGTH: u32 = 10 * 1024 * 1024;
125 if len > MAX_LENGTH {
126 log(0, "Invalid length in read_length_prefixed".as_ptr() as i32, "Invalid length in read_length_prefixed".len() as i32);
127 return Vec::new();
128 }
129
130 if len == 0 {
132 return Vec::new();
133 }
134
135 let data_ptr = ptr.add(4);
136 let data_slice = slice::from_raw_parts(data_ptr, len as usize);
137
138 Vec::from(data_slice)
140 }
141}
142
143#[inline]
147pub fn write_length_prefixed(data: &[u8]) -> *mut u8 {
148 let len = data.len() as u32;
149 let total_size = 4 + data.len();
150 let ptr = allocate_internal(total_size as i32);
151
152 unsafe {
153 *(ptr as *mut u32) = len;
155 let data_ptr = ptr.add(4);
157 core::ptr::copy_nonoverlapping(data.as_ptr(), data_ptr, data.len());
158 }
159
160 ptr
161}
162
163#[inline]
165pub fn return_bytes(data: &[u8]) -> i32 {
166 write_length_prefixed(data) as i32
167}
168
169#[inline]
171pub fn return_json<T: serde::Serialize>(value: &T) -> Result<i32, serde_json::Error> {
172 let json = serde_json::to_vec(value)?;
173 Ok(return_bytes(&json))
174}
175
176#[macro_export]
196macro_rules! wrap_handler {
197 ($export_name:ident, $handler_fn:ident) => {
198 #[unsafe(no_mangle)]
199 pub extern "C" fn $export_name(ctx_ptr: i32, ctx_len: i32) -> i32 {
200 use $crate::sdk::prelude::*;
201
202 let ctx = match Context::from_raw(ctx_ptr, ctx_len) {
204 Ok(c) => c,
205 Err(e) => {
206 let error_message = format!("Failed to parse context: {}", e);
208 unsafe { $crate::sdk::ffi::log(0, error_message.as_ptr() as i32, error_message.len() as i32); }
209 return Response::error(400, &format!("Invalid context: {}", e))
210 .to_raw()
211 .unwrap_or(0);
212 }
213 };
214
215 match $handler_fn(ctx) {
217 Ok(response) => response.to_raw().unwrap_or(0),
218 Err(e) => {
219 let error_message = format!("Handler error: {}", e);
220 unsafe { $crate::sdk::ffi::log(0, error_message.as_ptr() as i32, error_message.len() as i32); }
221 Response::error(500, &e.to_string())
222 .to_raw()
223 .unwrap_or(0)
224 }
225 }
226 }
227 };
228}
229#[macro_export]
236macro_rules! orbis_allocators {
237 () => {
238 #[unsafe(no_mangle)]
240 #[inline(never)]
241 pub extern "C" fn allocate(size: i32) -> *mut u8 {
242 if size <= 0 {
243 return core::ptr::null_mut();
244 }
245 use core::alloc::Layout;
246 let layout = match Layout::from_size_align(size as usize, 1) {
247 Ok(l) => l,
248 Err(_) => return core::ptr::null_mut(),
249 };
250 let ptr = unsafe { std::alloc::alloc(layout) };
252 if ptr.is_null() {
253 return core::ptr::null_mut();
254 }
255 ptr
256 }
257
258 #[unsafe(no_mangle)]
259 #[inline(never)]
260 pub extern "C" fn deallocate(ptr: *mut u8, size: i32) {
261 if ptr.is_null() || size <= 0 {
262 return;
263 }
264 use core::alloc::Layout;
265 let layout = match Layout::from_size_align(size as usize, 1) {
266 Ok(l) => l,
267 Err(_) => return,
268 };
269 unsafe { std::alloc::dealloc(ptr, layout) }
271 }
272 };
273}
274
275#[macro_export]
295macro_rules! orbis_plugin {
296 (
298 init: $init:expr,
299 cleanup: $cleanup:expr $(,)?
300 ) => {
301 #[unsafe(no_mangle)]
302 pub extern "C" fn init() -> i32 {
303 let init_fn: fn() -> $crate::sdk::Result<()> = $init;
304 match init_fn() {
305 Ok(()) => 1,
306 Err(e) => {
307 let error_message = format!("Init failed: {}", e);
308 unsafe { $crate::sdk::ffi::log(0, error_message.as_ptr() as i32, error_message.len() as i32); }
309 0
310 }
311 }
312 }
313
314 #[unsafe(no_mangle)]
315 pub extern "C" fn cleanup() -> i32 {
316 let cleanup_fn: fn() -> $crate::sdk::Result<()> = $cleanup;
317 match cleanup_fn() {
318 Ok(()) => 1,
319 Err(e) => {
320 let error_message = format!("Cleanup failed: {}", e);
321 unsafe { $crate::sdk::ffi::log(0, error_message.as_ptr() as i32, error_message.len() as i32); }
322 0
323 }
324 }
325 }
326
327 $crate::sdk::ffi::orbis_allocators!();
328 };
329
330 (init: $init:expr $(,)?) => {
332 #[unsafe(no_mangle)]
333 pub extern "C" fn init() -> i32 {
334 let init_fn: fn() -> $crate::sdk::Result<()> = $init;
335 match init_fn() {
336 Ok(()) => 1,
337 Err(e) => {
338 let error_message = format!("Init failed: {}", e);
339 unsafe { $crate::sdk::ffi::log(0, error_message.as_ptr() as i32, error_message.len() as i32); }
340 0
341 }
342 }
343 }
344
345 #[unsafe(no_mangle)]
346 pub extern "C" fn cleanup() -> i32 {
347 1 }
349
350 $crate::sdk::ffi::orbis_allocators!();
351 };
352
353 () => {
355 #[unsafe(no_mangle)]
356 pub extern "C" fn init() -> i32 {
357 1
358 }
359
360 #[unsafe(no_mangle)]
361 pub extern "C" fn cleanup() -> i32 {
362 1
363 }
364
365 $crate::sdk::ffi::orbis_allocators!();
366 };
367}
368
369pub use orbis_plugin;
370pub use wrap_handler;
371pub use orbis_allocators;