{%- if doc %}
{{ doc }}{%- endif %}
public func {{ variant_name }}(_ handler: @escaping (String) -> String{% if signature_params %}, {% for param in signature_params %}{{ param.name }}: {{ param.swift_type }}{{ "," if not loop.last else "" }}{% endfor %}{% 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` instead of
// `innerPtr` itself would hand Rust a pointer to the stack slot
// holding the address, not the address of the instance itself.
let rawAddr = RustBridge.{{ service_camel }}RawPtr(inner)
let innerPtr = OpaquePointer(bitPattern: rawAddr)!
{% for arg in wrapper_call_args %}{% if arg.is_opaque_wrapper %}
// Same reasoning applies to `{{ arg.name }}`: it is a swift-bridge opaque
// class, ABI-wise a single pointer, so its raw heap address must be
// extracted explicitly for this plain `extern "C"` function.
let {{ arg.name }}RawAddr = RustBridge.{{ arg.raw_ptr_swift_name }}({{ arg.name }})
let {{ arg.name }}Ptr = OpaquePointer(bitPattern: {{ arg.name }}RawAddr)!
{% endif %}{% endfor %}
let result = _{{ service_snake }}_{{ base_method_name }}_via_callback(
innerPtr,
{% for arg in wrapper_call_args %}{% if arg.is_opaque_wrapper %}{{ arg.name }}Ptr,
{% else %}{{ arg.name }},
{% endif %}{% endfor %}contextPtr,
trampolineFunc
)
guard result == 0 else { throw ServiceError.registrationFailed }
}