diff --git a/.github/workflows/swift-release.yml b/.github/workflows/swift-release.yml new file mode 100644 index 0000000..675d0f4 --- /dev/null +++ b/.github/workflows/swift-release.yml @@ -0,0 +1,105 @@ +name: Release Swift Package + +on: + workflow_dispatch: + inputs: + tag: + description: Release tag (vX.Y.Z or X.Y.Z) + required: true + +permissions: + contents: write + +jobs: + release-swift: + runs-on: macos-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Normalize tag + id: tag + run: | + TAG="${{ inputs.tag }}" + if [[ "${TAG}" != v* ]]; then + TAG="v${TAG}" + fi + echo "tag=${TAG}" >> "${GITHUB_OUTPUT}" + + - name: Ensure workflow runs on a branch + run: | + if [[ "${GITHUB_REF_TYPE}" != "branch" ]]; then + echo "This workflow must run on a branch ref." + exit 1 + fi + + - name: Check for existing tag or release + env: + GH_TOKEN: ${{ github.token }} + run: | + TAG="${{ steps.tag.outputs.tag }}" + if git ls-remote --tags origin "refs/tags/${TAG}" | grep -q "${TAG}$"; then + echo "Tag already exists on origin: ${TAG}" + exit 1 + fi + if gh release view "${TAG}" >/dev/null 2>&1; then + echo "Release already exists: ${TAG}" + exit 1 + fi + + - name: Configure git + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + + - name: Build xcframework + run: bash cktap-swift/build-xcframework.sh + + - name: Create archive and update checksum + run: bash scripts/swift_create_xcframework_archive.sh + + - name: Update Package.swift tag + run: python3 scripts/swift_update_package_checksum.py --tag "${{ steps.tag.outputs.tag }}" + + - name: Commit updated Swift files + run: | + git add Package.swift cktap-swift/Sources + if git diff --cached --quiet; then + echo "No Swift package changes to commit." + exit 0 + fi + git commit -m "chore(swift): release ${{ steps.tag.outputs.tag }}" + + - name: Create tag and push atomically + run: | + if git rev-parse "${{ steps.tag.outputs.tag }}" >/dev/null 2>&1; then + echo "Tag already exists: ${{ steps.tag.outputs.tag }}" + exit 1 + fi + git tag "${{ steps.tag.outputs.tag }}" + git push --atomic origin HEAD "${{ steps.tag.outputs.tag }}" + + - name: Create GitHub release and upload asset + env: + GH_TOKEN: ${{ github.token }} + run: | + gh release create "${{ steps.tag.outputs.tag }}" \ + cktap-swift/cktapFFI.xcframework.zip \ + --title "${{ steps.tag.outputs.tag }}" \ + --notes "Swift bindings release ${{ steps.tag.outputs.tag }}" + + - name: Cleanup failed release + if: failure() + env: + GH_TOKEN: ${{ github.token }} + run: | + TAG="${{ steps.tag.outputs.tag }}" + if gh release view "${TAG}" >/dev/null 2>&1; then + gh release delete "${TAG}" --yes || true + fi + if git ls-remote --tags origin "refs/tags/${TAG}" | grep -q "${TAG}$"; then + git push --delete origin "${TAG}" || true + fi diff --git a/.gitignore b/.gitignore index 04bf0a3..f22ecce 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,8 @@ DerivedData/ *.xcframework.zip Info.plist Sources +!cktap-swift/Sources/ +!cktap-swift/Sources/** lib*.a # Python ck-tap emulator diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000..36833d5 --- /dev/null +++ b/Package.swift @@ -0,0 +1,35 @@ +// swift-tools-version:5.5 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +// Update tag and checksum when publishing a new Swift binary release. +let tag = "v0.1.0" +let checksum = "0000000000000000000000000000000000000000000000000000000000000000" +let url = "https://github.com/notmandatory/rust-cktap/releases/download/\(tag)/cktapFFI.xcframework.zip" + +let package = Package( + name: "rust-cktap", + platforms: [ + .macOS(.v12), + .iOS(.v18), + ], + products: [ + .library( + name: "CKTap", + targets: ["cktapFFI", "CKTap"] + ), + ], + targets: [ + .target( + name: "CKTap", + dependencies: ["cktapFFI"], + path: "./cktap-swift/Sources" + ), + .binaryTarget( + name: "cktapFFI", + url: url, + checksum: checksum + ), + ] +) diff --git a/README.md b/README.md index 0a28aee..f1b6c72 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,14 @@ just run certs just run read ``` +## Swift Package + +Swift bindings are distributed from this repo as a Swift Package. Once a +release includes `cktapFFI.xcframework.zip`, add the repository URL in Xcode +and use the `CKTap` product. + +See `cktap-swift/README.md` for local development and release steps. + ## Minimum Supported Rust Version (MSRV) This library should always compile with any valid combination of features on Rust **1.85.0**. diff --git a/cktap-swift/Package.swift b/cktap-swift/Package.swift index 6f25130..2e1b6b2 100644 --- a/cktap-swift/Package.swift +++ b/cktap-swift/Package.swift @@ -7,7 +7,7 @@ let package = Package( name: "cktap-swift", platforms: [ .macOS(.v12), - .iOS(.v15) + .iOS(.v18) ], products: [ .library( diff --git a/cktap-swift/README.md b/cktap-swift/README.md new file mode 100644 index 0000000..ef72f38 --- /dev/null +++ b/cktap-swift/README.md @@ -0,0 +1,40 @@ +# CKTAP Swift bindings + +This directory contains Swift bindings generated by UniFFI and a local SwiftPM +manifest used during development. + +## Local development + +- Run `just build` (or `./build-xcframework.sh`) to regenerate + `Sources/CKTap/cktap_ffi.swift` and `cktapFFI.xcframework`. +- Run `just test` to execute Swift tests. + +## Publishing the Swift package + +The repo root `Package.swift` is the public SwiftPM entry point. It expects a +GitHub release asset named `cktapFFI.xcframework.zip`. + +### Manual + +1. Build the xcframework: + ``` + just build + ``` +2. Create the zip from `cktap-swift`: + ``` + cd cktap-swift + zip -r cktapFFI.xcframework.zip cktapFFI.xcframework + ``` +3. Upload the zip to the GitHub release tag. +4. Compute the checksum: + ``` + swift package compute-checksum cktapFFI.xcframework.zip + ``` +5. Update the root `Package.swift` `tag` and `checksum`, then tag and release. + +### Automated + +Run the `Release Swift Package` workflow with a tag (e.g. `v0.1.1`). It builds +the xcframework, creates the zip, updates the root `Package.swift`, commits the +Swift sources, tags the release, and uploads the asset. If the workflow fails +after committing, it will not auto-revert the commit. diff --git a/cktap-swift/Sources/CKTap/cktap_ffi.swift b/cktap-swift/Sources/CKTap/cktap_ffi.swift new file mode 100644 index 0000000..60d53d2 --- /dev/null +++ b/cktap-swift/Sources/CKTap/cktap_ffi.swift @@ -0,0 +1,3835 @@ +// This file was autogenerated by some hot garbage in the `uniffi` crate. +// Trust me, you don't want to mess with it! + +// swiftlint:disable all +import Foundation + +// Depending on the consumer's build setup, the low-level FFI code +// might be in a separate module, or it might be compiled inline into +// this module. This is a bit of light hackery to work with both. +#if canImport(cktap_ffiFFI) +import cktap_ffiFFI +#endif + +fileprivate extension RustBuffer { + // Allocate a new buffer, copying the contents of a `UInt8` array. + init(bytes: [UInt8]) { + let rbuf = bytes.withUnsafeBufferPointer { ptr in + RustBuffer.from(ptr) + } + self.init(capacity: rbuf.capacity, len: rbuf.len, data: rbuf.data) + } + + static func empty() -> RustBuffer { + RustBuffer(capacity: 0, len:0, data: nil) + } + + static func from(_ ptr: UnsafeBufferPointer) -> RustBuffer { + try! rustCall { ffi_cktap_ffi_rustbuffer_from_bytes(ForeignBytes(bufferPointer: ptr), $0) } + } + + // Frees the buffer in place. + // The buffer must not be used after this is called. + func deallocate() { + try! rustCall { ffi_cktap_ffi_rustbuffer_free(self, $0) } + } +} + +fileprivate extension ForeignBytes { + init(bufferPointer: UnsafeBufferPointer) { + self.init(len: Int32(bufferPointer.count), data: bufferPointer.baseAddress) + } +} + +// For every type used in the interface, we provide helper methods for conveniently +// lifting and lowering that type from C-compatible data, and for reading and writing +// values of that type in a buffer. + +// Helper classes/extensions that don't change. +// Someday, this will be in a library of its own. + +fileprivate extension Data { + init(rustBuffer: RustBuffer) { + self.init( + bytesNoCopy: rustBuffer.data!, + count: Int(rustBuffer.len), + deallocator: .none + ) + } +} + +// Define reader functionality. Normally this would be defined in a class or +// struct, but we use standalone functions instead in order to make external +// types work. +// +// With external types, one swift source file needs to be able to call the read +// method on another source file's FfiConverter, but then what visibility +// should Reader have? +// - If Reader is fileprivate, then this means the read() must also +// be fileprivate, which doesn't work with external types. +// - If Reader is internal/public, we'll get compile errors since both source +// files will try define the same type. +// +// Instead, the read() method and these helper functions input a tuple of data + +fileprivate func createReader(data: Data) -> (data: Data, offset: Data.Index) { + (data: data, offset: 0) +} + +// Reads an integer at the current offset, in big-endian order, and advances +// the offset on success. Throws if reading the integer would move the +// offset past the end of the buffer. +fileprivate func readInt(_ reader: inout (data: Data, offset: Data.Index)) throws -> T { + let range = reader.offset...size + guard reader.data.count >= range.upperBound else { + throw UniffiInternalError.bufferOverflow + } + if T.self == UInt8.self { + let value = reader.data[reader.offset] + reader.offset += 1 + return value as! T + } + var value: T = 0 + let _ = withUnsafeMutableBytes(of: &value, { reader.data.copyBytes(to: $0, from: range)}) + reader.offset = range.upperBound + return value.bigEndian +} + +// Reads an arbitrary number of bytes, to be used to read +// raw bytes, this is useful when lifting strings +fileprivate func readBytes(_ reader: inout (data: Data, offset: Data.Index), count: Int) throws -> Array { + let range = reader.offset..<(reader.offset+count) + guard reader.data.count >= range.upperBound else { + throw UniffiInternalError.bufferOverflow + } + var value = [UInt8](repeating: 0, count: count) + value.withUnsafeMutableBufferPointer({ buffer in + reader.data.copyBytes(to: buffer, from: range) + }) + reader.offset = range.upperBound + return value +} + +// Reads a float at the current offset. +fileprivate func readFloat(_ reader: inout (data: Data, offset: Data.Index)) throws -> Float { + return Float(bitPattern: try readInt(&reader)) +} + +// Reads a float at the current offset. +fileprivate func readDouble(_ reader: inout (data: Data, offset: Data.Index)) throws -> Double { + return Double(bitPattern: try readInt(&reader)) +} + +// Indicates if the offset has reached the end of the buffer. +fileprivate func hasRemaining(_ reader: (data: Data, offset: Data.Index)) -> Bool { + return reader.offset < reader.data.count +} + +// Define writer functionality. Normally this would be defined in a class or +// struct, but we use standalone functions instead in order to make external +// types work. See the above discussion on Readers for details. + +fileprivate func createWriter() -> [UInt8] { + return [] +} + +fileprivate func writeBytes(_ writer: inout [UInt8], _ byteArr: S) where S: Sequence, S.Element == UInt8 { + writer.append(contentsOf: byteArr) +} + +// Writes an integer in big-endian order. +// +// Warning: make sure what you are trying to write +// is in the correct type! +fileprivate func writeInt(_ writer: inout [UInt8], _ value: T) { + var value = value.bigEndian + withUnsafeBytes(of: &value) { writer.append(contentsOf: $0) } +} + +fileprivate func writeFloat(_ writer: inout [UInt8], _ value: Float) { + writeInt(&writer, value.bitPattern) +} + +fileprivate func writeDouble(_ writer: inout [UInt8], _ value: Double) { + writeInt(&writer, value.bitPattern) +} + +// Protocol for types that transfer other types across the FFI. This is +// analogous to the Rust trait of the same name. +fileprivate protocol FfiConverter { + associatedtype FfiType + associatedtype SwiftType + + static func lift(_ value: FfiType) throws -> SwiftType + static func lower(_ value: SwiftType) -> FfiType + static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType + static func write(_ value: SwiftType, into buf: inout [UInt8]) +} + +// Types conforming to `Primitive` pass themselves directly over the FFI. +fileprivate protocol FfiConverterPrimitive: FfiConverter where FfiType == SwiftType { } + +extension FfiConverterPrimitive { +#if swift(>=5.8) + @_documentation(visibility: private) +#endif + public static func lift(_ value: FfiType) throws -> SwiftType { + return value + } + +#if swift(>=5.8) + @_documentation(visibility: private) +#endif + public static func lower(_ value: SwiftType) -> FfiType { + return value + } +} + +// Types conforming to `FfiConverterRustBuffer` lift and lower into a `RustBuffer`. +// Used for complex types where it's hard to write a custom lift/lower. +fileprivate protocol FfiConverterRustBuffer: FfiConverter where FfiType == RustBuffer {} + +extension FfiConverterRustBuffer { +#if swift(>=5.8) + @_documentation(visibility: private) +#endif + public static func lift(_ buf: RustBuffer) throws -> SwiftType { + var reader = createReader(data: Data(rustBuffer: buf)) + let value = try read(from: &reader) + if hasRemaining(reader) { + throw UniffiInternalError.incompleteData + } + buf.deallocate() + return value + } + +#if swift(>=5.8) + @_documentation(visibility: private) +#endif + public static func lower(_ value: SwiftType) -> RustBuffer { + var writer = createWriter() + write(value, into: &writer) + return RustBuffer(bytes: writer) + } +} +// 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(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(_ callback: (UnsafeMutablePointer) -> T) throws -> T { + let neverThrow: ((RustBuffer) throws -> Never)? = nil + return try makeRustCall(callback, errorHandler: neverThrow) +} + +private func rustCallWithError( + _ errorHandler: @escaping (RustBuffer) throws -> E, + _ callback: (UnsafeMutablePointer) -> T) throws -> T { + try makeRustCall(callback, errorHandler: errorHandler) +} + +private func makeRustCall( + _ callback: (UnsafeMutablePointer) -> T, + errorHandler: ((RustBuffer) throws -> E)? +) throws -> T { + uniffiEnsureCktapFfiInitialized() + var callStatus = RustCallStatus.init() + let returnedVal = callback(&callStatus) + try uniffiCheckCallStatus(callStatus: callStatus, errorHandler: errorHandler) + return returnedVal +} + +private func uniffiCheckCallStatus( + 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 FfiConverterString.lift(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( + callStatus: UnsafeMutablePointer, + makeCall: () throws -> T, + writeReturn: (T) -> () +) { + do { + try writeReturn(makeCall()) + } catch let error { + callStatus.pointee.code = CALL_UNEXPECTED_ERROR + callStatus.pointee.errorBuf = FfiConverterString.lower(String(describing: error)) + } +} + +private func uniffiTraitInterfaceCallWithError( + callStatus: UnsafeMutablePointer, + 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 = FfiConverterString.lower(String(describing: error)) + } +} +// Initial value and increment amount for handles. +// These ensure that SWIFT handles always have the lowest bit set +fileprivate let UNIFFI_HANDLEMAP_INITIAL: UInt64 = 1 +fileprivate let UNIFFI_HANDLEMAP_DELTA: UInt64 = 2 + +fileprivate final class UniffiHandleMap: @unchecked Sendable { + // All mutation happens with this lock held, which is why we implement @unchecked Sendable. + private let lock = NSLock() + private var map: [UInt64: T] = [:] + private var currentHandle: UInt64 = UNIFFI_HANDLEMAP_INITIAL + + func insert(obj: T) -> UInt64 { + lock.withLock { + return doInsert(obj) + } + } + + // Low-level insert function, this assumes `lock` is held. + private func doInsert(_ obj: T) -> UInt64 { + let handle = currentHandle + currentHandle += UNIFFI_HANDLEMAP_DELTA + map[handle] = obj + return handle + } + + func get(handle: UInt64) throws -> T { + try lock.withLock { + guard let obj = map[handle] else { + throw UniffiInternalError.unexpectedStaleHandle + } + return obj + } + } + + func clone(handle: UInt64) throws -> UInt64 { + try lock.withLock { + guard let obj = map[handle] else { + throw UniffiInternalError.unexpectedStaleHandle + } + return doInsert(obj) + } + } + + @discardableResult + func remove(handle: UInt64) throws -> T { + try lock.withLock { + guard let obj = map.removeValue(forKey: handle) else { + throw UniffiInternalError.unexpectedStaleHandle + } + return obj + } + } + + var count: Int { + get { + map.count + } + } +} + + +// Public interface members begin here. +// Magic number for the Rust proxy to call using the same mechanism as every other method, +// to free the callback once it's dropped by Rust. +private let IDX_CALLBACK_FREE: Int32 = 0 +// Callback return codes +private let UNIFFI_CALLBACK_SUCCESS: Int32 = 0 +private let UNIFFI_CALLBACK_ERROR: Int32 = 1 +private let UNIFFI_CALLBACK_UNEXPECTED_ERROR: Int32 = 2 + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterUInt8: FfiConverterPrimitive { + typealias FfiType = UInt8 + typealias SwiftType = UInt8 + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UInt8 { + return try lift(readInt(&buf)) + } + + public static func write(_ value: UInt8, into buf: inout [UInt8]) { + writeInt(&buf, lower(value)) + } +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterUInt32: FfiConverterPrimitive { + typealias FfiType = UInt32 + typealias SwiftType = UInt32 + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UInt32 { + return try lift(readInt(&buf)) + } + + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { + writeInt(&buf, lower(value)) + } +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterUInt64: FfiConverterPrimitive { + typealias FfiType = UInt64 + typealias SwiftType = UInt64 + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UInt64 { + return try lift(readInt(&buf)) + } + + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { + writeInt(&buf, lower(value)) + } +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterBool : FfiConverter { + typealias FfiType = Int8 + typealias SwiftType = Bool + + public static func lift(_ value: Int8) throws -> Bool { + return value != 0 + } + + public static func lower(_ value: Bool) -> Int8 { + return value ? 1 : 0 + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Bool { + return try lift(readInt(&buf)) + } + + public static func write(_ value: Bool, into buf: inout [UInt8]) { + writeInt(&buf, lower(value)) + } +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterString: FfiConverter { + typealias SwiftType = String + typealias FfiType = RustBuffer + + public static func lift(_ value: RustBuffer) throws -> String { + defer { + value.deallocate() + } + if value.data == nil { + return String() + } + let bytes = UnsafeBufferPointer(start: value.data!, count: Int(value.len)) + return String(bytes: bytes, encoding: String.Encoding.utf8)! + } + + public static func lower(_ value: String) -> RustBuffer { + return value.utf8CString.withUnsafeBufferPointer { ptr in + // The swift string gives us int8_t, we want uint8_t. + ptr.withMemoryRebound(to: UInt8.self) { ptr in + // The swift string gives us a trailing null byte, we don't want it. + let buf = UnsafeBufferPointer(rebasing: ptr.prefix(upTo: ptr.count - 1)) + return RustBuffer.from(buf) + } + } + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> String { + let len: Int32 = try readInt(&buf) + return String(bytes: try readBytes(&buf, count: Int(len)), encoding: String.Encoding.utf8)! + } + + public static func write(_ value: String, into buf: inout [UInt8]) { + let len = Int32(value.utf8.count) + writeInt(&buf, len) + writeBytes(&buf, value.utf8) + } +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterData: FfiConverterRustBuffer { + typealias SwiftType = Data + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Data { + let len: Int32 = try readInt(&buf) + return Data(try readBytes(&buf, count: Int(len))) + } + + public static func write(_ value: Data, into buf: inout [UInt8]) { + let len = Int32(value.count) + writeInt(&buf, len) + writeBytes(&buf, value) + } +} + + + + +public protocol SatsCardProtocol: AnyObject, Sendable { + + /** + * Get the current active slot's receive address + */ + func address() async throws -> String + + /** + * Verify the card has authentic Coinkite root certificate + */ + func checkCert() async throws + + /** + * This is only needed for debugging, use `sign_psbt` for signing + * If no CVC given only pubkey and pubkey descriptor returned. + */ + func dump(slot: UInt8, cvc: String?) async throws -> SlotDetails + + /** + * Open a new slot, it will be the current active but must be unused (no address) + */ + func newSlot(cvc: String) async throws -> UInt8 + + /** + * Return the same URL as given with a NFC tap. + */ + func nfc() async throws -> String + + /** + * Get the current active slot's wpkh public key descriptor + */ + func read() async throws -> String + + /** + * Sign PSBT, base64 encoded + */ + func signPsbt(slot: UInt8, psbt: String, cvc: String) async throws -> String + + func status() async -> SatsCardStatus + + /** + * Unseal currently active slot + */ + func unseal(cvc: String) async throws -> SlotDetails + + /** + * Wait 15 seconds or until auth delay timeout is done + */ + func wait() async throws + +} +open class SatsCard: SatsCardProtocol, @unchecked Sendable { + 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 { uniffi_cktap_ffi_fn_clone_satscard(self.handle, $0) } + } + // No primary constructor declared for this class. + + deinit { + try! rustCall { uniffi_cktap_ffi_fn_free_satscard(handle, $0) } + } + + + + + /** + * Get the current active slot's receive address + */ +open func address()async throws -> String { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_cktap_ffi_fn_method_satscard_address( + self.uniffiCloneHandle() + + ) + }, + pollFunc: ffi_cktap_ffi_rust_future_poll_rust_buffer, + completeFunc: ffi_cktap_ffi_rust_future_complete_rust_buffer, + freeFunc: ffi_cktap_ffi_rust_future_free_rust_buffer, + liftFunc: FfiConverterString.lift, + errorHandler: FfiConverterTypeReadError_lift + ) +} + + /** + * Verify the card has authentic Coinkite root certificate + */ +open func checkCert()async throws { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_cktap_ffi_fn_method_satscard_check_cert( + self.uniffiCloneHandle() + + ) + }, + pollFunc: ffi_cktap_ffi_rust_future_poll_void, + completeFunc: ffi_cktap_ffi_rust_future_complete_void, + freeFunc: ffi_cktap_ffi_rust_future_free_void, + liftFunc: { $0 }, + errorHandler: FfiConverterTypeCertsError_lift + ) +} + + /** + * This is only needed for debugging, use `sign_psbt` for signing + * If no CVC given only pubkey and pubkey descriptor returned. + */ +open func dump(slot: UInt8, cvc: String?)async throws -> SlotDetails { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_cktap_ffi_fn_method_satscard_dump( + self.uniffiCloneHandle(), + FfiConverterUInt8.lower(slot),FfiConverterOptionString.lower(cvc) + ) + }, + pollFunc: ffi_cktap_ffi_rust_future_poll_rust_buffer, + completeFunc: ffi_cktap_ffi_rust_future_complete_rust_buffer, + freeFunc: ffi_cktap_ffi_rust_future_free_rust_buffer, + liftFunc: FfiConverterTypeSlotDetails_lift, + errorHandler: FfiConverterTypeDumpError_lift + ) +} + + /** + * Open a new slot, it will be the current active but must be unused (no address) + */ +open func newSlot(cvc: String)async throws -> UInt8 { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_cktap_ffi_fn_method_satscard_new_slot( + self.uniffiCloneHandle(), + FfiConverterString.lower(cvc) + ) + }, + pollFunc: ffi_cktap_ffi_rust_future_poll_u8, + completeFunc: ffi_cktap_ffi_rust_future_complete_u8, + freeFunc: ffi_cktap_ffi_rust_future_free_u8, + liftFunc: FfiConverterUInt8.lift, + errorHandler: FfiConverterTypeDeriveError_lift + ) +} + + /** + * Return the same URL as given with a NFC tap. + */ +open func nfc()async throws -> String { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_cktap_ffi_fn_method_satscard_nfc( + self.uniffiCloneHandle() + + ) + }, + pollFunc: ffi_cktap_ffi_rust_future_poll_rust_buffer, + completeFunc: ffi_cktap_ffi_rust_future_complete_rust_buffer, + freeFunc: ffi_cktap_ffi_rust_future_free_rust_buffer, + liftFunc: FfiConverterString.lift, + errorHandler: FfiConverterTypeCkTapError_lift + ) +} + + /** + * Get the current active slot's wpkh public key descriptor + */ +open func read()async throws -> String { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_cktap_ffi_fn_method_satscard_read( + self.uniffiCloneHandle() + + ) + }, + pollFunc: ffi_cktap_ffi_rust_future_poll_rust_buffer, + completeFunc: ffi_cktap_ffi_rust_future_complete_rust_buffer, + freeFunc: ffi_cktap_ffi_rust_future_free_rust_buffer, + liftFunc: FfiConverterString.lift, + errorHandler: FfiConverterTypeReadError_lift + ) +} + + /** + * Sign PSBT, base64 encoded + */ +open func signPsbt(slot: UInt8, psbt: String, cvc: String)async throws -> String { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_cktap_ffi_fn_method_satscard_sign_psbt( + self.uniffiCloneHandle(), + FfiConverterUInt8.lower(slot),FfiConverterString.lower(psbt),FfiConverterString.lower(cvc) + ) + }, + pollFunc: ffi_cktap_ffi_rust_future_poll_rust_buffer, + completeFunc: ffi_cktap_ffi_rust_future_complete_rust_buffer, + freeFunc: ffi_cktap_ffi_rust_future_free_rust_buffer, + liftFunc: FfiConverterString.lift, + errorHandler: FfiConverterTypeSignPsbtError_lift + ) +} + +open func status()async -> SatsCardStatus { + return + try! await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_cktap_ffi_fn_method_satscard_status( + self.uniffiCloneHandle() + + ) + }, + pollFunc: ffi_cktap_ffi_rust_future_poll_rust_buffer, + completeFunc: ffi_cktap_ffi_rust_future_complete_rust_buffer, + freeFunc: ffi_cktap_ffi_rust_future_free_rust_buffer, + liftFunc: FfiConverterTypeSatsCardStatus_lift, + errorHandler: nil + + ) +} + + /** + * Unseal currently active slot + */ +open func unseal(cvc: String)async throws -> SlotDetails { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_cktap_ffi_fn_method_satscard_unseal( + self.uniffiCloneHandle(), + FfiConverterString.lower(cvc) + ) + }, + pollFunc: ffi_cktap_ffi_rust_future_poll_rust_buffer, + completeFunc: ffi_cktap_ffi_rust_future_complete_rust_buffer, + freeFunc: ffi_cktap_ffi_rust_future_free_rust_buffer, + liftFunc: FfiConverterTypeSlotDetails_lift, + errorHandler: FfiConverterTypeUnsealError_lift + ) +} + + /** + * Wait 15 seconds or until auth delay timeout is done + */ +open func wait()async throws { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_cktap_ffi_fn_method_satscard_wait( + self.uniffiCloneHandle() + + ) + }, + pollFunc: ffi_cktap_ffi_rust_future_poll_void, + completeFunc: ffi_cktap_ffi_rust_future_complete_void, + freeFunc: ffi_cktap_ffi_rust_future_free_void, + liftFunc: { $0 }, + errorHandler: FfiConverterTypeCkTapError_lift + ) +} + + +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeSatsCard: FfiConverter { + typealias FfiType = UInt64 + typealias SwiftType = SatsCard + + public static func lift(_ handle: UInt64) throws -> SatsCard { + return SatsCard(unsafeFromHandle: handle) + } + + public static func lower(_ value: SatsCard) -> UInt64 { + return value.uniffiCloneHandle() + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SatsCard { + let handle: UInt64 = try readInt(&buf) + return try lift(handle) + } + + public static func write(_ value: SatsCard, into buf: inout [UInt8]) { + writeInt(&buf, lower(value)) + } +} + + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeSatsCard_lift(_ handle: UInt64) throws -> SatsCard { + return try FfiConverterTypeSatsCard.lift(handle) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeSatsCard_lower(_ value: SatsCard) -> UInt64 { + return FfiConverterTypeSatsCard.lower(value) +} + + + + + + +public protocol SatsChipProtocol: AnyObject, Sendable { + + func change(newCvc: String, cvc: String) async throws + + func checkCert() async throws + + func derive(path: [UInt32], cvc: String) async throws -> String + + func `init`(cvc: String) async throws + + func nfc() async throws -> String + + func read() async throws -> String + + func signPsbt(psbt: String, cvc: String) async throws -> String + + func status() async -> SatsChipStatus + + func wait() async throws + + func xpub(master: Bool, cvc: String) async throws -> String + +} +open class SatsChip: SatsChipProtocol, @unchecked Sendable { + 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 { uniffi_cktap_ffi_fn_clone_satschip(self.handle, $0) } + } + // No primary constructor declared for this class. + + deinit { + try! rustCall { uniffi_cktap_ffi_fn_free_satschip(handle, $0) } + } + + + + +open func change(newCvc: String, cvc: String)async throws { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_cktap_ffi_fn_method_satschip_change( + self.uniffiCloneHandle(), + FfiConverterString.lower(newCvc),FfiConverterString.lower(cvc) + ) + }, + pollFunc: ffi_cktap_ffi_rust_future_poll_void, + completeFunc: ffi_cktap_ffi_rust_future_complete_void, + freeFunc: ffi_cktap_ffi_rust_future_free_void, + liftFunc: { $0 }, + errorHandler: FfiConverterTypeChangeError_lift + ) +} + +open func checkCert()async throws { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_cktap_ffi_fn_method_satschip_check_cert( + self.uniffiCloneHandle() + + ) + }, + pollFunc: ffi_cktap_ffi_rust_future_poll_void, + completeFunc: ffi_cktap_ffi_rust_future_complete_void, + freeFunc: ffi_cktap_ffi_rust_future_free_void, + liftFunc: { $0 }, + errorHandler: FfiConverterTypeCertsError_lift + ) +} + +open func derive(path: [UInt32], cvc: String)async throws -> String { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_cktap_ffi_fn_method_satschip_derive( + self.uniffiCloneHandle(), + FfiConverterSequenceUInt32.lower(path),FfiConverterString.lower(cvc) + ) + }, + pollFunc: ffi_cktap_ffi_rust_future_poll_rust_buffer, + completeFunc: ffi_cktap_ffi_rust_future_complete_rust_buffer, + freeFunc: ffi_cktap_ffi_rust_future_free_rust_buffer, + liftFunc: FfiConverterString.lift, + errorHandler: FfiConverterTypeDeriveError_lift + ) +} + +open func `init`(cvc: String)async throws { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_cktap_ffi_fn_method_satschip_init( + self.uniffiCloneHandle(), + FfiConverterString.lower(cvc) + ) + }, + pollFunc: ffi_cktap_ffi_rust_future_poll_void, + completeFunc: ffi_cktap_ffi_rust_future_complete_void, + freeFunc: ffi_cktap_ffi_rust_future_free_void, + liftFunc: { $0 }, + errorHandler: FfiConverterTypeCkTapError_lift + ) +} + +open func nfc()async throws -> String { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_cktap_ffi_fn_method_satschip_nfc( + self.uniffiCloneHandle() + + ) + }, + pollFunc: ffi_cktap_ffi_rust_future_poll_rust_buffer, + completeFunc: ffi_cktap_ffi_rust_future_complete_rust_buffer, + freeFunc: ffi_cktap_ffi_rust_future_free_rust_buffer, + liftFunc: FfiConverterString.lift, + errorHandler: FfiConverterTypeCkTapError_lift + ) +} + +open func read()async throws -> String { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_cktap_ffi_fn_method_satschip_read( + self.uniffiCloneHandle() + + ) + }, + pollFunc: ffi_cktap_ffi_rust_future_poll_rust_buffer, + completeFunc: ffi_cktap_ffi_rust_future_complete_rust_buffer, + freeFunc: ffi_cktap_ffi_rust_future_free_rust_buffer, + liftFunc: FfiConverterString.lift, + errorHandler: FfiConverterTypeReadError_lift + ) +} + +open func signPsbt(psbt: String, cvc: String)async throws -> String { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_cktap_ffi_fn_method_satschip_sign_psbt( + self.uniffiCloneHandle(), + FfiConverterString.lower(psbt),FfiConverterString.lower(cvc) + ) + }, + pollFunc: ffi_cktap_ffi_rust_future_poll_rust_buffer, + completeFunc: ffi_cktap_ffi_rust_future_complete_rust_buffer, + freeFunc: ffi_cktap_ffi_rust_future_free_rust_buffer, + liftFunc: FfiConverterString.lift, + errorHandler: FfiConverterTypeSignPsbtError_lift + ) +} + +open func status()async -> SatsChipStatus { + return + try! await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_cktap_ffi_fn_method_satschip_status( + self.uniffiCloneHandle() + + ) + }, + pollFunc: ffi_cktap_ffi_rust_future_poll_rust_buffer, + completeFunc: ffi_cktap_ffi_rust_future_complete_rust_buffer, + freeFunc: ffi_cktap_ffi_rust_future_free_rust_buffer, + liftFunc: FfiConverterTypeSatsChipStatus_lift, + errorHandler: nil + + ) +} + +open func wait()async throws { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_cktap_ffi_fn_method_satschip_wait( + self.uniffiCloneHandle() + + ) + }, + pollFunc: ffi_cktap_ffi_rust_future_poll_void, + completeFunc: ffi_cktap_ffi_rust_future_complete_void, + freeFunc: ffi_cktap_ffi_rust_future_free_void, + liftFunc: { $0 }, + errorHandler: FfiConverterTypeCkTapError_lift + ) +} + +open func xpub(master: Bool, cvc: String)async throws -> String { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_cktap_ffi_fn_method_satschip_xpub( + self.uniffiCloneHandle(), + FfiConverterBool.lower(master),FfiConverterString.lower(cvc) + ) + }, + pollFunc: ffi_cktap_ffi_rust_future_poll_rust_buffer, + completeFunc: ffi_cktap_ffi_rust_future_complete_rust_buffer, + freeFunc: ffi_cktap_ffi_rust_future_free_rust_buffer, + liftFunc: FfiConverterString.lift, + errorHandler: FfiConverterTypeXpubError_lift + ) +} + + +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeSatsChip: FfiConverter { + typealias FfiType = UInt64 + typealias SwiftType = SatsChip + + public static func lift(_ handle: UInt64) throws -> SatsChip { + return SatsChip(unsafeFromHandle: handle) + } + + public static func lower(_ value: SatsChip) -> UInt64 { + return value.uniffiCloneHandle() + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SatsChip { + let handle: UInt64 = try readInt(&buf) + return try lift(handle) + } + + public static func write(_ value: SatsChip, into buf: inout [UInt8]) { + writeInt(&buf, lower(value)) + } +} + + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeSatsChip_lift(_ handle: UInt64) throws -> SatsChip { + return try FfiConverterTypeSatsChip.lift(handle) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeSatsChip_lower(_ value: SatsChip) -> UInt64 { + return FfiConverterTypeSatsChip.lower(value) +} + + + + + + +public protocol TapSignerProtocol: AnyObject, Sendable { + + func change(newCvc: String, cvc: String) async throws + + func checkCert() async throws + + func derive(path: [UInt32], cvc: String) async throws -> String + + func `init`(cvc: String) async throws + + func nfc() async throws -> String + + func read(cvc: String) async throws -> String + + func signPsbt(psbt: String, cvc: String) async throws -> String + + func status() async -> TapSignerStatus + + func wait() async throws + + func xpub(master: Bool, cvc: String) async throws -> String + +} +open class TapSigner: TapSignerProtocol, @unchecked Sendable { + 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 { uniffi_cktap_ffi_fn_clone_tapsigner(self.handle, $0) } + } + // No primary constructor declared for this class. + + deinit { + try! rustCall { uniffi_cktap_ffi_fn_free_tapsigner(handle, $0) } + } + + + + +open func change(newCvc: String, cvc: String)async throws { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_cktap_ffi_fn_method_tapsigner_change( + self.uniffiCloneHandle(), + FfiConverterString.lower(newCvc),FfiConverterString.lower(cvc) + ) + }, + pollFunc: ffi_cktap_ffi_rust_future_poll_void, + completeFunc: ffi_cktap_ffi_rust_future_complete_void, + freeFunc: ffi_cktap_ffi_rust_future_free_void, + liftFunc: { $0 }, + errorHandler: FfiConverterTypeChangeError_lift + ) +} + +open func checkCert()async throws { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_cktap_ffi_fn_method_tapsigner_check_cert( + self.uniffiCloneHandle() + + ) + }, + pollFunc: ffi_cktap_ffi_rust_future_poll_void, + completeFunc: ffi_cktap_ffi_rust_future_complete_void, + freeFunc: ffi_cktap_ffi_rust_future_free_void, + liftFunc: { $0 }, + errorHandler: FfiConverterTypeCertsError_lift + ) +} + +open func derive(path: [UInt32], cvc: String)async throws -> String { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_cktap_ffi_fn_method_tapsigner_derive( + self.uniffiCloneHandle(), + FfiConverterSequenceUInt32.lower(path),FfiConverterString.lower(cvc) + ) + }, + pollFunc: ffi_cktap_ffi_rust_future_poll_rust_buffer, + completeFunc: ffi_cktap_ffi_rust_future_complete_rust_buffer, + freeFunc: ffi_cktap_ffi_rust_future_free_rust_buffer, + liftFunc: FfiConverterString.lift, + errorHandler: FfiConverterTypeDeriveError_lift + ) +} + +open func `init`(cvc: String)async throws { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_cktap_ffi_fn_method_tapsigner_init( + self.uniffiCloneHandle(), + FfiConverterString.lower(cvc) + ) + }, + pollFunc: ffi_cktap_ffi_rust_future_poll_void, + completeFunc: ffi_cktap_ffi_rust_future_complete_void, + freeFunc: ffi_cktap_ffi_rust_future_free_void, + liftFunc: { $0 }, + errorHandler: FfiConverterTypeCkTapError_lift + ) +} + +open func nfc()async throws -> String { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_cktap_ffi_fn_method_tapsigner_nfc( + self.uniffiCloneHandle() + + ) + }, + pollFunc: ffi_cktap_ffi_rust_future_poll_rust_buffer, + completeFunc: ffi_cktap_ffi_rust_future_complete_rust_buffer, + freeFunc: ffi_cktap_ffi_rust_future_free_rust_buffer, + liftFunc: FfiConverterString.lift, + errorHandler: FfiConverterTypeCkTapError_lift + ) +} + +open func read(cvc: String)async throws -> String { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_cktap_ffi_fn_method_tapsigner_read( + self.uniffiCloneHandle(), + FfiConverterString.lower(cvc) + ) + }, + pollFunc: ffi_cktap_ffi_rust_future_poll_rust_buffer, + completeFunc: ffi_cktap_ffi_rust_future_complete_rust_buffer, + freeFunc: ffi_cktap_ffi_rust_future_free_rust_buffer, + liftFunc: FfiConverterString.lift, + errorHandler: FfiConverterTypeReadError_lift + ) +} + +open func signPsbt(psbt: String, cvc: String)async throws -> String { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_cktap_ffi_fn_method_tapsigner_sign_psbt( + self.uniffiCloneHandle(), + FfiConverterString.lower(psbt),FfiConverterString.lower(cvc) + ) + }, + pollFunc: ffi_cktap_ffi_rust_future_poll_rust_buffer, + completeFunc: ffi_cktap_ffi_rust_future_complete_rust_buffer, + freeFunc: ffi_cktap_ffi_rust_future_free_rust_buffer, + liftFunc: FfiConverterString.lift, + errorHandler: FfiConverterTypeSignPsbtError_lift + ) +} + +open func status()async -> TapSignerStatus { + return + try! await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_cktap_ffi_fn_method_tapsigner_status( + self.uniffiCloneHandle() + + ) + }, + pollFunc: ffi_cktap_ffi_rust_future_poll_rust_buffer, + completeFunc: ffi_cktap_ffi_rust_future_complete_rust_buffer, + freeFunc: ffi_cktap_ffi_rust_future_free_rust_buffer, + liftFunc: FfiConverterTypeTapSignerStatus_lift, + errorHandler: nil + + ) +} + +open func wait()async throws { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_cktap_ffi_fn_method_tapsigner_wait( + self.uniffiCloneHandle() + + ) + }, + pollFunc: ffi_cktap_ffi_rust_future_poll_void, + completeFunc: ffi_cktap_ffi_rust_future_complete_void, + freeFunc: ffi_cktap_ffi_rust_future_free_void, + liftFunc: { $0 }, + errorHandler: FfiConverterTypeCkTapError_lift + ) +} + +open func xpub(master: Bool, cvc: String)async throws -> String { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_cktap_ffi_fn_method_tapsigner_xpub( + self.uniffiCloneHandle(), + FfiConverterBool.lower(master),FfiConverterString.lower(cvc) + ) + }, + pollFunc: ffi_cktap_ffi_rust_future_poll_rust_buffer, + completeFunc: ffi_cktap_ffi_rust_future_complete_rust_buffer, + freeFunc: ffi_cktap_ffi_rust_future_free_rust_buffer, + liftFunc: FfiConverterString.lift, + errorHandler: FfiConverterTypeXpubError_lift + ) +} + + +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeTapSigner: FfiConverter { + typealias FfiType = UInt64 + typealias SwiftType = TapSigner + + public static func lift(_ handle: UInt64) throws -> TapSigner { + return TapSigner(unsafeFromHandle: handle) + } + + public static func lower(_ value: TapSigner) -> UInt64 { + return value.uniffiCloneHandle() + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TapSigner { + let handle: UInt64 = try readInt(&buf) + return try lift(handle) + } + + public static func write(_ value: TapSigner, into buf: inout [UInt8]) { + writeInt(&buf, lower(value)) + } +} + + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeTapSigner_lift(_ handle: UInt64) throws -> TapSigner { + return try FfiConverterTypeTapSigner.lift(handle) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeTapSigner_lower(_ value: TapSigner) -> UInt64 { + return FfiConverterTypeTapSigner.lower(value) +} + + + + +public struct SatsCardStatus { + public var proto: UInt64 + public var ver: String + public var birth: UInt64 + public var activeSlot: UInt8 + public var numSlots: UInt8 + public var addr: String? + public var pubkey: String + public var cardIdent: String + public var authDelay: UInt8? + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(proto: UInt64, ver: String, birth: UInt64, activeSlot: UInt8, numSlots: UInt8, addr: String?, pubkey: String, cardIdent: String, authDelay: UInt8?) { + self.proto = proto + self.ver = ver + self.birth = birth + self.activeSlot = activeSlot + self.numSlots = numSlots + self.addr = addr + self.pubkey = pubkey + self.cardIdent = cardIdent + self.authDelay = authDelay + } +} + +#if compiler(>=6) +extension SatsCardStatus: Sendable {} +#endif + + +extension SatsCardStatus: Equatable, Hashable { + public static func ==(lhs: SatsCardStatus, rhs: SatsCardStatus) -> Bool { + if lhs.proto != rhs.proto { + return false + } + if lhs.ver != rhs.ver { + return false + } + if lhs.birth != rhs.birth { + return false + } + if lhs.activeSlot != rhs.activeSlot { + return false + } + if lhs.numSlots != rhs.numSlots { + return false + } + if lhs.addr != rhs.addr { + return false + } + if lhs.pubkey != rhs.pubkey { + return false + } + if lhs.cardIdent != rhs.cardIdent { + return false + } + if lhs.authDelay != rhs.authDelay { + return false + } + return true + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(proto) + hasher.combine(ver) + hasher.combine(birth) + hasher.combine(activeSlot) + hasher.combine(numSlots) + hasher.combine(addr) + hasher.combine(pubkey) + hasher.combine(cardIdent) + hasher.combine(authDelay) + } +} + + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeSatsCardStatus: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SatsCardStatus { + return + try SatsCardStatus( + proto: FfiConverterUInt64.read(from: &buf), + ver: FfiConverterString.read(from: &buf), + birth: FfiConverterUInt64.read(from: &buf), + activeSlot: FfiConverterUInt8.read(from: &buf), + numSlots: FfiConverterUInt8.read(from: &buf), + addr: FfiConverterOptionString.read(from: &buf), + pubkey: FfiConverterString.read(from: &buf), + cardIdent: FfiConverterString.read(from: &buf), + authDelay: FfiConverterOptionUInt8.read(from: &buf) + ) + } + + public static func write(_ value: SatsCardStatus, into buf: inout [UInt8]) { + FfiConverterUInt64.write(value.proto, into: &buf) + FfiConverterString.write(value.ver, into: &buf) + FfiConverterUInt64.write(value.birth, into: &buf) + FfiConverterUInt8.write(value.activeSlot, into: &buf) + FfiConverterUInt8.write(value.numSlots, into: &buf) + FfiConverterOptionString.write(value.addr, into: &buf) + FfiConverterString.write(value.pubkey, into: &buf) + FfiConverterString.write(value.cardIdent, into: &buf) + FfiConverterOptionUInt8.write(value.authDelay, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeSatsCardStatus_lift(_ buf: RustBuffer) throws -> SatsCardStatus { + return try FfiConverterTypeSatsCardStatus.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeSatsCardStatus_lower(_ value: SatsCardStatus) -> RustBuffer { + return FfiConverterTypeSatsCardStatus.lower(value) +} + + +public struct SatsChipStatus { + public var proto: UInt64 + public var ver: String + public var birth: UInt64 + public var path: [UInt64]? + public var pubkey: String + public var cardIdent: String + public var authDelay: UInt8? + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(proto: UInt64, ver: String, birth: UInt64, path: [UInt64]?, pubkey: String, cardIdent: String, authDelay: UInt8?) { + self.proto = proto + self.ver = ver + self.birth = birth + self.path = path + self.pubkey = pubkey + self.cardIdent = cardIdent + self.authDelay = authDelay + } +} + +#if compiler(>=6) +extension SatsChipStatus: Sendable {} +#endif + + +extension SatsChipStatus: Equatable, Hashable { + public static func ==(lhs: SatsChipStatus, rhs: SatsChipStatus) -> Bool { + if lhs.proto != rhs.proto { + return false + } + if lhs.ver != rhs.ver { + return false + } + if lhs.birth != rhs.birth { + return false + } + if lhs.path != rhs.path { + return false + } + if lhs.pubkey != rhs.pubkey { + return false + } + if lhs.cardIdent != rhs.cardIdent { + return false + } + if lhs.authDelay != rhs.authDelay { + return false + } + return true + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(proto) + hasher.combine(ver) + hasher.combine(birth) + hasher.combine(path) + hasher.combine(pubkey) + hasher.combine(cardIdent) + hasher.combine(authDelay) + } +} + + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeSatsChipStatus: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SatsChipStatus { + return + try SatsChipStatus( + proto: FfiConverterUInt64.read(from: &buf), + ver: FfiConverterString.read(from: &buf), + birth: FfiConverterUInt64.read(from: &buf), + path: FfiConverterOptionSequenceUInt64.read(from: &buf), + pubkey: FfiConverterString.read(from: &buf), + cardIdent: FfiConverterString.read(from: &buf), + authDelay: FfiConverterOptionUInt8.read(from: &buf) + ) + } + + public static func write(_ value: SatsChipStatus, into buf: inout [UInt8]) { + FfiConverterUInt64.write(value.proto, into: &buf) + FfiConverterString.write(value.ver, into: &buf) + FfiConverterUInt64.write(value.birth, into: &buf) + FfiConverterOptionSequenceUInt64.write(value.path, into: &buf) + FfiConverterString.write(value.pubkey, into: &buf) + FfiConverterString.write(value.cardIdent, into: &buf) + FfiConverterOptionUInt8.write(value.authDelay, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeSatsChipStatus_lift(_ buf: RustBuffer) throws -> SatsChipStatus { + return try FfiConverterTypeSatsChipStatus.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeSatsChipStatus_lower(_ value: SatsChipStatus) -> RustBuffer { + return FfiConverterTypeSatsChipStatus.lower(value) +} + + +public struct SlotDetails { + public var privkey: String? + public var pubkey: String + public var pubkeyDescriptor: String + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(privkey: String?, pubkey: String, pubkeyDescriptor: String) { + self.privkey = privkey + self.pubkey = pubkey + self.pubkeyDescriptor = pubkeyDescriptor + } +} + +#if compiler(>=6) +extension SlotDetails: Sendable {} +#endif + + +extension SlotDetails: Equatable, Hashable { + public static func ==(lhs: SlotDetails, rhs: SlotDetails) -> Bool { + if lhs.privkey != rhs.privkey { + return false + } + if lhs.pubkey != rhs.pubkey { + return false + } + if lhs.pubkeyDescriptor != rhs.pubkeyDescriptor { + return false + } + return true + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(privkey) + hasher.combine(pubkey) + hasher.combine(pubkeyDescriptor) + } +} + + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeSlotDetails: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SlotDetails { + return + try SlotDetails( + privkey: FfiConverterOptionString.read(from: &buf), + pubkey: FfiConverterString.read(from: &buf), + pubkeyDescriptor: FfiConverterString.read(from: &buf) + ) + } + + public static func write(_ value: SlotDetails, into buf: inout [UInt8]) { + FfiConverterOptionString.write(value.privkey, into: &buf) + FfiConverterString.write(value.pubkey, into: &buf) + FfiConverterString.write(value.pubkeyDescriptor, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeSlotDetails_lift(_ buf: RustBuffer) throws -> SlotDetails { + return try FfiConverterTypeSlotDetails.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeSlotDetails_lower(_ value: SlotDetails) -> RustBuffer { + return FfiConverterTypeSlotDetails.lower(value) +} + + +public struct TapSignerStatus { + public var proto: UInt64 + public var ver: String + public var birth: UInt64 + public var path: [UInt64]? + public var numBackups: UInt64 + public var pubkey: String + public var cardIdent: String + public var authDelay: UInt8? + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(proto: UInt64, ver: String, birth: UInt64, path: [UInt64]?, numBackups: UInt64, pubkey: String, cardIdent: String, authDelay: UInt8?) { + self.proto = proto + self.ver = ver + self.birth = birth + self.path = path + self.numBackups = numBackups + self.pubkey = pubkey + self.cardIdent = cardIdent + self.authDelay = authDelay + } +} + +#if compiler(>=6) +extension TapSignerStatus: Sendable {} +#endif + + +extension TapSignerStatus: Equatable, Hashable { + public static func ==(lhs: TapSignerStatus, rhs: TapSignerStatus) -> Bool { + if lhs.proto != rhs.proto { + return false + } + if lhs.ver != rhs.ver { + return false + } + if lhs.birth != rhs.birth { + return false + } + if lhs.path != rhs.path { + return false + } + if lhs.numBackups != rhs.numBackups { + return false + } + if lhs.pubkey != rhs.pubkey { + return false + } + if lhs.cardIdent != rhs.cardIdent { + return false + } + if lhs.authDelay != rhs.authDelay { + return false + } + return true + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(proto) + hasher.combine(ver) + hasher.combine(birth) + hasher.combine(path) + hasher.combine(numBackups) + hasher.combine(pubkey) + hasher.combine(cardIdent) + hasher.combine(authDelay) + } +} + + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeTapSignerStatus: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TapSignerStatus { + return + try TapSignerStatus( + proto: FfiConverterUInt64.read(from: &buf), + ver: FfiConverterString.read(from: &buf), + birth: FfiConverterUInt64.read(from: &buf), + path: FfiConverterOptionSequenceUInt64.read(from: &buf), + numBackups: FfiConverterUInt64.read(from: &buf), + pubkey: FfiConverterString.read(from: &buf), + cardIdent: FfiConverterString.read(from: &buf), + authDelay: FfiConverterOptionUInt8.read(from: &buf) + ) + } + + public static func write(_ value: TapSignerStatus, into buf: inout [UInt8]) { + FfiConverterUInt64.write(value.proto, into: &buf) + FfiConverterString.write(value.ver, into: &buf) + FfiConverterUInt64.write(value.birth, into: &buf) + FfiConverterOptionSequenceUInt64.write(value.path, into: &buf) + FfiConverterUInt64.write(value.numBackups, into: &buf) + FfiConverterString.write(value.pubkey, into: &buf) + FfiConverterString.write(value.cardIdent, into: &buf) + FfiConverterOptionUInt8.write(value.authDelay, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeTapSignerStatus_lift(_ buf: RustBuffer) throws -> TapSignerStatus { + return try FfiConverterTypeTapSignerStatus.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeTapSignerStatus_lower(_ value: TapSignerStatus) -> RustBuffer { + return FfiConverterTypeTapSignerStatus.lower(value) +} + + +/** + * Errors returned by the CkTap card. + */ +public enum CardError: Swift.Error { + + + + case UnluckyNumber + case BadArguments + case BadAuth + case NeedsAuth + case UnknownCommand + case InvalidCommand + case InvalidState + case WeakNonce + case BadCbor + case BackupFirst + case RateLimited +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeCardError: FfiConverterRustBuffer { + typealias SwiftType = CardError + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> CardError { + let variant: Int32 = try readInt(&buf) + switch variant { + + + + + case 1: return .UnluckyNumber + case 2: return .BadArguments + case 3: return .BadAuth + case 4: return .NeedsAuth + case 5: return .UnknownCommand + case 6: return .InvalidCommand + case 7: return .InvalidState + case 8: return .WeakNonce + case 9: return .BadCbor + case 10: return .BackupFirst + case 11: return .RateLimited + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: CardError, into buf: inout [UInt8]) { + switch value { + + + + + + case .UnluckyNumber: + writeInt(&buf, Int32(1)) + + + case .BadArguments: + writeInt(&buf, Int32(2)) + + + case .BadAuth: + writeInt(&buf, Int32(3)) + + + case .NeedsAuth: + writeInt(&buf, Int32(4)) + + + case .UnknownCommand: + writeInt(&buf, Int32(5)) + + + case .InvalidCommand: + writeInt(&buf, Int32(6)) + + + case .InvalidState: + writeInt(&buf, Int32(7)) + + + case .WeakNonce: + writeInt(&buf, Int32(8)) + + + case .BadCbor: + writeInt(&buf, Int32(9)) + + + case .BackupFirst: + writeInt(&buf, Int32(10)) + + + case .RateLimited: + writeInt(&buf, Int32(11)) + + } + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeCardError_lift(_ buf: RustBuffer) throws -> CardError { + return try FfiConverterTypeCardError.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeCardError_lower(_ value: CardError) -> RustBuffer { + return FfiConverterTypeCardError.lower(value) +} + + +extension CardError: Equatable, Hashable {} + + + + +extension CardError: Foundation.LocalizedError { + public var errorDescription: String? { + String(reflecting: self) + } +} + + + + + +/** + * Errors returned by the `certs` command. + */ +public enum CertsError: Swift.Error { + + + + case CkTap(err: CkTapError + ) + case Key(err: KeyError + ) + case InvalidRootCert(msg: String + ) +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeCertsError: FfiConverterRustBuffer { + typealias SwiftType = CertsError + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> CertsError { + let variant: Int32 = try readInt(&buf) + switch variant { + + + + + case 1: return .CkTap( + err: try FfiConverterTypeCkTapError.read(from: &buf) + ) + case 2: return .Key( + err: try FfiConverterTypeKeyError.read(from: &buf) + ) + case 3: return .InvalidRootCert( + msg: try FfiConverterString.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: CertsError, into buf: inout [UInt8]) { + switch value { + + + + + + case let .CkTap(err): + writeInt(&buf, Int32(1)) + FfiConverterTypeCkTapError.write(err, into: &buf) + + + case let .Key(err): + writeInt(&buf, Int32(2)) + FfiConverterTypeKeyError.write(err, into: &buf) + + + case let .InvalidRootCert(msg): + writeInt(&buf, Int32(3)) + FfiConverterString.write(msg, into: &buf) + + } + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeCertsError_lift(_ buf: RustBuffer) throws -> CertsError { + return try FfiConverterTypeCertsError.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeCertsError_lower(_ value: CertsError) -> RustBuffer { + return FfiConverterTypeCertsError.lower(value) +} + + +extension CertsError: Equatable, Hashable {} + + + + +extension CertsError: Foundation.LocalizedError { + public var errorDescription: String? { + String(reflecting: self) + } +} + + + + + +/** + * Errors returned by the `change` command. + */ +public enum ChangeError: Swift.Error { + + + + case CkTap(err: CkTapError + ) + case TooShort(len: UInt64 + ) + case TooLong(len: UInt64 + ) + case SameAsOld +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeChangeError: FfiConverterRustBuffer { + typealias SwiftType = ChangeError + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ChangeError { + let variant: Int32 = try readInt(&buf) + switch variant { + + + + + case 1: return .CkTap( + err: try FfiConverterTypeCkTapError.read(from: &buf) + ) + case 2: return .TooShort( + len: try FfiConverterUInt64.read(from: &buf) + ) + case 3: return .TooLong( + len: try FfiConverterUInt64.read(from: &buf) + ) + case 4: return .SameAsOld + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: ChangeError, into buf: inout [UInt8]) { + switch value { + + + + + + case let .CkTap(err): + writeInt(&buf, Int32(1)) + FfiConverterTypeCkTapError.write(err, into: &buf) + + + case let .TooShort(len): + writeInt(&buf, Int32(2)) + FfiConverterUInt64.write(len, into: &buf) + + + case let .TooLong(len): + writeInt(&buf, Int32(3)) + FfiConverterUInt64.write(len, into: &buf) + + + case .SameAsOld: + writeInt(&buf, Int32(4)) + + } + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeChangeError_lift(_ buf: RustBuffer) throws -> ChangeError { + return try FfiConverterTypeChangeError.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeChangeError_lower(_ value: ChangeError) -> RustBuffer { + return FfiConverterTypeChangeError.lower(value) +} + + +extension ChangeError: Equatable, Hashable {} + + + + +extension ChangeError: Foundation.LocalizedError { + public var errorDescription: String? { + String(reflecting: self) + } +} + + + + +// Note that we don't yet support `indirect` for enums. +// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. + +public enum CkTapCard { + + case satsCard(SatsCard + ) + case tapSigner(TapSigner + ) + case satsChip(SatsChip + ) +} + + +#if compiler(>=6) +extension CkTapCard: Sendable {} +#endif + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeCkTapCard: FfiConverterRustBuffer { + typealias SwiftType = CkTapCard + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> CkTapCard { + let variant: Int32 = try readInt(&buf) + switch variant { + + case 1: return .satsCard(try FfiConverterTypeSatsCard.read(from: &buf) + ) + + case 2: return .tapSigner(try FfiConverterTypeTapSigner.read(from: &buf) + ) + + case 3: return .satsChip(try FfiConverterTypeSatsChip.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: CkTapCard, into buf: inout [UInt8]) { + switch value { + + + case let .satsCard(v1): + writeInt(&buf, Int32(1)) + FfiConverterTypeSatsCard.write(v1, into: &buf) + + + case let .tapSigner(v1): + writeInt(&buf, Int32(2)) + FfiConverterTypeTapSigner.write(v1, into: &buf) + + + case let .satsChip(v1): + writeInt(&buf, Int32(3)) + FfiConverterTypeSatsChip.write(v1, into: &buf) + + } + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeCkTapCard_lift(_ buf: RustBuffer) throws -> CkTapCard { + return try FfiConverterTypeCkTapCard.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeCkTapCard_lower(_ value: CkTapCard) -> RustBuffer { + return FfiConverterTypeCkTapCard.lower(value) +} + + + + + + + +/** + * Errors returned by the card, CBOR deserialization or value encoding, or the APDU transport. + */ +public enum CkTapError: Swift.Error { + + + + case Card(err: CardError + ) + case CborDe(msg: String + ) + case CborValue(msg: String + ) + case Transport(msg: String + ) + case UnknownCardType +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeCkTapError: FfiConverterRustBuffer { + typealias SwiftType = CkTapError + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> CkTapError { + let variant: Int32 = try readInt(&buf) + switch variant { + + + + + case 1: return .Card( + err: try FfiConverterTypeCardError.read(from: &buf) + ) + case 2: return .CborDe( + msg: try FfiConverterString.read(from: &buf) + ) + case 3: return .CborValue( + msg: try FfiConverterString.read(from: &buf) + ) + case 4: return .Transport( + msg: try FfiConverterString.read(from: &buf) + ) + case 5: return .UnknownCardType + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: CkTapError, into buf: inout [UInt8]) { + switch value { + + + + + + case let .Card(err): + writeInt(&buf, Int32(1)) + FfiConverterTypeCardError.write(err, into: &buf) + + + case let .CborDe(msg): + writeInt(&buf, Int32(2)) + FfiConverterString.write(msg, into: &buf) + + + case let .CborValue(msg): + writeInt(&buf, Int32(3)) + FfiConverterString.write(msg, into: &buf) + + + case let .Transport(msg): + writeInt(&buf, Int32(4)) + FfiConverterString.write(msg, into: &buf) + + + case .UnknownCardType: + writeInt(&buf, Int32(5)) + + } + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeCkTapError_lift(_ buf: RustBuffer) throws -> CkTapError { + return try FfiConverterTypeCkTapError.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeCkTapError_lower(_ value: CkTapError) -> RustBuffer { + return FfiConverterTypeCkTapError.lower(value) +} + + +extension CkTapError: Equatable, Hashable {} + + + + +extension CkTapError: Foundation.LocalizedError { + public var errorDescription: String? { + String(reflecting: self) + } +} + + + + + +/** + * Errors returned by the `derive` command. + */ +public enum DeriveError: Swift.Error { + + + + case CkTap(err: CkTapError + ) + case Key(err: KeyError + ) + case InvalidChainCode(msg: String + ) +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeDeriveError: FfiConverterRustBuffer { + typealias SwiftType = DeriveError + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> DeriveError { + let variant: Int32 = try readInt(&buf) + switch variant { + + + + + case 1: return .CkTap( + err: try FfiConverterTypeCkTapError.read(from: &buf) + ) + case 2: return .Key( + err: try FfiConverterTypeKeyError.read(from: &buf) + ) + case 3: return .InvalidChainCode( + msg: try FfiConverterString.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: DeriveError, into buf: inout [UInt8]) { + switch value { + + + + + + case let .CkTap(err): + writeInt(&buf, Int32(1)) + FfiConverterTypeCkTapError.write(err, into: &buf) + + + case let .Key(err): + writeInt(&buf, Int32(2)) + FfiConverterTypeKeyError.write(err, into: &buf) + + + case let .InvalidChainCode(msg): + writeInt(&buf, Int32(3)) + FfiConverterString.write(msg, into: &buf) + + } + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeDeriveError_lift(_ buf: RustBuffer) throws -> DeriveError { + return try FfiConverterTypeDeriveError.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeDeriveError_lower(_ value: DeriveError) -> RustBuffer { + return FfiConverterTypeDeriveError.lower(value) +} + + +extension DeriveError: Equatable, Hashable {} + + + + +extension DeriveError: Foundation.LocalizedError { + public var errorDescription: String? { + String(reflecting: self) + } +} + + + + + +/** + * Errors returned by the `dump` command. + */ +public enum DumpError: Swift.Error { + + + + case CkTap(err: CkTapError + ) + case Key(err: KeyError + ) + case SlotSealed(slot: UInt8 + ) + case SlotUnused(slot: UInt8 + ) + /** + * If the slot was unsealed due to confusion or uncertainty about its status. + * In other words, if the card unsealed itself rather than via a + * successful `unseal` command. + */ + case SlotTampered(slot: UInt8 + ) +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeDumpError: FfiConverterRustBuffer { + typealias SwiftType = DumpError + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> DumpError { + let variant: Int32 = try readInt(&buf) + switch variant { + + + + + case 1: return .CkTap( + err: try FfiConverterTypeCkTapError.read(from: &buf) + ) + case 2: return .Key( + err: try FfiConverterTypeKeyError.read(from: &buf) + ) + case 3: return .SlotSealed( + slot: try FfiConverterUInt8.read(from: &buf) + ) + case 4: return .SlotUnused( + slot: try FfiConverterUInt8.read(from: &buf) + ) + case 5: return .SlotTampered( + slot: try FfiConverterUInt8.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: DumpError, into buf: inout [UInt8]) { + switch value { + + + + + + case let .CkTap(err): + writeInt(&buf, Int32(1)) + FfiConverterTypeCkTapError.write(err, into: &buf) + + + case let .Key(err): + writeInt(&buf, Int32(2)) + FfiConverterTypeKeyError.write(err, into: &buf) + + + case let .SlotSealed(slot): + writeInt(&buf, Int32(3)) + FfiConverterUInt8.write(slot, into: &buf) + + + case let .SlotUnused(slot): + writeInt(&buf, Int32(4)) + FfiConverterUInt8.write(slot, into: &buf) + + + case let .SlotTampered(slot): + writeInt(&buf, Int32(5)) + FfiConverterUInt8.write(slot, into: &buf) + + } + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeDumpError_lift(_ buf: RustBuffer) throws -> DumpError { + return try FfiConverterTypeDumpError.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeDumpError_lower(_ value: DumpError) -> RustBuffer { + return FfiConverterTypeDumpError.lower(value) +} + + +extension DumpError: Equatable, Hashable {} + + + + +extension DumpError: Foundation.LocalizedError { + public var errorDescription: String? { + String(reflecting: self) + } +} + + + + + +public enum KeyError: Swift.Error { + + + + case Secp256k1(msg: String + ) + case KeyFromSlice(msg: String + ) +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeKeyError: FfiConverterRustBuffer { + typealias SwiftType = KeyError + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> KeyError { + let variant: Int32 = try readInt(&buf) + switch variant { + + + + + case 1: return .Secp256k1( + msg: try FfiConverterString.read(from: &buf) + ) + case 2: return .KeyFromSlice( + msg: try FfiConverterString.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: KeyError, into buf: inout [UInt8]) { + switch value { + + + + + + case let .Secp256k1(msg): + writeInt(&buf, Int32(1)) + FfiConverterString.write(msg, into: &buf) + + + case let .KeyFromSlice(msg): + writeInt(&buf, Int32(2)) + FfiConverterString.write(msg, into: &buf) + + } + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeKeyError_lift(_ buf: RustBuffer) throws -> KeyError { + return try FfiConverterTypeKeyError.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeKeyError_lower(_ value: KeyError) -> RustBuffer { + return FfiConverterTypeKeyError.lower(value) +} + + +extension KeyError: Equatable, Hashable {} + + + + +extension KeyError: Foundation.LocalizedError { + public var errorDescription: String? { + String(reflecting: self) + } +} + + + + + +/** + * Errors returned by the `read` command. + */ +public enum ReadError: Swift.Error { + + + + case CkTap(err: CkTapError + ) + case Key(err: KeyError + ) +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeReadError: FfiConverterRustBuffer { + typealias SwiftType = ReadError + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ReadError { + let variant: Int32 = try readInt(&buf) + switch variant { + + + + + case 1: return .CkTap( + err: try FfiConverterTypeCkTapError.read(from: &buf) + ) + case 2: return .Key( + err: try FfiConverterTypeKeyError.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: ReadError, into buf: inout [UInt8]) { + switch value { + + + + + + case let .CkTap(err): + writeInt(&buf, Int32(1)) + FfiConverterTypeCkTapError.write(err, into: &buf) + + + case let .Key(err): + writeInt(&buf, Int32(2)) + FfiConverterTypeKeyError.write(err, into: &buf) + + } + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeReadError_lift(_ buf: RustBuffer) throws -> ReadError { + return try FfiConverterTypeReadError.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeReadError_lower(_ value: ReadError) -> RustBuffer { + return FfiConverterTypeReadError.lower(value) +} + + +extension ReadError: Equatable, Hashable {} + + + + +extension ReadError: Foundation.LocalizedError { + public var errorDescription: String? { + String(reflecting: self) + } +} + + + + + +public enum SignPsbtError: Swift.Error { + + + + case InvalidPath(index: UInt64 + ) + case InvalidScript(index: UInt64 + ) + case MissingPubkey(index: UInt64 + ) + case MissingUtxo(index: UInt64 + ) + case PubkeyMismatch(index: UInt64 + ) + case SighashError(msg: String + ) + case SignatureError(msg: String + ) + case SlotNotUnsealed(slot: UInt8 + ) + case CkTap(err: CkTapError + ) + case WitnessProgram(msg: String + ) + case PsbtEncoding(msg: String + ) + case Base64Encoding(msg: String + ) +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeSignPsbtError: FfiConverterRustBuffer { + typealias SwiftType = SignPsbtError + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SignPsbtError { + let variant: Int32 = try readInt(&buf) + switch variant { + + + + + case 1: return .InvalidPath( + index: try FfiConverterUInt64.read(from: &buf) + ) + case 2: return .InvalidScript( + index: try FfiConverterUInt64.read(from: &buf) + ) + case 3: return .MissingPubkey( + index: try FfiConverterUInt64.read(from: &buf) + ) + case 4: return .MissingUtxo( + index: try FfiConverterUInt64.read(from: &buf) + ) + case 5: return .PubkeyMismatch( + index: try FfiConverterUInt64.read(from: &buf) + ) + case 6: return .SighashError( + msg: try FfiConverterString.read(from: &buf) + ) + case 7: return .SignatureError( + msg: try FfiConverterString.read(from: &buf) + ) + case 8: return .SlotNotUnsealed( + slot: try FfiConverterUInt8.read(from: &buf) + ) + case 9: return .CkTap( + err: try FfiConverterTypeCkTapError.read(from: &buf) + ) + case 10: return .WitnessProgram( + msg: try FfiConverterString.read(from: &buf) + ) + case 11: return .PsbtEncoding( + msg: try FfiConverterString.read(from: &buf) + ) + case 12: return .Base64Encoding( + msg: try FfiConverterString.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: SignPsbtError, into buf: inout [UInt8]) { + switch value { + + + + + + case let .InvalidPath(index): + writeInt(&buf, Int32(1)) + FfiConverterUInt64.write(index, into: &buf) + + + case let .InvalidScript(index): + writeInt(&buf, Int32(2)) + FfiConverterUInt64.write(index, into: &buf) + + + case let .MissingPubkey(index): + writeInt(&buf, Int32(3)) + FfiConverterUInt64.write(index, into: &buf) + + + case let .MissingUtxo(index): + writeInt(&buf, Int32(4)) + FfiConverterUInt64.write(index, into: &buf) + + + case let .PubkeyMismatch(index): + writeInt(&buf, Int32(5)) + FfiConverterUInt64.write(index, into: &buf) + + + case let .SighashError(msg): + writeInt(&buf, Int32(6)) + FfiConverterString.write(msg, into: &buf) + + + case let .SignatureError(msg): + writeInt(&buf, Int32(7)) + FfiConverterString.write(msg, into: &buf) + + + case let .SlotNotUnsealed(slot): + writeInt(&buf, Int32(8)) + FfiConverterUInt8.write(slot, into: &buf) + + + case let .CkTap(err): + writeInt(&buf, Int32(9)) + FfiConverterTypeCkTapError.write(err, into: &buf) + + + case let .WitnessProgram(msg): + writeInt(&buf, Int32(10)) + FfiConverterString.write(msg, into: &buf) + + + case let .PsbtEncoding(msg): + writeInt(&buf, Int32(11)) + FfiConverterString.write(msg, into: &buf) + + + case let .Base64Encoding(msg): + writeInt(&buf, Int32(12)) + FfiConverterString.write(msg, into: &buf) + + } + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeSignPsbtError_lift(_ buf: RustBuffer) throws -> SignPsbtError { + return try FfiConverterTypeSignPsbtError.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeSignPsbtError_lower(_ value: SignPsbtError) -> RustBuffer { + return FfiConverterTypeSignPsbtError.lower(value) +} + + +extension SignPsbtError: Equatable, Hashable {} + + + + +extension SignPsbtError: Foundation.LocalizedError { + public var errorDescription: String? { + String(reflecting: self) + } +} + + + + + +/** + * Errors returned by the `status` command. + */ +public enum StatusError: Swift.Error { + + + + case CkTap(err: CkTapError + ) + case Key(err: KeyError + ) +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeStatusError: FfiConverterRustBuffer { + typealias SwiftType = StatusError + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> StatusError { + let variant: Int32 = try readInt(&buf) + switch variant { + + + + + case 1: return .CkTap( + err: try FfiConverterTypeCkTapError.read(from: &buf) + ) + case 2: return .Key( + err: try FfiConverterTypeKeyError.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: StatusError, into buf: inout [UInt8]) { + switch value { + + + + + + case let .CkTap(err): + writeInt(&buf, Int32(1)) + FfiConverterTypeCkTapError.write(err, into: &buf) + + + case let .Key(err): + writeInt(&buf, Int32(2)) + FfiConverterTypeKeyError.write(err, into: &buf) + + } + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeStatusError_lift(_ buf: RustBuffer) throws -> StatusError { + return try FfiConverterTypeStatusError.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeStatusError_lower(_ value: StatusError) -> RustBuffer { + return FfiConverterTypeStatusError.lower(value) +} + + +extension StatusError: Equatable, Hashable {} + + + + +extension StatusError: Foundation.LocalizedError { + public var errorDescription: String? { + String(reflecting: self) + } +} + + + + + +/** + * Errors returned by the `unseal` command. + */ +public enum UnsealError: Swift.Error { + + + + case CkTap(err: CkTapError + ) + case Key(err: KeyError + ) +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeUnsealError: FfiConverterRustBuffer { + typealias SwiftType = UnsealError + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UnsealError { + let variant: Int32 = try readInt(&buf) + switch variant { + + + + + case 1: return .CkTap( + err: try FfiConverterTypeCkTapError.read(from: &buf) + ) + case 2: return .Key( + err: try FfiConverterTypeKeyError.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: UnsealError, into buf: inout [UInt8]) { + switch value { + + + + + + case let .CkTap(err): + writeInt(&buf, Int32(1)) + FfiConverterTypeCkTapError.write(err, into: &buf) + + + case let .Key(err): + writeInt(&buf, Int32(2)) + FfiConverterTypeKeyError.write(err, into: &buf) + + } + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeUnsealError_lift(_ buf: RustBuffer) throws -> UnsealError { + return try FfiConverterTypeUnsealError.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeUnsealError_lower(_ value: UnsealError) -> RustBuffer { + return FfiConverterTypeUnsealError.lower(value) +} + + +extension UnsealError: Equatable, Hashable {} + + + + +extension UnsealError: Foundation.LocalizedError { + public var errorDescription: String? { + String(reflecting: self) + } +} + + + + + +/** + * Errors returned by the `xpub` command. + */ +public enum XpubError: Swift.Error { + + + + case CkTap(err: CkTapError + ) + case Bip32(msg: String + ) +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeXpubError: FfiConverterRustBuffer { + typealias SwiftType = XpubError + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> XpubError { + let variant: Int32 = try readInt(&buf) + switch variant { + + + + + case 1: return .CkTap( + err: try FfiConverterTypeCkTapError.read(from: &buf) + ) + case 2: return .Bip32( + msg: try FfiConverterString.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: XpubError, into buf: inout [UInt8]) { + switch value { + + + + + + case let .CkTap(err): + writeInt(&buf, Int32(1)) + FfiConverterTypeCkTapError.write(err, into: &buf) + + + case let .Bip32(msg): + writeInt(&buf, Int32(2)) + FfiConverterString.write(msg, into: &buf) + + } + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeXpubError_lift(_ buf: RustBuffer) throws -> XpubError { + return try FfiConverterTypeXpubError.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeXpubError_lower(_ value: XpubError) -> RustBuffer { + return FfiConverterTypeXpubError.lower(value) +} + + +extension XpubError: Equatable, Hashable {} + + + + +extension XpubError: Foundation.LocalizedError { + public var errorDescription: String? { + String(reflecting: self) + } +} + + + + + + + +public protocol CkTransport: AnyObject, Sendable { + + func transmitApdu(commandApdu: Data) async throws -> Data + +} + + +// Put the implementation in a struct so we don't pollute the top-level namespace +fileprivate struct UniffiCallbackInterfaceCkTransport { + + // Create the VTable using a series of closures. + // Swift automatically converts these into C callback functions. + // + // This creates 1-element array, since this seems to be the only way to construct a const + // pointer that we can pass to the Rust code. + static let vtable: [UniffiVTableCallbackInterfaceCkTransport] = [UniffiVTableCallbackInterfaceCkTransport( + uniffiFree: { (uniffiHandle: UInt64) -> () in + do { + try FfiConverterCallbackInterfaceCkTransport.handleMap.remove(handle: uniffiHandle) + } catch { + print("Uniffi callback interface CkTransport: handle missing in uniffiFree") + } + }, + uniffiClone: { (uniffiHandle: UInt64) -> UInt64 in + do { + return try FfiConverterCallbackInterfaceCkTransport.handleMap.clone(handle: uniffiHandle) + } catch { + fatalError("Uniffi callback interface CkTransport: handle missing in uniffiClone") + } + }, + transmitApdu: { ( + uniffiHandle: UInt64, + commandApdu: RustBuffer, + uniffiFutureCallback: @escaping UniffiForeignFutureCompleteRustBuffer, + uniffiCallbackData: UInt64, + uniffiOutDroppedCallback: UnsafeMutablePointer + ) in + let makeCall = { + () async throws -> Data in + guard let uniffiObj = try? FfiConverterCallbackInterfaceCkTransport.handleMap.get(handle: uniffiHandle) else { + throw UniffiInternalError.unexpectedStaleHandle + } + return try await uniffiObj.transmitApdu( + commandApdu: try FfiConverterData.lift(commandApdu) + ) + } + + let uniffiHandleSuccess = { (returnValue: Data) in + uniffiFutureCallback( + uniffiCallbackData, + UniffiForeignFutureResultRustBuffer( + returnValue: FfiConverterData.lower(returnValue), + callStatus: RustCallStatus() + ) + ) + } + let uniffiHandleError = { (statusCode, errorBuf) in + uniffiFutureCallback( + uniffiCallbackData, + UniffiForeignFutureResultRustBuffer( + returnValue: RustBuffer.empty(), + callStatus: RustCallStatus(code: statusCode, errorBuf: errorBuf) + ) + ) + } + uniffiTraitInterfaceCallAsyncWithError( + makeCall: makeCall, + handleSuccess: uniffiHandleSuccess, + handleError: uniffiHandleError, + lowerError: FfiConverterTypeCkTapError_lower, + droppedCallback: uniffiOutDroppedCallback + ) + } + )] +} + +private func uniffiCallbackInitCkTransport() { + uniffi_cktap_ffi_fn_init_callback_vtable_cktransport(UniffiCallbackInterfaceCkTransport.vtable) +} + +// FfiConverter protocol for callback interfaces +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterCallbackInterfaceCkTransport { + fileprivate static let handleMap = UniffiHandleMap() +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +extension FfiConverterCallbackInterfaceCkTransport : FfiConverter { + typealias SwiftType = CkTransport + typealias FfiType = UInt64 + +#if swift(>=5.8) + @_documentation(visibility: private) +#endif + public static func lift(_ handle: UInt64) throws -> SwiftType { + try handleMap.get(handle: handle) + } + +#if swift(>=5.8) + @_documentation(visibility: private) +#endif + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { + let handle: UInt64 = try readInt(&buf) + return try lift(handle) + } + +#if swift(>=5.8) + @_documentation(visibility: private) +#endif + public static func lower(_ v: SwiftType) -> UInt64 { + return handleMap.insert(obj: v) + } + +#if swift(>=5.8) + @_documentation(visibility: private) +#endif + public static func write(_ v: SwiftType, into buf: inout [UInt8]) { + writeInt(&buf, lower(v)) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterCallbackInterfaceCkTransport_lift(_ handle: UInt64) throws -> CkTransport { + return try FfiConverterCallbackInterfaceCkTransport.lift(handle) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterCallbackInterfaceCkTransport_lower(_ v: CkTransport) -> UInt64 { + return FfiConverterCallbackInterfaceCkTransport.lower(v) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterOptionUInt8: FfiConverterRustBuffer { + typealias SwiftType = UInt8? + + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { + guard let value = value else { + writeInt(&buf, Int8(0)) + return + } + writeInt(&buf, Int8(1)) + FfiConverterUInt8.write(value, into: &buf) + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { + switch try readInt(&buf) as Int8 { + case 0: return nil + case 1: return try FfiConverterUInt8.read(from: &buf) + default: throw UniffiInternalError.unexpectedOptionalTag + } + } +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterOptionString: FfiConverterRustBuffer { + typealias SwiftType = String? + + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { + guard let value = value else { + writeInt(&buf, Int8(0)) + return + } + writeInt(&buf, Int8(1)) + FfiConverterString.write(value, into: &buf) + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { + switch try readInt(&buf) as Int8 { + case 0: return nil + case 1: return try FfiConverterString.read(from: &buf) + default: throw UniffiInternalError.unexpectedOptionalTag + } + } +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterOptionSequenceUInt64: FfiConverterRustBuffer { + typealias SwiftType = [UInt64]? + + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { + guard let value = value else { + writeInt(&buf, Int8(0)) + return + } + writeInt(&buf, Int8(1)) + FfiConverterSequenceUInt64.write(value, into: &buf) + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { + switch try readInt(&buf) as Int8 { + case 0: return nil + case 1: return try FfiConverterSequenceUInt64.read(from: &buf) + default: throw UniffiInternalError.unexpectedOptionalTag + } + } +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterSequenceUInt32: FfiConverterRustBuffer { + typealias SwiftType = [UInt32] + + public static func write(_ value: [UInt32], into buf: inout [UInt8]) { + let len = Int32(value.count) + writeInt(&buf, len) + for item in value { + FfiConverterUInt32.write(item, into: &buf) + } + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [UInt32] { + let len: Int32 = try readInt(&buf) + var seq = [UInt32]() + seq.reserveCapacity(Int(len)) + for _ in 0 ..< len { + seq.append(try FfiConverterUInt32.read(from: &buf)) + } + return seq + } +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterSequenceUInt64: FfiConverterRustBuffer { + typealias SwiftType = [UInt64] + + public static func write(_ value: [UInt64], into buf: inout [UInt8]) { + let len = Int32(value.count) + writeInt(&buf, len) + for item in value { + FfiConverterUInt64.write(item, into: &buf) + } + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [UInt64] { + let len: Int32 = try readInt(&buf) + var seq = [UInt64]() + seq.reserveCapacity(Int(len)) + for _ in 0 ..< len { + seq.append(try FfiConverterUInt64.read(from: &buf)) + } + return seq + } +} +private let UNIFFI_RUST_FUTURE_POLL_READY: Int8 = 0 +private let UNIFFI_RUST_FUTURE_POLL_WAKE: Int8 = 1 + +fileprivate let uniffiContinuationHandleMap = UniffiHandleMap>() + +fileprivate func uniffiRustCallAsync( + rustFutureFunc: () -> UInt64, + pollFunc: (UInt64, @escaping UniffiRustFutureContinuationCallback, UInt64) -> (), + completeFunc: (UInt64, UnsafeMutablePointer) -> F, + freeFunc: (UInt64) -> (), + liftFunc: (F) throws -> T, + errorHandler: ((RustBuffer) throws -> Swift.Error)? +) async throws -> T { + // Make sure to call the ensure init function since future creation doesn't have a + // RustCallStatus param, so doesn't use makeRustCall() + uniffiEnsureCktapFfiInitialized() + let rustFuture = rustFutureFunc() + defer { + freeFunc(rustFuture) + } + var pollResult: Int8; + repeat { + pollResult = await withUnsafeContinuation { + pollFunc( + rustFuture, + uniffiFutureContinuationCallback, + uniffiContinuationHandleMap.insert(obj: $0) + ) + } + } while pollResult != UNIFFI_RUST_FUTURE_POLL_READY + + return try liftFunc(makeRustCall( + { completeFunc(rustFuture, $0) }, + errorHandler: errorHandler + )) +} + +// Callback handlers for an async calls. These are invoked by Rust when the future is ready. They +// lift the return value or error and resume the suspended function. +fileprivate func uniffiFutureContinuationCallback(handle: UInt64, pollResult: Int8) { + if let continuation = try? uniffiContinuationHandleMap.remove(handle: handle) { + continuation.resume(returning: pollResult) + } else { + print("uniffiFutureContinuationCallback invalid handle") + } +} +private func uniffiTraitInterfaceCallAsync( + makeCall: @escaping () async throws -> T, + handleSuccess: @escaping (T) -> (), + handleError: @escaping (Int8, RustBuffer) -> (), + droppedCallback: UnsafeMutablePointer +) { + let task = Task { + do { + handleSuccess(try await makeCall()) + } catch { + handleError(CALL_UNEXPECTED_ERROR, FfiConverterString.lower(String(describing: error))) + } + } + let handle = UNIFFI_FOREIGN_FUTURE_HANDLE_MAP.insert(obj: task) + droppedCallback.pointee = UniffiForeignFutureDroppedCallbackStruct( + handle: handle, + free: uniffiForeignFutureDroppedCallback + ) +} + +private func uniffiTraitInterfaceCallAsyncWithError( + makeCall: @escaping () async throws -> T, + handleSuccess: @escaping (T) -> (), + handleError: @escaping (Int8, RustBuffer) -> (), + lowerError: @escaping (E) -> RustBuffer, + droppedCallback: UnsafeMutablePointer +) { + let task = Task { + do { + handleSuccess(try await makeCall()) + } catch let error as E { + handleError(CALL_ERROR, lowerError(error)) + } catch { + handleError(CALL_UNEXPECTED_ERROR, FfiConverterString.lower(String(describing: error))) + } + } + let handle = UNIFFI_FOREIGN_FUTURE_HANDLE_MAP.insert(obj: task) + droppedCallback.pointee = UniffiForeignFutureDroppedCallbackStruct( + handle: handle, + free: uniffiForeignFutureDroppedCallback + ) +} + +// Borrow the callback handle map implementation to store foreign future handles +// TODO: consolidate the handle-map code (https://github.com/mozilla/uniffi-rs/pull/1823) +fileprivate let UNIFFI_FOREIGN_FUTURE_HANDLE_MAP = UniffiHandleMap() + +// Protocol for tasks that handle foreign futures. +// +// Defining a protocol allows all tasks to be stored in the same handle map. This can't be done +// with the task object itself, since has generic parameters. +fileprivate protocol UniffiForeignFutureTask { + func cancel() +} + +extension Task: UniffiForeignFutureTask {} + +private func uniffiForeignFutureDroppedCallback(handle: UInt64) { + do { + let task = try UNIFFI_FOREIGN_FUTURE_HANDLE_MAP.remove(handle: handle) + // Set the cancellation flag on the task. If it's still running, the code can check the + // cancellation flag or call `Task.checkCancellation()`. If the task has completed, this is + // a no-op. + task.cancel() + } catch { + print("uniffiForeignFutureDroppedCallback: handle missing from handlemap") + } +} + +// For testing +public func uniffiForeignFutureHandleCountCktapFfi() -> Int { + UNIFFI_FOREIGN_FUTURE_HANDLE_MAP.count +} +public func toCktap(transport: CkTransport)async throws -> CkTapCard { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_cktap_ffi_fn_func_to_cktap(FfiConverterCallbackInterfaceCkTransport_lower(transport) + ) + }, + pollFunc: ffi_cktap_ffi_rust_future_poll_rust_buffer, + completeFunc: ffi_cktap_ffi_rust_future_complete_rust_buffer, + freeFunc: ffi_cktap_ffi_rust_future_free_rust_buffer, + liftFunc: FfiConverterTypeCkTapCard_lift, + errorHandler: FfiConverterTypeStatusError_lift + ) +} + +private enum InitializationResult { + case ok + case contractVersionMismatch + case apiChecksumMismatch +} +// Use a global variable to perform the versioning checks. Swift ensures that +// the code inside is only computed once. +private let initializationResult: InitializationResult = { + // Get the bindings contract version from our ComponentInterface + let bindings_contract_version = 30 + // Get the scaffolding contract version by calling the into the dylib + let scaffolding_contract_version = ffi_cktap_ffi_uniffi_contract_version() + if bindings_contract_version != scaffolding_contract_version { + return InitializationResult.contractVersionMismatch + } + if (uniffi_cktap_ffi_checksum_func_to_cktap() != 56485) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_cktap_ffi_checksum_method_satscard_address() != 9818) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_cktap_ffi_checksum_method_satscard_check_cert() != 6533) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_cktap_ffi_checksum_method_satscard_dump() != 37004) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_cktap_ffi_checksum_method_satscard_new_slot() != 45340) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_cktap_ffi_checksum_method_satscard_nfc() != 40391) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_cktap_ffi_checksum_method_satscard_read() != 43229) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_cktap_ffi_checksum_method_satscard_sign_psbt() != 883) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_cktap_ffi_checksum_method_satscard_status() != 20150) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_cktap_ffi_checksum_method_satscard_unseal() != 57458) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_cktap_ffi_checksum_method_satscard_wait() != 18368) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_cktap_ffi_checksum_method_satschip_change() != 49089) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_cktap_ffi_checksum_method_satschip_check_cert() != 60748) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_cktap_ffi_checksum_method_satschip_derive() != 63960) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_cktap_ffi_checksum_method_satschip_init() != 64376) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_cktap_ffi_checksum_method_satschip_nfc() != 6460) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_cktap_ffi_checksum_method_satschip_read() != 36900) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_cktap_ffi_checksum_method_satschip_sign_psbt() != 39519) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_cktap_ffi_checksum_method_satschip_status() != 14916) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_cktap_ffi_checksum_method_satschip_wait() != 25914) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_cktap_ffi_checksum_method_satschip_xpub() != 24217) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_cktap_ffi_checksum_method_tapsigner_change() != 45) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_cktap_ffi_checksum_method_tapsigner_check_cert() != 64898) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_cktap_ffi_checksum_method_tapsigner_derive() != 1802) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_cktap_ffi_checksum_method_tapsigner_init() != 56161) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_cktap_ffi_checksum_method_tapsigner_nfc() != 1711) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_cktap_ffi_checksum_method_tapsigner_read() != 26591) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_cktap_ffi_checksum_method_tapsigner_sign_psbt() != 19540) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_cktap_ffi_checksum_method_tapsigner_status() != 24164) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_cktap_ffi_checksum_method_tapsigner_wait() != 32878) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_cktap_ffi_checksum_method_tapsigner_xpub() != 22073) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_cktap_ffi_checksum_method_cktransport_transmit_apdu() != 15044) { + return InitializationResult.apiChecksumMismatch + } + + uniffiCallbackInitCkTransport() + return InitializationResult.ok +}() + +// Make the ensure init function public so that other modules which have external type references to +// our types can call it. +public func uniffiEnsureCktapFfiInitialized() { + switch initializationResult { + case .ok: + break + case .contractVersionMismatch: + fatalError("UniFFI contract version mismatch: try cleaning and rebuilding your project") + case .apiChecksumMismatch: + fatalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } +} + +// swiftlint:enable all \ No newline at end of file diff --git a/scripts/swift_create_xcframework_archive.sh b/scripts/swift_create_xcframework_archive.sh new file mode 100755 index 0000000..00ccbc3 --- /dev/null +++ b/scripts/swift_create_xcframework_archive.sh @@ -0,0 +1,17 @@ +#!/bin/bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +XCFRAMEWORK_PATH="${ROOT_DIR}/cktap-swift/cktapFFI.xcframework" +ZIP_PATH="${ROOT_DIR}/cktap-swift/cktapFFI.xcframework.zip" + +if [ ! -d "${XCFRAMEWORK_PATH}" ]; then + echo "Missing xcframework at ${XCFRAMEWORK_PATH}" >&2 + exit 1 +fi + +ditto -c -k --sequesterRsrc --keepParent "${XCFRAMEWORK_PATH}" "${ZIP_PATH}" +CHECKSUM="$(swift package compute-checksum "${ZIP_PATH}")" +echo "New checksum: ${CHECKSUM}" + +python3 "${ROOT_DIR}/scripts/swift_update_package_checksum.py" --checksum "${CHECKSUM}" diff --git a/scripts/swift_update_package_checksum.py b/scripts/swift_update_package_checksum.py new file mode 100644 index 0000000..f6a8330 --- /dev/null +++ b/scripts/swift_update_package_checksum.py @@ -0,0 +1,103 @@ +import argparse +import json +import os +import re +import sys + + +def run(new_checksum: str = None, new_tag: str = None): + if new_checksum is None and new_tag is None: + print("At least one of --checksum or --tag arguments must be provided.", file=sys.stderr) + sys.exit(1) + + if new_checksum is not None: + if not new_checksum.isalnum(): + print("Checksum must be alphanumeric.", file=sys.stderr) + sys.exit(1) + + if not new_checksum.islower(): + print("Checksum must be lowercase.", file=sys.stderr) + sys.exit(1) + + try: + int(new_checksum, 16) + except ValueError: + print("Checksum must be hexadecimal.", file=sys.stderr) + sys.exit(1) + + if new_tag is not None: + if new_tag.strip() != new_tag: + print("Tag must not contain any whitespace.", file=sys.stderr) + sys.exit(1) + + tag_regex = re.compile(r"^v?\d+[.]\d+[.]\d+$") + tag_match = tag_regex.match(new_tag) + if tag_match is None: + print("Tag must adhere to vX.Y.Z or X.Y.Z format.", file=sys.stderr) + sys.exit(1) + + settings = [ + {"variable_name": "checksum", "value": new_checksum}, + {"variable_name": "tag", "value": new_tag}, + ] + + package_file_path = os.path.realpath( + os.path.join(os.path.dirname(__file__), "../Package.swift") + ) + + try: + with open(package_file_path, "r") as package_file_handle: + package_file = package_file_handle.read() + except OSError: + print("Failed to read Package.swift file.", file=sys.stderr) + sys.exit(1) + + updated_package_file = package_file + for current_setting in settings: + current_variable_name = current_setting["variable_name"] + new_value = current_setting["value"] + if new_value is None: + continue + + print(f"setting {current_variable_name} (JSON-serialization):") + print(json.dumps(new_value)) + + regex = re.compile( + rf"^(\s*let\s+{current_variable_name}\s*=\s*).*$", + re.MULTILINE, + ) + updated_package_file, replacements = regex.subn( + rf'\1"{new_value}"', + updated_package_file, + ) + if replacements != 1: + print( + f"Failed to update {current_variable_name} in Package.swift.", + file=sys.stderr, + ) + sys.exit(1) + + with open(package_file_path, "w") as package_file_handle: + package_file_handle.write(updated_package_file) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description="Update Swift package checksum and tag." + ) + parser.add_argument( + "--checksum", + type=str, + help="new checksum of cktapFFI.xcframework.zip", + required=False, + default=None, + ) + parser.add_argument( + "--tag", + type=str, + help="new release tag (vX.Y.Z or X.Y.Z)", + required=False, + default=None, + ) + args = parser.parse_args() + run(new_checksum=args.checksum, new_tag=args.tag)