#[repr(C)]
pub struct TestClientOpaque {
pub(crate) inner: Option<Box<spikard::TestClient>>,
}
#[no_mangle]
pub extern "C" fn spikard_test_client_new(app: *mut AppOpaque) -> *mut TestClientOpaque {
if app.is_null() {
return std::ptr::null_mut();
}
let inner_app = match unsafe { (*app).inner.take() } {
Some(boxed) => *boxed,
None => return std::ptr::null_mut(),
};
let router = match inner_app.into_router() {
Ok(r) => r,
Err(_) => return std::ptr::null_mut(),
};
let client = match spikard::TestClient::from_router(router) {
Ok(c) => c,
Err(_) => return std::ptr::null_mut(),
};
Box::into_raw(Box::new(TestClientOpaque {
inner: Some(Box::new(client)),
}))
}
#[no_mangle]
pub extern "C" fn spikard_test_client_free(ptr: *mut TestClientOpaque) {
if !ptr.is_null() {
unsafe { drop(Box::from_raw(ptr)) };
}
}
#[repr(C)]
pub struct TestResponseOpaque {
pub(crate) inner: Box<spikard::ResponseSnapshot>,
}
#[no_mangle]
pub extern "C" fn spikard_test_response_free(ptr: *mut TestResponseOpaque) {
if !ptr.is_null() {
unsafe { drop(Box::from_raw(ptr)) };
}
}
#[no_mangle]
pub extern "C" fn spikard_test_response_status(ptr: *const TestResponseOpaque) -> u16 {
if ptr.is_null() {
return 0;
}
unsafe { (*ptr).inner.status }
}
#[no_mangle]
pub extern "C" fn spikard_test_response_json(ptr: *const TestResponseOpaque) -> *mut c_char {
if ptr.is_null() {
return std::ptr::null_mut();
}
let snapshot = unsafe { &(*ptr).inner };
match snapshot.json() {
Ok(v) => {
let s = v.to_string();
CString::new(s).map(CString::into_raw).unwrap_or(std::ptr::null_mut())
}
Err(_) => std::ptr::null_mut(),
}
}
#[no_mangle]
pub extern "C" fn spikard_test_response_text(ptr: *const TestResponseOpaque) -> *mut c_char {
if ptr.is_null() {
return std::ptr::null_mut();
}
let snapshot = unsafe { &(*ptr).inner };
match snapshot.text() {
Ok(s) => CString::new(s).map(CString::into_raw).unwrap_or(std::ptr::null_mut()),
Err(_) => std::ptr::null_mut(),
}
}
fn block_on<F, T>(future: F) -> T
where
F: std::future::Future<Output = T>,
{
tokio::runtime::Runtime::new()
.expect("failed to create Tokio runtime for TestClient")
.block_on(future)
}
#[no_mangle]
pub extern "C" fn spikard_test_client_get(
ptr: *const TestClientOpaque,
path: *const c_char,
) -> *mut TestResponseOpaque {
if ptr.is_null() || path.is_null() {
return std::ptr::null_mut();
}
let client = match unsafe { (*ptr).inner.as_ref() } {
Some(c) => c,
None => return std::ptr::null_mut(),
};
let path_str = unsafe { CStr::from_ptr(path) }.to_string_lossy().into_owned();
match block_on(client.get(&path_str, None, None)) {
Ok(snapshot) => Box::into_raw(Box::new(TestResponseOpaque {
inner: Box::new(snapshot),
})),
Err(_) => std::ptr::null_mut(),
}
}
#[no_mangle]
pub extern "C" fn spikard_test_client_post(
ptr: *const TestClientOpaque,
path: *const c_char,
json_body: *const c_char,
) -> *mut TestResponseOpaque {
if ptr.is_null() || path.is_null() {
return std::ptr::null_mut();
}
let client = match unsafe { (*ptr).inner.as_ref() } {
Some(c) => c,
None => return std::ptr::null_mut(),
};
let path_str = unsafe { CStr::from_ptr(path) }.to_string_lossy().into_owned();
let body = if json_body.is_null() {
None
} else {
let raw = unsafe { CStr::from_ptr(json_body) }.to_string_lossy();
serde_json::from_str::<serde_json::Value>(&raw).ok()
};
match block_on(client.post(&path_str, body, None, None, None, None)) {
Ok(snapshot) => Box::into_raw(Box::new(TestResponseOpaque {
inner: Box::new(snapshot),
})),
Err(_) => std::ptr::null_mut(),
}
}
#[no_mangle]
pub extern "C" fn spikard_test_client_delete(
ptr: *const TestClientOpaque,
path: *const c_char,
) -> *mut TestResponseOpaque {
if ptr.is_null() || path.is_null() {
return std::ptr::null_mut();
}
let client = match unsafe { (*ptr).inner.as_ref() } {
Some(c) => c,
None => return std::ptr::null_mut(),
};
let path_str = unsafe { CStr::from_ptr(path) }.to_string_lossy().into_owned();
match block_on(client.delete(&path_str, None, None)) {
Ok(snapshot) => Box::into_raw(Box::new(TestResponseOpaque {
inner: Box::new(snapshot),
})),
Err(_) => std::ptr::null_mut(),
}
}