#![allow(
clippy::too_many_arguments,
clippy::not_unsafe_ptr_arg_deref,
unused_variables,
unused_mut
)]
use std::ffi::{c_char, c_void, CStr, CString};
use std::panic;
use std::sync::Arc;
pub struct FfiHandlerBridge {
callback: extern "C" fn(*mut c_void, *const c_char) -> *mut c_char,
context: *mut c_void,
}
unsafe impl Send for FfiHandlerBridge {}
unsafe impl Sync for FfiHandlerBridge {}
impl spikard::Handler for FfiHandlerBridge {
fn call(
&self,
_request: spikard::Request<spikard::Body>,
request_data: spikard::RequestData,
) -> std::pin::Pin<Box<dyn std::future::Future<Output = spikard::HandlerResult> + Send + '_>> {
Box::pin(async move {
let outcome: Result<spikard::Response, Box<dyn std::error::Error + Send + Sync>> = async move {
let req_json = serde_json::to_string(&request_data)
.map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send + Sync>)?;
let req_c_str =
CString::new(req_json).map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send + Sync>)?;
let callback = self.callback;
let context = self.context as usize;
let resp_addr = tokio::task::spawn_blocking(move || {
(callback)(context as *mut c_void, req_c_str.as_ptr()) as usize
})
.await
.map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send + Sync>)?;
let resp_ptr = resp_addr as *mut c_char;
if resp_ptr.is_null() {
return Err("C callback returned null response".into());
}
let resp_c_str = unsafe { CStr::from_ptr(resp_ptr) };
let resp_json = resp_c_str.to_string_lossy();
let response: spikard::Response = serde_json::from_str(&resp_json)
.map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send + Sync>)?;
unsafe {
extern "C" {
fn free(ptr: *mut c_void);
}
free(resp_ptr as *mut c_void);
}
Ok(response)
}
.await;
spikard::handler_result_from_response(outcome)
})
}
}
#[repr(C)]
pub struct AppOpaque {
inner: Option<Box<spikard::App>>,
}
#[no_mangle]
pub extern "C" fn spikard_app_new() -> *mut AppOpaque {
let owner = spikard::App::new();
Box::into_raw(Box::new(AppOpaque {
inner: Some(Box::new(owner)),
}))
}
#[no_mangle]
pub extern "C" fn spikard_app_free(ptr: *mut AppOpaque) {
if !ptr.is_null() {
unsafe {
drop(Box::from_raw(ptr));
}
}
}
#[no_mangle]
pub extern "C" fn spikard_app_register_route(
owner: *mut AppOpaque,
callback: extern "C" fn(*mut c_void, *const c_char) -> *mut c_char,
context: *mut c_void,
builder: *mut spikard::RouteBuilder,
) -> i32 {
if owner.is_null() {
return 1; }
if builder.is_null() {
return 1; }
let builder = unsafe { &*builder };
let bridge = FfiHandlerBridge { callback, context };
let handler: Arc<dyn spikard::Handler> = Arc::new(bridge);
match unsafe {
match (*owner).inner.as_mut() {
Some(owner_ref) => owner_ref.route(builder.clone(), handler),
None => return 1, }
} {
Ok(_) => 0, Err(_) => 1, }
}
#[no_mangle]
pub extern "C" fn spikard_app_get(
owner: *mut AppOpaque,
callback: extern "C" fn(*mut c_void, *const c_char) -> *mut c_char,
context: *mut c_void,
path: *const c_char,
) -> i32 {
if owner.is_null() {
return 1; }
if path.is_null() {
return 1; }
let path = if path.is_null() {
String::new()
} else {
unsafe { CStr::from_ptr(path) }.to_string_lossy().into_owned()
};
let builder = spikard::RouteBuilder::new(spikard::Method::Get, path);
let bridge = FfiHandlerBridge { callback, context };
let handler: Arc<dyn spikard::Handler> = Arc::new(bridge);
match unsafe {
match (*owner).inner.as_mut() {
Some(owner_ref) => owner_ref.route(builder, handler),
None => return 1, }
} {
Ok(_) => 0, Err(_) => 1, }
}
#[no_mangle]
pub extern "C" fn spikard_app_post(
owner: *mut AppOpaque,
callback: extern "C" fn(*mut c_void, *const c_char) -> *mut c_char,
context: *mut c_void,
path: *const c_char,
) -> i32 {
if owner.is_null() {
return 1; }
if path.is_null() {
return 1; }
let path = if path.is_null() {
String::new()
} else {
unsafe { CStr::from_ptr(path) }.to_string_lossy().into_owned()
};
let builder = spikard::RouteBuilder::new(spikard::Method::Post, path);
let bridge = FfiHandlerBridge { callback, context };
let handler: Arc<dyn spikard::Handler> = Arc::new(bridge);
match unsafe {
match (*owner).inner.as_mut() {
Some(owner_ref) => owner_ref.route(builder, handler),
None => return 1, }
} {
Ok(_) => 0, Err(_) => 1, }
}
#[no_mangle]
pub extern "C" fn spikard_app_put(
owner: *mut AppOpaque,
callback: extern "C" fn(*mut c_void, *const c_char) -> *mut c_char,
context: *mut c_void,
path: *const c_char,
) -> i32 {
if owner.is_null() {
return 1; }
if path.is_null() {
return 1; }
let path = if path.is_null() {
String::new()
} else {
unsafe { CStr::from_ptr(path) }.to_string_lossy().into_owned()
};
let builder = spikard::RouteBuilder::new(spikard::Method::Put, path);
let bridge = FfiHandlerBridge { callback, context };
let handler: Arc<dyn spikard::Handler> = Arc::new(bridge);
match unsafe {
match (*owner).inner.as_mut() {
Some(owner_ref) => owner_ref.route(builder, handler),
None => return 1, }
} {
Ok(_) => 0, Err(_) => 1, }
}
#[no_mangle]
pub extern "C" fn spikard_app_patch(
owner: *mut AppOpaque,
callback: extern "C" fn(*mut c_void, *const c_char) -> *mut c_char,
context: *mut c_void,
path: *const c_char,
) -> i32 {
if owner.is_null() {
return 1; }
if path.is_null() {
return 1; }
let path = if path.is_null() {
String::new()
} else {
unsafe { CStr::from_ptr(path) }.to_string_lossy().into_owned()
};
let builder = spikard::RouteBuilder::new(spikard::Method::Patch, path);
let bridge = FfiHandlerBridge { callback, context };
let handler: Arc<dyn spikard::Handler> = Arc::new(bridge);
match unsafe {
match (*owner).inner.as_mut() {
Some(owner_ref) => owner_ref.route(builder, handler),
None => return 1, }
} {
Ok(_) => 0, Err(_) => 1, }
}
#[no_mangle]
pub extern "C" fn spikard_app_delete(
owner: *mut AppOpaque,
callback: extern "C" fn(*mut c_void, *const c_char) -> *mut c_char,
context: *mut c_void,
path: *const c_char,
) -> i32 {
if owner.is_null() {
return 1; }
if path.is_null() {
return 1; }
let path = if path.is_null() {
String::new()
} else {
unsafe { CStr::from_ptr(path) }.to_string_lossy().into_owned()
};
let builder = spikard::RouteBuilder::new(spikard::Method::Delete, path);
let bridge = FfiHandlerBridge { callback, context };
let handler: Arc<dyn spikard::Handler> = Arc::new(bridge);
match unsafe {
match (*owner).inner.as_mut() {
Some(owner_ref) => owner_ref.route(builder, handler),
None => return 1, }
} {
Ok(_) => 0, Err(_) => 1, }
}
#[no_mangle]
pub extern "C" fn spikard_app_head(
owner: *mut AppOpaque,
callback: extern "C" fn(*mut c_void, *const c_char) -> *mut c_char,
context: *mut c_void,
path: *const c_char,
) -> i32 {
if owner.is_null() {
return 1; }
if path.is_null() {
return 1; }
let path = if path.is_null() {
String::new()
} else {
unsafe { CStr::from_ptr(path) }.to_string_lossy().into_owned()
};
let builder = spikard::RouteBuilder::new(spikard::Method::Head, path);
let bridge = FfiHandlerBridge { callback, context };
let handler: Arc<dyn spikard::Handler> = Arc::new(bridge);
match unsafe {
match (*owner).inner.as_mut() {
Some(owner_ref) => owner_ref.route(builder, handler),
None => return 1, }
} {
Ok(_) => 0, Err(_) => 1, }
}
#[no_mangle]
pub extern "C" fn spikard_app_options(
owner: *mut AppOpaque,
callback: extern "C" fn(*mut c_void, *const c_char) -> *mut c_char,
context: *mut c_void,
path: *const c_char,
) -> i32 {
if owner.is_null() {
return 1; }
if path.is_null() {
return 1; }
let path = if path.is_null() {
String::new()
} else {
unsafe { CStr::from_ptr(path) }.to_string_lossy().into_owned()
};
let builder = spikard::RouteBuilder::new(spikard::Method::Options, path);
let bridge = FfiHandlerBridge { callback, context };
let handler: Arc<dyn spikard::Handler> = Arc::new(bridge);
match unsafe {
match (*owner).inner.as_mut() {
Some(owner_ref) => owner_ref.route(builder, handler),
None => return 1, }
} {
Ok(_) => 0, Err(_) => 1, }
}
#[no_mangle]
pub extern "C" fn spikard_app_connect(
owner: *mut AppOpaque,
callback: extern "C" fn(*mut c_void, *const c_char) -> *mut c_char,
context: *mut c_void,
path: *const c_char,
) -> i32 {
if owner.is_null() {
return 1; }
if path.is_null() {
return 1; }
let path = if path.is_null() {
String::new()
} else {
unsafe { CStr::from_ptr(path) }.to_string_lossy().into_owned()
};
let builder = spikard::RouteBuilder::new(spikard::Method::Connect, path);
let bridge = FfiHandlerBridge { callback, context };
let handler: Arc<dyn spikard::Handler> = Arc::new(bridge);
match unsafe {
match (*owner).inner.as_mut() {
Some(owner_ref) => owner_ref.route(builder, handler),
None => return 1, }
} {
Ok(_) => 0, Err(_) => 1, }
}
#[no_mangle]
pub extern "C" fn spikard_app_trace(
owner: *mut AppOpaque,
callback: extern "C" fn(*mut c_void, *const c_char) -> *mut c_char,
context: *mut c_void,
path: *const c_char,
) -> i32 {
if owner.is_null() {
return 1; }
if path.is_null() {
return 1; }
let path = if path.is_null() {
String::new()
} else {
unsafe { CStr::from_ptr(path) }.to_string_lossy().into_owned()
};
let builder = spikard::RouteBuilder::new(spikard::Method::Trace, path);
let bridge = FfiHandlerBridge { callback, context };
let handler: Arc<dyn spikard::Handler> = Arc::new(bridge);
match unsafe {
match (*owner).inner.as_mut() {
Some(owner_ref) => owner_ref.route(builder, handler),
None => return 1, }
} {
Ok(_) => 0, Err(_) => 1, }
}
#[no_mangle]
pub extern "C" fn spikard_app_config(owner: *mut AppOpaque, config: *mut spikard::ServerConfig) -> *mut AppOpaque {
if owner.is_null() {
return std::ptr::null_mut();
}
if config.is_null() {
return std::ptr::null_mut();
}
let config = unsafe { &*config };
unsafe {
let inner = match (*owner).inner.take() {
Some(boxed) => *boxed,
None => return std::ptr::null_mut(),
};
(*owner).inner = Some(Box::new(inner.config(config.clone())));
}
owner
}
#[no_mangle]
pub extern "C" fn spikard_app_ep_run(owner: *mut AppOpaque) -> i32 {
if owner.is_null() {
return 1;
}
let inner = match unsafe { (*owner).inner.take() } {
Some(boxed) => *boxed,
None => return 1,
};
let rt = tokio::runtime::Runtime::new().expect("failed to create tokio runtime");
match rt.block_on(inner.run()) {
Ok(_) => 0,
Err(_) => 1,
}
}
#[no_mangle]
pub extern "C" fn spikard_app_ep_into_router(owner: *mut AppOpaque) -> i32 {
if owner.is_null() {
return 1;
}
let inner = match unsafe { (*owner).inner.take() } {
Some(boxed) => *boxed,
None => return 1,
};
match inner.into_router() {
Ok(_) => 0,
Err(_) => 1,
}
}