Source code

Revision control

Copy as Markdown

Other Tools

{%- let obj = ci.get_object_definition(name).unwrap() %}
{%- let (protocol_name, impl_class_name) = obj|object_names %}
{%- let methods = obj.methods() %}
{%- let protocol_docstring = obj.docstring() %}
{%- let is_error = ci.is_name_used_as_error(name) %}
{% include "Protocol.swift" %}
{%- call swift::docstring(obj, 0) %}
open class {{ impl_class_name }}: {{ protocol_name }}, {{ config.conformance_list_for_object(obj, is_error) }} {
fileprivate let handle: UInt64
/// Used to instantiate a [FFIObject] without an actual handle, for fakes in tests, mostly.
#if swift(>=5.8)
@_documentation(visibility: private)
#endif
public struct NoHandle {
public init() {}
}
// TODO: We'd like this to be `private` but for Swifty reasons,
// we can't implement `FfiConverter` without making this `required` and we can't
// make it `required` without making it `public`.
#if swift(>=5.8)
@_documentation(visibility: private)
#endif
required public init(unsafeFromHandle handle: UInt64) {
self.handle = handle
}
// This constructor can be used to instantiate a fake object.
// - Parameter noHandle: Placeholder value so we can have a constructor separate from the default empty one that may be implemented for classes extending [FFIObject].
//
// - Warning:
// Any object instantiated with this constructor cannot be passed to an actual Rust-backed object. Since there isn't a backing handle the FFI lower functions will crash.
#if swift(>=5.8)
@_documentation(visibility: private)
#endif
public init(noHandle: NoHandle) {
self.handle = 0
}
#if swift(>=5.8)
@_documentation(visibility: private)
#endif
public func uniffiCloneHandle() -> UInt64 {
return try! rustCall { {{ obj.ffi_object_clone().name() }}(self.handle, $0) }
}
{%- match obj.primary_constructor() %}
{%- when Some(cons) %}
{%- call swift::ctor_decl(cons, 4) %}
{%- when None %}
// No primary constructor declared for this class.
{%- endmatch %}
deinit {
if handle == 0 {
// Mock objects have handle=0 don't try to free them
return
}
try! rustCall { {{ obj.ffi_object_free().name() }}(handle, $0) }
}
{% for cons in obj.alternate_constructors() %}
{%- call swift::func_decl("public static func", cons, 4) %}
{% endfor %}
{% for meth in obj.methods() -%}
{%- call swift::func_decl("open func", meth, 4) %}
{% endfor %}
{% call swift::uniffi_trait_impls(obj.uniffi_trait_methods()) %}
{%- if is_error %}
{% if !config.omit_localized_error_conformance() %}
public var errorDescription: String? {
String(reflecting: self)
}
{% endif %}
{% endif %}
}
{%- if !obj.has_callback_interface() %}
{# Simple case: the interface can only be implemented in Rust #}
#if swift(>=5.8)
@_documentation(visibility: private)
#endif
public struct {{ ffi_converter_name }}: FfiConverter {
typealias FfiType = UInt64
typealias SwiftType = {{ type_name }}
public static func lift(_ handle: UInt64) throws -> {{ type_name }} {
return {{ impl_class_name }}(unsafeFromHandle: handle)
}
public static func lower(_ value: {{ type_name }}) -> UInt64 {
return value.uniffiCloneHandle()
}
public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> {{ type_name }} {
let handle: UInt64 = try readInt(&buf)
return try lift(handle)
}
public static func write(_ value: {{ type_name }}, into buf: inout [UInt8]) {
writeInt(&buf, lower(value))
}
}
{%- else %}
{#
# The interface can be implemented in Rust or Swift
# * Generate a callback interface implementation to handle the Swift side
# * In the FfiConverter, check which side a handle came from to know how to handle correctly.
#}
{%- let callback_handler = format!("uniffiCallbackInterface{}", name) %}
{%- let callback_init = format!("uniffiCallbackInit{}", name) %}
{%- let vtable = obj.vtable().expect("trait interface should have a vtable") %}
{%- let vtable_methods = obj.vtable_methods() %}
{%- let ffi_init_callback = obj.ffi_init_callback() %}
{% include "CallbackInterfaceImpl.swift" %}
#if swift(>=5.8)
@_documentation(visibility: private)
#endif
public struct {{ ffi_converter_name }}: FfiConverter {
fileprivate static let handleMap = UniffiHandleMap<{{ type_name }}>()
typealias FfiType = UInt64
typealias SwiftType = {{ type_name }}
public static func lift(_ handle: UInt64) throws -> {{ type_name }} {
if ((handle & 1) == 0) {
// Rust-generated handle, construct a new class that uses the handle to implement the
// interface
return {{ impl_class_name }}(unsafeFromHandle: handle)
} else {
// Swift-generated handle, get the object from the handle map
return try handleMap.remove(handle: handle)
}
}
public static func lower(_ value: {{ type_name }}) -> UInt64 {
if let rustImpl = value as? {{ impl_class_name }} {
// Rust-implemented object. Clone the handle and return it
return rustImpl.uniffiCloneHandle()
} else {
// Swift object, generate a new vtable handle and return that.
return handleMap.insert(obj: value)
}
}
public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> {{ type_name }} {
let handle: UInt64 = try readInt(&buf)
return try lift(handle)
}
public static func write(_ value: {{ type_name }}, into buf: inout [UInt8]) {
writeInt(&buf, lower(value))
}
}
{%- endif %}
{%- for t in obj.trait_impls() %}
extension {{impl_class_name}}: {{ self::trait_protocol_name(ci, t.trait_ty)? }} {}
{% endfor %}
{#
We always write these public functions just in case the object is used as
an external type by another crate.
#}
#if swift(>=5.8)
@_documentation(visibility: private)
#endif
public func {{ ffi_converter_name }}_lift(_ handle: UInt64) throws -> {{ type_name }} {
return try {{ ffi_converter_name }}.lift(handle)
}
#if swift(>=5.8)
@_documentation(visibility: private)
#endif
public func {{ ffi_converter_name }}_lower(_ value: {{ type_name }}) -> UInt64 {
return {{ ffi_converter_name }}.lower(value)
}
{# Objects as error #}
{%- if is_error %}
{# Due to some mismatches in the ffi converter mechanisms, errors are a RustBuffer storing a handle #}
#if swift(>=5.8)
@_documentation(visibility: private)
#endif
public struct {{ ffi_converter_name }}__as_error: FfiConverterRustBuffer {
public static func lift(_ buf: RustBuffer) throws -> {{ type_name }} {
var reader = createReader(data: Data(rustBuffer: buf))
return try {{ ffi_converter_name }}.read(from: &reader)
}
public static func lower(_ value: {{ type_name }}) -> RustBuffer {
fatalError("not implemented")
}
public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> {{ type_name }} {
fatalError("not implemented")
}
public static func write(_ value: {{ type_name }}, into buf: inout [UInt8]) {
fatalError("not implemented")
}
}
{# Error FFI converters also need these public functions. #}
#if swift(>=5.8)
@_documentation(visibility: private)
#endif
public func {{ ffi_converter_name }}__as_error_lift(_ buf: RustBuffer) throws -> {{ type_name }} {
return try {{ ffi_converter_name }}__as_error.lift(buf)
}
#if swift(>=5.8)
@_documentation(visibility: private)
#endif
public func {{ ffi_converter_name }}__as_error_lower(_ value: {{ type_name }}) -> RustBuffer {
return {{ ffi_converter_name }}__as_error.lower(value)
}
{%- endif %}