use core::slice;
#[cfg(target_arch = "wasm32")]
#[link(wasm_import_module = "env")]
unsafe extern "C" {
pub fn state_get(key_ptr: i32, key_len: i32) -> i32;
pub fn state_set(key_ptr: i32, key_len: i32, value_ptr: i32, value_len: i32) -> i32;
pub fn state_remove(key_ptr: i32, key_len: i32) -> i32;
pub fn log(level: i32, ptr: i32, len: i32);
pub fn db_query(query_ptr: i32, query_len: i32, params_ptr: i32, params_len: i32) -> i32;
pub fn db_execute(query_ptr: i32, query_len: i32, params_ptr: i32, params_len: i32) -> i32;
pub fn http_request(
method_ptr: i32,
method_len: i32,
url_ptr: i32,
url_len: i32,
headers_ptr: i32,
headers_len: i32,
body_ptr: i32,
body_len: i32,
) -> i32;
pub fn emit_event(event_ptr: i32, event_len: i32, payload_ptr: i32, payload_len: i32) -> i32;
pub fn get_config(key_ptr: i32, key_len: i32) -> i32;
pub fn crypto_hash(algorithm: i32, data_ptr: i32, data_len: i32) -> i32;
pub fn crypto_random(len: i32) -> i32;
}
#[cfg(not(target_arch = "wasm32"))]
pub fn log(level: i32, ptr: i32, len: i32) {
let message = unsafe {
let slice = slice::from_raw_parts(ptr as *const u8, len as usize);
std::str::from_utf8(slice).unwrap_or("<invalid utf8>")
};
let level_str = match level {
0 => "ERROR",
1 => "WARN",
2 => "INFO",
3 => "DEBUG",
_ => "TRACE",
};
eprintln!("[{}] {}", level_str, message);
}
pub fn allocate_internal(size: i32) -> *mut u8 {
let layout = core::alloc::Layout::from_size_align(size as usize, 1).unwrap();
unsafe { std::alloc::alloc(layout) }
}
pub fn deallocate_internal(ptr: *mut u8, size: i32) {
if ptr.is_null() {
return;
}
let layout = core::alloc::Layout::from_size_align(size as usize, 1).unwrap();
unsafe { std::alloc::dealloc(ptr, layout) }
}
#[inline]
pub unsafe fn read_bytes(ptr: *const u8, len: usize) -> Vec<u8> {
if ptr.is_null() || len == 0 {
return Vec::new();
}
unsafe { slice::from_raw_parts(ptr, len).to_vec() }
}
#[inline]
pub unsafe fn read_length_prefixed(ptr: i32) -> Vec<u8> {
if ptr == 0 {
return Vec::new();
}
let ptr = ptr as *const u8;
unsafe {
let len_bytes = slice::from_raw_parts(ptr, 4);
let len = u32::from_le_bytes([len_bytes[0], len_bytes[1], len_bytes[2], len_bytes[3]]);
const MAX_LENGTH: u32 = 10 * 1024 * 1024;
if len > MAX_LENGTH {
log(0, "Invalid length in read_length_prefixed".as_ptr() as i32, "Invalid length in read_length_prefixed".len() as i32);
return Vec::new();
}
if len == 0 {
return Vec::new();
}
let data_ptr = ptr.add(4);
let data_slice = slice::from_raw_parts(data_ptr, len as usize);
Vec::from(data_slice)
}
}
#[inline]
pub fn write_length_prefixed(data: &[u8]) -> *mut u8 {
let len = data.len() as u32;
let total_size = 4 + data.len();
let ptr = allocate_internal(total_size as i32);
unsafe {
*(ptr as *mut u32) = len;
let data_ptr = ptr.add(4);
core::ptr::copy_nonoverlapping(data.as_ptr(), data_ptr, data.len());
}
ptr
}
#[inline]
pub fn return_bytes(data: &[u8]) -> i32 {
write_length_prefixed(data) as i32
}
#[inline]
pub fn return_json<T: serde::Serialize>(value: &T) -> Result<i32, serde_json::Error> {
let json = serde_json::to_vec(value)?;
Ok(return_bytes(&json))
}
#[macro_export]
macro_rules! wrap_handler {
($export_name:ident, $handler_fn:ident) => {
#[unsafe(no_mangle)]
pub extern "C" fn $export_name(ctx_ptr: i32, ctx_len: i32) -> i32 {
use $crate::sdk::prelude::*;
let ctx = match Context::from_raw(ctx_ptr, ctx_len) {
Ok(c) => c,
Err(e) => {
let error_message = format!("Failed to parse context: {}", e);
unsafe { $crate::sdk::ffi::log(0, error_message.as_ptr() as i32, error_message.len() as i32); }
return Response::error(400, &format!("Invalid context: {}", e))
.to_raw()
.unwrap_or(0);
}
};
match $handler_fn(ctx) {
Ok(response) => response.to_raw().unwrap_or(0),
Err(e) => {
let error_message = format!("Handler error: {}", e);
unsafe { $crate::sdk::ffi::log(0, error_message.as_ptr() as i32, error_message.len() as i32); }
Response::error(500, &e.to_string())
.to_raw()
.unwrap_or(0)
}
}
}
};
}
#[macro_export]
macro_rules! orbis_allocators {
() => {
#[unsafe(no_mangle)]
#[inline(never)]
pub extern "C" fn allocate(size: i32) -> *mut u8 {
if size <= 0 {
return core::ptr::null_mut();
}
use core::alloc::Layout;
let layout = match Layout::from_size_align(size as usize, 1) {
Ok(l) => l,
Err(_) => return core::ptr::null_mut(),
};
let ptr = unsafe { std::alloc::alloc(layout) };
if ptr.is_null() {
return core::ptr::null_mut();
}
ptr
}
#[unsafe(no_mangle)]
#[inline(never)]
pub extern "C" fn deallocate(ptr: *mut u8, size: i32) {
if ptr.is_null() || size <= 0 {
return;
}
use core::alloc::Layout;
let layout = match Layout::from_size_align(size as usize, 1) {
Ok(l) => l,
Err(_) => return,
};
unsafe { std::alloc::dealloc(ptr, layout) }
}
};
}
#[macro_export]
macro_rules! orbis_plugin {
(
init: $init:expr,
cleanup: $cleanup:expr $(,)?
) => {
#[unsafe(no_mangle)]
pub extern "C" fn init() -> i32 {
let init_fn: fn() -> $crate::sdk::Result<()> = $init;
match init_fn() {
Ok(()) => 1,
Err(e) => {
let error_message = format!("Init failed: {}", e);
unsafe { $crate::sdk::ffi::log(0, error_message.as_ptr() as i32, error_message.len() as i32); }
0
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn cleanup() -> i32 {
let cleanup_fn: fn() -> $crate::sdk::Result<()> = $cleanup;
match cleanup_fn() {
Ok(()) => 1,
Err(e) => {
let error_message = format!("Cleanup failed: {}", e);
unsafe { $crate::sdk::ffi::log(0, error_message.as_ptr() as i32, error_message.len() as i32); }
0
}
}
}
$crate::sdk::ffi::orbis_allocators!();
};
(init: $init:expr $(,)?) => {
#[unsafe(no_mangle)]
pub extern "C" fn init() -> i32 {
let init_fn: fn() -> $crate::sdk::Result<()> = $init;
match init_fn() {
Ok(()) => 1,
Err(e) => {
let error_message = format!("Init failed: {}", e);
unsafe { $crate::sdk::ffi::log(0, error_message.as_ptr() as i32, error_message.len() as i32); }
0
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn cleanup() -> i32 {
1 }
$crate::sdk::ffi::orbis_allocators!();
};
() => {
#[unsafe(no_mangle)]
pub extern "C" fn init() -> i32 {
1
}
#[unsafe(no_mangle)]
pub extern "C" fn cleanup() -> i32 {
1
}
$crate::sdk::ffi::orbis_allocators!();
};
}
pub use orbis_plugin;
pub use wrap_handler;
pub use orbis_allocators;