{{ self::swift_doc_block(func.doc, "") }}public func {{ func.name }}({% for p in func.params %}{{ p.signature() }}{% if !loop.last %}, {% endif %}{% endfor %}){% if func.mode.is_async() %} async throws{% if let Some(result) = func.mode.async_result() %}{% if let Some(ret) = result.swift_type() %} -> {{ ret }}{% endif %}{% endif %}{% else %}{% if func.returns.is_throws() %} throws{% endif %}{% if let Some(ret) = func.returns.swift_type() %} -> {{ ret }}{% endif %}{% endif %} {
{%- match func.mode %}
{%- when SwiftCallMode::Async { start, poll, complete, cancel, free, result } %}
{%- for param in func.params %}
{%- if let Some(wrapper) = param.wrapper_code() %}
    {{ wrapper }}
{%- endif %}
{%- endfor %}
    let futureHandle = {% for w in func.closure_wrappers() %}{{ w }} {% endfor %}{{ func.start_call_expr() }}{% for _ in func.closure_wrappers() %} }{% endfor %}

{%- if result.is_direct() || result.is_unit() %}
{%- if result.is_direct() %}
    let rawResult = try await boltffiAsyncCallDirect(
        futureHandle: futureHandle,
        pollFn: {{ poll }},
        completeFn: {{ complete }},
        cancelFn: {{ cancel }},
        freeFn: {{ free }}
    )
{%- if let Some(expr) = result.direct_return_expr("rawResult") %}
    return {{ expr }}
{%- endif %}
{%- else %}
    return try await boltffiAsyncCallDirect(
        futureHandle: futureHandle,
        pollFn: {{ poll }},
        completeFn: {{ complete }},
        cancelFn: {{ cancel }},
        freeFn: {{ free }}
    )
{%- endif %}
{%- elif result.is_direct_buffer() %}
    return try await boltffiAsyncCall(
        futureHandle: futureHandle,
        pollFn: {{ poll }},
        completeFn: {{ complete }},
        cancelFn: {{ cancel }},
        freeFn: {{ free }}
    ) { buf, status in
        guard status.code == 0 else { {{ prefix }}_free_buf(buf); throw FfiError(message: "ffi call failed") }
        defer { {{ prefix }}_free_buf(buf) }
{%- if result.direct_buffer_is_data() %}
        guard buf.len > 0, let ptr = buf.ptr else { return Data() }
        return Data(bytes: ptr, count: Int(buf.len))
{%- else %}
        guard buf.len > 0, let ptr = buf.ptr else { return [] }
        let count = Int(buf.len) / MemoryLayout<{{ result.direct_buffer_element_type().unwrap() }}>.stride
        let rawPtr = UnsafeRawPointer(ptr).assumingMemoryBound(to: {{ result.direct_buffer_element_type().unwrap() }}.self)
{%- if let Some(mapping) = result.direct_buffer_composite_mapping() %}
        return (0..<count).map { i in
            let _raw = rawPtr[i]
            return {{ mapping.swift_record_type }}({% for f in mapping.fields %}{{ f.swift_name }}: _raw.{{ f.c_name }}{% if !loop.last %}, {% endif %}{% endfor %})
        }
{%- elif let Some(enum_name) = result.direct_buffer_enum_mapping() %}
        return Array(UnsafeBufferPointer(start: UnsafeRawPointer(rawPtr).assumingMemoryBound(to: {{ enum_name }}.self), count: count))
{%- else %}
        return Array(UnsafeBufferPointer(start: rawPtr, count: count))
{%- endif %}
{%- endif %}
    }
{%- else %}
    return try await boltffiAsyncCall(
        futureHandle: futureHandle,
        pollFn: {{ poll }},
        completeFn: {{ complete }},
        cancelFn: {{ cancel }},
        freeFn: {{ free }}
    ) { buf, status in
        guard status.code == 0 else { {{ prefix }}_free_buf(buf); throw FfiError(message: "ffi call failed") }
{%- if result.throws() %}
        defer { {{ prefix }}_free_buf(buf) }
        return try boltffiDecodeOwnedBuf(buf.ptr, Int(buf.len)) { reader in {{ result.reader_decode_expr().unwrap() }} }
{%- else %}
        defer { {{ prefix }}_free_buf(buf) }
        return boltffiDecodeOwnedBuf(buf.ptr, Int(buf.len)) { reader in {{ result.reader_decode_expr().unwrap() }} }
{%- endif %}
    }
{%- endif %}
{%- when SwiftCallMode::Sync { symbol } %}
{%- for param in func.params %}
{%- if let Some(wrapper) = param.wrapper_code() %}
    {{ wrapper }}
{%- endif %}
{%- endfor %}
{%- for open in func.sync_closure_opens() %}
{{ open }}
{%- endfor %}
{%- if func.returns.is_void() %}
{{ func.body_indent() }}{{ symbol }}({% for p in func.params %}{{ p.ffi_arg() }}{% if !loop.last %}, {% endif %}{% endfor %})
{%- elif func.returns.is_direct_buffer() %}
{{ func.body_indent() }}let buf = {{ symbol }}({% for p in func.params %}{{ p.ffi_arg() }}{% if !loop.last %}, {% endif %}{% endfor %})
{{ func.body_indent() }}defer { {{ prefix }}_free_buf(buf) }
{%- if func.returns.direct_buffer_is_data() %}
{{ func.body_indent() }}guard buf.len > 0, let ptr = buf.ptr else { return Data() }
{{ func.body_indent() }}return Data(bytes: ptr, count: Int(buf.len))
{%- else %}
{{ func.body_indent() }}guard buf.len > 0, let ptr = buf.ptr else { return [] }
{{ func.body_indent() }}let count = Int(buf.len) / MemoryLayout<{{ func.returns.direct_buffer_element_type().unwrap() }}>.stride
{{ func.body_indent() }}let rawPtr = UnsafeRawPointer(ptr).assumingMemoryBound(to: {{ func.returns.direct_buffer_element_type().unwrap() }}.self)
{%- if let Some(mapping) = func.returns.direct_buffer_composite_mapping() %}
{{ func.body_indent() }}return (0..<count).map { i in
{{ func.body_indent() }}    let _raw = rawPtr[i]
{{ func.body_indent() }}    return {{ mapping.swift_record_type }}({% for f in mapping.fields %}{{ f.swift_name }}: _raw.{{ f.c_name }}{% if !loop.last %}, {% endif %}{% endfor %})
{{ func.body_indent() }}}
{%- elif let Some(enum_name) = func.returns.direct_buffer_enum_mapping() %}
{{ func.body_indent() }}return Array(UnsafeBufferPointer(start: UnsafeRawPointer(rawPtr).assumingMemoryBound(to: {{ enum_name }}.self), count: count))
{%- else %}
{{ func.body_indent() }}return Array(UnsafeBufferPointer(start: rawPtr, count: count))
{%- endif %}
{%- endif %}
{%- elif func.returns.is_wire_encoded() %}
{{ func.body_indent() }}let buf = {{ symbol }}({% for p in func.params %}{{ p.ffi_arg() }}{% if !loop.last %}, {% endif %}{% endfor %})
{{ func.body_indent() }}defer { {{ prefix }}_free_buf(buf) }
{%- if let Some(reader_expr) = func.returns.reader_decode_expr() %}
{%- if func.returns.is_throws() %}
{{ func.body_indent() }}return try boltffiDecodeOwnedBuf(buf.ptr, Int(buf.len)) { reader in {{ reader_expr }} }
{%- else %}
{{ func.body_indent() }}return boltffiDecodeOwnedBuf(buf.ptr, Int(buf.len)) { reader in {{ reader_expr }} }
{%- endif %}
{%- elif let Some(decode_expr) = func.returns.decode_expr() %}
{{ func.body_indent() }}let wire = WireBuffer(ptr: buf.ptr!, len: Int(buf.len))
{{ func.body_indent() }}return {{ decode_expr }}
{%- endif %}
{%- elif func.returns.is_throws() %}
{{ func.body_indent() }}fatalError("throwing functions not yet implemented in IR backend")
{%- elif let Some((class_name, nullable)) = func.returns.handle_info() %}
{%- if nullable %}
{{ func.body_indent() }}guard let ptr = {{ symbol }}({% for p in func.params %}{{ p.ffi_arg() }}{% if !loop.last %}, {% endif %}{% endfor %}) else { return nil }
{{ func.body_indent() }}return {{ class_name }}(handle: ptr)
{%- else %}
{{ func.body_indent() }}let ptr = {{ symbol }}({% for p in func.params %}{{ p.ffi_arg() }}{% if !loop.last %}, {% endif %}{% endfor %})!
{{ func.body_indent() }}return {{ class_name }}(handle: ptr)
{%- endif %}
{%- elif let Some((protocol_name, nullable)) = func.returns.callback_info() %}
{{ func.body_indent() }}let callback = {{ symbol }}({% for p in func.params %}{{ p.ffi_arg() }}{% if !loop.last %}, {% endif %}{% endfor %})
{%- if nullable %}
{{ func.body_indent() }}guard callback.handle != 0 else { return nil }
{%- endif %}
{{ func.body_indent() }}return {{ protocol_name }}Bridge.wrap(callback)
{%- elif func.returns.is_composite() %}
{%- if let Some(convert_expr) = func.returns.composite_convert_expr("_raw") %}
{{ func.body_indent() }}let _raw = {{ symbol }}({% for p in func.params %}{{ p.ffi_arg() }}{% if !loop.last %}, {% endif %}{% endfor %})
{{ func.body_indent() }}return {{ convert_expr }}
{%- endif %}
{%- elif func.returns.is_c_style_enum() %}
{{ func.body_indent() }}return {{ func.returns.c_style_enum_type().unwrap() }}(rawValue: {{ symbol }}({% for p in func.params %}{{ p.ffi_arg() }}{% if !loop.last %}, {% endif %}{% endfor %}))!
{%- else %}
{{ func.body_indent() }}return {{ symbol }}({% for p in func.params %}{{ p.ffi_arg() }}{% if !loop.last %}, {% endif %}{% endfor %})
{%- endif %}
{%- for close in func.sync_closure_closes() %}
{{ close }}
{%- endfor %}
{%- endmatch %}
}
