├── .github
└── ISSUE_TEMPLATE
│ ├── bug_report.md
│ ├── config.yaml
│ └── feature_request.md
├── .gitignore
├── .swiftpm
└── xcode
│ └── package.xcworkspace
│ └── xcshareddata
│ └── IDEWorkspaceChecks.plist
├── CONTRIBUTING.md
├── LICENSE
├── Package.swift
├── README.md
├── SECURITY.md
├── Sources
└── MatrixRustSDK
│ ├── matrix_sdk.swift
│ ├── matrix_sdk_base.swift
│ ├── matrix_sdk_common.swift
│ ├── matrix_sdk_crypto.swift
│ ├── matrix_sdk_ffi.swift
│ └── matrix_sdk_ui.swift
├── Tools
└── Release
│ ├── Package.swift
│ ├── README.md
│ └── Sources
│ └── Release.swift
└── docs
└── Getting Started.md
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yaml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: true
2 | contact_links:
3 | - name: Matrix Community Support
4 | url: "https://matrix.to/#/#matrix:matrix.org"
5 | about: General support questions can be asked here.
6 | - name: Matrix Security Policy
7 | url: https://www.matrix.org/security-disclosure-policy/
8 | about: Learn more about our security disclosure policy.
9 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .build
3 | /Packages
4 | /*.xcodeproj
5 | xcuserdata/
6 | DerivedData/
7 | .swiftpm
8 | Package.resolved
9 | MatrixSDKFFI.xcframework.zip
10 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to Swift package for Matrix Rust components
2 |
3 | Thank you for taking the time to contribute to Matrix!
4 |
5 | This is the repository for Swift package for Matrix Rust components, a a Swift Package for distributing releases of the Matrix Rust SDK.
6 |
7 | ## Sign off
8 |
9 | We ask that everybody who contributes to this project signs off their contributions, as explained below.
10 |
11 | We follow a simple 'inbound=outbound' model for contributions: the act of submitting an 'inbound' contribution means that the contributor agrees to license their contribution under the same terms as the project's overall 'outbound' license - in our case, this is Apache Software License v2 (see [LICENSE](./LICENSE)).
12 |
13 | In order to have a concrete record that your contribution is intentional and you agree to license it under the same terms as the project's license, we've adopted the same lightweight approach used by the [Linux Kernel](https://www.kernel.org/doc/html/latest/process/submitting-patches.html), [Docker](https://github.com/docker/docker/blob/master/CONTRIBUTING.md), and many other projects: the [Developer Certificate of Origin](https://developercertificate.org/) (DCO). This is a simple declaration that you wrote the contribution or otherwise have the right to contribute it to Matrix:
14 |
15 | ```
16 | Developer Certificate of Origin
17 | Version 1.1
18 |
19 | Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
20 | 660 York Street, Suite 102,
21 | San Francisco, CA 94110 USA
22 |
23 | Everyone is permitted to copy and distribute verbatim copies of this
24 | license document, but changing it is not allowed.
25 |
26 | Developer's Certificate of Origin 1.1
27 |
28 | By making a contribution to this project, I certify that:
29 |
30 | (a) The contribution was created in whole or in part by me and I
31 | have the right to submit it under the open source license
32 | indicated in the file; or
33 |
34 | (b) The contribution is based upon previous work that, to the best
35 | of my knowledge, is covered under an appropriate open source
36 | license and I have the right under that license to submit that
37 | work with modifications, whether created in whole or in part
38 | by me, under the same open source license (unless I am
39 | permitted to submit under a different license), as indicated
40 | in the file; or
41 |
42 | (c) The contribution was provided directly to me by some other
43 | person who certified (a), (b) or (c) and I have not modified
44 | it.
45 |
46 | (d) I understand and agree that this project and the contribution
47 | are public and that a record of the contribution (including all
48 | personal information I submit with it, including my sign-off) is
49 | maintained indefinitely and may be redistributed consistent with
50 | this project or the open source license(s) involved.
51 | ```
52 |
53 | If you agree to this for your contribution, then all that's needed is to include the line in your commit or pull request comment:
54 |
55 | ```
56 | Signed-off-by: Your Name
57 | ```
58 |
59 | Git allows you to add this signoff automatically when using the `-s` flag to `git commit`, which uses the name and email set in your `user.name` and `user.email` git configs.
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
203 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.5
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 | import PackageDescription
4 | let checksum = "f02624cb6a3f4dc111b163962899a3788508625e6b364960d5f418bf872bbbba"
5 | let version = "25.04.16"
6 | let url = "https://github.com/matrix-org/matrix-rust-components-swift/releases/download/\(version)/MatrixSDKFFI.xcframework.zip"
7 | let package = Package(
8 | name: "MatrixRustSDK",
9 | platforms: [
10 | .iOS(.v15),
11 | .macOS(.v12)
12 | ],
13 | products: [
14 | .library(name: "MatrixRustSDK", targets: ["MatrixRustSDK"]),
15 | ],
16 | targets: [
17 | .binaryTarget(name: "MatrixSDKFFI", url: url, checksum: checksum),
18 | .target(name: "MatrixRustSDK", dependencies: [.target(name: "MatrixSDKFFI")])
19 | ]
20 | )
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Swift package for Matrix Rust components
2 |
3 | This repository is a Swift Package for distributing releases of the [Matrix Rust SDK](https://github.com/matrix-org/matrix-rust-sdk). It provides the Swift source code packaged in a format understood by the Swift package manager, and depends on a pre-compiled binary release of the underlying Rust code published from [Matrix Rust SDK](https://github.com/matrix-org/matrix-rust-sdk).
4 |
5 | ## Usage
6 |
7 | For more information about using the package, please read the [Getting Started](docs/Getting%20Started.md) guide.
8 |
9 | ## Releasing
10 |
11 | Whenever a new release of the underlying components is available, we need to tag a new release in this repo to make them available to Swift components.
12 | This is done with the [release script](Tools/Release/README.md) found in the Tools directory.
13 |
14 | ## Testing locally
15 |
16 | As the package vendors a pre-built binary of the SDK, all local development is done via the SDK's repo instead of this one.
17 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Reporting a Vulnerability
2 |
3 | **If you've found a security vulnerability, please report it to security@matrix.org**
4 |
5 | For more information on our security disclosure policy, visit https://www.matrix.org/security-disclosure-policy/
6 |
--------------------------------------------------------------------------------
/Sources/MatrixRustSDK/matrix_sdk.swift:
--------------------------------------------------------------------------------
1 | // This file was autogenerated by some hot garbage in the `uniffi` crate.
2 | // Trust me, you don't want to mess with it!
3 |
4 | // swiftlint:disable all
5 | import Foundation
6 |
7 | // Depending on the consumer's build setup, the low-level FFI code
8 | // might be in a separate module, or it might be compiled inline into
9 | // this module. This is a bit of light hackery to work with both.
10 | #if canImport(matrix_sdkFFI)
11 | import matrix_sdkFFI
12 | #endif
13 |
14 | fileprivate extension RustBuffer {
15 | // Allocate a new buffer, copying the contents of a `UInt8` array.
16 | init(bytes: [UInt8]) {
17 | let rbuf = bytes.withUnsafeBufferPointer { ptr in
18 | RustBuffer.from(ptr)
19 | }
20 | self.init(capacity: rbuf.capacity, len: rbuf.len, data: rbuf.data)
21 | }
22 |
23 | static func empty() -> RustBuffer {
24 | RustBuffer(capacity: 0, len:0, data: nil)
25 | }
26 |
27 | static func from(_ ptr: UnsafeBufferPointer) -> RustBuffer {
28 | try! rustCall { ffi_matrix_sdk_rustbuffer_from_bytes(ForeignBytes(bufferPointer: ptr), $0) }
29 | }
30 |
31 | // Frees the buffer in place.
32 | // The buffer must not be used after this is called.
33 | func deallocate() {
34 | try! rustCall { ffi_matrix_sdk_rustbuffer_free(self, $0) }
35 | }
36 | }
37 |
38 | fileprivate extension ForeignBytes {
39 | init(bufferPointer: UnsafeBufferPointer) {
40 | self.init(len: Int32(bufferPointer.count), data: bufferPointer.baseAddress)
41 | }
42 | }
43 |
44 | // For every type used in the interface, we provide helper methods for conveniently
45 | // lifting and lowering that type from C-compatible data, and for reading and writing
46 | // values of that type in a buffer.
47 |
48 | // Helper classes/extensions that don't change.
49 | // Someday, this will be in a library of its own.
50 |
51 | fileprivate extension Data {
52 | init(rustBuffer: RustBuffer) {
53 | // TODO: This copies the buffer. Can we read directly from a
54 | // Rust buffer?
55 | self.init(bytes: rustBuffer.data!, count: Int(rustBuffer.len))
56 | }
57 | }
58 |
59 | // Define reader functionality. Normally this would be defined in a class or
60 | // struct, but we use standalone functions instead in order to make external
61 | // types work.
62 | //
63 | // With external types, one swift source file needs to be able to call the read
64 | // method on another source file's FfiConverter, but then what visibility
65 | // should Reader have?
66 | // - If Reader is fileprivate, then this means the read() must also
67 | // be fileprivate, which doesn't work with external types.
68 | // - If Reader is internal/public, we'll get compile errors since both source
69 | // files will try define the same type.
70 | //
71 | // Instead, the read() method and these helper functions input a tuple of data
72 |
73 | fileprivate func createReader(data: Data) -> (data: Data, offset: Data.Index) {
74 | (data: data, offset: 0)
75 | }
76 |
77 | // Reads an integer at the current offset, in big-endian order, and advances
78 | // the offset on success. Throws if reading the integer would move the
79 | // offset past the end of the buffer.
80 | fileprivate func readInt(_ reader: inout (data: Data, offset: Data.Index)) throws -> T {
81 | let range = reader.offset...size
82 | guard reader.data.count >= range.upperBound else {
83 | throw UniffiInternalError.bufferOverflow
84 | }
85 | if T.self == UInt8.self {
86 | let value = reader.data[reader.offset]
87 | reader.offset += 1
88 | return value as! T
89 | }
90 | var value: T = 0
91 | let _ = withUnsafeMutableBytes(of: &value, { reader.data.copyBytes(to: $0, from: range)})
92 | reader.offset = range.upperBound
93 | return value.bigEndian
94 | }
95 |
96 | // Reads an arbitrary number of bytes, to be used to read
97 | // raw bytes, this is useful when lifting strings
98 | fileprivate func readBytes(_ reader: inout (data: Data, offset: Data.Index), count: Int) throws -> Array {
99 | let range = reader.offset..<(reader.offset+count)
100 | guard reader.data.count >= range.upperBound else {
101 | throw UniffiInternalError.bufferOverflow
102 | }
103 | var value = [UInt8](repeating: 0, count: count)
104 | value.withUnsafeMutableBufferPointer({ buffer in
105 | reader.data.copyBytes(to: buffer, from: range)
106 | })
107 | reader.offset = range.upperBound
108 | return value
109 | }
110 |
111 | // Reads a float at the current offset.
112 | fileprivate func readFloat(_ reader: inout (data: Data, offset: Data.Index)) throws -> Float {
113 | return Float(bitPattern: try readInt(&reader))
114 | }
115 |
116 | // Reads a float at the current offset.
117 | fileprivate func readDouble(_ reader: inout (data: Data, offset: Data.Index)) throws -> Double {
118 | return Double(bitPattern: try readInt(&reader))
119 | }
120 |
121 | // Indicates if the offset has reached the end of the buffer.
122 | fileprivate func hasRemaining(_ reader: (data: Data, offset: Data.Index)) -> Bool {
123 | return reader.offset < reader.data.count
124 | }
125 |
126 | // Define writer functionality. Normally this would be defined in a class or
127 | // struct, but we use standalone functions instead in order to make external
128 | // types work. See the above discussion on Readers for details.
129 |
130 | fileprivate func createWriter() -> [UInt8] {
131 | return []
132 | }
133 |
134 | fileprivate func writeBytes(_ writer: inout [UInt8], _ byteArr: S) where S: Sequence, S.Element == UInt8 {
135 | writer.append(contentsOf: byteArr)
136 | }
137 |
138 | // Writes an integer in big-endian order.
139 | //
140 | // Warning: make sure what you are trying to write
141 | // is in the correct type!
142 | fileprivate func writeInt(_ writer: inout [UInt8], _ value: T) {
143 | var value = value.bigEndian
144 | withUnsafeBytes(of: &value) { writer.append(contentsOf: $0) }
145 | }
146 |
147 | fileprivate func writeFloat(_ writer: inout [UInt8], _ value: Float) {
148 | writeInt(&writer, value.bitPattern)
149 | }
150 |
151 | fileprivate func writeDouble(_ writer: inout [UInt8], _ value: Double) {
152 | writeInt(&writer, value.bitPattern)
153 | }
154 |
155 | // Protocol for types that transfer other types across the FFI. This is
156 | // analogous to the Rust trait of the same name.
157 | fileprivate protocol FfiConverter {
158 | associatedtype FfiType
159 | associatedtype SwiftType
160 |
161 | static func lift(_ value: FfiType) throws -> SwiftType
162 | static func lower(_ value: SwiftType) -> FfiType
163 | static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType
164 | static func write(_ value: SwiftType, into buf: inout [UInt8])
165 | }
166 |
167 | // Types conforming to `Primitive` pass themselves directly over the FFI.
168 | fileprivate protocol FfiConverterPrimitive: FfiConverter where FfiType == SwiftType { }
169 |
170 | extension FfiConverterPrimitive {
171 | public static func lift(_ value: FfiType) throws -> SwiftType {
172 | return value
173 | }
174 |
175 | public static func lower(_ value: SwiftType) -> FfiType {
176 | return value
177 | }
178 | }
179 |
180 | // Types conforming to `FfiConverterRustBuffer` lift and lower into a `RustBuffer`.
181 | // Used for complex types where it's hard to write a custom lift/lower.
182 | fileprivate protocol FfiConverterRustBuffer: FfiConverter where FfiType == RustBuffer {}
183 |
184 | extension FfiConverterRustBuffer {
185 | public static func lift(_ buf: RustBuffer) throws -> SwiftType {
186 | var reader = createReader(data: Data(rustBuffer: buf))
187 | let value = try read(from: &reader)
188 | if hasRemaining(reader) {
189 | throw UniffiInternalError.incompleteData
190 | }
191 | buf.deallocate()
192 | return value
193 | }
194 |
195 | public static func lower(_ value: SwiftType) -> RustBuffer {
196 | var writer = createWriter()
197 | write(value, into: &writer)
198 | return RustBuffer(bytes: writer)
199 | }
200 | }
201 | // An error type for FFI errors. These errors occur at the UniFFI level, not
202 | // the library level.
203 | fileprivate enum UniffiInternalError: LocalizedError {
204 | case bufferOverflow
205 | case incompleteData
206 | case unexpectedOptionalTag
207 | case unexpectedEnumCase
208 | case unexpectedNullPointer
209 | case unexpectedRustCallStatusCode
210 | case unexpectedRustCallError
211 | case unexpectedStaleHandle
212 | case rustPanic(_ message: String)
213 |
214 | public var errorDescription: String? {
215 | switch self {
216 | case .bufferOverflow: return "Reading the requested value would read past the end of the buffer"
217 | case .incompleteData: return "The buffer still has data after lifting its containing value"
218 | case .unexpectedOptionalTag: return "Unexpected optional tag; should be 0 or 1"
219 | case .unexpectedEnumCase: return "Raw enum value doesn't match any cases"
220 | case .unexpectedNullPointer: return "Raw pointer value was null"
221 | case .unexpectedRustCallStatusCode: return "Unexpected RustCallStatus code"
222 | case .unexpectedRustCallError: return "CALL_ERROR but no errorClass specified"
223 | case .unexpectedStaleHandle: return "The object in the handle map has been dropped already"
224 | case let .rustPanic(message): return message
225 | }
226 | }
227 | }
228 |
229 | fileprivate extension NSLock {
230 | func withLock(f: () throws -> T) rethrows -> T {
231 | self.lock()
232 | defer { self.unlock() }
233 | return try f()
234 | }
235 | }
236 |
237 | fileprivate let CALL_SUCCESS: Int8 = 0
238 | fileprivate let CALL_ERROR: Int8 = 1
239 | fileprivate let CALL_UNEXPECTED_ERROR: Int8 = 2
240 | fileprivate let CALL_CANCELLED: Int8 = 3
241 |
242 | fileprivate extension RustCallStatus {
243 | init() {
244 | self.init(
245 | code: CALL_SUCCESS,
246 | errorBuf: RustBuffer.init(
247 | capacity: 0,
248 | len: 0,
249 | data: nil
250 | )
251 | )
252 | }
253 | }
254 |
255 | private func rustCall(_ callback: (UnsafeMutablePointer) -> T) throws -> T {
256 | let neverThrow: ((RustBuffer) throws -> Never)? = nil
257 | return try makeRustCall(callback, errorHandler: neverThrow)
258 | }
259 |
260 | private func rustCallWithError(
261 | _ errorHandler: @escaping (RustBuffer) throws -> E,
262 | _ callback: (UnsafeMutablePointer) -> T) throws -> T {
263 | try makeRustCall(callback, errorHandler: errorHandler)
264 | }
265 |
266 | private func makeRustCall(
267 | _ callback: (UnsafeMutablePointer) -> T,
268 | errorHandler: ((RustBuffer) throws -> E)?
269 | ) throws -> T {
270 | uniffiEnsureInitialized()
271 | var callStatus = RustCallStatus.init()
272 | let returnedVal = callback(&callStatus)
273 | try uniffiCheckCallStatus(callStatus: callStatus, errorHandler: errorHandler)
274 | return returnedVal
275 | }
276 |
277 | private func uniffiCheckCallStatus(
278 | callStatus: RustCallStatus,
279 | errorHandler: ((RustBuffer) throws -> E)?
280 | ) throws {
281 | switch callStatus.code {
282 | case CALL_SUCCESS:
283 | return
284 |
285 | case CALL_ERROR:
286 | if let errorHandler = errorHandler {
287 | throw try errorHandler(callStatus.errorBuf)
288 | } else {
289 | callStatus.errorBuf.deallocate()
290 | throw UniffiInternalError.unexpectedRustCallError
291 | }
292 |
293 | case CALL_UNEXPECTED_ERROR:
294 | // When the rust code sees a panic, it tries to construct a RustBuffer
295 | // with the message. But if that code panics, then it just sends back
296 | // an empty buffer.
297 | if callStatus.errorBuf.len > 0 {
298 | throw UniffiInternalError.rustPanic(try FfiConverterString.lift(callStatus.errorBuf))
299 | } else {
300 | callStatus.errorBuf.deallocate()
301 | throw UniffiInternalError.rustPanic("Rust panic")
302 | }
303 |
304 | case CALL_CANCELLED:
305 | fatalError("Cancellation not supported yet")
306 |
307 | default:
308 | throw UniffiInternalError.unexpectedRustCallStatusCode
309 | }
310 | }
311 |
312 | private func uniffiTraitInterfaceCall(
313 | callStatus: UnsafeMutablePointer,
314 | makeCall: () throws -> T,
315 | writeReturn: (T) -> ()
316 | ) {
317 | do {
318 | try writeReturn(makeCall())
319 | } catch let error {
320 | callStatus.pointee.code = CALL_UNEXPECTED_ERROR
321 | callStatus.pointee.errorBuf = FfiConverterString.lower(String(describing: error))
322 | }
323 | }
324 |
325 | private func uniffiTraitInterfaceCallWithError(
326 | callStatus: UnsafeMutablePointer,
327 | makeCall: () throws -> T,
328 | writeReturn: (T) -> (),
329 | lowerError: (E) -> RustBuffer
330 | ) {
331 | do {
332 | try writeReturn(makeCall())
333 | } catch let error as E {
334 | callStatus.pointee.code = CALL_ERROR
335 | callStatus.pointee.errorBuf = lowerError(error)
336 | } catch {
337 | callStatus.pointee.code = CALL_UNEXPECTED_ERROR
338 | callStatus.pointee.errorBuf = FfiConverterString.lower(String(describing: error))
339 | }
340 | }
341 | fileprivate class UniffiHandleMap {
342 | private var map: [UInt64: T] = [:]
343 | private let lock = NSLock()
344 | private var currentHandle: UInt64 = 1
345 |
346 | func insert(obj: T) -> UInt64 {
347 | lock.withLock {
348 | let handle = currentHandle
349 | currentHandle += 1
350 | map[handle] = obj
351 | return handle
352 | }
353 | }
354 |
355 | func get(handle: UInt64) throws -> T {
356 | try lock.withLock {
357 | guard let obj = map[handle] else {
358 | throw UniffiInternalError.unexpectedStaleHandle
359 | }
360 | return obj
361 | }
362 | }
363 |
364 | @discardableResult
365 | func remove(handle: UInt64) throws -> T {
366 | try lock.withLock {
367 | guard let obj = map.removeValue(forKey: handle) else {
368 | throw UniffiInternalError.unexpectedStaleHandle
369 | }
370 | return obj
371 | }
372 | }
373 |
374 | var count: Int {
375 | get {
376 | map.count
377 | }
378 | }
379 | }
380 |
381 |
382 | // Public interface members begin here.
383 |
384 |
385 | fileprivate struct FfiConverterInt64: FfiConverterPrimitive {
386 | typealias FfiType = Int64
387 | typealias SwiftType = Int64
388 |
389 | public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Int64 {
390 | return try lift(readInt(&buf))
391 | }
392 |
393 | public static func write(_ value: Int64, into buf: inout [UInt8]) {
394 | writeInt(&buf, lower(value))
395 | }
396 | }
397 |
398 | fileprivate struct FfiConverterBool : FfiConverter {
399 | typealias FfiType = Int8
400 | typealias SwiftType = Bool
401 |
402 | public static func lift(_ value: Int8) throws -> Bool {
403 | return value != 0
404 | }
405 |
406 | public static func lower(_ value: Bool) -> Int8 {
407 | return value ? 1 : 0
408 | }
409 |
410 | public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Bool {
411 | return try lift(readInt(&buf))
412 | }
413 |
414 | public static func write(_ value: Bool, into buf: inout [UInt8]) {
415 | writeInt(&buf, lower(value))
416 | }
417 | }
418 |
419 | fileprivate struct FfiConverterString: FfiConverter {
420 | typealias SwiftType = String
421 | typealias FfiType = RustBuffer
422 |
423 | public static func lift(_ value: RustBuffer) throws -> String {
424 | defer {
425 | value.deallocate()
426 | }
427 | if value.data == nil {
428 | return String()
429 | }
430 | let bytes = UnsafeBufferPointer(start: value.data!, count: Int(value.len))
431 | return String(bytes: bytes, encoding: String.Encoding.utf8)!
432 | }
433 |
434 | public static func lower(_ value: String) -> RustBuffer {
435 | return value.utf8CString.withUnsafeBufferPointer { ptr in
436 | // The swift string gives us int8_t, we want uint8_t.
437 | ptr.withMemoryRebound(to: UInt8.self) { ptr in
438 | // The swift string gives us a trailing null byte, we don't want it.
439 | let buf = UnsafeBufferPointer(rebasing: ptr.prefix(upTo: ptr.count - 1))
440 | return RustBuffer.from(buf)
441 | }
442 | }
443 | }
444 |
445 | public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> String {
446 | let len: Int32 = try readInt(&buf)
447 | return String(bytes: try readBytes(&buf, count: Int(len)), encoding: String.Encoding.utf8)!
448 | }
449 |
450 | public static func write(_ value: String, into buf: inout [UInt8]) {
451 | let len = Int32(value.utf8.count)
452 | writeInt(&buf, len)
453 | writeBytes(&buf, value.utf8)
454 | }
455 | }
456 |
457 |
458 |
459 |
460 | /**
461 | * The data needed to perform authorization using OAuth 2.0.
462 | */
463 | public protocol OAuthAuthorizationDataProtocol : AnyObject {
464 |
465 | /**
466 | * The login URL to use for authorization.
467 | */
468 | func loginUrl() -> String
469 |
470 | }
471 |
472 | /**
473 | * The data needed to perform authorization using OAuth 2.0.
474 | */
475 | open class OAuthAuthorizationData:
476 | OAuthAuthorizationDataProtocol {
477 | fileprivate let pointer: UnsafeMutableRawPointer!
478 |
479 | /// Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly.
480 | public struct NoPointer {
481 | public init() {}
482 | }
483 |
484 | // TODO: We'd like this to be `private` but for Swifty reasons,
485 | // we can't implement `FfiConverter` without making this `required` and we can't
486 | // make it `required` without making it `public`.
487 | required public init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) {
488 | self.pointer = pointer
489 | }
490 |
491 | /// This constructor can be used to instantiate a fake object.
492 | /// - Parameter noPointer: Placeholder value so we can have a constructor separate from the default empty one that may be implemented for classes extending [FFIObject].
493 | ///
494 | /// - Warning:
495 | /// Any object instantiated with this constructor cannot be passed to an actual Rust-backed object. Since there isn't a backing [Pointer] the FFI lower functions will crash.
496 | public init(noPointer: NoPointer) {
497 | self.pointer = nil
498 | }
499 |
500 | public func uniffiClonePointer() -> UnsafeMutableRawPointer {
501 | return try! rustCall { uniffi_matrix_sdk_fn_clone_oauthauthorizationdata(self.pointer, $0) }
502 | }
503 | // No primary constructor declared for this class.
504 |
505 | deinit {
506 | guard let pointer = pointer else {
507 | return
508 | }
509 |
510 | try! rustCall { uniffi_matrix_sdk_fn_free_oauthauthorizationdata(pointer, $0) }
511 | }
512 |
513 |
514 |
515 |
516 | /**
517 | * The login URL to use for authorization.
518 | */
519 | open func loginUrl() -> String {
520 | return try! FfiConverterString.lift(try! rustCall() {
521 | uniffi_matrix_sdk_fn_method_oauthauthorizationdata_login_url(self.uniffiClonePointer(),$0
522 | )
523 | })
524 | }
525 |
526 |
527 | }
528 |
529 | public struct FfiConverterTypeOAuthAuthorizationData: FfiConverter {
530 |
531 | typealias FfiType = UnsafeMutableRawPointer
532 | typealias SwiftType = OAuthAuthorizationData
533 |
534 | public static func lift(_ pointer: UnsafeMutableRawPointer) throws -> OAuthAuthorizationData {
535 | return OAuthAuthorizationData(unsafeFromRawPointer: pointer)
536 | }
537 |
538 | public static func lower(_ value: OAuthAuthorizationData) -> UnsafeMutableRawPointer {
539 | return value.uniffiClonePointer()
540 | }
541 |
542 | public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> OAuthAuthorizationData {
543 | let v: UInt64 = try readInt(&buf)
544 | // The Rust code won't compile if a pointer won't fit in a UInt64.
545 | // We have to go via `UInt` because that's the thing that's the size of a pointer.
546 | let ptr = UnsafeMutableRawPointer(bitPattern: UInt(truncatingIfNeeded: v))
547 | if (ptr == nil) {
548 | throw UniffiInternalError.unexpectedNullPointer
549 | }
550 | return try lift(ptr!)
551 | }
552 |
553 | public static func write(_ value: OAuthAuthorizationData, into buf: inout [UInt8]) {
554 | // This fiddling is because `Int` is the thing that's the same size as a pointer.
555 | // The Rust code won't compile if a pointer won't fit in a `UInt64`.
556 | writeInt(&buf, UInt64(bitPattern: Int64(Int(bitPattern: lower(value)))))
557 | }
558 | }
559 |
560 |
561 |
562 |
563 | public func FfiConverterTypeOAuthAuthorizationData_lift(_ pointer: UnsafeMutableRawPointer) throws -> OAuthAuthorizationData {
564 | return try FfiConverterTypeOAuthAuthorizationData.lift(pointer)
565 | }
566 |
567 | public func FfiConverterTypeOAuthAuthorizationData_lower(_ value: OAuthAuthorizationData) -> UnsafeMutableRawPointer {
568 | return FfiConverterTypeOAuthAuthorizationData.lower(value)
569 | }
570 |
571 |
572 | /**
573 | * A set of common power levels required for various operations within a room,
574 | * that can be applied as a single operation. When updating these
575 | * settings, any levels that are `None` will remain unchanged.
576 | */
577 | public struct RoomPowerLevelChanges {
578 | /**
579 | * The level required to ban a user.
580 | */
581 | public var ban: Int64?
582 | /**
583 | * The level required to invite a user.
584 | */
585 | public var invite: Int64?
586 | /**
587 | * The level required to kick a user.
588 | */
589 | public var kick: Int64?
590 | /**
591 | * The level required to redact an event.
592 | */
593 | public var redact: Int64?
594 | /**
595 | * The default level required to send message events.
596 | */
597 | public var eventsDefault: Int64?
598 | /**
599 | * The default level required to send state events.
600 | */
601 | public var stateDefault: Int64?
602 | /**
603 | * The default power level for every user in the room.
604 | */
605 | public var usersDefault: Int64?
606 | /**
607 | * The level required to change the room's name.
608 | */
609 | public var roomName: Int64?
610 | /**
611 | * The level required to change the room's avatar.
612 | */
613 | public var roomAvatar: Int64?
614 | /**
615 | * The level required to change the room's topic.
616 | */
617 | public var roomTopic: Int64?
618 |
619 | // Default memberwise initializers are never public by default, so we
620 | // declare one manually.
621 | public init(
622 | /**
623 | * The level required to ban a user.
624 | */ban: Int64? = nil,
625 | /**
626 | * The level required to invite a user.
627 | */invite: Int64? = nil,
628 | /**
629 | * The level required to kick a user.
630 | */kick: Int64? = nil,
631 | /**
632 | * The level required to redact an event.
633 | */redact: Int64? = nil,
634 | /**
635 | * The default level required to send message events.
636 | */eventsDefault: Int64? = nil,
637 | /**
638 | * The default level required to send state events.
639 | */stateDefault: Int64? = nil,
640 | /**
641 | * The default power level for every user in the room.
642 | */usersDefault: Int64? = nil,
643 | /**
644 | * The level required to change the room's name.
645 | */roomName: Int64? = nil,
646 | /**
647 | * The level required to change the room's avatar.
648 | */roomAvatar: Int64? = nil,
649 | /**
650 | * The level required to change the room's topic.
651 | */roomTopic: Int64? = nil) {
652 | self.ban = ban
653 | self.invite = invite
654 | self.kick = kick
655 | self.redact = redact
656 | self.eventsDefault = eventsDefault
657 | self.stateDefault = stateDefault
658 | self.usersDefault = usersDefault
659 | self.roomName = roomName
660 | self.roomAvatar = roomAvatar
661 | self.roomTopic = roomTopic
662 | }
663 | }
664 |
665 |
666 |
667 | extension RoomPowerLevelChanges: Equatable, Hashable {
668 | public static func ==(lhs: RoomPowerLevelChanges, rhs: RoomPowerLevelChanges) -> Bool {
669 | if lhs.ban != rhs.ban {
670 | return false
671 | }
672 | if lhs.invite != rhs.invite {
673 | return false
674 | }
675 | if lhs.kick != rhs.kick {
676 | return false
677 | }
678 | if lhs.redact != rhs.redact {
679 | return false
680 | }
681 | if lhs.eventsDefault != rhs.eventsDefault {
682 | return false
683 | }
684 | if lhs.stateDefault != rhs.stateDefault {
685 | return false
686 | }
687 | if lhs.usersDefault != rhs.usersDefault {
688 | return false
689 | }
690 | if lhs.roomName != rhs.roomName {
691 | return false
692 | }
693 | if lhs.roomAvatar != rhs.roomAvatar {
694 | return false
695 | }
696 | if lhs.roomTopic != rhs.roomTopic {
697 | return false
698 | }
699 | return true
700 | }
701 |
702 | public func hash(into hasher: inout Hasher) {
703 | hasher.combine(ban)
704 | hasher.combine(invite)
705 | hasher.combine(kick)
706 | hasher.combine(redact)
707 | hasher.combine(eventsDefault)
708 | hasher.combine(stateDefault)
709 | hasher.combine(usersDefault)
710 | hasher.combine(roomName)
711 | hasher.combine(roomAvatar)
712 | hasher.combine(roomTopic)
713 | }
714 | }
715 |
716 |
717 | public struct FfiConverterTypeRoomPowerLevelChanges: FfiConverterRustBuffer {
718 | public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> RoomPowerLevelChanges {
719 | return
720 | try RoomPowerLevelChanges(
721 | ban: FfiConverterOptionInt64.read(from: &buf),
722 | invite: FfiConverterOptionInt64.read(from: &buf),
723 | kick: FfiConverterOptionInt64.read(from: &buf),
724 | redact: FfiConverterOptionInt64.read(from: &buf),
725 | eventsDefault: FfiConverterOptionInt64.read(from: &buf),
726 | stateDefault: FfiConverterOptionInt64.read(from: &buf),
727 | usersDefault: FfiConverterOptionInt64.read(from: &buf),
728 | roomName: FfiConverterOptionInt64.read(from: &buf),
729 | roomAvatar: FfiConverterOptionInt64.read(from: &buf),
730 | roomTopic: FfiConverterOptionInt64.read(from: &buf)
731 | )
732 | }
733 |
734 | public static func write(_ value: RoomPowerLevelChanges, into buf: inout [UInt8]) {
735 | FfiConverterOptionInt64.write(value.ban, into: &buf)
736 | FfiConverterOptionInt64.write(value.invite, into: &buf)
737 | FfiConverterOptionInt64.write(value.kick, into: &buf)
738 | FfiConverterOptionInt64.write(value.redact, into: &buf)
739 | FfiConverterOptionInt64.write(value.eventsDefault, into: &buf)
740 | FfiConverterOptionInt64.write(value.stateDefault, into: &buf)
741 | FfiConverterOptionInt64.write(value.usersDefault, into: &buf)
742 | FfiConverterOptionInt64.write(value.roomName, into: &buf)
743 | FfiConverterOptionInt64.write(value.roomAvatar, into: &buf)
744 | FfiConverterOptionInt64.write(value.roomTopic, into: &buf)
745 | }
746 | }
747 |
748 |
749 | public func FfiConverterTypeRoomPowerLevelChanges_lift(_ buf: RustBuffer) throws -> RoomPowerLevelChanges {
750 | return try FfiConverterTypeRoomPowerLevelChanges.lift(buf)
751 | }
752 |
753 | public func FfiConverterTypeRoomPowerLevelChanges_lower(_ value: RoomPowerLevelChanges) -> RustBuffer {
754 | return FfiConverterTypeRoomPowerLevelChanges.lower(value)
755 | }
756 |
757 | // Note that we don't yet support `indirect` for enums.
758 | // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion.
759 | /**
760 | * Settings for end-to-end encryption features.
761 | */
762 |
763 | public enum BackupDownloadStrategy {
764 |
765 | /**
766 | * Automatically download all room keys from the backup when the backup
767 | * recovery key has been received. The backup recovery key can be received
768 | * in two ways:
769 | *
770 | * 1. Received as a `m.secret.send` to-device event, after a successful
771 | * interactive verification.
772 | * 2. Imported from secret storage (4S) using the
773 | * [`SecretStore::import_secrets()`] method.
774 | *
775 | * [`SecretStore::import_secrets()`]: crate::encryption::secret_storage::SecretStore::import_secrets
776 | */
777 | case oneShot
778 | /**
779 | * Attempt to download a single room key if an event fails to be decrypted.
780 | */
781 | case afterDecryptionFailure
782 | /**
783 | * Don't download any room keys automatically. The user can manually
784 | * download room keys using the [`Backups::download_room_key()`] methods.
785 | *
786 | * This is the default option.
787 | */
788 | case manual
789 | }
790 |
791 |
792 | public struct FfiConverterTypeBackupDownloadStrategy: FfiConverterRustBuffer {
793 | typealias SwiftType = BackupDownloadStrategy
794 |
795 | public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BackupDownloadStrategy {
796 | let variant: Int32 = try readInt(&buf)
797 | switch variant {
798 |
799 | case 1: return .oneShot
800 |
801 | case 2: return .afterDecryptionFailure
802 |
803 | case 3: return .manual
804 |
805 | default: throw UniffiInternalError.unexpectedEnumCase
806 | }
807 | }
808 |
809 | public static func write(_ value: BackupDownloadStrategy, into buf: inout [UInt8]) {
810 | switch value {
811 |
812 |
813 | case .oneShot:
814 | writeInt(&buf, Int32(1))
815 |
816 |
817 | case .afterDecryptionFailure:
818 | writeInt(&buf, Int32(2))
819 |
820 |
821 | case .manual:
822 | writeInt(&buf, Int32(3))
823 |
824 | }
825 | }
826 | }
827 |
828 |
829 | public func FfiConverterTypeBackupDownloadStrategy_lift(_ buf: RustBuffer) throws -> BackupDownloadStrategy {
830 | return try FfiConverterTypeBackupDownloadStrategy.lift(buf)
831 | }
832 |
833 | public func FfiConverterTypeBackupDownloadStrategy_lower(_ value: BackupDownloadStrategy) -> RustBuffer {
834 | return FfiConverterTypeBackupDownloadStrategy.lower(value)
835 | }
836 |
837 |
838 |
839 | extension BackupDownloadStrategy: Equatable, Hashable {}
840 |
841 |
842 |
843 | // Note that we don't yet support `indirect` for enums.
844 | // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion.
845 | /**
846 | * Current state of a [`Paginator`].
847 | */
848 |
849 | public enum PaginatorState {
850 |
851 | /**
852 | * The initial state of the paginator.
853 | */
854 | case initial
855 | /**
856 | * The paginator is fetching the target initial event.
857 | */
858 | case fetchingTargetEvent
859 | /**
860 | * The target initial event could be found, zero or more paginations have
861 | * happened since then, and the paginator is at rest now.
862 | */
863 | case idle
864 | /**
865 | * The paginator is… paginating one direction or another.
866 | */
867 | case paginating
868 | }
869 |
870 |
871 | public struct FfiConverterTypePaginatorState: FfiConverterRustBuffer {
872 | typealias SwiftType = PaginatorState
873 |
874 | public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PaginatorState {
875 | let variant: Int32 = try readInt(&buf)
876 | switch variant {
877 |
878 | case 1: return .initial
879 |
880 | case 2: return .fetchingTargetEvent
881 |
882 | case 3: return .idle
883 |
884 | case 4: return .paginating
885 |
886 | default: throw UniffiInternalError.unexpectedEnumCase
887 | }
888 | }
889 |
890 | public static func write(_ value: PaginatorState, into buf: inout [UInt8]) {
891 | switch value {
892 |
893 |
894 | case .initial:
895 | writeInt(&buf, Int32(1))
896 |
897 |
898 | case .fetchingTargetEvent:
899 | writeInt(&buf, Int32(2))
900 |
901 |
902 | case .idle:
903 | writeInt(&buf, Int32(3))
904 |
905 |
906 | case .paginating:
907 | writeInt(&buf, Int32(4))
908 |
909 | }
910 | }
911 | }
912 |
913 |
914 | public func FfiConverterTypePaginatorState_lift(_ buf: RustBuffer) throws -> PaginatorState {
915 | return try FfiConverterTypePaginatorState.lift(buf)
916 | }
917 |
918 | public func FfiConverterTypePaginatorState_lower(_ value: PaginatorState) -> RustBuffer {
919 | return FfiConverterTypePaginatorState.lower(value)
920 | }
921 |
922 |
923 |
924 | extension PaginatorState: Equatable, Hashable {}
925 |
926 |
927 |
928 |
929 | /**
930 | * The error type for failures while trying to log in a new device using a QR
931 | * code.
932 | */
933 | public enum QrCodeLoginError {
934 |
935 |
936 |
937 | /**
938 | * An error happened while we were communicating with the OAuth 2.0
939 | * authorization server.
940 | */
941 | case OAuth(message: String)
942 |
943 | /**
944 | * The other device has signaled to us that the login has failed.
945 | */
946 | case LoginFailure(message: String)
947 |
948 | /**
949 | * An unexpected message was received from the other device.
950 | */
951 | case UnexpectedMessage(message: String)
952 |
953 | /**
954 | * An error happened while exchanging messages with the other device.
955 | */
956 | case SecureChannel(message: String)
957 |
958 | /**
959 | * The cross-process refresh lock failed to be initialized.
960 | */
961 | case CrossProcessRefreshLock(message: String)
962 |
963 | /**
964 | * An error happened while we were trying to discover our user and device
965 | * ID, after we have acquired an access token from the OAuth 2.0
966 | * authorization server.
967 | */
968 | case UserIdDiscovery(message: String)
969 |
970 | /**
971 | * We failed to set the session tokens after we figured out our device and
972 | * user IDs.
973 | */
974 | case SessionTokens(message: String)
975 |
976 | /**
977 | * The device keys failed to be uploaded after we successfully logged in.
978 | */
979 | case DeviceKeyUpload(message: String)
980 |
981 | /**
982 | * The secrets bundle we received from the existing device failed to be
983 | * imported.
984 | */
985 | case SecretImport(message: String)
986 |
987 | }
988 |
989 |
990 | public struct FfiConverterTypeQRCodeLoginError: FfiConverterRustBuffer {
991 | typealias SwiftType = QrCodeLoginError
992 |
993 | public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> QrCodeLoginError {
994 | let variant: Int32 = try readInt(&buf)
995 | switch variant {
996 |
997 |
998 |
999 |
1000 | case 1: return .OAuth(
1001 | message: try FfiConverterString.read(from: &buf)
1002 | )
1003 |
1004 | case 2: return .LoginFailure(
1005 | message: try FfiConverterString.read(from: &buf)
1006 | )
1007 |
1008 | case 3: return .UnexpectedMessage(
1009 | message: try FfiConverterString.read(from: &buf)
1010 | )
1011 |
1012 | case 4: return .SecureChannel(
1013 | message: try FfiConverterString.read(from: &buf)
1014 | )
1015 |
1016 | case 5: return .CrossProcessRefreshLock(
1017 | message: try FfiConverterString.read(from: &buf)
1018 | )
1019 |
1020 | case 6: return .UserIdDiscovery(
1021 | message: try FfiConverterString.read(from: &buf)
1022 | )
1023 |
1024 | case 7: return .SessionTokens(
1025 | message: try FfiConverterString.read(from: &buf)
1026 | )
1027 |
1028 | case 8: return .DeviceKeyUpload(
1029 | message: try FfiConverterString.read(from: &buf)
1030 | )
1031 |
1032 | case 9: return .SecretImport(
1033 | message: try FfiConverterString.read(from: &buf)
1034 | )
1035 |
1036 |
1037 | default: throw UniffiInternalError.unexpectedEnumCase
1038 | }
1039 | }
1040 |
1041 | public static func write(_ value: QrCodeLoginError, into buf: inout [UInt8]) {
1042 | switch value {
1043 |
1044 |
1045 |
1046 |
1047 | case .OAuth(_ /* message is ignored*/):
1048 | writeInt(&buf, Int32(1))
1049 | case .LoginFailure(_ /* message is ignored*/):
1050 | writeInt(&buf, Int32(2))
1051 | case .UnexpectedMessage(_ /* message is ignored*/):
1052 | writeInt(&buf, Int32(3))
1053 | case .SecureChannel(_ /* message is ignored*/):
1054 | writeInt(&buf, Int32(4))
1055 | case .CrossProcessRefreshLock(_ /* message is ignored*/):
1056 | writeInt(&buf, Int32(5))
1057 | case .UserIdDiscovery(_ /* message is ignored*/):
1058 | writeInt(&buf, Int32(6))
1059 | case .SessionTokens(_ /* message is ignored*/):
1060 | writeInt(&buf, Int32(7))
1061 | case .DeviceKeyUpload(_ /* message is ignored*/):
1062 | writeInt(&buf, Int32(8))
1063 | case .SecretImport(_ /* message is ignored*/):
1064 | writeInt(&buf, Int32(9))
1065 |
1066 |
1067 | }
1068 | }
1069 | }
1070 |
1071 |
1072 | extension QrCodeLoginError: Equatable, Hashable {}
1073 |
1074 | extension QrCodeLoginError: Foundation.LocalizedError {
1075 | public var errorDescription: String? {
1076 | String(reflecting: self)
1077 | }
1078 | }
1079 |
1080 | // Note that we don't yet support `indirect` for enums.
1081 | // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion.
1082 | /**
1083 | * The role of a member in a room.
1084 | */
1085 |
1086 | public enum RoomMemberRole {
1087 |
1088 | /**
1089 | * The member is an administrator.
1090 | */
1091 | case administrator
1092 | /**
1093 | * The member is a moderator.
1094 | */
1095 | case moderator
1096 | /**
1097 | * The member is a regular user.
1098 | */
1099 | case user
1100 | }
1101 |
1102 |
1103 | public struct FfiConverterTypeRoomMemberRole: FfiConverterRustBuffer {
1104 | typealias SwiftType = RoomMemberRole
1105 |
1106 | public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> RoomMemberRole {
1107 | let variant: Int32 = try readInt(&buf)
1108 | switch variant {
1109 |
1110 | case 1: return .administrator
1111 |
1112 | case 2: return .moderator
1113 |
1114 | case 3: return .user
1115 |
1116 | default: throw UniffiInternalError.unexpectedEnumCase
1117 | }
1118 | }
1119 |
1120 | public static func write(_ value: RoomMemberRole, into buf: inout [UInt8]) {
1121 | switch value {
1122 |
1123 |
1124 | case .administrator:
1125 | writeInt(&buf, Int32(1))
1126 |
1127 |
1128 | case .moderator:
1129 | writeInt(&buf, Int32(2))
1130 |
1131 |
1132 | case .user:
1133 | writeInt(&buf, Int32(3))
1134 |
1135 | }
1136 | }
1137 | }
1138 |
1139 |
1140 | public func FfiConverterTypeRoomMemberRole_lift(_ buf: RustBuffer) throws -> RoomMemberRole {
1141 | return try FfiConverterTypeRoomMemberRole.lift(buf)
1142 | }
1143 |
1144 | public func FfiConverterTypeRoomMemberRole_lower(_ value: RoomMemberRole) -> RustBuffer {
1145 | return FfiConverterTypeRoomMemberRole.lower(value)
1146 | }
1147 |
1148 |
1149 |
1150 | extension RoomMemberRole: Equatable, Hashable {}
1151 |
1152 |
1153 |
1154 | // Note that we don't yet support `indirect` for enums.
1155 | // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion.
1156 | /**
1157 | * Status for the back-pagination on a room event cache.
1158 | */
1159 |
1160 | public enum RoomPaginationStatus {
1161 |
1162 | /**
1163 | * No back-pagination is happening right now.
1164 | */
1165 | case idle(
1166 | /**
1167 | * Have we hit the start of the timeline, i.e. back-paginating wouldn't
1168 | * have any effect?
1169 | */hitTimelineStart: Bool
1170 | )
1171 | /**
1172 | * Back-pagination is already running in the background.
1173 | */
1174 | case paginating
1175 | }
1176 |
1177 |
1178 | public struct FfiConverterTypeRoomPaginationStatus: FfiConverterRustBuffer {
1179 | typealias SwiftType = RoomPaginationStatus
1180 |
1181 | public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> RoomPaginationStatus {
1182 | let variant: Int32 = try readInt(&buf)
1183 | switch variant {
1184 |
1185 | case 1: return .idle(hitTimelineStart: try FfiConverterBool.read(from: &buf)
1186 | )
1187 |
1188 | case 2: return .paginating
1189 |
1190 | default: throw UniffiInternalError.unexpectedEnumCase
1191 | }
1192 | }
1193 |
1194 | public static func write(_ value: RoomPaginationStatus, into buf: inout [UInt8]) {
1195 | switch value {
1196 |
1197 |
1198 | case let .idle(hitTimelineStart):
1199 | writeInt(&buf, Int32(1))
1200 | FfiConverterBool.write(hitTimelineStart, into: &buf)
1201 |
1202 |
1203 | case .paginating:
1204 | writeInt(&buf, Int32(2))
1205 |
1206 | }
1207 | }
1208 | }
1209 |
1210 |
1211 | public func FfiConverterTypeRoomPaginationStatus_lift(_ buf: RustBuffer) throws -> RoomPaginationStatus {
1212 | return try FfiConverterTypeRoomPaginationStatus.lift(buf)
1213 | }
1214 |
1215 | public func FfiConverterTypeRoomPaginationStatus_lower(_ value: RoomPaginationStatus) -> RustBuffer {
1216 | return FfiConverterTypeRoomPaginationStatus.lower(value)
1217 | }
1218 |
1219 |
1220 |
1221 | extension RoomPaginationStatus: Equatable, Hashable {}
1222 |
1223 |
1224 |
1225 | fileprivate struct FfiConverterOptionInt64: FfiConverterRustBuffer {
1226 | typealias SwiftType = Int64?
1227 |
1228 | public static func write(_ value: SwiftType, into buf: inout [UInt8]) {
1229 | guard let value = value else {
1230 | writeInt(&buf, Int8(0))
1231 | return
1232 | }
1233 | writeInt(&buf, Int8(1))
1234 | FfiConverterInt64.write(value, into: &buf)
1235 | }
1236 |
1237 | public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType {
1238 | switch try readInt(&buf) as Int8 {
1239 | case 0: return nil
1240 | case 1: return try FfiConverterInt64.read(from: &buf)
1241 | default: throw UniffiInternalError.unexpectedOptionalTag
1242 | }
1243 | }
1244 | }
1245 |
1246 | private enum InitializationResult {
1247 | case ok
1248 | case contractVersionMismatch
1249 | case apiChecksumMismatch
1250 | }
1251 | // Use a global variable to perform the versioning checks. Swift ensures that
1252 | // the code inside is only computed once.
1253 | private var initializationResult: InitializationResult = {
1254 | // Get the bindings contract version from our ComponentInterface
1255 | let bindings_contract_version = 26
1256 | // Get the scaffolding contract version by calling the into the dylib
1257 | let scaffolding_contract_version = ffi_matrix_sdk_uniffi_contract_version()
1258 | if bindings_contract_version != scaffolding_contract_version {
1259 | return InitializationResult.contractVersionMismatch
1260 | }
1261 | if (uniffi_matrix_sdk_checksum_method_oauthauthorizationdata_login_url() != 25566) {
1262 | return InitializationResult.apiChecksumMismatch
1263 | }
1264 |
1265 | return InitializationResult.ok
1266 | }()
1267 |
1268 | private func uniffiEnsureInitialized() {
1269 | switch initializationResult {
1270 | case .ok:
1271 | break
1272 | case .contractVersionMismatch:
1273 | fatalError("UniFFI contract version mismatch: try cleaning and rebuilding your project")
1274 | case .apiChecksumMismatch:
1275 | fatalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
1276 | }
1277 | }
1278 |
1279 | // swiftlint:enable all
--------------------------------------------------------------------------------
/Sources/MatrixRustSDK/matrix_sdk_base.swift:
--------------------------------------------------------------------------------
1 | // This file was autogenerated by some hot garbage in the `uniffi` crate.
2 | // Trust me, you don't want to mess with it!
3 |
4 | // swiftlint:disable all
5 | import Foundation
6 |
7 | // Depending on the consumer's build setup, the low-level FFI code
8 | // might be in a separate module, or it might be compiled inline into
9 | // this module. This is a bit of light hackery to work with both.
10 | #if canImport(matrix_sdk_baseFFI)
11 | import matrix_sdk_baseFFI
12 | #endif
13 |
14 | fileprivate extension RustBuffer {
15 | // Allocate a new buffer, copying the contents of a `UInt8` array.
16 | init(bytes: [UInt8]) {
17 | let rbuf = bytes.withUnsafeBufferPointer { ptr in
18 | RustBuffer.from(ptr)
19 | }
20 | self.init(capacity: rbuf.capacity, len: rbuf.len, data: rbuf.data)
21 | }
22 |
23 | static func empty() -> RustBuffer {
24 | RustBuffer(capacity: 0, len:0, data: nil)
25 | }
26 |
27 | static func from(_ ptr: UnsafeBufferPointer) -> RustBuffer {
28 | try! rustCall { ffi_matrix_sdk_base_rustbuffer_from_bytes(ForeignBytes(bufferPointer: ptr), $0) }
29 | }
30 |
31 | // Frees the buffer in place.
32 | // The buffer must not be used after this is called.
33 | func deallocate() {
34 | try! rustCall { ffi_matrix_sdk_base_rustbuffer_free(self, $0) }
35 | }
36 | }
37 |
38 | fileprivate extension ForeignBytes {
39 | init(bufferPointer: UnsafeBufferPointer) {
40 | self.init(len: Int32(bufferPointer.count), data: bufferPointer.baseAddress)
41 | }
42 | }
43 |
44 | // For every type used in the interface, we provide helper methods for conveniently
45 | // lifting and lowering that type from C-compatible data, and for reading and writing
46 | // values of that type in a buffer.
47 |
48 | // Helper classes/extensions that don't change.
49 | // Someday, this will be in a library of its own.
50 |
51 | fileprivate extension Data {
52 | init(rustBuffer: RustBuffer) {
53 | // TODO: This copies the buffer. Can we read directly from a
54 | // Rust buffer?
55 | self.init(bytes: rustBuffer.data!, count: Int(rustBuffer.len))
56 | }
57 | }
58 |
59 | // Define reader functionality. Normally this would be defined in a class or
60 | // struct, but we use standalone functions instead in order to make external
61 | // types work.
62 | //
63 | // With external types, one swift source file needs to be able to call the read
64 | // method on another source file's FfiConverter, but then what visibility
65 | // should Reader have?
66 | // - If Reader is fileprivate, then this means the read() must also
67 | // be fileprivate, which doesn't work with external types.
68 | // - If Reader is internal/public, we'll get compile errors since both source
69 | // files will try define the same type.
70 | //
71 | // Instead, the read() method and these helper functions input a tuple of data
72 |
73 | fileprivate func createReader(data: Data) -> (data: Data, offset: Data.Index) {
74 | (data: data, offset: 0)
75 | }
76 |
77 | // Reads an integer at the current offset, in big-endian order, and advances
78 | // the offset on success. Throws if reading the integer would move the
79 | // offset past the end of the buffer.
80 | fileprivate func readInt(_ reader: inout (data: Data, offset: Data.Index)) throws -> T {
81 | let range = reader.offset...size
82 | guard reader.data.count >= range.upperBound else {
83 | throw UniffiInternalError.bufferOverflow
84 | }
85 | if T.self == UInt8.self {
86 | let value = reader.data[reader.offset]
87 | reader.offset += 1
88 | return value as! T
89 | }
90 | var value: T = 0
91 | let _ = withUnsafeMutableBytes(of: &value, { reader.data.copyBytes(to: $0, from: range)})
92 | reader.offset = range.upperBound
93 | return value.bigEndian
94 | }
95 |
96 | // Reads an arbitrary number of bytes, to be used to read
97 | // raw bytes, this is useful when lifting strings
98 | fileprivate func readBytes(_ reader: inout (data: Data, offset: Data.Index), count: Int) throws -> Array {
99 | let range = reader.offset..<(reader.offset+count)
100 | guard reader.data.count >= range.upperBound else {
101 | throw UniffiInternalError.bufferOverflow
102 | }
103 | var value = [UInt8](repeating: 0, count: count)
104 | value.withUnsafeMutableBufferPointer({ buffer in
105 | reader.data.copyBytes(to: buffer, from: range)
106 | })
107 | reader.offset = range.upperBound
108 | return value
109 | }
110 |
111 | // Reads a float at the current offset.
112 | fileprivate func readFloat(_ reader: inout (data: Data, offset: Data.Index)) throws -> Float {
113 | return Float(bitPattern: try readInt(&reader))
114 | }
115 |
116 | // Reads a float at the current offset.
117 | fileprivate func readDouble(_ reader: inout (data: Data, offset: Data.Index)) throws -> Double {
118 | return Double(bitPattern: try readInt(&reader))
119 | }
120 |
121 | // Indicates if the offset has reached the end of the buffer.
122 | fileprivate func hasRemaining(_ reader: (data: Data, offset: Data.Index)) -> Bool {
123 | return reader.offset < reader.data.count
124 | }
125 |
126 | // Define writer functionality. Normally this would be defined in a class or
127 | // struct, but we use standalone functions instead in order to make external
128 | // types work. See the above discussion on Readers for details.
129 |
130 | fileprivate func createWriter() -> [UInt8] {
131 | return []
132 | }
133 |
134 | fileprivate func writeBytes(_ writer: inout [UInt8], _ byteArr: S) where S: Sequence, S.Element == UInt8 {
135 | writer.append(contentsOf: byteArr)
136 | }
137 |
138 | // Writes an integer in big-endian order.
139 | //
140 | // Warning: make sure what you are trying to write
141 | // is in the correct type!
142 | fileprivate func writeInt(_ writer: inout [UInt8], _ value: T) {
143 | var value = value.bigEndian
144 | withUnsafeBytes(of: &value) { writer.append(contentsOf: $0) }
145 | }
146 |
147 | fileprivate func writeFloat(_ writer: inout [UInt8], _ value: Float) {
148 | writeInt(&writer, value.bitPattern)
149 | }
150 |
151 | fileprivate func writeDouble(_ writer: inout [UInt8], _ value: Double) {
152 | writeInt(&writer, value.bitPattern)
153 | }
154 |
155 | // Protocol for types that transfer other types across the FFI. This is
156 | // analogous to the Rust trait of the same name.
157 | fileprivate protocol FfiConverter {
158 | associatedtype FfiType
159 | associatedtype SwiftType
160 |
161 | static func lift(_ value: FfiType) throws -> SwiftType
162 | static func lower(_ value: SwiftType) -> FfiType
163 | static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType
164 | static func write(_ value: SwiftType, into buf: inout [UInt8])
165 | }
166 |
167 | // Types conforming to `Primitive` pass themselves directly over the FFI.
168 | fileprivate protocol FfiConverterPrimitive: FfiConverter where FfiType == SwiftType { }
169 |
170 | extension FfiConverterPrimitive {
171 | public static func lift(_ value: FfiType) throws -> SwiftType {
172 | return value
173 | }
174 |
175 | public static func lower(_ value: SwiftType) -> FfiType {
176 | return value
177 | }
178 | }
179 |
180 | // Types conforming to `FfiConverterRustBuffer` lift and lower into a `RustBuffer`.
181 | // Used for complex types where it's hard to write a custom lift/lower.
182 | fileprivate protocol FfiConverterRustBuffer: FfiConverter where FfiType == RustBuffer {}
183 |
184 | extension FfiConverterRustBuffer {
185 | public static func lift(_ buf: RustBuffer) throws -> SwiftType {
186 | var reader = createReader(data: Data(rustBuffer: buf))
187 | let value = try read(from: &reader)
188 | if hasRemaining(reader) {
189 | throw UniffiInternalError.incompleteData
190 | }
191 | buf.deallocate()
192 | return value
193 | }
194 |
195 | public static func lower(_ value: SwiftType) -> RustBuffer {
196 | var writer = createWriter()
197 | write(value, into: &writer)
198 | return RustBuffer(bytes: writer)
199 | }
200 | }
201 | // An error type for FFI errors. These errors occur at the UniFFI level, not
202 | // the library level.
203 | fileprivate enum UniffiInternalError: LocalizedError {
204 | case bufferOverflow
205 | case incompleteData
206 | case unexpectedOptionalTag
207 | case unexpectedEnumCase
208 | case unexpectedNullPointer
209 | case unexpectedRustCallStatusCode
210 | case unexpectedRustCallError
211 | case unexpectedStaleHandle
212 | case rustPanic(_ message: String)
213 |
214 | public var errorDescription: String? {
215 | switch self {
216 | case .bufferOverflow: return "Reading the requested value would read past the end of the buffer"
217 | case .incompleteData: return "The buffer still has data after lifting its containing value"
218 | case .unexpectedOptionalTag: return "Unexpected optional tag; should be 0 or 1"
219 | case .unexpectedEnumCase: return "Raw enum value doesn't match any cases"
220 | case .unexpectedNullPointer: return "Raw pointer value was null"
221 | case .unexpectedRustCallStatusCode: return "Unexpected RustCallStatus code"
222 | case .unexpectedRustCallError: return "CALL_ERROR but no errorClass specified"
223 | case .unexpectedStaleHandle: return "The object in the handle map has been dropped already"
224 | case let .rustPanic(message): return message
225 | }
226 | }
227 | }
228 |
229 | fileprivate extension NSLock {
230 | func withLock(f: () throws -> T) rethrows -> T {
231 | self.lock()
232 | defer { self.unlock() }
233 | return try f()
234 | }
235 | }
236 |
237 | fileprivate let CALL_SUCCESS: Int8 = 0
238 | fileprivate let CALL_ERROR: Int8 = 1
239 | fileprivate let CALL_UNEXPECTED_ERROR: Int8 = 2
240 | fileprivate let CALL_CANCELLED: Int8 = 3
241 |
242 | fileprivate extension RustCallStatus {
243 | init() {
244 | self.init(
245 | code: CALL_SUCCESS,
246 | errorBuf: RustBuffer.init(
247 | capacity: 0,
248 | len: 0,
249 | data: nil
250 | )
251 | )
252 | }
253 | }
254 |
255 | private func rustCall(_ callback: (UnsafeMutablePointer) -> T) throws -> T {
256 | let neverThrow: ((RustBuffer) throws -> Never)? = nil
257 | return try makeRustCall(callback, errorHandler: neverThrow)
258 | }
259 |
260 | private func rustCallWithError(
261 | _ errorHandler: @escaping (RustBuffer) throws -> E,
262 | _ callback: (UnsafeMutablePointer) -> T) throws -> T {
263 | try makeRustCall(callback, errorHandler: errorHandler)
264 | }
265 |
266 | private func makeRustCall(
267 | _ callback: (UnsafeMutablePointer) -> T,
268 | errorHandler: ((RustBuffer) throws -> E)?
269 | ) throws -> T {
270 | uniffiEnsureInitialized()
271 | var callStatus = RustCallStatus.init()
272 | let returnedVal = callback(&callStatus)
273 | try uniffiCheckCallStatus(callStatus: callStatus, errorHandler: errorHandler)
274 | return returnedVal
275 | }
276 |
277 | private func uniffiCheckCallStatus(
278 | callStatus: RustCallStatus,
279 | errorHandler: ((RustBuffer) throws -> E)?
280 | ) throws {
281 | switch callStatus.code {
282 | case CALL_SUCCESS:
283 | return
284 |
285 | case CALL_ERROR:
286 | if let errorHandler = errorHandler {
287 | throw try errorHandler(callStatus.errorBuf)
288 | } else {
289 | callStatus.errorBuf.deallocate()
290 | throw UniffiInternalError.unexpectedRustCallError
291 | }
292 |
293 | case CALL_UNEXPECTED_ERROR:
294 | // When the rust code sees a panic, it tries to construct a RustBuffer
295 | // with the message. But if that code panics, then it just sends back
296 | // an empty buffer.
297 | if callStatus.errorBuf.len > 0 {
298 | throw UniffiInternalError.rustPanic(try FfiConverterString.lift(callStatus.errorBuf))
299 | } else {
300 | callStatus.errorBuf.deallocate()
301 | throw UniffiInternalError.rustPanic("Rust panic")
302 | }
303 |
304 | case CALL_CANCELLED:
305 | fatalError("Cancellation not supported yet")
306 |
307 | default:
308 | throw UniffiInternalError.unexpectedRustCallStatusCode
309 | }
310 | }
311 |
312 | private func uniffiTraitInterfaceCall(
313 | callStatus: UnsafeMutablePointer,
314 | makeCall: () throws -> T,
315 | writeReturn: (T) -> ()
316 | ) {
317 | do {
318 | try writeReturn(makeCall())
319 | } catch let error {
320 | callStatus.pointee.code = CALL_UNEXPECTED_ERROR
321 | callStatus.pointee.errorBuf = FfiConverterString.lower(String(describing: error))
322 | }
323 | }
324 |
325 | private func uniffiTraitInterfaceCallWithError(
326 | callStatus: UnsafeMutablePointer,
327 | makeCall: () throws -> T,
328 | writeReturn: (T) -> (),
329 | lowerError: (E) -> RustBuffer
330 | ) {
331 | do {
332 | try writeReturn(makeCall())
333 | } catch let error as E {
334 | callStatus.pointee.code = CALL_ERROR
335 | callStatus.pointee.errorBuf = lowerError(error)
336 | } catch {
337 | callStatus.pointee.code = CALL_UNEXPECTED_ERROR
338 | callStatus.pointee.errorBuf = FfiConverterString.lower(String(describing: error))
339 | }
340 | }
341 | fileprivate class UniffiHandleMap {
342 | private var map: [UInt64: T] = [:]
343 | private let lock = NSLock()
344 | private var currentHandle: UInt64 = 1
345 |
346 | func insert(obj: T) -> UInt64 {
347 | lock.withLock {
348 | let handle = currentHandle
349 | currentHandle += 1
350 | map[handle] = obj
351 | return handle
352 | }
353 | }
354 |
355 | func get(handle: UInt64) throws -> T {
356 | try lock.withLock {
357 | guard let obj = map[handle] else {
358 | throw UniffiInternalError.unexpectedStaleHandle
359 | }
360 | return obj
361 | }
362 | }
363 |
364 | @discardableResult
365 | func remove(handle: UInt64) throws -> T {
366 | try lock.withLock {
367 | guard let obj = map.removeValue(forKey: handle) else {
368 | throw UniffiInternalError.unexpectedStaleHandle
369 | }
370 | return obj
371 | }
372 | }
373 |
374 | var count: Int {
375 | get {
376 | map.count
377 | }
378 | }
379 | }
380 |
381 |
382 | // Public interface members begin here.
383 |
384 |
385 | fileprivate struct FfiConverterUInt64: FfiConverterPrimitive {
386 | typealias FfiType = UInt64
387 | typealias SwiftType = UInt64
388 |
389 | public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UInt64 {
390 | return try lift(readInt(&buf))
391 | }
392 |
393 | public static func write(_ value: SwiftType, into buf: inout [UInt8]) {
394 | writeInt(&buf, lower(value))
395 | }
396 | }
397 |
398 | fileprivate struct FfiConverterString: FfiConverter {
399 | typealias SwiftType = String
400 | typealias FfiType = RustBuffer
401 |
402 | public static func lift(_ value: RustBuffer) throws -> String {
403 | defer {
404 | value.deallocate()
405 | }
406 | if value.data == nil {
407 | return String()
408 | }
409 | let bytes = UnsafeBufferPointer(start: value.data!, count: Int(value.len))
410 | return String(bytes: bytes, encoding: String.Encoding.utf8)!
411 | }
412 |
413 | public static func lower(_ value: String) -> RustBuffer {
414 | return value.utf8CString.withUnsafeBufferPointer { ptr in
415 | // The swift string gives us int8_t, we want uint8_t.
416 | ptr.withMemoryRebound(to: UInt8.self) { ptr in
417 | // The swift string gives us a trailing null byte, we don't want it.
418 | let buf = UnsafeBufferPointer(rebasing: ptr.prefix(upTo: ptr.count - 1))
419 | return RustBuffer.from(buf)
420 | }
421 | }
422 | }
423 |
424 | public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> String {
425 | let len: Int32 = try readInt(&buf)
426 | return String(bytes: try readBytes(&buf, count: Int(len)), encoding: String.Encoding.utf8)!
427 | }
428 |
429 | public static func write(_ value: String, into buf: inout [UInt8]) {
430 | let len = Int32(value.utf8.count)
431 | writeInt(&buf, len)
432 | writeBytes(&buf, value.utf8)
433 | }
434 | }
435 |
436 | fileprivate struct FfiConverterDuration: FfiConverterRustBuffer {
437 | typealias SwiftType = TimeInterval
438 |
439 | public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TimeInterval {
440 | let seconds: UInt64 = try readInt(&buf)
441 | let nanoseconds: UInt32 = try readInt(&buf)
442 | return Double(seconds) + (Double(nanoseconds) / 1.0e9)
443 | }
444 |
445 | public static func write(_ value: TimeInterval, into buf: inout [UInt8]) {
446 | if value.rounded(.down) > Double(Int64.max) {
447 | fatalError("Duration overflow, exceeds max bounds supported by Uniffi")
448 | }
449 |
450 | if value < 0 {
451 | fatalError("Invalid duration, must be non-negative")
452 | }
453 |
454 | let seconds = UInt64(value)
455 | let nanoseconds = UInt32((value - Double(seconds)) * 1.0e9)
456 | writeInt(&buf, seconds)
457 | writeInt(&buf, nanoseconds)
458 | }
459 | }
460 |
461 |
462 | /**
463 | * The retention policy for media content used by the [`EventCacheStore`].
464 | *
465 | * [`EventCacheStore`]: crate::event_cache::store::EventCacheStore
466 | */
467 | public struct MediaRetentionPolicy {
468 | /**
469 | * The maximum authorized size of the overall media cache, in bytes.
470 | *
471 | * The cache size is defined as the sum of the sizes of all the (possibly
472 | * encrypted) media contents in the cache, excluding any metadata
473 | * associated with them.
474 | *
475 | * If this is set and the cache size is bigger than this value, the oldest
476 | * media contents in the cache will be removed during a cleanup until the
477 | * cache size is below this threshold.
478 | *
479 | * Note that it is possible for the cache size to temporarily exceed this
480 | * value between two cleanups.
481 | *
482 | * Defaults to 400 MiB.
483 | */
484 | public var maxCacheSize: UInt64?
485 | /**
486 | * The maximum authorized size of a single media content, in bytes.
487 | *
488 | * The size of a media content is the size taken by the content in the
489 | * database, after it was possibly encrypted, so it might differ from the
490 | * initial size of the content.
491 | *
492 | * The maximum authorized size of a single media content is actually the
493 | * lowest value between `max_cache_size` and `max_file_size`.
494 | *
495 | * If it is set, media content bigger than the maximum size will not be
496 | * cached. If the maximum size changed after media content that exceeds the
497 | * new value was cached, the corresponding content will be removed
498 | * during a cleanup.
499 | *
500 | * Defaults to 20 MiB.
501 | */
502 | public var maxFileSize: UInt64?
503 | /**
504 | * The duration after which unaccessed media content is considered
505 | * expired.
506 | *
507 | * If this is set, media content whose last access is older than this
508 | * duration will be removed from the media cache during a cleanup.
509 | *
510 | * Defaults to 60 days.
511 | */
512 | public var lastAccessExpiry: TimeInterval?
513 | /**
514 | * The duration between two automatic media cache cleanups.
515 | *
516 | * If this is set, a cleanup will be triggered after the given duration
517 | * is elapsed, at the next call to the media cache API. If this is set to
518 | * zero, each call to the media cache API will trigger a cleanup. If this
519 | * is `None`, cleanups will only occur if they are triggered manually.
520 | *
521 | * Defaults to running cleanups daily.
522 | */
523 | public var cleanupFrequency: TimeInterval?
524 |
525 | // Default memberwise initializers are never public by default, so we
526 | // declare one manually.
527 | public init(
528 | /**
529 | * The maximum authorized size of the overall media cache, in bytes.
530 | *
531 | * The cache size is defined as the sum of the sizes of all the (possibly
532 | * encrypted) media contents in the cache, excluding any metadata
533 | * associated with them.
534 | *
535 | * If this is set and the cache size is bigger than this value, the oldest
536 | * media contents in the cache will be removed during a cleanup until the
537 | * cache size is below this threshold.
538 | *
539 | * Note that it is possible for the cache size to temporarily exceed this
540 | * value between two cleanups.
541 | *
542 | * Defaults to 400 MiB.
543 | */maxCacheSize: UInt64?,
544 | /**
545 | * The maximum authorized size of a single media content, in bytes.
546 | *
547 | * The size of a media content is the size taken by the content in the
548 | * database, after it was possibly encrypted, so it might differ from the
549 | * initial size of the content.
550 | *
551 | * The maximum authorized size of a single media content is actually the
552 | * lowest value between `max_cache_size` and `max_file_size`.
553 | *
554 | * If it is set, media content bigger than the maximum size will not be
555 | * cached. If the maximum size changed after media content that exceeds the
556 | * new value was cached, the corresponding content will be removed
557 | * during a cleanup.
558 | *
559 | * Defaults to 20 MiB.
560 | */maxFileSize: UInt64?,
561 | /**
562 | * The duration after which unaccessed media content is considered
563 | * expired.
564 | *
565 | * If this is set, media content whose last access is older than this
566 | * duration will be removed from the media cache during a cleanup.
567 | *
568 | * Defaults to 60 days.
569 | */lastAccessExpiry: TimeInterval?,
570 | /**
571 | * The duration between two automatic media cache cleanups.
572 | *
573 | * If this is set, a cleanup will be triggered after the given duration
574 | * is elapsed, at the next call to the media cache API. If this is set to
575 | * zero, each call to the media cache API will trigger a cleanup. If this
576 | * is `None`, cleanups will only occur if they are triggered manually.
577 | *
578 | * Defaults to running cleanups daily.
579 | */cleanupFrequency: TimeInterval?) {
580 | self.maxCacheSize = maxCacheSize
581 | self.maxFileSize = maxFileSize
582 | self.lastAccessExpiry = lastAccessExpiry
583 | self.cleanupFrequency = cleanupFrequency
584 | }
585 | }
586 |
587 |
588 |
589 | extension MediaRetentionPolicy: Equatable, Hashable {
590 | public static func ==(lhs: MediaRetentionPolicy, rhs: MediaRetentionPolicy) -> Bool {
591 | if lhs.maxCacheSize != rhs.maxCacheSize {
592 | return false
593 | }
594 | if lhs.maxFileSize != rhs.maxFileSize {
595 | return false
596 | }
597 | if lhs.lastAccessExpiry != rhs.lastAccessExpiry {
598 | return false
599 | }
600 | if lhs.cleanupFrequency != rhs.cleanupFrequency {
601 | return false
602 | }
603 | return true
604 | }
605 |
606 | public func hash(into hasher: inout Hasher) {
607 | hasher.combine(maxCacheSize)
608 | hasher.combine(maxFileSize)
609 | hasher.combine(lastAccessExpiry)
610 | hasher.combine(cleanupFrequency)
611 | }
612 | }
613 |
614 |
615 | public struct FfiConverterTypeMediaRetentionPolicy: FfiConverterRustBuffer {
616 | public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> MediaRetentionPolicy {
617 | return
618 | try MediaRetentionPolicy(
619 | maxCacheSize: FfiConverterOptionUInt64.read(from: &buf),
620 | maxFileSize: FfiConverterOptionUInt64.read(from: &buf),
621 | lastAccessExpiry: FfiConverterOptionDuration.read(from: &buf),
622 | cleanupFrequency: FfiConverterOptionDuration.read(from: &buf)
623 | )
624 | }
625 |
626 | public static func write(_ value: MediaRetentionPolicy, into buf: inout [UInt8]) {
627 | FfiConverterOptionUInt64.write(value.maxCacheSize, into: &buf)
628 | FfiConverterOptionUInt64.write(value.maxFileSize, into: &buf)
629 | FfiConverterOptionDuration.write(value.lastAccessExpiry, into: &buf)
630 | FfiConverterOptionDuration.write(value.cleanupFrequency, into: &buf)
631 | }
632 | }
633 |
634 |
635 | public func FfiConverterTypeMediaRetentionPolicy_lift(_ buf: RustBuffer) throws -> MediaRetentionPolicy {
636 | return try FfiConverterTypeMediaRetentionPolicy.lift(buf)
637 | }
638 |
639 | public func FfiConverterTypeMediaRetentionPolicy_lower(_ value: MediaRetentionPolicy) -> RustBuffer {
640 | return FfiConverterTypeMediaRetentionPolicy.lower(value)
641 | }
642 |
643 | // Note that we don't yet support `indirect` for enums.
644 | // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion.
645 | /**
646 | * Represents the state of a room encryption.
647 | */
648 |
649 | public enum EncryptionState {
650 |
651 | /**
652 | * The room is encrypted.
653 | */
654 | case encrypted
655 | /**
656 | * The room is not encrypted.
657 | */
658 | case notEncrypted
659 | /**
660 | * The state of the room encryption is unknown, probably because the
661 | * `/sync` did not provide all data needed to decide.
662 | */
663 | case unknown
664 | }
665 |
666 |
667 | public struct FfiConverterTypeEncryptionState: FfiConverterRustBuffer {
668 | typealias SwiftType = EncryptionState
669 |
670 | public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> EncryptionState {
671 | let variant: Int32 = try readInt(&buf)
672 | switch variant {
673 |
674 | case 1: return .encrypted
675 |
676 | case 2: return .notEncrypted
677 |
678 | case 3: return .unknown
679 |
680 | default: throw UniffiInternalError.unexpectedEnumCase
681 | }
682 | }
683 |
684 | public static func write(_ value: EncryptionState, into buf: inout [UInt8]) {
685 | switch value {
686 |
687 |
688 | case .encrypted:
689 | writeInt(&buf, Int32(1))
690 |
691 |
692 | case .notEncrypted:
693 | writeInt(&buf, Int32(2))
694 |
695 |
696 | case .unknown:
697 | writeInt(&buf, Int32(3))
698 |
699 | }
700 | }
701 | }
702 |
703 |
704 | public func FfiConverterTypeEncryptionState_lift(_ buf: RustBuffer) throws -> EncryptionState {
705 | return try FfiConverterTypeEncryptionState.lift(buf)
706 | }
707 |
708 | public func FfiConverterTypeEncryptionState_lower(_ value: EncryptionState) -> RustBuffer {
709 | return FfiConverterTypeEncryptionState.lower(value)
710 | }
711 |
712 |
713 |
714 | extension EncryptionState: Equatable, Hashable {}
715 |
716 |
717 |
718 | fileprivate struct FfiConverterOptionUInt64: FfiConverterRustBuffer {
719 | typealias SwiftType = UInt64?
720 |
721 | public static func write(_ value: SwiftType, into buf: inout [UInt8]) {
722 | guard let value = value else {
723 | writeInt(&buf, Int8(0))
724 | return
725 | }
726 | writeInt(&buf, Int8(1))
727 | FfiConverterUInt64.write(value, into: &buf)
728 | }
729 |
730 | public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType {
731 | switch try readInt(&buf) as Int8 {
732 | case 0: return nil
733 | case 1: return try FfiConverterUInt64.read(from: &buf)
734 | default: throw UniffiInternalError.unexpectedOptionalTag
735 | }
736 | }
737 | }
738 |
739 | fileprivate struct FfiConverterOptionDuration: FfiConverterRustBuffer {
740 | typealias SwiftType = TimeInterval?
741 |
742 | public static func write(_ value: SwiftType, into buf: inout [UInt8]) {
743 | guard let value = value else {
744 | writeInt(&buf, Int8(0))
745 | return
746 | }
747 | writeInt(&buf, Int8(1))
748 | FfiConverterDuration.write(value, into: &buf)
749 | }
750 |
751 | public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType {
752 | switch try readInt(&buf) as Int8 {
753 | case 0: return nil
754 | case 1: return try FfiConverterDuration.read(from: &buf)
755 | default: throw UniffiInternalError.unexpectedOptionalTag
756 | }
757 | }
758 | }
759 |
760 | private enum InitializationResult {
761 | case ok
762 | case contractVersionMismatch
763 | case apiChecksumMismatch
764 | }
765 | // Use a global variable to perform the versioning checks. Swift ensures that
766 | // the code inside is only computed once.
767 | private var initializationResult: InitializationResult = {
768 | // Get the bindings contract version from our ComponentInterface
769 | let bindings_contract_version = 26
770 | // Get the scaffolding contract version by calling the into the dylib
771 | let scaffolding_contract_version = ffi_matrix_sdk_base_uniffi_contract_version()
772 | if bindings_contract_version != scaffolding_contract_version {
773 | return InitializationResult.contractVersionMismatch
774 | }
775 |
776 | return InitializationResult.ok
777 | }()
778 |
779 | private func uniffiEnsureInitialized() {
780 | switch initializationResult {
781 | case .ok:
782 | break
783 | case .contractVersionMismatch:
784 | fatalError("UniFFI contract version mismatch: try cleaning and rebuilding your project")
785 | case .apiChecksumMismatch:
786 | fatalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
787 | }
788 | }
789 |
790 | // swiftlint:enable all
--------------------------------------------------------------------------------
/Sources/MatrixRustSDK/matrix_sdk_common.swift:
--------------------------------------------------------------------------------
1 | // This file was autogenerated by some hot garbage in the `uniffi` crate.
2 | // Trust me, you don't want to mess with it!
3 |
4 | // swiftlint:disable all
5 | import Foundation
6 |
7 | // Depending on the consumer's build setup, the low-level FFI code
8 | // might be in a separate module, or it might be compiled inline into
9 | // this module. This is a bit of light hackery to work with both.
10 | #if canImport(matrix_sdk_commonFFI)
11 | import matrix_sdk_commonFFI
12 | #endif
13 |
14 | fileprivate extension RustBuffer {
15 | // Allocate a new buffer, copying the contents of a `UInt8` array.
16 | init(bytes: [UInt8]) {
17 | let rbuf = bytes.withUnsafeBufferPointer { ptr in
18 | RustBuffer.from(ptr)
19 | }
20 | self.init(capacity: rbuf.capacity, len: rbuf.len, data: rbuf.data)
21 | }
22 |
23 | static func empty() -> RustBuffer {
24 | RustBuffer(capacity: 0, len:0, data: nil)
25 | }
26 |
27 | static func from(_ ptr: UnsafeBufferPointer) -> RustBuffer {
28 | try! rustCall { ffi_matrix_sdk_common_rustbuffer_from_bytes(ForeignBytes(bufferPointer: ptr), $0) }
29 | }
30 |
31 | // Frees the buffer in place.
32 | // The buffer must not be used after this is called.
33 | func deallocate() {
34 | try! rustCall { ffi_matrix_sdk_common_rustbuffer_free(self, $0) }
35 | }
36 | }
37 |
38 | fileprivate extension ForeignBytes {
39 | init(bufferPointer: UnsafeBufferPointer) {
40 | self.init(len: Int32(bufferPointer.count), data: bufferPointer.baseAddress)
41 | }
42 | }
43 |
44 | // For every type used in the interface, we provide helper methods for conveniently
45 | // lifting and lowering that type from C-compatible data, and for reading and writing
46 | // values of that type in a buffer.
47 |
48 | // Helper classes/extensions that don't change.
49 | // Someday, this will be in a library of its own.
50 |
51 | fileprivate extension Data {
52 | init(rustBuffer: RustBuffer) {
53 | // TODO: This copies the buffer. Can we read directly from a
54 | // Rust buffer?
55 | self.init(bytes: rustBuffer.data!, count: Int(rustBuffer.len))
56 | }
57 | }
58 |
59 | // Define reader functionality. Normally this would be defined in a class or
60 | // struct, but we use standalone functions instead in order to make external
61 | // types work.
62 | //
63 | // With external types, one swift source file needs to be able to call the read
64 | // method on another source file's FfiConverter, but then what visibility
65 | // should Reader have?
66 | // - If Reader is fileprivate, then this means the read() must also
67 | // be fileprivate, which doesn't work with external types.
68 | // - If Reader is internal/public, we'll get compile errors since both source
69 | // files will try define the same type.
70 | //
71 | // Instead, the read() method and these helper functions input a tuple of data
72 |
73 | fileprivate func createReader(data: Data) -> (data: Data, offset: Data.Index) {
74 | (data: data, offset: 0)
75 | }
76 |
77 | // Reads an integer at the current offset, in big-endian order, and advances
78 | // the offset on success. Throws if reading the integer would move the
79 | // offset past the end of the buffer.
80 | fileprivate func readInt(_ reader: inout (data: Data, offset: Data.Index)) throws -> T {
81 | let range = reader.offset...size
82 | guard reader.data.count >= range.upperBound else {
83 | throw UniffiInternalError.bufferOverflow
84 | }
85 | if T.self == UInt8.self {
86 | let value = reader.data[reader.offset]
87 | reader.offset += 1
88 | return value as! T
89 | }
90 | var value: T = 0
91 | let _ = withUnsafeMutableBytes(of: &value, { reader.data.copyBytes(to: $0, from: range)})
92 | reader.offset = range.upperBound
93 | return value.bigEndian
94 | }
95 |
96 | // Reads an arbitrary number of bytes, to be used to read
97 | // raw bytes, this is useful when lifting strings
98 | fileprivate func readBytes(_ reader: inout (data: Data, offset: Data.Index), count: Int) throws -> Array {
99 | let range = reader.offset..<(reader.offset+count)
100 | guard reader.data.count >= range.upperBound else {
101 | throw UniffiInternalError.bufferOverflow
102 | }
103 | var value = [UInt8](repeating: 0, count: count)
104 | value.withUnsafeMutableBufferPointer({ buffer in
105 | reader.data.copyBytes(to: buffer, from: range)
106 | })
107 | reader.offset = range.upperBound
108 | return value
109 | }
110 |
111 | // Reads a float at the current offset.
112 | fileprivate func readFloat(_ reader: inout (data: Data, offset: Data.Index)) throws -> Float {
113 | return Float(bitPattern: try readInt(&reader))
114 | }
115 |
116 | // Reads a float at the current offset.
117 | fileprivate func readDouble(_ reader: inout (data: Data, offset: Data.Index)) throws -> Double {
118 | return Double(bitPattern: try readInt(&reader))
119 | }
120 |
121 | // Indicates if the offset has reached the end of the buffer.
122 | fileprivate func hasRemaining(_ reader: (data: Data, offset: Data.Index)) -> Bool {
123 | return reader.offset < reader.data.count
124 | }
125 |
126 | // Define writer functionality. Normally this would be defined in a class or
127 | // struct, but we use standalone functions instead in order to make external
128 | // types work. See the above discussion on Readers for details.
129 |
130 | fileprivate func createWriter() -> [UInt8] {
131 | return []
132 | }
133 |
134 | fileprivate func writeBytes(_ writer: inout [UInt8], _ byteArr: S) where S: Sequence, S.Element == UInt8 {
135 | writer.append(contentsOf: byteArr)
136 | }
137 |
138 | // Writes an integer in big-endian order.
139 | //
140 | // Warning: make sure what you are trying to write
141 | // is in the correct type!
142 | fileprivate func writeInt(_ writer: inout [UInt8], _ value: T) {
143 | var value = value.bigEndian
144 | withUnsafeBytes(of: &value) { writer.append(contentsOf: $0) }
145 | }
146 |
147 | fileprivate func writeFloat(_ writer: inout [UInt8], _ value: Float) {
148 | writeInt(&writer, value.bitPattern)
149 | }
150 |
151 | fileprivate func writeDouble(_ writer: inout [UInt8], _ value: Double) {
152 | writeInt(&writer, value.bitPattern)
153 | }
154 |
155 | // Protocol for types that transfer other types across the FFI. This is
156 | // analogous to the Rust trait of the same name.
157 | fileprivate protocol FfiConverter {
158 | associatedtype FfiType
159 | associatedtype SwiftType
160 |
161 | static func lift(_ value: FfiType) throws -> SwiftType
162 | static func lower(_ value: SwiftType) -> FfiType
163 | static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType
164 | static func write(_ value: SwiftType, into buf: inout [UInt8])
165 | }
166 |
167 | // Types conforming to `Primitive` pass themselves directly over the FFI.
168 | fileprivate protocol FfiConverterPrimitive: FfiConverter where FfiType == SwiftType { }
169 |
170 | extension FfiConverterPrimitive {
171 | public static func lift(_ value: FfiType) throws -> SwiftType {
172 | return value
173 | }
174 |
175 | public static func lower(_ value: SwiftType) -> FfiType {
176 | return value
177 | }
178 | }
179 |
180 | // Types conforming to `FfiConverterRustBuffer` lift and lower into a `RustBuffer`.
181 | // Used for complex types where it's hard to write a custom lift/lower.
182 | fileprivate protocol FfiConverterRustBuffer: FfiConverter where FfiType == RustBuffer {}
183 |
184 | extension FfiConverterRustBuffer {
185 | public static func lift(_ buf: RustBuffer) throws -> SwiftType {
186 | var reader = createReader(data: Data(rustBuffer: buf))
187 | let value = try read(from: &reader)
188 | if hasRemaining(reader) {
189 | throw UniffiInternalError.incompleteData
190 | }
191 | buf.deallocate()
192 | return value
193 | }
194 |
195 | public static func lower(_ value: SwiftType) -> RustBuffer {
196 | var writer = createWriter()
197 | write(value, into: &writer)
198 | return RustBuffer(bytes: writer)
199 | }
200 | }
201 | // An error type for FFI errors. These errors occur at the UniFFI level, not
202 | // the library level.
203 | fileprivate enum UniffiInternalError: LocalizedError {
204 | case bufferOverflow
205 | case incompleteData
206 | case unexpectedOptionalTag
207 | case unexpectedEnumCase
208 | case unexpectedNullPointer
209 | case unexpectedRustCallStatusCode
210 | case unexpectedRustCallError
211 | case unexpectedStaleHandle
212 | case rustPanic(_ message: String)
213 |
214 | public var errorDescription: String? {
215 | switch self {
216 | case .bufferOverflow: return "Reading the requested value would read past the end of the buffer"
217 | case .incompleteData: return "The buffer still has data after lifting its containing value"
218 | case .unexpectedOptionalTag: return "Unexpected optional tag; should be 0 or 1"
219 | case .unexpectedEnumCase: return "Raw enum value doesn't match any cases"
220 | case .unexpectedNullPointer: return "Raw pointer value was null"
221 | case .unexpectedRustCallStatusCode: return "Unexpected RustCallStatus code"
222 | case .unexpectedRustCallError: return "CALL_ERROR but no errorClass specified"
223 | case .unexpectedStaleHandle: return "The object in the handle map has been dropped already"
224 | case let .rustPanic(message): return message
225 | }
226 | }
227 | }
228 |
229 | fileprivate extension NSLock {
230 | func withLock(f: () throws -> T) rethrows -> T {
231 | self.lock()
232 | defer { self.unlock() }
233 | return try f()
234 | }
235 | }
236 |
237 | fileprivate let CALL_SUCCESS: Int8 = 0
238 | fileprivate let CALL_ERROR: Int8 = 1
239 | fileprivate let CALL_UNEXPECTED_ERROR: Int8 = 2
240 | fileprivate let CALL_CANCELLED: Int8 = 3
241 |
242 | fileprivate extension RustCallStatus {
243 | init() {
244 | self.init(
245 | code: CALL_SUCCESS,
246 | errorBuf: RustBuffer.init(
247 | capacity: 0,
248 | len: 0,
249 | data: nil
250 | )
251 | )
252 | }
253 | }
254 |
255 | private func rustCall(_ callback: (UnsafeMutablePointer) -> T) throws -> T {
256 | let neverThrow: ((RustBuffer) throws -> Never)? = nil
257 | return try makeRustCall(callback, errorHandler: neverThrow)
258 | }
259 |
260 | private func rustCallWithError(
261 | _ errorHandler: @escaping (RustBuffer) throws -> E,
262 | _ callback: (UnsafeMutablePointer) -> T) throws -> T {
263 | try makeRustCall(callback, errorHandler: errorHandler)
264 | }
265 |
266 | private func makeRustCall(
267 | _ callback: (UnsafeMutablePointer) -> T,
268 | errorHandler: ((RustBuffer) throws -> E)?
269 | ) throws -> T {
270 | uniffiEnsureInitialized()
271 | var callStatus = RustCallStatus.init()
272 | let returnedVal = callback(&callStatus)
273 | try uniffiCheckCallStatus(callStatus: callStatus, errorHandler: errorHandler)
274 | return returnedVal
275 | }
276 |
277 | private func uniffiCheckCallStatus(
278 | callStatus: RustCallStatus,
279 | errorHandler: ((RustBuffer) throws -> E)?
280 | ) throws {
281 | switch callStatus.code {
282 | case CALL_SUCCESS:
283 | return
284 |
285 | case CALL_ERROR:
286 | if let errorHandler = errorHandler {
287 | throw try errorHandler(callStatus.errorBuf)
288 | } else {
289 | callStatus.errorBuf.deallocate()
290 | throw UniffiInternalError.unexpectedRustCallError
291 | }
292 |
293 | case CALL_UNEXPECTED_ERROR:
294 | // When the rust code sees a panic, it tries to construct a RustBuffer
295 | // with the message. But if that code panics, then it just sends back
296 | // an empty buffer.
297 | if callStatus.errorBuf.len > 0 {
298 | throw UniffiInternalError.rustPanic(try FfiConverterString.lift(callStatus.errorBuf))
299 | } else {
300 | callStatus.errorBuf.deallocate()
301 | throw UniffiInternalError.rustPanic("Rust panic")
302 | }
303 |
304 | case CALL_CANCELLED:
305 | fatalError("Cancellation not supported yet")
306 |
307 | default:
308 | throw UniffiInternalError.unexpectedRustCallStatusCode
309 | }
310 | }
311 |
312 | private func uniffiTraitInterfaceCall(
313 | callStatus: UnsafeMutablePointer,
314 | makeCall: () throws -> T,
315 | writeReturn: (T) -> ()
316 | ) {
317 | do {
318 | try writeReturn(makeCall())
319 | } catch let error {
320 | callStatus.pointee.code = CALL_UNEXPECTED_ERROR
321 | callStatus.pointee.errorBuf = FfiConverterString.lower(String(describing: error))
322 | }
323 | }
324 |
325 | private func uniffiTraitInterfaceCallWithError(
326 | callStatus: UnsafeMutablePointer,
327 | makeCall: () throws -> T,
328 | writeReturn: (T) -> (),
329 | lowerError: (E) -> RustBuffer
330 | ) {
331 | do {
332 | try writeReturn(makeCall())
333 | } catch let error as E {
334 | callStatus.pointee.code = CALL_ERROR
335 | callStatus.pointee.errorBuf = lowerError(error)
336 | } catch {
337 | callStatus.pointee.code = CALL_UNEXPECTED_ERROR
338 | callStatus.pointee.errorBuf = FfiConverterString.lower(String(describing: error))
339 | }
340 | }
341 | fileprivate class UniffiHandleMap {
342 | private var map: [UInt64: T] = [:]
343 | private let lock = NSLock()
344 | private var currentHandle: UInt64 = 1
345 |
346 | func insert(obj: T) -> UInt64 {
347 | lock.withLock {
348 | let handle = currentHandle
349 | currentHandle += 1
350 | map[handle] = obj
351 | return handle
352 | }
353 | }
354 |
355 | func get(handle: UInt64) throws -> T {
356 | try lock.withLock {
357 | guard let obj = map[handle] else {
358 | throw UniffiInternalError.unexpectedStaleHandle
359 | }
360 | return obj
361 | }
362 | }
363 |
364 | @discardableResult
365 | func remove(handle: UInt64) throws -> T {
366 | try lock.withLock {
367 | guard let obj = map.removeValue(forKey: handle) else {
368 | throw UniffiInternalError.unexpectedStaleHandle
369 | }
370 | return obj
371 | }
372 | }
373 |
374 | var count: Int {
375 | get {
376 | map.count
377 | }
378 | }
379 | }
380 |
381 |
382 | // Public interface members begin here.
383 |
384 |
385 | fileprivate struct FfiConverterString: FfiConverter {
386 | typealias SwiftType = String
387 | typealias FfiType = RustBuffer
388 |
389 | public static func lift(_ value: RustBuffer) throws -> String {
390 | defer {
391 | value.deallocate()
392 | }
393 | if value.data == nil {
394 | return String()
395 | }
396 | let bytes = UnsafeBufferPointer(start: value.data!, count: Int(value.len))
397 | return String(bytes: bytes, encoding: String.Encoding.utf8)!
398 | }
399 |
400 | public static func lower(_ value: String) -> RustBuffer {
401 | return value.utf8CString.withUnsafeBufferPointer { ptr in
402 | // The swift string gives us int8_t, we want uint8_t.
403 | ptr.withMemoryRebound(to: UInt8.self) { ptr in
404 | // The swift string gives us a trailing null byte, we don't want it.
405 | let buf = UnsafeBufferPointer(rebasing: ptr.prefix(upTo: ptr.count - 1))
406 | return RustBuffer.from(buf)
407 | }
408 | }
409 | }
410 |
411 | public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> String {
412 | let len: Int32 = try readInt(&buf)
413 | return String(bytes: try readBytes(&buf, count: Int(len)), encoding: String.Encoding.utf8)!
414 | }
415 |
416 | public static func write(_ value: String, into buf: inout [UInt8]) {
417 | let len = Int32(value.utf8.count)
418 | writeInt(&buf, len)
419 | writeBytes(&buf, value.utf8)
420 | }
421 | }
422 |
423 | // Note that we don't yet support `indirect` for enums.
424 | // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion.
425 | /**
426 | * A machine-readable representation of the authenticity for a `ShieldState`.
427 | */
428 |
429 | public enum ShieldStateCode {
430 |
431 | /**
432 | * Not enough information available to check the authenticity.
433 | */
434 | case authenticityNotGuaranteed
435 | /**
436 | * The sending device isn't yet known by the Client.
437 | */
438 | case unknownDevice
439 | /**
440 | * The sending device hasn't been verified by the sender.
441 | */
442 | case unsignedDevice
443 | /**
444 | * The sender hasn't been verified by the Client's user.
445 | */
446 | case unverifiedIdentity
447 | /**
448 | * An unencrypted event in an encrypted room.
449 | */
450 | case sentInClear
451 | /**
452 | * The sender was previously verified but changed their identity.
453 | */
454 | case verificationViolation
455 | }
456 |
457 |
458 | public struct FfiConverterTypeShieldStateCode: FfiConverterRustBuffer {
459 | typealias SwiftType = ShieldStateCode
460 |
461 | public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ShieldStateCode {
462 | let variant: Int32 = try readInt(&buf)
463 | switch variant {
464 |
465 | case 1: return .authenticityNotGuaranteed
466 |
467 | case 2: return .unknownDevice
468 |
469 | case 3: return .unsignedDevice
470 |
471 | case 4: return .unverifiedIdentity
472 |
473 | case 5: return .sentInClear
474 |
475 | case 6: return .verificationViolation
476 |
477 | default: throw UniffiInternalError.unexpectedEnumCase
478 | }
479 | }
480 |
481 | public static func write(_ value: ShieldStateCode, into buf: inout [UInt8]) {
482 | switch value {
483 |
484 |
485 | case .authenticityNotGuaranteed:
486 | writeInt(&buf, Int32(1))
487 |
488 |
489 | case .unknownDevice:
490 | writeInt(&buf, Int32(2))
491 |
492 |
493 | case .unsignedDevice:
494 | writeInt(&buf, Int32(3))
495 |
496 |
497 | case .unverifiedIdentity:
498 | writeInt(&buf, Int32(4))
499 |
500 |
501 | case .sentInClear:
502 | writeInt(&buf, Int32(5))
503 |
504 |
505 | case .verificationViolation:
506 | writeInt(&buf, Int32(6))
507 |
508 | }
509 | }
510 | }
511 |
512 |
513 | public func FfiConverterTypeShieldStateCode_lift(_ buf: RustBuffer) throws -> ShieldStateCode {
514 | return try FfiConverterTypeShieldStateCode.lift(buf)
515 | }
516 |
517 | public func FfiConverterTypeShieldStateCode_lower(_ value: ShieldStateCode) -> RustBuffer {
518 | return FfiConverterTypeShieldStateCode.lower(value)
519 | }
520 |
521 |
522 |
523 | extension ShieldStateCode: Equatable, Hashable {}
524 |
525 |
526 |
527 | private enum InitializationResult {
528 | case ok
529 | case contractVersionMismatch
530 | case apiChecksumMismatch
531 | }
532 | // Use a global variable to perform the versioning checks. Swift ensures that
533 | // the code inside is only computed once.
534 | private var initializationResult: InitializationResult = {
535 | // Get the bindings contract version from our ComponentInterface
536 | let bindings_contract_version = 26
537 | // Get the scaffolding contract version by calling the into the dylib
538 | let scaffolding_contract_version = ffi_matrix_sdk_common_uniffi_contract_version()
539 | if bindings_contract_version != scaffolding_contract_version {
540 | return InitializationResult.contractVersionMismatch
541 | }
542 |
543 | return InitializationResult.ok
544 | }()
545 |
546 | private func uniffiEnsureInitialized() {
547 | switch initializationResult {
548 | case .ok:
549 | break
550 | case .contractVersionMismatch:
551 | fatalError("UniFFI contract version mismatch: try cleaning and rebuilding your project")
552 | case .apiChecksumMismatch:
553 | fatalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
554 | }
555 | }
556 |
557 | // swiftlint:enable all
--------------------------------------------------------------------------------
/Sources/MatrixRustSDK/matrix_sdk_crypto.swift:
--------------------------------------------------------------------------------
1 | // This file was autogenerated by some hot garbage in the `uniffi` crate.
2 | // Trust me, you don't want to mess with it!
3 |
4 | // swiftlint:disable all
5 | import Foundation
6 |
7 | // Depending on the consumer's build setup, the low-level FFI code
8 | // might be in a separate module, or it might be compiled inline into
9 | // this module. This is a bit of light hackery to work with both.
10 | #if canImport(matrix_sdk_cryptoFFI)
11 | import matrix_sdk_cryptoFFI
12 | #endif
13 |
14 | fileprivate extension RustBuffer {
15 | // Allocate a new buffer, copying the contents of a `UInt8` array.
16 | init(bytes: [UInt8]) {
17 | let rbuf = bytes.withUnsafeBufferPointer { ptr in
18 | RustBuffer.from(ptr)
19 | }
20 | self.init(capacity: rbuf.capacity, len: rbuf.len, data: rbuf.data)
21 | }
22 |
23 | static func empty() -> RustBuffer {
24 | RustBuffer(capacity: 0, len:0, data: nil)
25 | }
26 |
27 | static func from(_ ptr: UnsafeBufferPointer) -> RustBuffer {
28 | try! rustCall { ffi_matrix_sdk_crypto_rustbuffer_from_bytes(ForeignBytes(bufferPointer: ptr), $0) }
29 | }
30 |
31 | // Frees the buffer in place.
32 | // The buffer must not be used after this is called.
33 | func deallocate() {
34 | try! rustCall { ffi_matrix_sdk_crypto_rustbuffer_free(self, $0) }
35 | }
36 | }
37 |
38 | fileprivate extension ForeignBytes {
39 | init(bufferPointer: UnsafeBufferPointer) {
40 | self.init(len: Int32(bufferPointer.count), data: bufferPointer.baseAddress)
41 | }
42 | }
43 |
44 | // For every type used in the interface, we provide helper methods for conveniently
45 | // lifting and lowering that type from C-compatible data, and for reading and writing
46 | // values of that type in a buffer.
47 |
48 | // Helper classes/extensions that don't change.
49 | // Someday, this will be in a library of its own.
50 |
51 | fileprivate extension Data {
52 | init(rustBuffer: RustBuffer) {
53 | // TODO: This copies the buffer. Can we read directly from a
54 | // Rust buffer?
55 | self.init(bytes: rustBuffer.data!, count: Int(rustBuffer.len))
56 | }
57 | }
58 |
59 | // Define reader functionality. Normally this would be defined in a class or
60 | // struct, but we use standalone functions instead in order to make external
61 | // types work.
62 | //
63 | // With external types, one swift source file needs to be able to call the read
64 | // method on another source file's FfiConverter, but then what visibility
65 | // should Reader have?
66 | // - If Reader is fileprivate, then this means the read() must also
67 | // be fileprivate, which doesn't work with external types.
68 | // - If Reader is internal/public, we'll get compile errors since both source
69 | // files will try define the same type.
70 | //
71 | // Instead, the read() method and these helper functions input a tuple of data
72 |
73 | fileprivate func createReader(data: Data) -> (data: Data, offset: Data.Index) {
74 | (data: data, offset: 0)
75 | }
76 |
77 | // Reads an integer at the current offset, in big-endian order, and advances
78 | // the offset on success. Throws if reading the integer would move the
79 | // offset past the end of the buffer.
80 | fileprivate func readInt(_ reader: inout (data: Data, offset: Data.Index)) throws -> T {
81 | let range = reader.offset...size
82 | guard reader.data.count >= range.upperBound else {
83 | throw UniffiInternalError.bufferOverflow
84 | }
85 | if T.self == UInt8.self {
86 | let value = reader.data[reader.offset]
87 | reader.offset += 1
88 | return value as! T
89 | }
90 | var value: T = 0
91 | let _ = withUnsafeMutableBytes(of: &value, { reader.data.copyBytes(to: $0, from: range)})
92 | reader.offset = range.upperBound
93 | return value.bigEndian
94 | }
95 |
96 | // Reads an arbitrary number of bytes, to be used to read
97 | // raw bytes, this is useful when lifting strings
98 | fileprivate func readBytes(_ reader: inout (data: Data, offset: Data.Index), count: Int) throws -> Array {
99 | let range = reader.offset..<(reader.offset+count)
100 | guard reader.data.count >= range.upperBound else {
101 | throw UniffiInternalError.bufferOverflow
102 | }
103 | var value = [UInt8](repeating: 0, count: count)
104 | value.withUnsafeMutableBufferPointer({ buffer in
105 | reader.data.copyBytes(to: buffer, from: range)
106 | })
107 | reader.offset = range.upperBound
108 | return value
109 | }
110 |
111 | // Reads a float at the current offset.
112 | fileprivate func readFloat(_ reader: inout (data: Data, offset: Data.Index)) throws -> Float {
113 | return Float(bitPattern: try readInt(&reader))
114 | }
115 |
116 | // Reads a float at the current offset.
117 | fileprivate func readDouble(_ reader: inout (data: Data, offset: Data.Index)) throws -> Double {
118 | return Double(bitPattern: try readInt(&reader))
119 | }
120 |
121 | // Indicates if the offset has reached the end of the buffer.
122 | fileprivate func hasRemaining(_ reader: (data: Data, offset: Data.Index)) -> Bool {
123 | return reader.offset < reader.data.count
124 | }
125 |
126 | // Define writer functionality. Normally this would be defined in a class or
127 | // struct, but we use standalone functions instead in order to make external
128 | // types work. See the above discussion on Readers for details.
129 |
130 | fileprivate func createWriter() -> [UInt8] {
131 | return []
132 | }
133 |
134 | fileprivate func writeBytes(_ writer: inout [UInt8], _ byteArr: S) where S: Sequence, S.Element == UInt8 {
135 | writer.append(contentsOf: byteArr)
136 | }
137 |
138 | // Writes an integer in big-endian order.
139 | //
140 | // Warning: make sure what you are trying to write
141 | // is in the correct type!
142 | fileprivate func writeInt(_ writer: inout [UInt8], _ value: T) {
143 | var value = value.bigEndian
144 | withUnsafeBytes(of: &value) { writer.append(contentsOf: $0) }
145 | }
146 |
147 | fileprivate func writeFloat(_ writer: inout [UInt8], _ value: Float) {
148 | writeInt(&writer, value.bitPattern)
149 | }
150 |
151 | fileprivate func writeDouble(_ writer: inout [UInt8], _ value: Double) {
152 | writeInt(&writer, value.bitPattern)
153 | }
154 |
155 | // Protocol for types that transfer other types across the FFI. This is
156 | // analogous to the Rust trait of the same name.
157 | fileprivate protocol FfiConverter {
158 | associatedtype FfiType
159 | associatedtype SwiftType
160 |
161 | static func lift(_ value: FfiType) throws -> SwiftType
162 | static func lower(_ value: SwiftType) -> FfiType
163 | static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType
164 | static func write(_ value: SwiftType, into buf: inout [UInt8])
165 | }
166 |
167 | // Types conforming to `Primitive` pass themselves directly over the FFI.
168 | fileprivate protocol FfiConverterPrimitive: FfiConverter where FfiType == SwiftType { }
169 |
170 | extension FfiConverterPrimitive {
171 | public static func lift(_ value: FfiType) throws -> SwiftType {
172 | return value
173 | }
174 |
175 | public static func lower(_ value: SwiftType) -> FfiType {
176 | return value
177 | }
178 | }
179 |
180 | // Types conforming to `FfiConverterRustBuffer` lift and lower into a `RustBuffer`.
181 | // Used for complex types where it's hard to write a custom lift/lower.
182 | fileprivate protocol FfiConverterRustBuffer: FfiConverter where FfiType == RustBuffer {}
183 |
184 | extension FfiConverterRustBuffer {
185 | public static func lift(_ buf: RustBuffer) throws -> SwiftType {
186 | var reader = createReader(data: Data(rustBuffer: buf))
187 | let value = try read(from: &reader)
188 | if hasRemaining(reader) {
189 | throw UniffiInternalError.incompleteData
190 | }
191 | buf.deallocate()
192 | return value
193 | }
194 |
195 | public static func lower(_ value: SwiftType) -> RustBuffer {
196 | var writer = createWriter()
197 | write(value, into: &writer)
198 | return RustBuffer(bytes: writer)
199 | }
200 | }
201 | // An error type for FFI errors. These errors occur at the UniFFI level, not
202 | // the library level.
203 | fileprivate enum UniffiInternalError: LocalizedError {
204 | case bufferOverflow
205 | case incompleteData
206 | case unexpectedOptionalTag
207 | case unexpectedEnumCase
208 | case unexpectedNullPointer
209 | case unexpectedRustCallStatusCode
210 | case unexpectedRustCallError
211 | case unexpectedStaleHandle
212 | case rustPanic(_ message: String)
213 |
214 | public var errorDescription: String? {
215 | switch self {
216 | case .bufferOverflow: return "Reading the requested value would read past the end of the buffer"
217 | case .incompleteData: return "The buffer still has data after lifting its containing value"
218 | case .unexpectedOptionalTag: return "Unexpected optional tag; should be 0 or 1"
219 | case .unexpectedEnumCase: return "Raw enum value doesn't match any cases"
220 | case .unexpectedNullPointer: return "Raw pointer value was null"
221 | case .unexpectedRustCallStatusCode: return "Unexpected RustCallStatus code"
222 | case .unexpectedRustCallError: return "CALL_ERROR but no errorClass specified"
223 | case .unexpectedStaleHandle: return "The object in the handle map has been dropped already"
224 | case let .rustPanic(message): return message
225 | }
226 | }
227 | }
228 |
229 | fileprivate extension NSLock {
230 | func withLock(f: () throws -> T) rethrows -> T {
231 | self.lock()
232 | defer { self.unlock() }
233 | return try f()
234 | }
235 | }
236 |
237 | fileprivate let CALL_SUCCESS: Int8 = 0
238 | fileprivate let CALL_ERROR: Int8 = 1
239 | fileprivate let CALL_UNEXPECTED_ERROR: Int8 = 2
240 | fileprivate let CALL_CANCELLED: Int8 = 3
241 |
242 | fileprivate extension RustCallStatus {
243 | init() {
244 | self.init(
245 | code: CALL_SUCCESS,
246 | errorBuf: RustBuffer.init(
247 | capacity: 0,
248 | len: 0,
249 | data: nil
250 | )
251 | )
252 | }
253 | }
254 |
255 | private func rustCall(_ callback: (UnsafeMutablePointer) -> T) throws -> T {
256 | let neverThrow: ((RustBuffer) throws -> Never)? = nil
257 | return try makeRustCall(callback, errorHandler: neverThrow)
258 | }
259 |
260 | private func rustCallWithError(
261 | _ errorHandler: @escaping (RustBuffer) throws -> E,
262 | _ callback: (UnsafeMutablePointer) -> T) throws -> T {
263 | try makeRustCall(callback, errorHandler: errorHandler)
264 | }
265 |
266 | private func makeRustCall(
267 | _ callback: (UnsafeMutablePointer) -> T,
268 | errorHandler: ((RustBuffer) throws -> E)?
269 | ) throws -> T {
270 | uniffiEnsureInitialized()
271 | var callStatus = RustCallStatus.init()
272 | let returnedVal = callback(&callStatus)
273 | try uniffiCheckCallStatus(callStatus: callStatus, errorHandler: errorHandler)
274 | return returnedVal
275 | }
276 |
277 | private func uniffiCheckCallStatus(
278 | callStatus: RustCallStatus,
279 | errorHandler: ((RustBuffer) throws -> E)?
280 | ) throws {
281 | switch callStatus.code {
282 | case CALL_SUCCESS:
283 | return
284 |
285 | case CALL_ERROR:
286 | if let errorHandler = errorHandler {
287 | throw try errorHandler(callStatus.errorBuf)
288 | } else {
289 | callStatus.errorBuf.deallocate()
290 | throw UniffiInternalError.unexpectedRustCallError
291 | }
292 |
293 | case CALL_UNEXPECTED_ERROR:
294 | // When the rust code sees a panic, it tries to construct a RustBuffer
295 | // with the message. But if that code panics, then it just sends back
296 | // an empty buffer.
297 | if callStatus.errorBuf.len > 0 {
298 | throw UniffiInternalError.rustPanic(try FfiConverterString.lift(callStatus.errorBuf))
299 | } else {
300 | callStatus.errorBuf.deallocate()
301 | throw UniffiInternalError.rustPanic("Rust panic")
302 | }
303 |
304 | case CALL_CANCELLED:
305 | fatalError("Cancellation not supported yet")
306 |
307 | default:
308 | throw UniffiInternalError.unexpectedRustCallStatusCode
309 | }
310 | }
311 |
312 | private func uniffiTraitInterfaceCall(
313 | callStatus: UnsafeMutablePointer,
314 | makeCall: () throws -> T,
315 | writeReturn: (T) -> ()
316 | ) {
317 | do {
318 | try writeReturn(makeCall())
319 | } catch let error {
320 | callStatus.pointee.code = CALL_UNEXPECTED_ERROR
321 | callStatus.pointee.errorBuf = FfiConverterString.lower(String(describing: error))
322 | }
323 | }
324 |
325 | private func uniffiTraitInterfaceCallWithError(
326 | callStatus: UnsafeMutablePointer,
327 | makeCall: () throws -> T,
328 | writeReturn: (T) -> (),
329 | lowerError: (E) -> RustBuffer
330 | ) {
331 | do {
332 | try writeReturn(makeCall())
333 | } catch let error as E {
334 | callStatus.pointee.code = CALL_ERROR
335 | callStatus.pointee.errorBuf = lowerError(error)
336 | } catch {
337 | callStatus.pointee.code = CALL_UNEXPECTED_ERROR
338 | callStatus.pointee.errorBuf = FfiConverterString.lower(String(describing: error))
339 | }
340 | }
341 | fileprivate class UniffiHandleMap {
342 | private var map: [UInt64: T] = [:]
343 | private let lock = NSLock()
344 | private var currentHandle: UInt64 = 1
345 |
346 | func insert(obj: T) -> UInt64 {
347 | lock.withLock {
348 | let handle = currentHandle
349 | currentHandle += 1
350 | map[handle] = obj
351 | return handle
352 | }
353 | }
354 |
355 | func get(handle: UInt64) throws -> T {
356 | try lock.withLock {
357 | guard let obj = map[handle] else {
358 | throw UniffiInternalError.unexpectedStaleHandle
359 | }
360 | return obj
361 | }
362 | }
363 |
364 | @discardableResult
365 | func remove(handle: UInt64) throws -> T {
366 | try lock.withLock {
367 | guard let obj = map.removeValue(forKey: handle) else {
368 | throw UniffiInternalError.unexpectedStaleHandle
369 | }
370 | return obj
371 | }
372 | }
373 |
374 | var count: Int {
375 | get {
376 | map.count
377 | }
378 | }
379 | }
380 |
381 |
382 | // Public interface members begin here.
383 |
384 |
385 | fileprivate struct FfiConverterString: FfiConverter {
386 | typealias SwiftType = String
387 | typealias FfiType = RustBuffer
388 |
389 | public static func lift(_ value: RustBuffer) throws -> String {
390 | defer {
391 | value.deallocate()
392 | }
393 | if value.data == nil {
394 | return String()
395 | }
396 | let bytes = UnsafeBufferPointer(start: value.data!, count: Int(value.len))
397 | return String(bytes: bytes, encoding: String.Encoding.utf8)!
398 | }
399 |
400 | public static func lower(_ value: String) -> RustBuffer {
401 | return value.utf8CString.withUnsafeBufferPointer { ptr in
402 | // The swift string gives us int8_t, we want uint8_t.
403 | ptr.withMemoryRebound(to: UInt8.self) { ptr in
404 | // The swift string gives us a trailing null byte, we don't want it.
405 | let buf = UnsafeBufferPointer(rebasing: ptr.prefix(upTo: ptr.count - 1))
406 | return RustBuffer.from(buf)
407 | }
408 | }
409 | }
410 |
411 | public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> String {
412 | let len: Int32 = try readInt(&buf)
413 | return String(bytes: try readBytes(&buf, count: Int(len)), encoding: String.Encoding.utf8)!
414 | }
415 |
416 | public static func write(_ value: String, into buf: inout [UInt8]) {
417 | let len = Int32(value.utf8.count)
418 | writeInt(&buf, len)
419 | writeBytes(&buf, value.utf8)
420 | }
421 | }
422 |
423 |
424 | /**
425 | * Settings for decrypting messages
426 | */
427 | public struct DecryptionSettings {
428 | /**
429 | * The trust level in the sender's device that is required to decrypt the
430 | * event. If the sender's device is not sufficiently trusted,
431 | * [`MegolmError::SenderIdentityNotTrusted`] will be returned.
432 | */
433 | public var senderDeviceTrustRequirement: TrustRequirement
434 |
435 | // Default memberwise initializers are never public by default, so we
436 | // declare one manually.
437 | public init(
438 | /**
439 | * The trust level in the sender's device that is required to decrypt the
440 | * event. If the sender's device is not sufficiently trusted,
441 | * [`MegolmError::SenderIdentityNotTrusted`] will be returned.
442 | */senderDeviceTrustRequirement: TrustRequirement) {
443 | self.senderDeviceTrustRequirement = senderDeviceTrustRequirement
444 | }
445 | }
446 |
447 |
448 |
449 | extension DecryptionSettings: Equatable, Hashable {
450 | public static func ==(lhs: DecryptionSettings, rhs: DecryptionSettings) -> Bool {
451 | if lhs.senderDeviceTrustRequirement != rhs.senderDeviceTrustRequirement {
452 | return false
453 | }
454 | return true
455 | }
456 |
457 | public func hash(into hasher: inout Hasher) {
458 | hasher.combine(senderDeviceTrustRequirement)
459 | }
460 | }
461 |
462 |
463 | public struct FfiConverterTypeDecryptionSettings: FfiConverterRustBuffer {
464 | public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> DecryptionSettings {
465 | return
466 | try DecryptionSettings(
467 | senderDeviceTrustRequirement: FfiConverterTypeTrustRequirement.read(from: &buf)
468 | )
469 | }
470 |
471 | public static func write(_ value: DecryptionSettings, into buf: inout [UInt8]) {
472 | FfiConverterTypeTrustRequirement.write(value.senderDeviceTrustRequirement, into: &buf)
473 | }
474 | }
475 |
476 |
477 | public func FfiConverterTypeDecryptionSettings_lift(_ buf: RustBuffer) throws -> DecryptionSettings {
478 | return try FfiConverterTypeDecryptionSettings.lift(buf)
479 | }
480 |
481 | public func FfiConverterTypeDecryptionSettings_lower(_ value: DecryptionSettings) -> RustBuffer {
482 | return FfiConverterTypeDecryptionSettings.lower(value)
483 | }
484 |
485 | // Note that we don't yet support `indirect` for enums.
486 | // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion.
487 | /**
488 | * Strategy to collect the devices that should receive room keys for the
489 | * current discussion.
490 | */
491 |
492 | public enum CollectStrategy {
493 |
494 | /**
495 | * Share with all (unblacklisted) devices.
496 | */
497 | case allDevices
498 | /**
499 | * Share with all devices, except errors for *verified* users cause sharing
500 | * to fail with an error.
501 | *
502 | * In this strategy, if a verified user has an unsigned device,
503 | * key sharing will fail with a
504 | * [`SessionRecipientCollectionError::VerifiedUserHasUnsignedDevice`].
505 | * If a verified user has replaced their identity, key
506 | * sharing will fail with a
507 | * [`SessionRecipientCollectionError::VerifiedUserChangedIdentity`].
508 | *
509 | * Otherwise, keys are shared with unsigned devices as normal.
510 | *
511 | * Once the problematic devices are blacklisted or whitelisted the
512 | * caller can retry to share a second time.
513 | */
514 | case errorOnVerifiedUserProblem
515 | /**
516 | * Share based on identity. Only distribute to devices signed by their
517 | * owner. If a user has no published identity he will not receive
518 | * any room keys.
519 | */
520 | case identityBasedStrategy
521 | /**
522 | * Only share keys with devices that we "trust". A device is trusted if any
523 | * of the following is true:
524 | * - It was manually marked as trusted.
525 | * - It was marked as verified via interactive verification.
526 | * - It is signed by its owner identity, and this identity has been
527 | * trusted via interactive verification.
528 | * - It is the current own device of the user.
529 | */
530 | case onlyTrustedDevices
531 | }
532 |
533 |
534 | public struct FfiConverterTypeCollectStrategy: FfiConverterRustBuffer {
535 | typealias SwiftType = CollectStrategy
536 |
537 | public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> CollectStrategy {
538 | let variant: Int32 = try readInt(&buf)
539 | switch variant {
540 |
541 | case 1: return .allDevices
542 |
543 | case 2: return .errorOnVerifiedUserProblem
544 |
545 | case 3: return .identityBasedStrategy
546 |
547 | case 4: return .onlyTrustedDevices
548 |
549 | default: throw UniffiInternalError.unexpectedEnumCase
550 | }
551 | }
552 |
553 | public static func write(_ value: CollectStrategy, into buf: inout [UInt8]) {
554 | switch value {
555 |
556 |
557 | case .allDevices:
558 | writeInt(&buf, Int32(1))
559 |
560 |
561 | case .errorOnVerifiedUserProblem:
562 | writeInt(&buf, Int32(2))
563 |
564 |
565 | case .identityBasedStrategy:
566 | writeInt(&buf, Int32(3))
567 |
568 |
569 | case .onlyTrustedDevices:
570 | writeInt(&buf, Int32(4))
571 |
572 | }
573 | }
574 | }
575 |
576 |
577 | public func FfiConverterTypeCollectStrategy_lift(_ buf: RustBuffer) throws -> CollectStrategy {
578 | return try FfiConverterTypeCollectStrategy.lift(buf)
579 | }
580 |
581 | public func FfiConverterTypeCollectStrategy_lower(_ value: CollectStrategy) -> RustBuffer {
582 | return FfiConverterTypeCollectStrategy.lower(value)
583 | }
584 |
585 |
586 |
587 | extension CollectStrategy: Equatable, Hashable {}
588 |
589 |
590 |
591 | // Note that we don't yet support `indirect` for enums.
592 | // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion.
593 | /**
594 | * The state of an identity - verified, pinned etc.
595 | */
596 |
597 | public enum IdentityState {
598 |
599 | /**
600 | * The user is verified with us
601 | */
602 | case verified
603 | /**
604 | * Either this is the first identity we have seen for this user, or the
605 | * user has acknowledged a change of identity explicitly e.g. by
606 | * clicking OK on a notification.
607 | */
608 | case pinned
609 | /**
610 | * The user's identity has changed since it was pinned. The user should be
611 | * notified about this and given the opportunity to acknowledge the
612 | * change, which will make the new identity pinned.
613 | * When the user acknowledges the change, the app should call
614 | * [`crate::OtherUserIdentity::pin_current_master_key`].
615 | */
616 | case pinViolation
617 | /**
618 | * The user's identity has changed, and before that it was verified. This
619 | * is a serious problem. The user can either verify again to make this
620 | * identity verified, or withdraw verification
621 | * [`UserIdentity::withdraw_verification`] to make it pinned.
622 | */
623 | case verificationViolation
624 | }
625 |
626 |
627 | public struct FfiConverterTypeIdentityState: FfiConverterRustBuffer {
628 | typealias SwiftType = IdentityState
629 |
630 | public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IdentityState {
631 | let variant: Int32 = try readInt(&buf)
632 | switch variant {
633 |
634 | case 1: return .verified
635 |
636 | case 2: return .pinned
637 |
638 | case 3: return .pinViolation
639 |
640 | case 4: return .verificationViolation
641 |
642 | default: throw UniffiInternalError.unexpectedEnumCase
643 | }
644 | }
645 |
646 | public static func write(_ value: IdentityState, into buf: inout [UInt8]) {
647 | switch value {
648 |
649 |
650 | case .verified:
651 | writeInt(&buf, Int32(1))
652 |
653 |
654 | case .pinned:
655 | writeInt(&buf, Int32(2))
656 |
657 |
658 | case .pinViolation:
659 | writeInt(&buf, Int32(3))
660 |
661 |
662 | case .verificationViolation:
663 | writeInt(&buf, Int32(4))
664 |
665 | }
666 | }
667 | }
668 |
669 |
670 | public func FfiConverterTypeIdentityState_lift(_ buf: RustBuffer) throws -> IdentityState {
671 | return try FfiConverterTypeIdentityState.lift(buf)
672 | }
673 |
674 | public func FfiConverterTypeIdentityState_lower(_ value: IdentityState) -> RustBuffer {
675 | return FfiConverterTypeIdentityState.lower(value)
676 | }
677 |
678 |
679 |
680 | extension IdentityState: Equatable, Hashable {}
681 |
682 |
683 |
684 | // Note that we don't yet support `indirect` for enums.
685 | // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion.
686 | /**
687 | * The local trust state of a device.
688 | */
689 |
690 | public enum LocalTrust {
691 |
692 | /**
693 | * The device has been verified and is trusted.
694 | */
695 | case verified
696 | /**
697 | * The device been blacklisted from communicating.
698 | */
699 | case blackListed
700 | /**
701 | * The trust state of the device is being ignored.
702 | */
703 | case ignored
704 | /**
705 | * The trust state is unset.
706 | */
707 | case unset
708 | }
709 |
710 |
711 | public struct FfiConverterTypeLocalTrust: FfiConverterRustBuffer {
712 | typealias SwiftType = LocalTrust
713 |
714 | public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> LocalTrust {
715 | let variant: Int32 = try readInt(&buf)
716 | switch variant {
717 |
718 | case 1: return .verified
719 |
720 | case 2: return .blackListed
721 |
722 | case 3: return .ignored
723 |
724 | case 4: return .unset
725 |
726 | default: throw UniffiInternalError.unexpectedEnumCase
727 | }
728 | }
729 |
730 | public static func write(_ value: LocalTrust, into buf: inout [UInt8]) {
731 | switch value {
732 |
733 |
734 | case .verified:
735 | writeInt(&buf, Int32(1))
736 |
737 |
738 | case .blackListed:
739 | writeInt(&buf, Int32(2))
740 |
741 |
742 | case .ignored:
743 | writeInt(&buf, Int32(3))
744 |
745 |
746 | case .unset:
747 | writeInt(&buf, Int32(4))
748 |
749 | }
750 | }
751 | }
752 |
753 |
754 | public func FfiConverterTypeLocalTrust_lift(_ buf: RustBuffer) throws -> LocalTrust {
755 | return try FfiConverterTypeLocalTrust.lift(buf)
756 | }
757 |
758 | public func FfiConverterTypeLocalTrust_lower(_ value: LocalTrust) -> RustBuffer {
759 | return FfiConverterTypeLocalTrust.lower(value)
760 | }
761 |
762 |
763 |
764 | extension LocalTrust: Equatable, Hashable {}
765 |
766 |
767 |
768 |
769 | /**
770 | * Error type for the decoding of the [`QrCodeData`].
771 | */
772 | public enum LoginQrCodeDecodeError {
773 |
774 |
775 |
776 | /**
777 | * The QR code data is no long enough, it's missing some fields.
778 | */
779 | case NotEnoughData(message: String)
780 |
781 | /**
782 | * One of the URLs in the QR code data is not a valid UTF-8 encoded string.
783 | */
784 | case NotUtf8(message: String)
785 |
786 | /**
787 | * One of the URLs in the QR code data could not be parsed.
788 | */
789 | case UrlParse(message: String)
790 |
791 | /**
792 | * The QR code data contains an invalid mode, we expect the login (0x03)
793 | * mode or the reciprocate mode (0x04).
794 | */
795 | case InvalidMode(message: String)
796 |
797 | /**
798 | * The QR code data contains an unsupported version.
799 | */
800 | case InvalidVersion(message: String)
801 |
802 | /**
803 | * The base64 encoded variant of the QR code data is not a valid base64
804 | * string.
805 | */
806 | case Base64(message: String)
807 |
808 | /**
809 | * The QR code data doesn't contain the expected `MATRIX` prefix.
810 | */
811 | case InvalidPrefix(message: String)
812 |
813 | }
814 |
815 |
816 | public struct FfiConverterTypeLoginQrCodeDecodeError: FfiConverterRustBuffer {
817 | typealias SwiftType = LoginQrCodeDecodeError
818 |
819 | public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> LoginQrCodeDecodeError {
820 | let variant: Int32 = try readInt(&buf)
821 | switch variant {
822 |
823 |
824 |
825 |
826 | case 1: return .NotEnoughData(
827 | message: try FfiConverterString.read(from: &buf)
828 | )
829 |
830 | case 2: return .NotUtf8(
831 | message: try FfiConverterString.read(from: &buf)
832 | )
833 |
834 | case 3: return .UrlParse(
835 | message: try FfiConverterString.read(from: &buf)
836 | )
837 |
838 | case 4: return .InvalidMode(
839 | message: try FfiConverterString.read(from: &buf)
840 | )
841 |
842 | case 5: return .InvalidVersion(
843 | message: try FfiConverterString.read(from: &buf)
844 | )
845 |
846 | case 6: return .Base64(
847 | message: try FfiConverterString.read(from: &buf)
848 | )
849 |
850 | case 7: return .InvalidPrefix(
851 | message: try FfiConverterString.read(from: &buf)
852 | )
853 |
854 |
855 | default: throw UniffiInternalError.unexpectedEnumCase
856 | }
857 | }
858 |
859 | public static func write(_ value: LoginQrCodeDecodeError, into buf: inout [UInt8]) {
860 | switch value {
861 |
862 |
863 |
864 |
865 | case .NotEnoughData(_ /* message is ignored*/):
866 | writeInt(&buf, Int32(1))
867 | case .NotUtf8(_ /* message is ignored*/):
868 | writeInt(&buf, Int32(2))
869 | case .UrlParse(_ /* message is ignored*/):
870 | writeInt(&buf, Int32(3))
871 | case .InvalidMode(_ /* message is ignored*/):
872 | writeInt(&buf, Int32(4))
873 | case .InvalidVersion(_ /* message is ignored*/):
874 | writeInt(&buf, Int32(5))
875 | case .Base64(_ /* message is ignored*/):
876 | writeInt(&buf, Int32(6))
877 | case .InvalidPrefix(_ /* message is ignored*/):
878 | writeInt(&buf, Int32(7))
879 |
880 |
881 | }
882 | }
883 | }
884 |
885 |
886 | extension LoginQrCodeDecodeError: Equatable, Hashable {}
887 |
888 | extension LoginQrCodeDecodeError: Foundation.LocalizedError {
889 | public var errorDescription: String? {
890 | String(reflecting: self)
891 | }
892 | }
893 |
894 | // Note that we don't yet support `indirect` for enums.
895 | // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion.
896 | /**
897 | * The mode-specific data for the QR code.
898 | *
899 | * The QR code login mechanism supports both, the new device, as well as the
900 | * existing device to display the QR code.
901 | *
902 | * Depending on which device is displaying the QR code, additional data will be
903 | * attached to the QR code.
904 | */
905 |
906 | public enum QrCodeModeData {
907 |
908 | /**
909 | * Enum variant for the case where the new device is displaying the QR
910 | * code.
911 | */
912 | case login
913 | /**
914 | * Enum variant for the case where the existing device is displaying the QR
915 | * code.
916 | */
917 | case reciprocate(
918 | /**
919 | * The homeserver the existing device is using. This will let the new
920 | * device know which homeserver it should use as well.
921 | */serverName: String
922 | )
923 | }
924 |
925 |
926 | public struct FfiConverterTypeQrCodeModeData: FfiConverterRustBuffer {
927 | typealias SwiftType = QrCodeModeData
928 |
929 | public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> QrCodeModeData {
930 | let variant: Int32 = try readInt(&buf)
931 | switch variant {
932 |
933 | case 1: return .login
934 |
935 | case 2: return .reciprocate(serverName: try FfiConverterString.read(from: &buf)
936 | )
937 |
938 | default: throw UniffiInternalError.unexpectedEnumCase
939 | }
940 | }
941 |
942 | public static func write(_ value: QrCodeModeData, into buf: inout [UInt8]) {
943 | switch value {
944 |
945 |
946 | case .login:
947 | writeInt(&buf, Int32(1))
948 |
949 |
950 | case let .reciprocate(serverName):
951 | writeInt(&buf, Int32(2))
952 | FfiConverterString.write(serverName, into: &buf)
953 |
954 | }
955 | }
956 | }
957 |
958 |
959 | public func FfiConverterTypeQrCodeModeData_lift(_ buf: RustBuffer) throws -> QrCodeModeData {
960 | return try FfiConverterTypeQrCodeModeData.lift(buf)
961 | }
962 |
963 | public func FfiConverterTypeQrCodeModeData_lower(_ value: QrCodeModeData) -> RustBuffer {
964 | return FfiConverterTypeQrCodeModeData.lower(value)
965 | }
966 |
967 |
968 |
969 | extension QrCodeModeData: Equatable, Hashable {}
970 |
971 |
972 |
973 | // Note that we don't yet support `indirect` for enums.
974 | // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion.
975 | /**
976 | * The result of a signature check.
977 | */
978 |
979 | public enum SignatureState {
980 |
981 | /**
982 | * The signature is missing.
983 | */
984 | case missing
985 | /**
986 | * The signature is invalid.
987 | */
988 | case invalid
989 | /**
990 | * The signature is valid but the device or user identity that created the
991 | * signature is not trusted.
992 | */
993 | case validButNotTrusted
994 | /**
995 | * The signature is valid and the device or user identity that created the
996 | * signature is trusted.
997 | */
998 | case validAndTrusted
999 | }
1000 |
1001 |
1002 | public struct FfiConverterTypeSignatureState: FfiConverterRustBuffer {
1003 | typealias SwiftType = SignatureState
1004 |
1005 | public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SignatureState {
1006 | let variant: Int32 = try readInt(&buf)
1007 | switch variant {
1008 |
1009 | case 1: return .missing
1010 |
1011 | case 2: return .invalid
1012 |
1013 | case 3: return .validButNotTrusted
1014 |
1015 | case 4: return .validAndTrusted
1016 |
1017 | default: throw UniffiInternalError.unexpectedEnumCase
1018 | }
1019 | }
1020 |
1021 | public static func write(_ value: SignatureState, into buf: inout [UInt8]) {
1022 | switch value {
1023 |
1024 |
1025 | case .missing:
1026 | writeInt(&buf, Int32(1))
1027 |
1028 |
1029 | case .invalid:
1030 | writeInt(&buf, Int32(2))
1031 |
1032 |
1033 | case .validButNotTrusted:
1034 | writeInt(&buf, Int32(3))
1035 |
1036 |
1037 | case .validAndTrusted:
1038 | writeInt(&buf, Int32(4))
1039 |
1040 | }
1041 | }
1042 | }
1043 |
1044 |
1045 | public func FfiConverterTypeSignatureState_lift(_ buf: RustBuffer) throws -> SignatureState {
1046 | return try FfiConverterTypeSignatureState.lift(buf)
1047 | }
1048 |
1049 | public func FfiConverterTypeSignatureState_lower(_ value: SignatureState) -> RustBuffer {
1050 | return FfiConverterTypeSignatureState.lower(value)
1051 | }
1052 |
1053 |
1054 |
1055 | extension SignatureState: Equatable, Hashable {}
1056 |
1057 |
1058 |
1059 | // Note that we don't yet support `indirect` for enums.
1060 | // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion.
1061 | /**
1062 | * The trust level in the sender's device that is required to decrypt an
1063 | * event.
1064 | */
1065 |
1066 | public enum TrustRequirement {
1067 |
1068 | /**
1069 | * Decrypt events from everyone regardless of trust.
1070 | */
1071 | case untrusted
1072 | /**
1073 | * Only decrypt events from cross-signed devices or legacy sessions (Megolm
1074 | * sessions created before we started collecting trust information).
1075 | */
1076 | case crossSignedOrLegacy
1077 | /**
1078 | * Only decrypt events from cross-signed devices.
1079 | */
1080 | case crossSigned
1081 | }
1082 |
1083 |
1084 | public struct FfiConverterTypeTrustRequirement: FfiConverterRustBuffer {
1085 | typealias SwiftType = TrustRequirement
1086 |
1087 | public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrustRequirement {
1088 | let variant: Int32 = try readInt(&buf)
1089 | switch variant {
1090 |
1091 | case 1: return .untrusted
1092 |
1093 | case 2: return .crossSignedOrLegacy
1094 |
1095 | case 3: return .crossSigned
1096 |
1097 | default: throw UniffiInternalError.unexpectedEnumCase
1098 | }
1099 | }
1100 |
1101 | public static func write(_ value: TrustRequirement, into buf: inout [UInt8]) {
1102 | switch value {
1103 |
1104 |
1105 | case .untrusted:
1106 | writeInt(&buf, Int32(1))
1107 |
1108 |
1109 | case .crossSignedOrLegacy:
1110 | writeInt(&buf, Int32(2))
1111 |
1112 |
1113 | case .crossSigned:
1114 | writeInt(&buf, Int32(3))
1115 |
1116 | }
1117 | }
1118 | }
1119 |
1120 |
1121 | public func FfiConverterTypeTrustRequirement_lift(_ buf: RustBuffer) throws -> TrustRequirement {
1122 | return try FfiConverterTypeTrustRequirement.lift(buf)
1123 | }
1124 |
1125 | public func FfiConverterTypeTrustRequirement_lower(_ value: TrustRequirement) -> RustBuffer {
1126 | return FfiConverterTypeTrustRequirement.lower(value)
1127 | }
1128 |
1129 |
1130 |
1131 | extension TrustRequirement: Equatable, Hashable {}
1132 |
1133 |
1134 |
1135 | // Note that we don't yet support `indirect` for enums.
1136 | // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion.
1137 | /**
1138 | * Our best guess at the reason why an event can't be decrypted.
1139 | */
1140 |
1141 | public enum UtdCause {
1142 |
1143 | /**
1144 | * We don't have an explanation for why this UTD happened - it is probably
1145 | * a bug, or a network split between the two homeservers.
1146 | *
1147 | * For example:
1148 | *
1149 | * - the keys for this event are missing, but a key storage backup exists
1150 | * and is working, so we should be able to find the keys in the backup.
1151 | *
1152 | * - the keys for this event are missing, and a key storage backup exists
1153 | * on the server, but that backup is not working on this client even
1154 | * though this device is verified.
1155 | */
1156 | case unknown
1157 | /**
1158 | * We are missing the keys for this event, and the event was sent when we
1159 | * were not a member of the room (or invited).
1160 | */
1161 | case sentBeforeWeJoined
1162 | /**
1163 | * The message was sent by a user identity we have not verified, but the
1164 | * user was previously verified.
1165 | */
1166 | case verificationViolation
1167 | /**
1168 | * The [`crate::TrustRequirement`] requires that the sending device be
1169 | * signed by its owner, and it was not.
1170 | */
1171 | case unsignedDevice
1172 | /**
1173 | * The [`crate::TrustRequirement`] requires that the sending device be
1174 | * signed by its owner, and we were unable to securely find the device.
1175 | *
1176 | * This could be because the device has since been deleted, because we
1177 | * haven't yet downloaded it from the server, or because the session
1178 | * data was obtained from an insecure source (imported from a file,
1179 | * obtained from a legacy (asymmetric) backup, unsafe key forward, etc.)
1180 | */
1181 | case unknownDevice
1182 | /**
1183 | * We are missing the keys for this event, but it is a "device-historical"
1184 | * message and there is no key storage backup on the server, presumably
1185 | * because the user has turned it off.
1186 | *
1187 | * Device-historical means that the message was sent before the current
1188 | * device existed (but the current user was probably a member of the room
1189 | * at the time the message was sent). Not to
1190 | * be confused with pre-join or pre-invite messages (see
1191 | * [`UtdCause::SentBeforeWeJoined`] for that).
1192 | *
1193 | * Expected message to user: "History is not available on this device".
1194 | */
1195 | case historicalMessageAndBackupIsDisabled
1196 | /**
1197 | * The keys for this event are intentionally withheld.
1198 | *
1199 | * The sender has refused to share the key because our device does not meet
1200 | * the sender's security requirements.
1201 | */
1202 | case withheldForUnverifiedOrInsecureDevice
1203 | /**
1204 | * The keys for this event are missing, likely because the sender was
1205 | * unable to share them (e.g., failure to establish an Olm 1:1
1206 | * channel). Alternatively, the sender may have deliberately excluded
1207 | * this device by cherry-picking and blocking it, in which case, no action
1208 | * can be taken on our side.
1209 | */
1210 | case withheldBySender
1211 | /**
1212 | * We are missing the keys for this event, but it is a "device-historical"
1213 | * message, and even though a key storage backup does exist, we can't use
1214 | * it because our device is unverified.
1215 | *
1216 | * Device-historical means that the message was sent before the current
1217 | * device existed (but the current user was probably a member of the room
1218 | * at the time the message was sent). Not to
1219 | * be confused with pre-join or pre-invite messages (see
1220 | * [`UtdCause::SentBeforeWeJoined`] for that).
1221 | *
1222 | * Expected message to user: "You need to verify this device".
1223 | */
1224 | case historicalMessageAndDeviceIsUnverified
1225 | }
1226 |
1227 |
1228 | public struct FfiConverterTypeUtdCause: FfiConverterRustBuffer {
1229 | typealias SwiftType = UtdCause
1230 |
1231 | public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UtdCause {
1232 | let variant: Int32 = try readInt(&buf)
1233 | switch variant {
1234 |
1235 | case 1: return .unknown
1236 |
1237 | case 2: return .sentBeforeWeJoined
1238 |
1239 | case 3: return .verificationViolation
1240 |
1241 | case 4: return .unsignedDevice
1242 |
1243 | case 5: return .unknownDevice
1244 |
1245 | case 6: return .historicalMessageAndBackupIsDisabled
1246 |
1247 | case 7: return .withheldForUnverifiedOrInsecureDevice
1248 |
1249 | case 8: return .withheldBySender
1250 |
1251 | case 9: return .historicalMessageAndDeviceIsUnverified
1252 |
1253 | default: throw UniffiInternalError.unexpectedEnumCase
1254 | }
1255 | }
1256 |
1257 | public static func write(_ value: UtdCause, into buf: inout [UInt8]) {
1258 | switch value {
1259 |
1260 |
1261 | case .unknown:
1262 | writeInt(&buf, Int32(1))
1263 |
1264 |
1265 | case .sentBeforeWeJoined:
1266 | writeInt(&buf, Int32(2))
1267 |
1268 |
1269 | case .verificationViolation:
1270 | writeInt(&buf, Int32(3))
1271 |
1272 |
1273 | case .unsignedDevice:
1274 | writeInt(&buf, Int32(4))
1275 |
1276 |
1277 | case .unknownDevice:
1278 | writeInt(&buf, Int32(5))
1279 |
1280 |
1281 | case .historicalMessageAndBackupIsDisabled:
1282 | writeInt(&buf, Int32(6))
1283 |
1284 |
1285 | case .withheldForUnverifiedOrInsecureDevice:
1286 | writeInt(&buf, Int32(7))
1287 |
1288 |
1289 | case .withheldBySender:
1290 | writeInt(&buf, Int32(8))
1291 |
1292 |
1293 | case .historicalMessageAndDeviceIsUnverified:
1294 | writeInt(&buf, Int32(9))
1295 |
1296 | }
1297 | }
1298 | }
1299 |
1300 |
1301 | public func FfiConverterTypeUtdCause_lift(_ buf: RustBuffer) throws -> UtdCause {
1302 | return try FfiConverterTypeUtdCause.lift(buf)
1303 | }
1304 |
1305 | public func FfiConverterTypeUtdCause_lower(_ value: UtdCause) -> RustBuffer {
1306 | return FfiConverterTypeUtdCause.lower(value)
1307 | }
1308 |
1309 |
1310 |
1311 | extension UtdCause: Equatable, Hashable {}
1312 |
1313 |
1314 |
1315 | private enum InitializationResult {
1316 | case ok
1317 | case contractVersionMismatch
1318 | case apiChecksumMismatch
1319 | }
1320 | // Use a global variable to perform the versioning checks. Swift ensures that
1321 | // the code inside is only computed once.
1322 | private var initializationResult: InitializationResult = {
1323 | // Get the bindings contract version from our ComponentInterface
1324 | let bindings_contract_version = 26
1325 | // Get the scaffolding contract version by calling the into the dylib
1326 | let scaffolding_contract_version = ffi_matrix_sdk_crypto_uniffi_contract_version()
1327 | if bindings_contract_version != scaffolding_contract_version {
1328 | return InitializationResult.contractVersionMismatch
1329 | }
1330 |
1331 | return InitializationResult.ok
1332 | }()
1333 |
1334 | private func uniffiEnsureInitialized() {
1335 | switch initializationResult {
1336 | case .ok:
1337 | break
1338 | case .contractVersionMismatch:
1339 | fatalError("UniFFI contract version mismatch: try cleaning and rebuilding your project")
1340 | case .apiChecksumMismatch:
1341 | fatalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
1342 | }
1343 | }
1344 |
1345 | // swiftlint:enable all
--------------------------------------------------------------------------------
/Sources/MatrixRustSDK/matrix_sdk_ui.swift:
--------------------------------------------------------------------------------
1 | // This file was autogenerated by some hot garbage in the `uniffi` crate.
2 | // Trust me, you don't want to mess with it!
3 |
4 | // swiftlint:disable all
5 | import Foundation
6 |
7 | // Depending on the consumer's build setup, the low-level FFI code
8 | // might be in a separate module, or it might be compiled inline into
9 | // this module. This is a bit of light hackery to work with both.
10 | #if canImport(matrix_sdk_uiFFI)
11 | import matrix_sdk_uiFFI
12 | #endif
13 |
14 | fileprivate extension RustBuffer {
15 | // Allocate a new buffer, copying the contents of a `UInt8` array.
16 | init(bytes: [UInt8]) {
17 | let rbuf = bytes.withUnsafeBufferPointer { ptr in
18 | RustBuffer.from(ptr)
19 | }
20 | self.init(capacity: rbuf.capacity, len: rbuf.len, data: rbuf.data)
21 | }
22 |
23 | static func empty() -> RustBuffer {
24 | RustBuffer(capacity: 0, len:0, data: nil)
25 | }
26 |
27 | static func from(_ ptr: UnsafeBufferPointer) -> RustBuffer {
28 | try! rustCall { ffi_matrix_sdk_ui_rustbuffer_from_bytes(ForeignBytes(bufferPointer: ptr), $0) }
29 | }
30 |
31 | // Frees the buffer in place.
32 | // The buffer must not be used after this is called.
33 | func deallocate() {
34 | try! rustCall { ffi_matrix_sdk_ui_rustbuffer_free(self, $0) }
35 | }
36 | }
37 |
38 | fileprivate extension ForeignBytes {
39 | init(bufferPointer: UnsafeBufferPointer) {
40 | self.init(len: Int32(bufferPointer.count), data: bufferPointer.baseAddress)
41 | }
42 | }
43 |
44 | // For every type used in the interface, we provide helper methods for conveniently
45 | // lifting and lowering that type from C-compatible data, and for reading and writing
46 | // values of that type in a buffer.
47 |
48 | // Helper classes/extensions that don't change.
49 | // Someday, this will be in a library of its own.
50 |
51 | fileprivate extension Data {
52 | init(rustBuffer: RustBuffer) {
53 | // TODO: This copies the buffer. Can we read directly from a
54 | // Rust buffer?
55 | self.init(bytes: rustBuffer.data!, count: Int(rustBuffer.len))
56 | }
57 | }
58 |
59 | // Define reader functionality. Normally this would be defined in a class or
60 | // struct, but we use standalone functions instead in order to make external
61 | // types work.
62 | //
63 | // With external types, one swift source file needs to be able to call the read
64 | // method on another source file's FfiConverter, but then what visibility
65 | // should Reader have?
66 | // - If Reader is fileprivate, then this means the read() must also
67 | // be fileprivate, which doesn't work with external types.
68 | // - If Reader is internal/public, we'll get compile errors since both source
69 | // files will try define the same type.
70 | //
71 | // Instead, the read() method and these helper functions input a tuple of data
72 |
73 | fileprivate func createReader(data: Data) -> (data: Data, offset: Data.Index) {
74 | (data: data, offset: 0)
75 | }
76 |
77 | // Reads an integer at the current offset, in big-endian order, and advances
78 | // the offset on success. Throws if reading the integer would move the
79 | // offset past the end of the buffer.
80 | fileprivate func readInt(_ reader: inout (data: Data, offset: Data.Index)) throws -> T {
81 | let range = reader.offset...size
82 | guard reader.data.count >= range.upperBound else {
83 | throw UniffiInternalError.bufferOverflow
84 | }
85 | if T.self == UInt8.self {
86 | let value = reader.data[reader.offset]
87 | reader.offset += 1
88 | return value as! T
89 | }
90 | var value: T = 0
91 | let _ = withUnsafeMutableBytes(of: &value, { reader.data.copyBytes(to: $0, from: range)})
92 | reader.offset = range.upperBound
93 | return value.bigEndian
94 | }
95 |
96 | // Reads an arbitrary number of bytes, to be used to read
97 | // raw bytes, this is useful when lifting strings
98 | fileprivate func readBytes(_ reader: inout (data: Data, offset: Data.Index), count: Int) throws -> Array {
99 | let range = reader.offset..<(reader.offset+count)
100 | guard reader.data.count >= range.upperBound else {
101 | throw UniffiInternalError.bufferOverflow
102 | }
103 | var value = [UInt8](repeating: 0, count: count)
104 | value.withUnsafeMutableBufferPointer({ buffer in
105 | reader.data.copyBytes(to: buffer, from: range)
106 | })
107 | reader.offset = range.upperBound
108 | return value
109 | }
110 |
111 | // Reads a float at the current offset.
112 | fileprivate func readFloat(_ reader: inout (data: Data, offset: Data.Index)) throws -> Float {
113 | return Float(bitPattern: try readInt(&reader))
114 | }
115 |
116 | // Reads a float at the current offset.
117 | fileprivate func readDouble(_ reader: inout (data: Data, offset: Data.Index)) throws -> Double {
118 | return Double(bitPattern: try readInt(&reader))
119 | }
120 |
121 | // Indicates if the offset has reached the end of the buffer.
122 | fileprivate func hasRemaining(_ reader: (data: Data, offset: Data.Index)) -> Bool {
123 | return reader.offset < reader.data.count
124 | }
125 |
126 | // Define writer functionality. Normally this would be defined in a class or
127 | // struct, but we use standalone functions instead in order to make external
128 | // types work. See the above discussion on Readers for details.
129 |
130 | fileprivate func createWriter() -> [UInt8] {
131 | return []
132 | }
133 |
134 | fileprivate func writeBytes(_ writer: inout [UInt8], _ byteArr: S) where S: Sequence, S.Element == UInt8 {
135 | writer.append(contentsOf: byteArr)
136 | }
137 |
138 | // Writes an integer in big-endian order.
139 | //
140 | // Warning: make sure what you are trying to write
141 | // is in the correct type!
142 | fileprivate func writeInt(_ writer: inout [UInt8], _ value: T) {
143 | var value = value.bigEndian
144 | withUnsafeBytes(of: &value) { writer.append(contentsOf: $0) }
145 | }
146 |
147 | fileprivate func writeFloat(_ writer: inout [UInt8], _ value: Float) {
148 | writeInt(&writer, value.bitPattern)
149 | }
150 |
151 | fileprivate func writeDouble(_ writer: inout [UInt8], _ value: Double) {
152 | writeInt(&writer, value.bitPattern)
153 | }
154 |
155 | // Protocol for types that transfer other types across the FFI. This is
156 | // analogous to the Rust trait of the same name.
157 | fileprivate protocol FfiConverter {
158 | associatedtype FfiType
159 | associatedtype SwiftType
160 |
161 | static func lift(_ value: FfiType) throws -> SwiftType
162 | static func lower(_ value: SwiftType) -> FfiType
163 | static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType
164 | static func write(_ value: SwiftType, into buf: inout [UInt8])
165 | }
166 |
167 | // Types conforming to `Primitive` pass themselves directly over the FFI.
168 | fileprivate protocol FfiConverterPrimitive: FfiConverter where FfiType == SwiftType { }
169 |
170 | extension FfiConverterPrimitive {
171 | public static func lift(_ value: FfiType) throws -> SwiftType {
172 | return value
173 | }
174 |
175 | public static func lower(_ value: SwiftType) -> FfiType {
176 | return value
177 | }
178 | }
179 |
180 | // Types conforming to `FfiConverterRustBuffer` lift and lower into a `RustBuffer`.
181 | // Used for complex types where it's hard to write a custom lift/lower.
182 | fileprivate protocol FfiConverterRustBuffer: FfiConverter where FfiType == RustBuffer {}
183 |
184 | extension FfiConverterRustBuffer {
185 | public static func lift(_ buf: RustBuffer) throws -> SwiftType {
186 | var reader = createReader(data: Data(rustBuffer: buf))
187 | let value = try read(from: &reader)
188 | if hasRemaining(reader) {
189 | throw UniffiInternalError.incompleteData
190 | }
191 | buf.deallocate()
192 | return value
193 | }
194 |
195 | public static func lower(_ value: SwiftType) -> RustBuffer {
196 | var writer = createWriter()
197 | write(value, into: &writer)
198 | return RustBuffer(bytes: writer)
199 | }
200 | }
201 | // An error type for FFI errors. These errors occur at the UniFFI level, not
202 | // the library level.
203 | fileprivate enum UniffiInternalError: LocalizedError {
204 | case bufferOverflow
205 | case incompleteData
206 | case unexpectedOptionalTag
207 | case unexpectedEnumCase
208 | case unexpectedNullPointer
209 | case unexpectedRustCallStatusCode
210 | case unexpectedRustCallError
211 | case unexpectedStaleHandle
212 | case rustPanic(_ message: String)
213 |
214 | public var errorDescription: String? {
215 | switch self {
216 | case .bufferOverflow: return "Reading the requested value would read past the end of the buffer"
217 | case .incompleteData: return "The buffer still has data after lifting its containing value"
218 | case .unexpectedOptionalTag: return "Unexpected optional tag; should be 0 or 1"
219 | case .unexpectedEnumCase: return "Raw enum value doesn't match any cases"
220 | case .unexpectedNullPointer: return "Raw pointer value was null"
221 | case .unexpectedRustCallStatusCode: return "Unexpected RustCallStatus code"
222 | case .unexpectedRustCallError: return "CALL_ERROR but no errorClass specified"
223 | case .unexpectedStaleHandle: return "The object in the handle map has been dropped already"
224 | case let .rustPanic(message): return message
225 | }
226 | }
227 | }
228 |
229 | fileprivate extension NSLock {
230 | func withLock(f: () throws -> T) rethrows -> T {
231 | self.lock()
232 | defer { self.unlock() }
233 | return try f()
234 | }
235 | }
236 |
237 | fileprivate let CALL_SUCCESS: Int8 = 0
238 | fileprivate let CALL_ERROR: Int8 = 1
239 | fileprivate let CALL_UNEXPECTED_ERROR: Int8 = 2
240 | fileprivate let CALL_CANCELLED: Int8 = 3
241 |
242 | fileprivate extension RustCallStatus {
243 | init() {
244 | self.init(
245 | code: CALL_SUCCESS,
246 | errorBuf: RustBuffer.init(
247 | capacity: 0,
248 | len: 0,
249 | data: nil
250 | )
251 | )
252 | }
253 | }
254 |
255 | private func rustCall(_ callback: (UnsafeMutablePointer) -> T) throws -> T {
256 | let neverThrow: ((RustBuffer) throws -> Never)? = nil
257 | return try makeRustCall(callback, errorHandler: neverThrow)
258 | }
259 |
260 | private func rustCallWithError(
261 | _ errorHandler: @escaping (RustBuffer) throws -> E,
262 | _ callback: (UnsafeMutablePointer) -> T) throws -> T {
263 | try makeRustCall(callback, errorHandler: errorHandler)
264 | }
265 |
266 | private func makeRustCall(
267 | _ callback: (UnsafeMutablePointer) -> T,
268 | errorHandler: ((RustBuffer) throws -> E)?
269 | ) throws -> T {
270 | uniffiEnsureInitialized()
271 | var callStatus = RustCallStatus.init()
272 | let returnedVal = callback(&callStatus)
273 | try uniffiCheckCallStatus(callStatus: callStatus, errorHandler: errorHandler)
274 | return returnedVal
275 | }
276 |
277 | private func uniffiCheckCallStatus(
278 | callStatus: RustCallStatus,
279 | errorHandler: ((RustBuffer) throws -> E)?
280 | ) throws {
281 | switch callStatus.code {
282 | case CALL_SUCCESS:
283 | return
284 |
285 | case CALL_ERROR:
286 | if let errorHandler = errorHandler {
287 | throw try errorHandler(callStatus.errorBuf)
288 | } else {
289 | callStatus.errorBuf.deallocate()
290 | throw UniffiInternalError.unexpectedRustCallError
291 | }
292 |
293 | case CALL_UNEXPECTED_ERROR:
294 | // When the rust code sees a panic, it tries to construct a RustBuffer
295 | // with the message. But if that code panics, then it just sends back
296 | // an empty buffer.
297 | if callStatus.errorBuf.len > 0 {
298 | throw UniffiInternalError.rustPanic(try FfiConverterString.lift(callStatus.errorBuf))
299 | } else {
300 | callStatus.errorBuf.deallocate()
301 | throw UniffiInternalError.rustPanic("Rust panic")
302 | }
303 |
304 | case CALL_CANCELLED:
305 | fatalError("Cancellation not supported yet")
306 |
307 | default:
308 | throw UniffiInternalError.unexpectedRustCallStatusCode
309 | }
310 | }
311 |
312 | private func uniffiTraitInterfaceCall(
313 | callStatus: UnsafeMutablePointer,
314 | makeCall: () throws -> T,
315 | writeReturn: (T) -> ()
316 | ) {
317 | do {
318 | try writeReturn(makeCall())
319 | } catch let error {
320 | callStatus.pointee.code = CALL_UNEXPECTED_ERROR
321 | callStatus.pointee.errorBuf = FfiConverterString.lower(String(describing: error))
322 | }
323 | }
324 |
325 | private func uniffiTraitInterfaceCallWithError(
326 | callStatus: UnsafeMutablePointer,
327 | makeCall: () throws -> T,
328 | writeReturn: (T) -> (),
329 | lowerError: (E) -> RustBuffer
330 | ) {
331 | do {
332 | try writeReturn(makeCall())
333 | } catch let error as E {
334 | callStatus.pointee.code = CALL_ERROR
335 | callStatus.pointee.errorBuf = lowerError(error)
336 | } catch {
337 | callStatus.pointee.code = CALL_UNEXPECTED_ERROR
338 | callStatus.pointee.errorBuf = FfiConverterString.lower(String(describing: error))
339 | }
340 | }
341 | fileprivate class UniffiHandleMap {
342 | private var map: [UInt64: T] = [:]
343 | private let lock = NSLock()
344 | private var currentHandle: UInt64 = 1
345 |
346 | func insert(obj: T) -> UInt64 {
347 | lock.withLock {
348 | let handle = currentHandle
349 | currentHandle += 1
350 | map[handle] = obj
351 | return handle
352 | }
353 | }
354 |
355 | func get(handle: UInt64) throws -> T {
356 | try lock.withLock {
357 | guard let obj = map[handle] else {
358 | throw UniffiInternalError.unexpectedStaleHandle
359 | }
360 | return obj
361 | }
362 | }
363 |
364 | @discardableResult
365 | func remove(handle: UInt64) throws -> T {
366 | try lock.withLock {
367 | guard let obj = map.removeValue(forKey: handle) else {
368 | throw UniffiInternalError.unexpectedStaleHandle
369 | }
370 | return obj
371 | }
372 | }
373 |
374 | var count: Int {
375 | get {
376 | map.count
377 | }
378 | }
379 | }
380 |
381 |
382 | // Public interface members begin here.
383 |
384 |
385 | fileprivate struct FfiConverterString: FfiConverter {
386 | typealias SwiftType = String
387 | typealias FfiType = RustBuffer
388 |
389 | public static func lift(_ value: RustBuffer) throws -> String {
390 | defer {
391 | value.deallocate()
392 | }
393 | if value.data == nil {
394 | return String()
395 | }
396 | let bytes = UnsafeBufferPointer(start: value.data!, count: Int(value.len))
397 | return String(bytes: bytes, encoding: String.Encoding.utf8)!
398 | }
399 |
400 | public static func lower(_ value: String) -> RustBuffer {
401 | return value.utf8CString.withUnsafeBufferPointer { ptr in
402 | // The swift string gives us int8_t, we want uint8_t.
403 | ptr.withMemoryRebound(to: UInt8.self) { ptr in
404 | // The swift string gives us a trailing null byte, we don't want it.
405 | let buf = UnsafeBufferPointer(rebasing: ptr.prefix(upTo: ptr.count - 1))
406 | return RustBuffer.from(buf)
407 | }
408 | }
409 | }
410 |
411 | public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> String {
412 | let len: Int32 = try readInt(&buf)
413 | return String(bytes: try readBytes(&buf, count: Int(len)), encoding: String.Encoding.utf8)!
414 | }
415 |
416 | public static func write(_ value: String, into buf: inout [UInt8]) {
417 | let len = Int32(value.utf8.count)
418 | writeInt(&buf, len)
419 | writeBytes(&buf, value.utf8)
420 | }
421 | }
422 |
423 | // Note that we don't yet support `indirect` for enums.
424 | // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion.
425 | /**
426 | * Where this event came.
427 | */
428 |
429 | public enum EventItemOrigin {
430 |
431 | /**
432 | * The event was created locally.
433 | */
434 | case local
435 | /**
436 | * The event came from a sync response.
437 | */
438 | case sync
439 | /**
440 | * The event came from pagination.
441 | */
442 | case pagination
443 | /**
444 | * The event came from a cache.
445 | */
446 | case cache
447 | }
448 |
449 |
450 | public struct FfiConverterTypeEventItemOrigin: FfiConverterRustBuffer {
451 | typealias SwiftType = EventItemOrigin
452 |
453 | public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> EventItemOrigin {
454 | let variant: Int32 = try readInt(&buf)
455 | switch variant {
456 |
457 | case 1: return .local
458 |
459 | case 2: return .sync
460 |
461 | case 3: return .pagination
462 |
463 | case 4: return .cache
464 |
465 | default: throw UniffiInternalError.unexpectedEnumCase
466 | }
467 | }
468 |
469 | public static func write(_ value: EventItemOrigin, into buf: inout [UInt8]) {
470 | switch value {
471 |
472 |
473 | case .local:
474 | writeInt(&buf, Int32(1))
475 |
476 |
477 | case .sync:
478 | writeInt(&buf, Int32(2))
479 |
480 |
481 | case .pagination:
482 | writeInt(&buf, Int32(3))
483 |
484 |
485 | case .cache:
486 | writeInt(&buf, Int32(4))
487 |
488 | }
489 | }
490 | }
491 |
492 |
493 | public func FfiConverterTypeEventItemOrigin_lift(_ buf: RustBuffer) throws -> EventItemOrigin {
494 | return try FfiConverterTypeEventItemOrigin.lift(buf)
495 | }
496 |
497 | public func FfiConverterTypeEventItemOrigin_lower(_ value: EventItemOrigin) -> RustBuffer {
498 | return FfiConverterTypeEventItemOrigin.lower(value)
499 | }
500 |
501 |
502 |
503 | extension EventItemOrigin: Equatable, Hashable {}
504 |
505 |
506 |
507 | // Note that we don't yet support `indirect` for enums.
508 | // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion.
509 | /**
510 | * The type of change between the previous and current pinned events.
511 | */
512 |
513 | public enum RoomPinnedEventsChange {
514 |
515 | /**
516 | * Only new event ids were added.
517 | */
518 | case added
519 | /**
520 | * Only event ids were removed.
521 | */
522 | case removed
523 | /**
524 | * Some change other than only adding or only removing ids happened.
525 | */
526 | case changed
527 | }
528 |
529 |
530 | public struct FfiConverterTypeRoomPinnedEventsChange: FfiConverterRustBuffer {
531 | typealias SwiftType = RoomPinnedEventsChange
532 |
533 | public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> RoomPinnedEventsChange {
534 | let variant: Int32 = try readInt(&buf)
535 | switch variant {
536 |
537 | case 1: return .added
538 |
539 | case 2: return .removed
540 |
541 | case 3: return .changed
542 |
543 | default: throw UniffiInternalError.unexpectedEnumCase
544 | }
545 | }
546 |
547 | public static func write(_ value: RoomPinnedEventsChange, into buf: inout [UInt8]) {
548 | switch value {
549 |
550 |
551 | case .added:
552 | writeInt(&buf, Int32(1))
553 |
554 |
555 | case .removed:
556 | writeInt(&buf, Int32(2))
557 |
558 |
559 | case .changed:
560 | writeInt(&buf, Int32(3))
561 |
562 | }
563 | }
564 | }
565 |
566 |
567 | public func FfiConverterTypeRoomPinnedEventsChange_lift(_ buf: RustBuffer) throws -> RoomPinnedEventsChange {
568 | return try FfiConverterTypeRoomPinnedEventsChange.lift(buf)
569 | }
570 |
571 | public func FfiConverterTypeRoomPinnedEventsChange_lower(_ value: RoomPinnedEventsChange) -> RustBuffer {
572 | return FfiConverterTypeRoomPinnedEventsChange.lower(value)
573 | }
574 |
575 |
576 |
577 | extension RoomPinnedEventsChange: Equatable, Hashable {}
578 |
579 |
580 |
581 | private enum InitializationResult {
582 | case ok
583 | case contractVersionMismatch
584 | case apiChecksumMismatch
585 | }
586 | // Use a global variable to perform the versioning checks. Swift ensures that
587 | // the code inside is only computed once.
588 | private var initializationResult: InitializationResult = {
589 | // Get the bindings contract version from our ComponentInterface
590 | let bindings_contract_version = 26
591 | // Get the scaffolding contract version by calling the into the dylib
592 | let scaffolding_contract_version = ffi_matrix_sdk_ui_uniffi_contract_version()
593 | if bindings_contract_version != scaffolding_contract_version {
594 | return InitializationResult.contractVersionMismatch
595 | }
596 |
597 | return InitializationResult.ok
598 | }()
599 |
600 | private func uniffiEnsureInitialized() {
601 | switch initializationResult {
602 | case .ok:
603 | break
604 | case .contractVersionMismatch:
605 | fatalError("UniFFI contract version mismatch: try cleaning and rebuilding your project")
606 | case .apiChecksumMismatch:
607 | fatalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
608 | }
609 | }
610 |
611 | // swiftlint:enable all
--------------------------------------------------------------------------------
/Tools/Release/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version: 5.9
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 |
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "Release",
8 | platforms: [.macOS(.v13)],
9 | products: [.executable(name: "release", targets: ["Release"])],
10 | dependencies: [
11 | .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.3.0"),
12 | .package(url: "https://github.com/element-hq/swift-command-line-tools.git", revision: "c085ad85c27970e4bba2dd1180c2a53ab77022ba")
13 | // .package(path: "../../../swift-command-line-tools")
14 | ],
15 | targets: [
16 | .executableTarget(name: "Release",
17 | dependencies: [
18 | .product(name: "ArgumentParser", package: "swift-argument-parser"),
19 | .product(name: "CommandLineTools", package: "swift-command-line-tools")
20 | ])
21 | ]
22 | )
23 |
--------------------------------------------------------------------------------
/Tools/Release/README.md:
--------------------------------------------------------------------------------
1 | # Scripts
2 |
3 | ## Release
4 | Creates a Github release from a matrix-rust-sdk repository.
5 |
6 | Usage:
7 | ```
8 | swift run release # will use a generated calver e.g. 25.12.31
9 | ```
10 |
11 | For help (including how to customise the version): `swift run release --help`
12 |
13 | ## Requirements
14 |
15 | To make the release you will need the following installed:
16 | 1. Set `api.github.com` in your .netrc file before using
17 | 2. cargo + rustup https://www.rust-lang.org/tools/install
18 | 3. matrix-rust-sdk cloned next to this repo `git clone https://github.com/matrix-org/matrix-rust-sdk`
19 | 4. Checkout the `main` branch of the SDK (or another custom branch to release from).
20 | 5. Any dependencies required to build the matrix-rust-sdk as mentioned in the [Apple platforms readme](https://github.com/matrix-org/matrix-rust-sdk/blob/main/bindings/apple/README.md).
21 |
--------------------------------------------------------------------------------
/Tools/Release/Sources/Release.swift:
--------------------------------------------------------------------------------
1 | import ArgumentParser
2 | import CommandLineTools
3 | import Foundation
4 |
5 | @main
6 | struct Release: AsyncParsableCommand {
7 | @Option(help: "An optional build number that will be appended to the generated version (e.g. 25.12.31-123). This option is ignored when a custom version is specified.")
8 | var buildNumber: Int? = nil
9 |
10 | @Option(help: "A custom version to use instead of generating a calendar version. This option takes precedence over --build-number.")
11 | var customVersion: String? = nil
12 |
13 | var version: String {
14 | customVersion ?? Date.now.calendarVersion(buildNumber: buildNumber)
15 | }
16 |
17 | @Flag(help: "Prevents the run from pushing anything to GitHub.")
18 | var localOnly = false
19 |
20 | var apiToken = (try? NetrcParser.parse(file: FileManager.default.homeDirectoryForCurrentUser.appending(component: ".netrc")))!
21 | .authorization(for: URL(string: "https://api.github.com")!)!
22 | .password
23 |
24 | var sourceRepo = Repository(owner: "matrix-org", name: "matrix-rust-sdk")
25 | var packageRepo = Repository(owner: "matrix-org", name: "matrix-rust-components-swift")
26 |
27 | var packageDirectory = URL(fileURLWithPath: #file)
28 | .deletingLastPathComponent() // Release.swift
29 | .deletingLastPathComponent() // Sources
30 | .deletingLastPathComponent() // Release
31 | .deletingLastPathComponent() // Tools
32 | lazy var buildDirectory = packageDirectory
33 | .deletingLastPathComponent() // matrix-rust-components-swift
34 | .appending(component: "matrix-rust-sdk")
35 |
36 | mutating func run() async throws {
37 | let package = Package(repository: packageRepo, directory: packageDirectory, apiToken: apiToken, urlSession: localOnly ? .releaseMock : .shared)
38 | Zsh.defaultDirectory = package.directory
39 |
40 | Log.info("Build directory: \(buildDirectory.path())")
41 |
42 | let product = try build()
43 | let (zipFileURL, checksum) = try package.zipBinary(with: product)
44 |
45 | try await updatePackage(package, with: product, checksum: checksum)
46 | try commitAndPush(package, with: product)
47 | try await package.makeRelease(with: product, uploading: zipFileURL)
48 | }
49 |
50 | mutating func build() throws -> BuildProduct {
51 | let git = Git(directory: buildDirectory)
52 | let commitHash = try git.commitHash
53 | let branch = try git.branchName
54 |
55 | Log.info("Building \(branch) at \(commitHash)")
56 |
57 | // unset fixes an issue where swift compilation prevents building for targets other than macOS
58 | try Zsh.run(command: "unset SDKROOT && cargo xtask swift build-framework --release", directory: buildDirectory)
59 |
60 | return BuildProduct(sourceRepo: sourceRepo,
61 | version: version,
62 | commitHash: commitHash,
63 | branch: branch,
64 | directory: buildDirectory.appending(component: "bindings/apple/generated/"),
65 | frameworkName: "MatrixSDKFFI.xcframework")
66 | }
67 |
68 | func updatePackage(_ package: Package, with product: BuildProduct, checksum: String) async throws {
69 | Log.info("Copying sources")
70 | let source = product.directory.appending(component: "swift", directoryHint: .isDirectory)
71 | let destination = package.directory.appending(component: "Sources/MatrixRustSDK", directoryHint: .isDirectory)
72 | try Zsh.run(command: "rsync -a --delete '\(source.path())' '\(destination.path())'")
73 |
74 | try await package.updateManifest(with: product, checksum: checksum)
75 | }
76 |
77 | func commitAndPush(_ package: Package, with product: BuildProduct) throws {
78 | Log.info("Pushing changes")
79 |
80 | let git = Git(directory: package.directory)
81 | try git.add(files: "Package.swift", "Sources")
82 | try git.commit(message: "Bump to version \(version) (\(product.sourceRepo.name)/\(product.branch) \(product.commitHash))")
83 |
84 | guard !localOnly else {
85 | Log.info("Skipping push for --local-only")
86 | return
87 | }
88 |
89 | try git.push()
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/docs/Getting Started.md:
--------------------------------------------------------------------------------
1 | # Getting Started
2 |
3 | The following instructions give a brief overview of using the Matrix Rust SDK from Swift. You can find more information about the API by browsing the [FFI crate](https://github.com/matrix-org/matrix-rust-sdk/tree/main/bindings/matrix-sdk-ffi/src) in the SDK's repository. In its current state, many of the calls to the SDK will block the thread that they're called on. You can determine this by looking for functions that use `RUNTIME.block_on` in their implementation. This is temporary and will be addressed in the future via [async support in Uniffi](https://github.com/mozilla/uniffi-rs/pull/1409).
4 |
5 | Please note: The Swift components for the Rust SDK are unstable meaning that the API could change at any point.
6 |
7 |
8 | ### Project setup
9 |
10 | Add https://github.com/matrix-org/matrix-rust-components-swift to your project as a Swift package.
11 |
12 | ### Usage
13 |
14 | First we need to authenticate the user.
15 |
16 | ```swift
17 | import MatrixRustSDK
18 |
19 | // Create a client for a particular homeserver.
20 | // Note that we can pass a server name (the second part of a Matrix user ID) instead of the direct URL.
21 | // This allows the SDK to discover the homeserver's well-known configuration for Sliding Sync support.
22 | let client = try await ClientBuilder()
23 | .serverNameOrHomeserverUrl(serverNameOrUrl: "matrix.org")
24 | .sessionPaths(dataPath: URL.applicationSupportDirectory.path(percentEncoded: false),
25 | cachePath: URL.cachesDirectory.path(percentEncoded: false))
26 | .slidingSyncVersionBuilder(versionBuilder: .discoverNative)
27 | .build()
28 |
29 | // Login using password authentication.
30 | try await client.login(username: "alice", password: "secret", initialDeviceName: nil, deviceId: nil)
31 |
32 | let session = try client.session()
33 | // Store the session in the keychain.
34 | ```
35 |
36 | Or, if the user has previously authenticated we can restore their session instead.
37 |
38 | ```swift
39 | // Get the session from the keychain.
40 | let session = …
41 |
42 | // Build a client for the homeserver.
43 | let client = try await ClientBuilder()
44 | .sessionPaths(dataPath: URL.applicationSupportDirectory.path(percentEncoded: false),
45 | cachePath: URL.cachesDirectory.path(percentEncoded: false))
46 | .homeserverUrl(url: session.homeserverUrl)
47 | .build()
48 |
49 | // Restore the client using the session.
50 | try await client.restoreSession(session: session)
51 | ```
52 |
53 | Next we need to start the sync loop and listen for room updates.
54 |
55 | ```swift
56 | class AllRoomsListener: RoomListEntriesListener {
57 | /// The user's list of rooms.
58 | var rooms: [RoomListItem] = []
59 |
60 | func onUpdate(roomEntriesUpdate: [MatrixRustSDK.RoomListEntriesUpdate]) {
61 | // Update the user's room list on each update.
62 | for update in roomEntriesUpdate {
63 | switch update {
64 | case .reset(values: let values):
65 | rooms = values
66 | default:
67 | break // Handle all the other cases accordingly.
68 | }
69 | }
70 | }
71 | }
72 |
73 | // Create a sync service which controls the sync loop.
74 | let syncService = try await client.syncService().finish()
75 |
76 | // Listen to room list updates.
77 | let listener = AllRoomsListener()
78 | let roomListService = syncService.roomListService()
79 | let handle = try await roomListService.allRooms().entriesWithDynamicAdapters(pageSize: 100, listener: listener)
80 |
81 | // Start the sync loop.
82 | await syncService.start()
83 | ```
84 |
85 | When we're ready to display the events from a room we use the timeline API.
86 |
87 | ```swift
88 | class TimelineItemListener: TimelineListener {
89 | /// The loaded items for this room's timeline
90 | var timelineItems: [TimelineItem] = []
91 |
92 | func onUpdate(diff: [TimelineDiff]) {
93 | // Update the timeline items on each update.
94 | for update in diff {
95 | switch update.change() {
96 | case .reset:
97 | timelineItems = update.reset()!
98 | default:
99 | break // Handle all the other cases accordingly.
100 | }
101 | }
102 | }
103 | }
104 |
105 | // Fetch the room from the listener and initialise it's timeline.
106 | let room = listener.rooms.first!
107 | if !room.isTimelineInitialized() {
108 | try await room.initTimeline(eventTypeFilter: nil, internalIdPrefix: nil)
109 | }
110 | let timeline = try await room.fullRoom().timeline()
111 |
112 | // Listen to timeline item updates.
113 | let timelineItemsListener = TimelineItemListener()
114 | let timelineHandle = await timeline.addListener(listener: timelineItemsListener)
115 |
116 | // Wait for the items array to be updated…
117 |
118 | // Get the event contents from an item.
119 | let timelineItem = timelineItemsListener.timelineItems.last!
120 | if let messageEvent = timelineItem.asEvent()?.content().asMessage() {
121 | print(messageEvent)
122 | }
123 | ```
124 |
125 | Finally we can send messages into the room (with built in support for markdown).
126 |
127 | ```swift
128 | // Create the message content from a markdown string.
129 | let message = messageEventContentFromMarkdown(md: "Hello, World!")
130 |
131 | // Send the message content via the room's timeline (so that we show a local echo).
132 | _ = try await timeline.send(msg: message)
133 | ```
134 |
--------------------------------------------------------------------------------