summaryrefslogtreecommitdiffstats
path: root/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Helpers.swift
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Helpers.swift')
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Helpers.swift101
1 files changed, 101 insertions, 0 deletions
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Helpers.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Helpers.swift
new file mode 100644
index 0000000000..a34b128e23
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Helpers.swift
@@ -0,0 +1,101 @@
+// 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 let CALL_SUCCESS: Int8 = 0
+fileprivate let CALL_ERROR: Int8 = 1
+fileprivate let CALL_PANIC: 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 {
+ try makeRustCall(callback, errorHandler: nil)
+}
+
+private func rustCallWithError<T>(
+ _ errorHandler: @escaping (RustBuffer) throws -> Error,
+ _ callback: (UnsafeMutablePointer<RustCallStatus>) -> T) throws -> T {
+ try makeRustCall(callback, errorHandler: errorHandler)
+}
+
+private func makeRustCall<T>(
+ _ callback: (UnsafeMutablePointer<RustCallStatus>) -> T,
+ errorHandler: ((RustBuffer) throws -> Error)?
+) throws -> T {
+ uniffiEnsureInitialized()
+ var callStatus = RustCallStatus.init()
+ let returnedVal = callback(&callStatus)
+ try uniffiCheckCallStatus(callStatus: callStatus, errorHandler: errorHandler)
+ return returnedVal
+}
+
+private func uniffiCheckCallStatus(
+ callStatus: RustCallStatus,
+ errorHandler: ((RustBuffer) throws -> Error)?
+) 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_PANIC:
+ // 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:
+ throw CancellationError()
+
+ default:
+ throw UniffiInternalError.unexpectedRustCallStatusCode
+ }
+}