// Callback registration for {{ service_name }}::{{ reg_snake }}
// This function is called from Swift via @_silgen_name("{{ service_snake }}_{{ reg_snake }}_via_callback").
// It is defined OUTSIDE the swift-bridge bridge module because swift-bridge 0.1.59
// cannot parse raw pointer types or extern "C" fn function pointers in extern "Rust" blocks.
//
// SAFETY: The opaque pointer (app) is the swift-bridge-wrapped handle from the
// bridge module. swift-bridge emits it as #[repr(transparent)] around the service,
// so reconstituting via &mut *app is safe given the caller's guarantee of validity.
// swift-bridge generates its own ABI for opaque wrapper types; the rustc warning
// about non-FFI-safe param types does not apply to the swift-bridge passing convention.
#[allow(improper_ctypes_definitions)]
#[unsafe(no_mangle)]
pub extern "C" fn {{ service_snake }}_{{ reg_snake }}_via_callback(
app: *mut {{ service_name }},
{%- for meta_param in metadata_params %}
{{ meta_param.name }}: {{ meta_param.rust_type }},
{%- endfor %}
ctx: *mut std::ffi::c_void,
callback: extern "C" fn(*mut std::ffi::c_void, *const u8, usize) -> *mut u8,
) -> i32 {
// SAFETY: The caller guarantees a valid, live pointer to the service instance.
let app = unsafe { &mut *app };
// Wrap the raw C callback function as Arc<dyn {{ trait_path }}>.
let handler: std::sync::Arc<dyn {{ trait_path }}> =
std::sync::Arc::new(SwiftCCallbackHandler { ctx, callback });
// Call the service's registration method with metadata params positionally + handler.
// Named metadata params arrive as swift-bridge tuple-struct wrappers; `.0` unwraps to
// the inner library type the service method expects.
app.inner.blocking_lock().as_mut().map_or(-1, |service| {
// Discard the registration result — the host-side wrapper conveys errors via
// status code only at this boundary; richer reporting is the service's job.
let _ = service.{{ method_name }}(
{%- for meta_param in metadata_params %}
{{ meta_param.name }}{% if meta_param.is_opaque_wrapper %}.0{% endif %},
{%- endfor %}
handler,
);
0
})
}
// Internal handler wrapper that adapts a raw C callback to the configured handler trait.
struct SwiftCCallbackHandler {
ctx: *mut std::ffi::c_void,
callback: extern "C" fn(*mut std::ffi::c_void, *const u8, usize) -> *mut u8,
}
unsafe impl Send for SwiftCCallbackHandler {}
unsafe impl Sync for SwiftCCallbackHandler {}
impl {{ trait_path }} for SwiftCCallbackHandler {
fn call(
&self,
_request: {{ source_crate }}::Request<{{ source_crate }}::Body>,
request_data: {{ request_path }},
) -> std::pin::Pin<Box<dyn std::future::Future<Output = {{ output_type }}> + Send + '_>> {
Box::pin(async move {
// Serialise request → JSON; on error, defer to the library-supplied adapter
// so no HTTP-specific types (StatusCode, axum::Body) leak into this generic
// codegen template.
let req_json = match serde_json::to_string(&request_data) {
Ok(s) => s,
Err(e) => {
let outcome: Result<{{ response_path }}, Box<dyn std::error::Error + Send + Sync>> =
Err(Box::new(e));
return {{ response_adapter }};
}
};
let req_bytes = req_json.as_bytes();
let resp_ptr = (self.callback)(self.ctx, req_bytes.as_ptr(), req_bytes.len());
// SAFETY: callback returns heap-allocated C string per contract; we must free it.
let resp_str = if resp_ptr.is_null() {
"{}".to_string()
} else {
let c_str = unsafe { std::ffi::CStr::from_ptr(resp_ptr as *const _) };
let s = c_str.to_string_lossy().into_owned();
unsafe { libc::free(resp_ptr as *mut _); }
s
};
let resp: {{ response_path }} = serde_json::from_str(&resp_str)
.unwrap_or_else(|_| {{ response_path }}::default());
let outcome: Result<{{ response_path }}, Box<dyn std::error::Error + Send + Sync>> = Ok(resp);
{{ response_adapter }}
})
}
}