alef 0.48.15

Opinionated polyglot binding generator for Rust libraries
Documentation
{%- if doc %}
{{ doc }}{%- endif %}
    public func {{ method_camel }}(_ handler: @escaping (String) -> String{% if meta_params %}, {{ meta_params }}{% endif %}) throws {
        // Box the handler and retain it; the box pointer is passed to the
        // C layer as the trampoline context and released in deinit.
        let handlerBox = HandlerBox(handler)
        let contextPtr = Unmanaged.passRetained(handlerBox).toOpaque()
        handlerBoxes.append(contextPtr)

        // Create a C-compatible callback wrapper
        let trampolineFunc: @convention(c) (UnsafeMutableRawPointer?, UnsafePointer<UInt8>?, Int) -> UnsafeMutablePointer<UInt8>? = { contextPtr, requestPtr, requestLen in
            guard let contextPtr = contextPtr else { return nil }
            guard let requestPtr = requestPtr else { return nil }

            // Recover the boxed handler closure from the context pointer
            let handlerBox = Unmanaged<HandlerBox>.fromOpaque(contextPtr).takeUnretainedValue()
            let requestData = Data(bytes: requestPtr, count: requestLen)
            let requestJSON = String(data: requestData, encoding: .utf8) ?? ""
            let responseJSON = handlerBox.handler(requestJSON)

            // Allocate response string on heap (Rust side frees via extern "C" { fn free })
            let responseBytes = responseJSON.utf8CString
            let responsePtr = UnsafeMutablePointer<UInt8>.allocate(capacity: responseBytes.count)
            for (i, byte) in responseBytes.enumerated() {
                responsePtr[i] = UInt8(bitPattern: byte)
            }
            return responsePtr
        }

        guard let inner = inner else { throw ServiceError.invalidHandle }

        // Call the C function via @_silgen_name. The callback registration is defined
        // OUTSIDE the swift-bridge module as a plain extern "C" function. swift-bridge
        // hides the wrapper's raw pointer behind an `internal` field, so call through
        // the alef-emitted `RustBridge.{{ service_camel }}RawPtr` shim which returns
        // the App address as a `usize` we can reconstitute into an OpaquePointer.
        //
        // NOTE: the Rust side declares `app` as a single level of pointer
        // indirection (`*mut <Service>`). Passing `&innerPtr` (the address of
        // this local variable) instead of `innerPtr` itself would hand Rust a
        // pointer to the stack slot holding the address, not the address of
        // the instance itself, causing it to treat unrelated stack memory as
        // the struct and crash.
        let rawAddr = RustBridge.{{ service_camel }}RawPtr(inner)
        let innerPtr = OpaquePointer(bitPattern: rawAddr)!
        {% for meta_param in metadata_params %}{% if meta_param.is_opaque_wrapper %}
        // Same reasoning applies to `{{ meta_param.name }}`: it is a swift-bridge opaque
        // class, ABI-wise a single pointer. This plain `extern "C"` function is
        // outside swift-bridge's generated glue, so we must extract and hand over
        // its raw heap address explicitly rather than passing the class by value.
        let {{ meta_param.name }}RawAddr = RustBridge.{{ meta_param.raw_ptr_swift_name }}({{ meta_param.name }})
        let {{ meta_param.name }}Ptr = OpaquePointer(bitPattern: {{ meta_param.name }}RawAddr)!
        {% endif %}{% endfor %}
        let result = _{{ service_snake }}_{{ method_name }}_via_callback(
            innerPtr,
            {% for meta_param in metadata_params %}{% if meta_param.is_opaque_wrapper %}{{ meta_param.name }}Ptr,
            {% else %}{{ meta_param.name }},
            {% endif %}{% endfor %}contextPtr,
            trampolineFunc
        )
        guard result == 0 else { throw ServiceError.registrationFailed }
    }