diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bb460e7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +.DS_Store +/.build +/Packages +/*.xcodeproj +xcuserdata/ +DerivedData/ +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata diff --git a/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/MatrixSDKFFIWrapper/dummy.m b/MatrixSDKFFIWrapper/dummy.m new file mode 100644 index 0000000..296ba74 --- /dev/null +++ b/MatrixSDKFFIWrapper/dummy.m @@ -0,0 +1 @@ +// Swift Package Manager needs at least one source file. diff --git a/MatrixSDKFFIWrapper/include/dummy.h b/MatrixSDKFFIWrapper/include/dummy.h new file mode 100644 index 0000000..11db125 --- /dev/null +++ b/MatrixSDKFFIWrapper/include/dummy.h @@ -0,0 +1,4 @@ +// Swift Package Manager needs at least one header to prevent a warning. See +// https://github.com/mozilla/application-services/issues/4422. + +#import "sdkFFI.h" diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000..e5ad3da --- /dev/null +++ b/Package.swift @@ -0,0 +1,41 @@ +// swift-tools-version:5.5 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let checksum = "051aa92b9fff0b18e54c6a82ea12231edf4cfa98b49889223870b3c63bf2829b" +let version = "v1.0.0-alpha" +let url = "https://github.com/matrix-org/matrix-rust-components-swift/releases/download/\(version)/MatrixSDKFFI.xcframework.zip" + +let package = Package( + name: "MatrixRustComponentsSwift", + products: [ + .library( + name: "MatrixRustComponentsSwift", + targets: ["MatrixRustComponentsSwift"]), + ], + targets: [ + .binaryTarget( + name: "MatrixSDKFFI", + url: url, + checksum: checksum), + /* + * A placeholder wrapper for our binaryTarget so that Xcode will ensure this is + * downloaded/built before trying to use it in the build process + * A bit hacky but necessary for now https://github.com/mozilla/application-services/issues/4422 + */ + .target( + name: "MatrixSDKFFIWrapper", + dependencies: [ + .target(name: "MatrixSDKFFI") + ], + path: "MatrixSDKFFIWrapper" + ), + .target( + name: "MatrixRustComponentsSwift", + dependencies: ["MatrixSDKFFIWrapper"]), + .testTarget( + name: "MatrixRustComponentsSwiftTests", + dependencies: ["MatrixRustComponentsSwift"]), + ] +) diff --git a/README.md b/README.md index 78a1e00..4f72799 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,3 @@ -# .github -Default metadata files for repos in this org. +# MatrixRustComponentsSwift -If you're seeing this readme in your project, your project is configured wrong. +A description of this package. diff --git a/Sources/MatrixRustComponentsSwift/ClientError+Error.swift b/Sources/MatrixRustComponentsSwift/ClientError+Error.swift new file mode 100644 index 0000000..354ecce --- /dev/null +++ b/Sources/MatrixRustComponentsSwift/ClientError+Error.swift @@ -0,0 +1,11 @@ +// +// ClientError+Error.swift +// MatrixRustSDK +// +// Created by Stefan Ceriu on 09.02.2022. +// + +import Foundation + +// Fixes code generation problem from uniffi +extension ClientError: Error { } diff --git a/Sources/MatrixRustComponentsSwift/sdk.swift b/Sources/MatrixRustComponentsSwift/sdk.swift new file mode 100644 index 0000000..5a0dd1c --- /dev/null +++ b/Sources/MatrixRustComponentsSwift/sdk.swift @@ -0,0 +1,768 @@ +// This file was autogenerated by some hot garbage in the `uniffi` crate. +// Trust me, you don't want to mess with it! +import Foundation +import MatrixSDKFFIWrapper + +// 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(sdkFFI) + import sdkFFI +#endif + +private 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 from(_ ptr: UnsafeBufferPointer) -> RustBuffer { + try! rustCall { ffi_sdk_a8d0_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_sdk_a8d0_rustbuffer_free(self, $0) } + } +} + +private 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 libray of its own. + +private extension Data { + init(rustBuffer: RustBuffer) { + // TODO: This copies the buffer. Can we read directly from a + // Rust buffer? + self.init(bytes: rustBuffer.data!, count: Int(rustBuffer.len)) + } +} + +// A helper class to read values out of a byte buffer. +private class Reader { + let data: Data + var offset: Data.Index + + init(data: Data) { + self.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. + func readInt() throws -> T { + let range = offset ..< offset + MemoryLayout.size + guard data.count >= range.upperBound else { + throw UniffiInternalError.bufferOverflow + } + if T.self == UInt8.self { + let value = data[offset] + offset += 1 + return value as! T + } + var value: T = 0 + let _ = withUnsafeMutableBytes(of: &value) { data.copyBytes(to: $0, from: range) } + 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 + func readBytes(count: Int) throws -> [UInt8] { + let range = offset ..< (offset + count) + guard data.count >= range.upperBound else { + throw UniffiInternalError.bufferOverflow + } + var value = [UInt8](repeating: 0, count: count) + value.withUnsafeMutableBufferPointer { buffer in + data.copyBytes(to: buffer, from: range) + } + offset = range.upperBound + return value + } + + // Reads a float at the current offset. + @inlinable + func readFloat() throws -> Float { + return Float(bitPattern: try readInt()) + } + + // Reads a float at the current offset. + @inlinable + func readDouble() throws -> Double { + return Double(bitPattern: try readInt()) + } + + // Indicates if the offset has reached the end of the buffer. + @inlinable + func hasRemaining() -> Bool { + return offset < data.count + } +} + +// A helper class to write values into a byte buffer. +private class Writer { + var bytes: [UInt8] + var offset: Array.Index + + init() { + bytes = [] + offset = 0 + } + + func writeBytes(_ byteArr: S) where S: Sequence, S.Element == UInt8 { + bytes.append(contentsOf: byteArr) + } + + // Writes an integer in big-endian order. + // + // Warning: make sure what you are trying to write + // is in the correct type! + func writeInt(_ value: T) { + var value = value.bigEndian + withUnsafeBytes(of: &value) { bytes.append(contentsOf: $0) } + } + + @inlinable + func writeFloat(_ value: Float) { + writeInt(value.bitPattern) + } + + @inlinable + func writeDouble(_ value: Double) { + writeInt(value.bitPattern) + } +} + +// Types conforming to `Serializable` can be read and written in a bytebuffer. +private protocol Serializable { + func write(into: Writer) + static func read(from: Reader) throws -> Self +} + +// Types confirming to `ViaFfi` can be transferred back-and-for over the FFI. +// This is analogous to the Rust trait of the same name. +private protocol ViaFfi: Serializable { + associatedtype FfiType + static func lift(_ v: FfiType) throws -> Self + func lower() -> FfiType +} + +// Types conforming to `Primitive` pass themselves directly over the FFI. +private protocol Primitive {} + +private extension Primitive { + typealias FfiType = Self + + static func lift(_ v: Self) throws -> Self { + return v + } + + func lower() -> Self { + return self + } +} + +// Types conforming to `ViaFfiUsingByteBuffer` lift and lower into a bytebuffer. +// Use this for complex types where it's hard to write a custom lift/lower. +private protocol ViaFfiUsingByteBuffer: Serializable {} + +private extension ViaFfiUsingByteBuffer { + typealias FfiType = RustBuffer + + static func lift(_ buf: FfiType) throws -> Self { + let reader = Reader(data: Data(rustBuffer: buf)) + let value = try Self.read(from: reader) + if reader.hasRemaining() { + throw UniffiInternalError.incompleteData + } + buf.deallocate() + return value + } + + func lower() -> FfiType { + let writer = Writer() + write(into: writer) + return RustBuffer(bytes: writer.bytes) + } +} + +// An error type for FFI errors. These errors occur at the UniFFI level, not +// the library level. +private 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 + } + } +} + +private let CALL_SUCCESS: Int8 = 0 +private let CALL_ERROR: Int8 = 1 +private let CALL_PANIC: Int8 = 2 + +private extension RustCallStatus { + init() { + self.init( + code: CALL_SUCCESS, + errorBuf: RustBuffer( + capacity: 0, + len: 0, + data: nil + ) + ) + } +} + +private func rustCall(_ callback: (UnsafeMutablePointer) -> T) throws -> T { + try makeRustCall(callback, errorHandler: { + $0.deallocate() + return UniffiInternalError.unexpectedRustCallError + }) +} + +private func rustCallWithError(_: E.Type, _ callback: (UnsafeMutablePointer) -> T) throws -> T { + try makeRustCall(callback, errorHandler: { try E.lift($0) }) +} + +private func makeRustCall(_ callback: (UnsafeMutablePointer) -> T, errorHandler: (RustBuffer) throws -> Error) throws -> T { + var callStatus = RustCallStatus() + let returnedVal = callback(&callStatus) + switch callStatus.code { + case CALL_SUCCESS: + return returnedVal + + case CALL_ERROR: + throw try errorHandler(callStatus.errorBuf) + + case CALL_PANIC: + // When the rust code sees a panic, it tries to construct a RustBuffer + // with the message. But if that code panics, then it just sends back + // an empty buffer. + if callStatus.errorBuf.len > 0 { + throw UniffiInternalError.rustPanic(try String.lift(callStatus.errorBuf)) + } else { + callStatus.errorBuf.deallocate() + throw UniffiInternalError.rustPanic("Rust panic") + } + + default: + throw UniffiInternalError.unexpectedRustCallStatusCode + } +} + +// Protocols for converters we'll implement in templates + +private protocol FfiConverter { + associatedtype SwiftType + associatedtype FfiType + + static func lift(_ ffiValue: FfiType) throws -> SwiftType + static func lower(_ value: SwiftType) -> FfiType + + static func read(from: Reader) throws -> SwiftType + static func write(_ value: SwiftType, into: Writer) +} + +private protocol FfiConverterUsingByteBuffer: FfiConverter where FfiType == RustBuffer { + // Empty, because we want to declare some helper methods in the extension below. +} + +extension FfiConverterUsingByteBuffer { + static func lower(_ value: SwiftType) -> FfiType { + let writer = Writer() + Self.write(value, into: writer) + return RustBuffer(bytes: writer.bytes) + } + + static func lift(_ buf: FfiType) throws -> SwiftType { + let reader = Reader(data: Data(rustBuffer: buf)) + let value = try Self.read(from: reader) + if reader.hasRemaining() { + throw UniffiInternalError.incompleteData + } + buf.deallocate() + return value + } +} + +// Helpers for structural types. Note that because of canonical_names, it /should/ be impossible +// to make another `FfiConverterSequence` etc just using the UDL. +private enum FfiConverterSequence { + static func write(_ value: [T], into buf: Writer, writeItem: (T, Writer) -> Void) { + let len = Int32(value.count) + buf.writeInt(len) + for item in value { + writeItem(item, buf) + } + } + + static func read(from buf: Reader, readItem: (Reader) throws -> T) throws -> [T] { + let len: Int32 = try buf.readInt() + var seq = [T]() + seq.reserveCapacity(Int(len)) + for _ in 0 ..< len { + seq.append(try readItem(buf)) + } + return seq + } +} + +private enum FfiConverterOptional { + static func write(_ value: T?, into buf: Writer, writeItem: (T, Writer) -> Void) { + guard let value = value else { + buf.writeInt(Int8(0)) + return + } + buf.writeInt(Int8(1)) + writeItem(value, buf) + } + + static func read(from buf: Reader, readItem: (Reader) throws -> T) throws -> T? { + switch try buf.readInt() as Int8 { + case 0: return nil + case 1: return try readItem(buf) + default: throw UniffiInternalError.unexpectedOptionalTag + } + } +} + +private enum FfiConverterDictionary { + static func write(_ value: [String: T], into buf: Writer, writeItem: (String, T, Writer) -> Void) { + let len = Int32(value.count) + buf.writeInt(len) + for (key, value) in value { + writeItem(key, value, buf) + } + } + + static func read(from buf: Reader, readItem: (Reader) throws -> (String, T)) throws -> [String: T] { + let len: Int32 = try buf.readInt() + var dict = [String: T]() + dict.reserveCapacity(Int(len)) + for _ in 0 ..< len { + let (key, value) = try readItem(buf) + dict[key] = value + } + return dict + } +} + +// Public interface members begin here. + +// Note that we don't yet support `indirect` for enums. +// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. + +public enum ClientError { + case generic(msg: String) +} + +extension ClientError: ViaFfiUsingByteBuffer, ViaFfi { + fileprivate static func read(from buf: Reader) throws -> ClientError { + let variant: Int32 = try buf.readInt() + switch variant { + case 1: return .generic( + msg: try String.read(from: buf) + ) + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + fileprivate func write(into buf: Writer) { + switch self { + case let .generic(msg): + buf.writeInt(Int32(1)) + msg.write(into: buf) + } + } +} + +extension ClientError: Equatable, Hashable {} + +public func loginNewClient(basePath: String, username: String, password: String) throws -> Client { + let _retval = try + + rustCallWithError(ClientError.self) { + sdk_a8d0_login_new_client(basePath.lower(), username.lower(), password.lower(), $0) + } + return try Client.lift(_retval) +} + +public func guestClient(basePath: String, homeserver: String) throws -> Client { + let _retval = try + + rustCallWithError(ClientError.self) { + sdk_a8d0_guest_client(basePath.lower(), homeserver.lower(), $0) + } + return try Client.lift(_retval) +} + +public func loginWithToken(basePath: String, restoreToken: String) throws -> Client { + let _retval = try + + rustCallWithError(ClientError.self) { + sdk_a8d0_login_with_token(basePath.lower(), restoreToken.lower(), $0) + } + return try Client.lift(_retval) +} + +public protocol RoomProtocol { + func displayName() throws -> String + func avatar() throws -> [UInt8] +} + +public class Room: RoomProtocol { + fileprivate let pointer: UnsafeMutableRawPointer + + // TODO: We'd like this to be `private` but for Swifty reasons, + // we can't implement `ViaFfi` without making this `required` and we can't + // make it `required` without making it `public`. + required init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) { + self.pointer = pointer + } + + deinit { + try! rustCall { ffi_sdk_a8d0_Room_object_free(pointer, $0) } + } + + public func displayName() throws -> String { + let _retval = try + rustCallWithError(ClientError.self) { + sdk_a8d0_Room_display_name(self.pointer, $0) + } + return try String.lift(_retval) + } + + public func avatar() throws -> [UInt8] { + let _retval = try + rustCallWithError(ClientError.self) { + sdk_a8d0_Room_avatar(self.pointer, $0) + } + return try FfiConverterSequenceUInt8.lift(_retval) + } +} + +private extension Room { + typealias FfiType = UnsafeMutableRawPointer + + static func read(from buf: Reader) throws -> Self { + let v: UInt64 = try buf.readInt() + // The Rust code won't compile if a pointer won't fit in a UInt64. + // We have to go via `UInt` because that's the thing that's the size of a pointer. + let ptr = UnsafeMutableRawPointer(bitPattern: UInt(truncatingIfNeeded: v)) + if ptr == nil { + throw UniffiInternalError.unexpectedNullPointer + } + return try lift(ptr!) + } + + func write(into buf: Writer) { + // This fiddling is because `Int` is the thing that's the same size as a pointer. + // The Rust code won't compile if a pointer won't fit in a `UInt64`. + buf.writeInt(UInt64(bitPattern: Int64(Int(bitPattern: lower())))) + } + + static func lift(_ pointer: UnsafeMutableRawPointer) throws -> Self { + return Self(unsafeFromRawPointer: pointer) + } + + func lower() -> UnsafeMutableRawPointer { + return pointer + } +} + +// Ideally this would be `fileprivate`, but Swift says: +// """ +// 'private' modifier cannot be used with extensions that declare protocol conformances +// """ +extension Room: ViaFfi, Serializable {} + +public protocol ClientProtocol { + func startSync() + func restoreToken() throws -> String + func isGuest() -> Bool + func hasFirstSynced() -> Bool + func isSyncing() -> Bool + func userId() throws -> String + func displayName() throws -> String + func deviceId() throws -> String + func avatar() throws -> [UInt8] + func conversations() -> [Room] +} + +public class Client: ClientProtocol { + fileprivate let pointer: UnsafeMutableRawPointer + + // TODO: We'd like this to be `private` but for Swifty reasons, + // we can't implement `ViaFfi` without making this `required` and we can't + // make it `required` without making it `public`. + required init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) { + self.pointer = pointer + } + + deinit { + try! rustCall { ffi_sdk_a8d0_Client_object_free(pointer, $0) } + } + + public func startSync() { + try! + rustCall { + sdk_a8d0_Client_start_sync(self.pointer, $0) + } + } + + public func restoreToken() throws -> String { + let _retval = try + rustCallWithError(ClientError.self) { + sdk_a8d0_Client_restore_token(self.pointer, $0) + } + return try String.lift(_retval) + } + + public func isGuest() -> Bool { + let _retval = try! + rustCall { + sdk_a8d0_Client_is_guest(self.pointer, $0) + } + return try! Bool.lift(_retval) + } + + public func hasFirstSynced() -> Bool { + let _retval = try! + rustCall { + sdk_a8d0_Client_has_first_synced(self.pointer, $0) + } + return try! Bool.lift(_retval) + } + + public func isSyncing() -> Bool { + let _retval = try! + rustCall { + sdk_a8d0_Client_is_syncing(self.pointer, $0) + } + return try! Bool.lift(_retval) + } + + public func userId() throws -> String { + let _retval = try + rustCallWithError(ClientError.self) { + sdk_a8d0_Client_user_id(self.pointer, $0) + } + return try String.lift(_retval) + } + + public func displayName() throws -> String { + let _retval = try + rustCallWithError(ClientError.self) { + sdk_a8d0_Client_display_name(self.pointer, $0) + } + return try String.lift(_retval) + } + + public func deviceId() throws -> String { + let _retval = try + rustCallWithError(ClientError.self) { + sdk_a8d0_Client_device_id(self.pointer, $0) + } + return try String.lift(_retval) + } + + public func avatar() throws -> [UInt8] { + let _retval = try + rustCallWithError(ClientError.self) { + sdk_a8d0_Client_avatar(self.pointer, $0) + } + return try FfiConverterSequenceUInt8.lift(_retval) + } + + public func conversations() -> [Room] { + let _retval = try! + rustCall { + sdk_a8d0_Client_conversations(self.pointer, $0) + } + return try! FfiConverterSequenceObjectRoom.lift(_retval) + } +} + +private extension Client { + typealias FfiType = UnsafeMutableRawPointer + + static func read(from buf: Reader) throws -> Self { + let v: UInt64 = try buf.readInt() + // The Rust code won't compile if a pointer won't fit in a UInt64. + // We have to go via `UInt` because that's the thing that's the size of a pointer. + let ptr = UnsafeMutableRawPointer(bitPattern: UInt(truncatingIfNeeded: v)) + if ptr == nil { + throw UniffiInternalError.unexpectedNullPointer + } + return try lift(ptr!) + } + + func write(into buf: Writer) { + // This fiddling is because `Int` is the thing that's the same size as a pointer. + // The Rust code won't compile if a pointer won't fit in a `UInt64`. + buf.writeInt(UInt64(bitPattern: Int64(Int(bitPattern: lower())))) + } + + static func lift(_ pointer: UnsafeMutableRawPointer) throws -> Self { + return Self(unsafeFromRawPointer: pointer) + } + + func lower() -> UnsafeMutableRawPointer { + return pointer + } +} + +// Ideally this would be `fileprivate`, but Swift says: +// """ +// 'private' modifier cannot be used with extensions that declare protocol conformances +// """ +extension Client: ViaFfi, Serializable {} +extension UInt8: Primitive, ViaFfi { + fileprivate static func read(from buf: Reader) throws -> Self { + return try lift(buf.readInt()) + } + + fileprivate func write(into buf: Writer) { + buf.writeInt(lower()) + } +} + +extension Bool: ViaFfi { + fileprivate typealias FfiType = Int8 + + fileprivate static func read(from buf: Reader) throws -> Self { + return try lift(buf.readInt()) + } + + fileprivate func write(into buf: Writer) { + buf.writeInt(lower()) + } + + fileprivate static func lift(_ v: FfiType) throws -> Self { + return v != 0 + } + + fileprivate func lower() -> FfiType { + return self ? 1 : 0 + } +} + +extension String: ViaFfi { + fileprivate typealias FfiType = RustBuffer + + fileprivate static func lift(_ v: FfiType) throws -> Self { + defer { + v.deallocate() + } + if v.data == nil { + return String() + } + let bytes = UnsafeBufferPointer(start: v.data!, count: Int(v.len)) + return String(bytes: bytes, encoding: String.Encoding.utf8)! + } + + fileprivate func lower() -> FfiType { + return 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) + } + } + } + + fileprivate static func read(from buf: Reader) throws -> Self { + let len: Int32 = try buf.readInt() + return String(bytes: try buf.readBytes(count: Int(len)), encoding: String.Encoding.utf8)! + } + + fileprivate func write(into buf: Writer) { + let len = Int32(utf8.count) + buf.writeInt(len) + buf.writeBytes(utf8) + } +} + +// Helper code for Client class is found in ObjectTemplate.swift +// Helper code for Room class is found in ObjectTemplate.swift +// Helper code for ClientError enum is found in EnumTemplate.swift + +private enum FfiConverterSequenceUInt8: FfiConverterUsingByteBuffer { + typealias SwiftType = [UInt8] + + static func write(_ value: SwiftType, into buf: Writer) { + FfiConverterSequence.write(value, into: buf) { item, buf in + item.write(into: buf) + } + } + + static func read(from buf: Reader) throws -> SwiftType { + try FfiConverterSequence.read(from: buf) { buf in + try UInt8.read(from: buf) + } + } +} + +private enum FfiConverterSequenceObjectRoom: FfiConverterUsingByteBuffer { + typealias SwiftType = [Room] + + static func write(_ value: SwiftType, into buf: Writer) { + FfiConverterSequence.write(value, into: buf) { item, buf in + item.write(into: buf) + } + } + + static func read(from buf: Reader) throws -> SwiftType { + try FfiConverterSequence.read(from: buf) { buf in + try Room.read(from: buf) + } + } +} + +/** + * Top level initializers and tear down methods. + * + * This is generated by uniffi. + */ +public enum SdkLifecycle { + /** + * Initialize the FFI and Rust library. This should be only called once per application. + */ + func initialize() { + // No initialization code needed + } +} diff --git a/Tests/MatrixRustComponentsSwiftTests/MatrixRustComponentsSwiftTests.swift b/Tests/MatrixRustComponentsSwiftTests/MatrixRustComponentsSwiftTests.swift new file mode 100644 index 0000000..e8cb4c9 --- /dev/null +++ b/Tests/MatrixRustComponentsSwiftTests/MatrixRustComponentsSwiftTests.swift @@ -0,0 +1,16 @@ +import XCTest +@testable import MatrixRustComponentsSwift + +final class MatrixRustComponentsSwiftTests: XCTestCase { + func testExample() throws { + do { + let client = try loginNewClient(basePath: "", username: "", password: "") + let displayName = try client.displayName() + print("Display name: \(displayName)") + } catch ClientError.generic(let msg) { + print("Failed with message \(msg)") + } catch { + fatalError() + } + } +}