Revision control
Copy as Markdown
Other Tools
// An error type for FFI errors. These errors occur at the UniFFI level, not
// the library level.
fileprivate enum UniffiInternalError: LocalizedError {
case bufferOverflow
case incompleteData
case unexpectedOptionalTag
case unexpectedEnumCase
case unexpectedNullPointer
case unexpectedRustCallStatusCode
case unexpectedRustCallError
case unexpectedStaleHandle
case rustPanic(_ message: String)
public var errorDescription: String? {
switch self {
case .bufferOverflow: return "Reading the requested value would read past the end of the buffer"
case .incompleteData: return "The buffer still has data after lifting its containing value"
case .unexpectedOptionalTag: return "Unexpected optional tag; should be 0 or 1"
case .unexpectedEnumCase: return "Raw enum value doesn't match any cases"
case .unexpectedNullPointer: return "Raw pointer value was null"
case .unexpectedRustCallStatusCode: return "Unexpected RustCallStatus code"
case .unexpectedRustCallError: return "CALL_ERROR but no errorClass specified"
case .unexpectedStaleHandle: return "The object in the handle map has been dropped already"
case let .rustPanic(message): return message
}
}
}
fileprivate extension NSLock {
func withLock<T>(f: () throws -> T) rethrows -> T {
self.lock()
defer { self.unlock() }
return try f()
}
}
fileprivate let CALL_SUCCESS: Int8 = 0
fileprivate let CALL_ERROR: Int8 = 1
fileprivate let CALL_UNEXPECTED_ERROR: Int8 = 2
fileprivate let CALL_CANCELLED: Int8 = 3
fileprivate extension RustCallStatus {
init() {
self.init(
code: CALL_SUCCESS,
errorBuf: RustBuffer.init(
capacity: 0,
len: 0,
data: nil
)
)
}
}
private func rustCall<T>(_ callback: (UnsafeMutablePointer<RustCallStatus>) -> T) throws -> T {
let neverThrow: ((RustBuffer) throws -> Never)? = nil
return try makeRustCall(callback, errorHandler: neverThrow)
}
private func rustCallWithError<T, E: Swift.Error>(
_ errorHandler: @escaping (RustBuffer) throws -> E,
_ callback: (UnsafeMutablePointer<RustCallStatus>) -> T) throws -> T {
try makeRustCall(callback, errorHandler: errorHandler)
}
private func makeRustCall<T, E: Swift.Error>(
_ callback: (UnsafeMutablePointer<RustCallStatus>) -> T,
errorHandler: ((RustBuffer) throws -> E)?
) throws -> T {
uniffiEnsureInitialized()
var callStatus = RustCallStatus.init()
let returnedVal = callback(&callStatus)
try uniffiCheckCallStatus(callStatus: callStatus, errorHandler: errorHandler)
return returnedVal
}
private func uniffiCheckCallStatus<E: Swift.Error>(
callStatus: RustCallStatus,
errorHandler: ((RustBuffer) throws -> E)?
) throws {
switch callStatus.code {
case CALL_SUCCESS:
return
case CALL_ERROR:
if let errorHandler = errorHandler {
throw try errorHandler(callStatus.errorBuf)
} else {
callStatus.errorBuf.deallocate()
throw UniffiInternalError.unexpectedRustCallError
}
case CALL_UNEXPECTED_ERROR:
// When the rust code sees a panic, it tries to construct a RustBuffer
// with the message. But if that code panics, then it just sends back
// an empty buffer.
if callStatus.errorBuf.len > 0 {
throw UniffiInternalError.rustPanic(try {{ Type::String.borrow()|lift_fn }}(callStatus.errorBuf))
} else {
callStatus.errorBuf.deallocate()
throw UniffiInternalError.rustPanic("Rust panic")
}
case CALL_CANCELLED:
fatalError("Cancellation not supported yet")
default:
throw UniffiInternalError.unexpectedRustCallStatusCode
}
}
private func uniffiTraitInterfaceCall<T>(
callStatus: UnsafeMutablePointer<RustCallStatus>,
makeCall: () throws -> T,
writeReturn: (T) -> ()
) {
do {
try writeReturn(makeCall())
} catch let error {
callStatus.pointee.code = CALL_UNEXPECTED_ERROR
callStatus.pointee.errorBuf = {{ Type::String.borrow()|lower_fn }}(String(describing: error))
}
}
private func uniffiTraitInterfaceCallWithError<T, E>(
callStatus: UnsafeMutablePointer<RustCallStatus>,
makeCall: () throws -> T,
writeReturn: (T) -> (),
lowerError: (E) -> RustBuffer
) {
do {
try writeReturn(makeCall())
} catch let error as E {
callStatus.pointee.code = CALL_ERROR
callStatus.pointee.errorBuf = lowerError(error)
} catch {
callStatus.pointee.code = CALL_UNEXPECTED_ERROR
callStatus.pointee.errorBuf = {{ Type::String.borrow()|lower_fn }}(String(describing: error))
}
}