alef 0.25.10

Opinionated polyglot binding generator for Rust libraries
Documentation
// 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 }}
        })
    }
}