summaryrefslogtreecommitdiffstats
path: root/third_party/rust/uniffi_bindgen
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /third_party/rust/uniffi_bindgen
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/uniffi_bindgen')
-rw-r--r--third_party/rust/uniffi_bindgen/.cargo-checksum.json1
-rw-r--r--third_party/rust/uniffi_bindgen/Cargo.toml81
-rw-r--r--third_party/rust/uniffi_bindgen/askama.toml21
-rw-r--r--third_party/rust/uniffi_bindgen/src/backend/config.rs17
-rw-r--r--third_party/rust/uniffi_bindgen/src/backend/filters.rs76
-rw-r--r--third_party/rust/uniffi_bindgen/src/backend/mod.rs11
-rw-r--r--third_party/rust/uniffi_bindgen/src/backend/types.rs78
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/callback_interface.rs31
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/compounds.rs98
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/custom.rs27
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/enum_.rs40
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/executor.rs24
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/external.rs27
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/miscellany.rs30
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs530
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/object.rs27
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/primitives.rs86
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/record.rs27
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/variant.rs33
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/mod.rs41
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Async.kt44
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/BooleanHelper.kt19
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ByteArrayHelper.kt15
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/CallbackInterfaceRuntime.kt78
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/CallbackInterfaceTemplate.kt129
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/CustomTypeTemplate.kt62
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/DurationHelper.kt36
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/EnumTemplate.kt110
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ErrorTemplate.kt111
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ExternalTypeTemplate.kt9
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/FfiConverterTemplate.kt71
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Float32Helper.kt19
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Float64Helper.kt19
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ForeignExecutorTemplate.kt83
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Helpers.kt161
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Int16Helper.kt19
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Int32Helper.kt19
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Int64Helper.kt19
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Int8Helper.kt19
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/MapTemplate.kt34
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/NamespaceLibraryTemplate.kt57
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ObjectRuntime.kt161
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ObjectTemplate.kt138
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/OptionalTemplate.kt27
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/RecordTemplate.kt42
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/RustBufferTemplate.kt87
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/SequenceTemplate.kt23
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/StringHelper.kt53
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/TimestampHelper.kt38
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFunctionTemplate.kt51
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Types.kt109
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/UInt16Helper.kt19
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/UInt32Helper.kt19
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/UInt64Helper.kt19
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/UInt8Helper.kt19
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/macros.kt77
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/wrapper.kt57
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/test.rs131
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/mod.rs125
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/callback_interface.rs31
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/compounds.rs108
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/custom.rs26
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/enum_.rs39
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/error.rs31
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/executor.rs18
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/external.rs26
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/miscellany.rs34
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/mod.rs474
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/object.rs31
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/primitives.rs71
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/record.rs31
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/mod.rs37
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/Async.py40
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/BooleanHelper.py16
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/BytesHelper.py16
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceRuntime.py77
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceTemplate.py105
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/CustomType.py58
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/DurationHelper.py21
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py97
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/ErrorTemplate.py70
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/ExternalTemplate.py9
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/Float32Helper.py8
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/Float64Helper.py8
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/ForeignExecutorTemplate.py63
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/Helpers.py75
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int16Helper.py12
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int32Helper.py12
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int64Helper.py12
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int8Helper.py12
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/MapTemplate.py27
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/NamespaceLibraryTemplate.py83
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py87
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/OptionalTemplate.py21
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/PointerManager.py68
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py49
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/RustBufferHelper.py59
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/RustBufferTemplate.py211
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/SequenceTemplate.py19
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/StringHelper.py33
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/TimestampHelper.py32
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/TopLevelFunctionTemplate.py38
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/Types.py102
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt16Helper.py12
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt32Helper.py12
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt64Helper.py12
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt8Helper.py12
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/macros.py147
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/wrapper.py74
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/test.rs69
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/ruby/gen_ruby/mod.rs375
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/ruby/gen_ruby/tests.rs47
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/ruby/mod.rs48
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/EnumTemplate.rb59
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/ErrorTemplate.rb121
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/Helpers.rb18
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/NamespaceLibraryTemplate.rb17
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/ObjectTemplate.rb73
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/RecordTemplate.rb20
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/RustBufferBuilder.rb264
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/RustBufferStream.rb315
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/RustBufferTemplate.rb236
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/TopLevelFunctionTemplate.rb16
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/macros.rb73
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/wrapper.rb52
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/ruby/test.rs52
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/callback_interface.rs26
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/compounds.rs108
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/custom.rs26
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/enum_.rs35
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/executor.rs23
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/external.rs36
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/miscellany.rs31
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/mod.rs688
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/object.rs26
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/primitives.rs91
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/record.rs26
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/mod.rs97
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Async.swift62
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/BooleanHelper.swift20
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/BridgingHeaderTemplate.h79
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/CallbackInterfaceRuntime.swift64
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/CallbackInterfaceTemplate.swift150
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/CustomType.swift86
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/DataHelper.swift14
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/DurationHelper.swift24
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/EnumTemplate.swift59
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ErrorTemplate.swift86
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Float32Helper.swift12
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Float64Helper.swift12
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ForeignExecutorTemplate.swift69
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Helpers.swift101
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Int16Helper.swift12
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Int32Helper.swift12
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Int64Helper.swift12
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Int8Helper.swift12
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/MapTemplate.swift22
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ModuleMapTemplate.modulemap6
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift138
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/OptionalTemplate.swift20
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift62
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/RustBufferTemplate.swift183
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/SequenceTemplate.swift21
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/StringHelper.swift37
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/TimestampHelper.swift34
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/TopLevelFunctionTemplate.swift48
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Types.swift98
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/UInt16Helper.swift12
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/UInt32Helper.swift12
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/UInt64Helper.swift12
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/UInt8Helper.swift12
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/macros.swift89
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/wrapper.swift68
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/test.rs202
-rw-r--r--third_party/rust/uniffi_bindgen/src/interface/callbacks.rs149
-rw-r--r--third_party/rust/uniffi_bindgen/src/interface/enum_.rs567
-rw-r--r--third_party/rust/uniffi_bindgen/src/interface/ffi.rs227
-rw-r--r--third_party/rust/uniffi_bindgen/src/interface/function.rs367
-rw-r--r--third_party/rust/uniffi_bindgen/src/interface/mod.rs1234
-rw-r--r--third_party/rust/uniffi_bindgen/src/interface/object.rs773
-rw-r--r--third_party/rust/uniffi_bindgen/src/interface/record.rs230
-rw-r--r--third_party/rust/uniffi_bindgen/src/interface/universe.rs136
-rw-r--r--third_party/rust/uniffi_bindgen/src/lib.rs519
-rw-r--r--third_party/rust/uniffi_bindgen/src/library_mode.rs287
-rw-r--r--third_party/rust/uniffi_bindgen/src/macro_metadata/ci.rs145
-rw-r--r--third_party/rust/uniffi_bindgen/src/macro_metadata/extract.rs192
-rw-r--r--third_party/rust/uniffi_bindgen/src/macro_metadata/mod.rs24
-rw-r--r--third_party/rust/uniffi_bindgen/src/scaffolding/mod.rs109
-rw-r--r--third_party/rust/uniffi_bindgen/src/scaffolding/templates/CallbackInterfaceTemplate.rs82
-rw-r--r--third_party/rust/uniffi_bindgen/src/scaffolding/templates/Checksums.rs7
-rw-r--r--third_party/rust/uniffi_bindgen/src/scaffolding/templates/EnumTemplate.rs19
-rw-r--r--third_party/rust/uniffi_bindgen/src/scaffolding/templates/ErrorTemplate.rs26
-rw-r--r--third_party/rust/uniffi_bindgen/src/scaffolding/templates/ExternalTypesTemplate.rs20
-rw-r--r--third_party/rust/uniffi_bindgen/src/scaffolding/templates/ObjectTemplate.rs89
-rw-r--r--third_party/rust/uniffi_bindgen/src/scaffolding/templates/RecordTemplate.rs16
-rw-r--r--third_party/rust/uniffi_bindgen/src/scaffolding/templates/ReexportUniFFIScaffolding.rs28
-rw-r--r--third_party/rust/uniffi_bindgen/src/scaffolding/templates/TopLevelFunctionTemplate.rs15
-rw-r--r--third_party/rust/uniffi_bindgen/src/scaffolding/templates/UdlMetadata.rs16
-rw-r--r--third_party/rust/uniffi_bindgen/src/scaffolding/templates/macros.rs28
-rw-r--r--third_party/rust/uniffi_bindgen/src/scaffolding/templates/scaffolding_template.rs52
200 files changed, 16787 insertions, 0 deletions
diff --git a/third_party/rust/uniffi_bindgen/.cargo-checksum.json b/third_party/rust/uniffi_bindgen/.cargo-checksum.json
new file mode 100644
index 0000000000..880d79a96f
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{"Cargo.toml":"5285a403f48ecf7a073cbe1839c7afbd0e14998e8315b1ff8ecefb9e7171fc4e","askama.toml":"1a245b7803adca782837e125c49100147d2de0d5a1c949ff95e91af1701f6058","src/backend/config.rs":"4861dbf251dbb10beb1ed7e3eea7d79499a0de1cd9ce9ee8381a0e729c097dea","src/backend/filters.rs":"2da4eaa9af92e449f2fa20d06fc2ab2f758a9a10d3ad6cb8a94975184d40d2ff","src/backend/mod.rs":"2ee9d974cd259f7fb156028b4f4f7601691e94fb5692a6daf0d362df3ecf79a8","src/backend/types.rs":"598df3a861f5d53b2c710848943f6049dd43cb4f37aa81f2c08fd36fc5b2f5d5","src/bindings/kotlin/gen_kotlin/callback_interface.rs":"a6c7796ca66cbaabeef401b939d3c707bba17a77581da36a3a0b46f87630440e","src/bindings/kotlin/gen_kotlin/compounds.rs":"ebd2111a74032b336e0768facfb756a9422da2f9b413ee929b24c1c4315e6a06","src/bindings/kotlin/gen_kotlin/custom.rs":"7e619f7320796ecd8c4ced82904b4bd3c6a0043b55d5829274ab3040cdf9cd7f","src/bindings/kotlin/gen_kotlin/enum_.rs":"6559bb00d8e359126b016e549263c0c9bc1dfc5654ed662c0c2912b47931b1e4","src/bindings/kotlin/gen_kotlin/executor.rs":"58a192123fd2dd4b625f29d95ae6bf5161c2fef7bf50aa8790c3ae0e7a9430d9","src/bindings/kotlin/gen_kotlin/external.rs":"bcd2a44f2559a124aa287944ab59296239033372c6b4a7a3b625b1d41c441de2","src/bindings/kotlin/gen_kotlin/miscellany.rs":"6541987e627c4ff27a17ebe919b2b5cd97cb66ff41187ed636396b4e35ea2414","src/bindings/kotlin/gen_kotlin/mod.rs":"ccae80314058df0b4988d0965ab62b0dc872e8676592e7b55037cd54011e6854","src/bindings/kotlin/gen_kotlin/object.rs":"539ec05386c1e844bef09d4de8374760daa5ba99b009615c04be9c3927feb4c9","src/bindings/kotlin/gen_kotlin/primitives.rs":"2c3020416384a67855ca5086e485c4db6d7dcc3ce51343217b4a914b318ae350","src/bindings/kotlin/gen_kotlin/record.rs":"96fd1a180095a062b4a9b71d4f603b232f0133f46355a3e427c4064701d900f2","src/bindings/kotlin/gen_kotlin/variant.rs":"d111d6888745195fc2c24bdddc57359e771616102a8d182c5c8ad268b0a13460","src/bindings/kotlin/mod.rs":"ef88eb9b5b7d6f920c62a525ea4d4bf2a3b1a9154afaa012cdb2feea597fbf23","src/bindings/kotlin/templates/Async.kt":"064ce385ac0e68719de625e0172908257714b62da296ff14b2c0153b9966212a","src/bindings/kotlin/templates/BooleanHelper.kt":"28e8a5088c8d58c9bfdbc575af8d8725060521fdd7d092684a8044b24ae567c7","src/bindings/kotlin/templates/ByteArrayHelper.kt":"6fbb424556f631beb7f28c4168c568ad840445496a29d5c8f40a9a591b1661b1","src/bindings/kotlin/templates/CallbackInterfaceRuntime.kt":"29fab10a8f6b699471e793e8d53f5bed74803a8c433ff80ccef5f56cf742c54a","src/bindings/kotlin/templates/CallbackInterfaceTemplate.kt":"3b063ea03959c95327b6082c7edc0db0df83ee8b9e8d643b92ca45cf4fda458a","src/bindings/kotlin/templates/CustomTypeTemplate.kt":"be9bdc716731f3935a4d48728e33bfeb4acd514f3719ddbb273adcd6fb4ab31f","src/bindings/kotlin/templates/DurationHelper.kt":"414a98161538a26f3a9b357353270c1f245ad6ceed99496aca7162cf473a92fd","src/bindings/kotlin/templates/EnumTemplate.kt":"865fb1badd1a128390903ab8d9f42f9208c6db0eac5e53b88207282176cfd67f","src/bindings/kotlin/templates/ErrorTemplate.kt":"394c0093c0c86a0f2a14cd5fa60a70ba9970c65448867b6aca86fc25cfe08a4c","src/bindings/kotlin/templates/ExternalTypeTemplate.kt":"40df49116f9ea227c9a64a4f45bb7c2e99275c62e93f75290808e2c930911fba","src/bindings/kotlin/templates/FfiConverterTemplate.kt":"aa22962aaa9f641d48ccf44cb56d9f8a7736cbfaa01e1a1656662cfe5dd5c1d7","src/bindings/kotlin/templates/Float32Helper.kt":"662d95af3b629d143fb4d47cb7e9aa26ed28a5f3846de0341e28b0f9fb08bc25","src/bindings/kotlin/templates/Float64Helper.kt":"a77d099fa7d91e8702c1700e7949ffb6aaba9c6e8041ff48bab34b8e1fc9a0aa","src/bindings/kotlin/templates/ForeignExecutorTemplate.kt":"09c63a67adb8c6cb807108f02d7695d3425401ea0cc51b582cfd469a322fcce0","src/bindings/kotlin/templates/Helpers.kt":"90a11ec576e82265ba0f95fc330053779aada5976477f0d4a6b38619da1282cf","src/bindings/kotlin/templates/Int16Helper.kt":"7f83c4a48e1f3b2a59a3ca6a2662be8bc9baf3a5a748b31223cb3f51721ef249","src/bindings/kotlin/templates/Int32Helper.kt":"e02e4702175554b09fd2dd6ac3089dcd2c395f08ec60e762159566a9c9889450","src/bindings/kotlin/templates/Int64Helper.kt":"7a6fd6ca486852c89399c699935a9dfa1c32b9356d9a965cfde532581f05d9fa","src/bindings/kotlin/templates/Int8Helper.kt":"0554545494b6b9a76ce94f9c1723f8cf4230a13076feb75d620b1c9ca1ac4668","src/bindings/kotlin/templates/MapTemplate.kt":"07d20d8cf58a4bca950ac22dbec5e3471ac6c18c3cca562e45628de827b03ccf","src/bindings/kotlin/templates/NamespaceLibraryTemplate.kt":"22226bb8dde52f12c77b40bbb8f7ceb7dcef313950b8d42b2f5fe44777158bfc","src/bindings/kotlin/templates/ObjectRuntime.kt":"7f38f54a0c889d7534d23afdace6b87b6ced5c024a36b5450078a06e071caad8","src/bindings/kotlin/templates/ObjectTemplate.kt":"3f3baea52b6923446827ea1ee3f5160edfe81e00c11f61ea1f72dbc6b796a6d8","src/bindings/kotlin/templates/OptionalTemplate.kt":"c916c4545d37087ee01a6b6aef966928691d26be539c388fce608e9e3ff4b0e7","src/bindings/kotlin/templates/RecordTemplate.kt":"8d573856de75b55b985594ac4e4a6f08da931dce6b52420654b5bb080eec414f","src/bindings/kotlin/templates/RustBufferTemplate.kt":"002878ce9ce9924231e55853b86768fc3dec2caef6a28098f01c3edd739ca076","src/bindings/kotlin/templates/SequenceTemplate.kt":"477a0f6714af151ca58cfc7c4f2cf0e878d391dd9db4efe964f19d5ad4f544f0","src/bindings/kotlin/templates/StringHelper.kt":"ec0441da90a394616d0ba3492eca50602161fe42062bc4f60e9a23191e71e009","src/bindings/kotlin/templates/TimestampHelper.kt":"353c2890f06ad6dda238e9aebb4bdff7bb838e17e46abf351ed3ff1fbc4e6580","src/bindings/kotlin/templates/TopLevelFunctionTemplate.kt":"888ef82e2637ab104f0821803666c77212b5d5940414f71e899f8f8968ffe572","src/bindings/kotlin/templates/Types.kt":"4d87ef529f666db532fb5355a339fd50be3edd6225a703564455b81ef4d4a16d","src/bindings/kotlin/templates/UInt16Helper.kt":"e84a1f30a5a899ba2c5db614d3f3c74f25bccf6dd99bf68b8830829332d051e9","src/bindings/kotlin/templates/UInt32Helper.kt":"7cdf08cc580046935f27ba07b53685968608a102e0a6be305111037c63d7ddf8","src/bindings/kotlin/templates/UInt64Helper.kt":"fd7baacbf3ab6202ff83edcc66e5f7beb11a10053ba66d0b49547616cc7cbe1f","src/bindings/kotlin/templates/UInt8Helper.kt":"bbf5a6d66c995aea9fe2fa9840c6bfa78b03520a09469b984f0e1d43191e453a","src/bindings/kotlin/templates/macros.kt":"0f64366a9d7523b3d20d7e9d8b04eb064568772dec529a12f878acf5d2246a41","src/bindings/kotlin/templates/wrapper.kt":"d515ca22d12f13b1b99c5daa411ea35d9a288076e4b7208eaf88170e5da7477c","src/bindings/kotlin/test.rs":"0f752ab0afde20194afca07af94da9d1422300032696d5f845cd864fc63c5d51","src/bindings/mod.rs":"949f323d6eb5c018497103dbb9dcffb8f395eb5960694b551a24b4887e853afb","src/bindings/python/gen_python/callback_interface.rs":"5df3e091d3c88ef7645e570f693942161a9b9c6307419c15a2534fbc5da974af","src/bindings/python/gen_python/compounds.rs":"9b7187d35826e1b12dbc2b16a13aec783a51f0952e3e2d24adaefbd0ac005016","src/bindings/python/gen_python/custom.rs":"81501641648eb638f5a338c01a71db0d0e96601c3dda83acdb2d49072b387d42","src/bindings/python/gen_python/enum_.rs":"7c3f8f6a97c1491175c8b93b8f9ab13748e2f8084bb717836b6935d024805439","src/bindings/python/gen_python/error.rs":"161bd2e041e3a63a91899de173eec8450cc10e1e9552d064969aa72a02fdfd5e","src/bindings/python/gen_python/executor.rs":"dbbf2292c79f73dacb317a8645185e42f328a017951662975c0488399c562058","src/bindings/python/gen_python/external.rs":"0325e9a39645eb5454d716d4db76a4a31083ddfafa8e9ca063257292372a0637","src/bindings/python/gen_python/miscellany.rs":"d6f6305dd0af85b7ba87b70cbe6ecba00c83d5082c5bdcaf25962fff853973ea","src/bindings/python/gen_python/mod.rs":"4ac0dc0fd9aaabf2e1f9245d13e0090ee0e9c1235ef203bec9314cac5974e9f0","src/bindings/python/gen_python/object.rs":"a4d4c20a0a52687feff2b9a547a13aa9bda09b3af9ec26508646658a88eec8b3","src/bindings/python/gen_python/primitives.rs":"b830c68e20d8539b8ac5566f1ca0dd262c1b14712a747f79e70004cd8f409ba1","src/bindings/python/gen_python/record.rs":"f8e12ce43d7e0f37f05420a849e7867b7251f9790933609a4cb99050fd063089","src/bindings/python/mod.rs":"eac32ce383460d58d3ccf1d406173465fc8a1db8a24408df67620b7d14dcd0cd","src/bindings/python/templates/Async.py":"4a35a878883a548f3bbed929a9ec74c133e2e9cf08375989503e73ddc2f9c648","src/bindings/python/templates/BooleanHelper.py":"c19e38ae3daa29a831f2394a0a2e74c924711e55ddc85db8ac9b5b8b6da9cd92","src/bindings/python/templates/BytesHelper.py":"e8fb9919acc784fb056bba4ab8d5c04ca7b2275211f8397ef2a391833e3d5e8f","src/bindings/python/templates/CallbackInterfaceRuntime.py":"795d8826d5d2b397a91c531c6b1b76d9425728efdcb90514170c8c4f35053e40","src/bindings/python/templates/CallbackInterfaceTemplate.py":"3f38e7b290fce198d188a63dcfa74486810153816283e683d6e04896c2dbea9a","src/bindings/python/templates/CustomType.py":"12064dde5e1baf4d78e541837c414cdc7ee9e827a284c54a98ac92dfaf3478e8","src/bindings/python/templates/DurationHelper.py":"271c301bc480cd48d5df2aec15789dd360f4d3098a9f360d7f8f33fa0a7fcd0a","src/bindings/python/templates/EnumTemplate.py":"1cbc2206f045c3050be1912df581a5393d6f0a4a79c96d8b49661696d25830e0","src/bindings/python/templates/ErrorTemplate.py":"f9ab6c910024e88ff92a7575d5d00cdafe448b2e85d07a9edea57ae7b6dc5864","src/bindings/python/templates/ExternalTemplate.py":"ed4d65caf2de3fd2c2a3fd2658eb44cf91cd2f0878c017be63afa4394bf56be3","src/bindings/python/templates/Float32Helper.py":"80c0a0619d2c58c100ea8db37125878c8e8cf56c42f77195aa9b4b6b6d5716c8","src/bindings/python/templates/Float64Helper.py":"62c3ed0d646d3383c890d1f8fd7cb8639433971b9ba9261ac43c1391472eb141","src/bindings/python/templates/ForeignExecutorTemplate.py":"6a7903acc65b9dac17524767d94b142d38d25d5f5bb27133ee9fd7ed8fb5f5a9","src/bindings/python/templates/Helpers.py":"3265eeb5917e0090a7dbd50fdbd12e7d1b1832a58b347cd003ecf8a433c9ebcd","src/bindings/python/templates/Int16Helper.py":"ef7fd0035a80aef556bdbfbcf074751d4e25661f4e07f9bf41f48601d171e5aa","src/bindings/python/templates/Int32Helper.py":"af7e0176ed41260089426498946e47565a7d57e98dffaae4562dfe541c3019d1","src/bindings/python/templates/Int64Helper.py":"171908319be9edcfe3b178d1d74f0173df2aae6a4a92895a2079fa476caed7df","src/bindings/python/templates/Int8Helper.py":"d02a4a5452ec1096b1b1953e4d661d699f9f8f0ce5086a6f3577a0119f479666","src/bindings/python/templates/MapTemplate.py":"fd0bd7e396a6288a16ecb3dae087ab725be556789887a4dc0c00ab97d815f3fb","src/bindings/python/templates/NamespaceLibraryTemplate.py":"b78b7161d43a95f5a0b5d7da6cae0d8bf47017c6c77ad210e15be9531413baa9","src/bindings/python/templates/ObjectTemplate.py":"3af0c737d1b482169d62dee1b6c49f1a18ea3f485e9fc323b070f35cf00c242c","src/bindings/python/templates/OptionalTemplate.py":"59df962441fb1c50cb99be014c87ecf69450c1a3b60fa6763bd40e9e948124b1","src/bindings/python/templates/PointerManager.py":"22faf6a2801cf756f3b09415b597f0cd403a3872ac99a7e44e3b7b6217606cd7","src/bindings/python/templates/RecordTemplate.py":"04fcbd662bc9817597366046e09321d2516eb8240e796dd9b6f971c347475429","src/bindings/python/templates/RustBufferHelper.py":"8a8c20d195534e465a173dc778ae98957c50e209ec824af2d2b143f5ba6061f5","src/bindings/python/templates/RustBufferTemplate.py":"f5e247b0f8988f29ce94ab50b5fe7749d0423cda1d37b6145cc8a6c5a9818449","src/bindings/python/templates/SequenceTemplate.py":"047f19074fe08982b59001da2ba7318b331ce431d73503e111330579a3ba065f","src/bindings/python/templates/StringHelper.py":"a3f874ea9330413e854c2ebbeff5507e32a166de6967c5cea63ede1f021e267a","src/bindings/python/templates/TimestampHelper.py":"de099ce51ceaa86519c28bb38e21933ead36ff341f4907695029212bcdfed3ac","src/bindings/python/templates/TopLevelFunctionTemplate.py":"93b6101fae2cafdf1a9325bed07019609ac35bacef2dc31ba4be5c256d827473","src/bindings/python/templates/Types.py":"feb69d895e9279e52479146e1dfe2fec48c547378ef2cc0fb988f6acaf6bcc63","src/bindings/python/templates/UInt16Helper.py":"5fbb30ece1f9a2680b60baf680ec4e2936d64de2ff107018e751ef1c041443ef","src/bindings/python/templates/UInt32Helper.py":"84207c380e38a38bb919d58769384a0f4fa175ebbd04ac451b37ccfe01ff68a1","src/bindings/python/templates/UInt64Helper.py":"4606f381834740319a9f604a418ba149917a6dbd43d3d3d8da50c655893e2c8e","src/bindings/python/templates/UInt8Helper.py":"aecf7cf08b7dc75fe81e8dcac78dfce166b132d5c20b4301d845c479ab9f49ba","src/bindings/python/templates/macros.py":"7d0d08f418edf65ff365bf1fb37e3132aebb720dfecdcef3e3ce50529fe0eb6b","src/bindings/python/templates/wrapper.py":"91e8cbf18e5b7d0b2be31c0e09b230318d19406c316b453dab973341eb2c6add","src/bindings/python/test.rs":"48e93959ce3e34ff0191126416301b170239d3e2665711da786e0b8b7a90a2ab","src/bindings/ruby/gen_ruby/mod.rs":"7f3a94537c331a941e6e010e35563247f11f1fedaf971cb8538e17797cb17efa","src/bindings/ruby/gen_ruby/tests.rs":"7dcb86b08e643c43503f4cac6396833497f6988b004321c0067700ee29ffbf32","src/bindings/ruby/mod.rs":"0fdfab5306dc5c05fbcbfb273340d96ad70c5caf5074834ad6851af1a9a56734","src/bindings/ruby/templates/EnumTemplate.rb":"5480edb347f5829e478d19474691babd72f37616ed846d519b5a61cb1d6cf047","src/bindings/ruby/templates/ErrorTemplate.rb":"301c177e639f0a27f19d4935c5317e672aadecbee2f9bfa778df982320f5148d","src/bindings/ruby/templates/Helpers.rb":"ce7ed4be97dad507b991c69c28dc7bb6427e5e79a4b2fba9dad9dccabc3e090c","src/bindings/ruby/templates/NamespaceLibraryTemplate.rb":"9a24c427b9eba99d9e13181a5559a385b5d1d16beae2b72a402f2645b22a9048","src/bindings/ruby/templates/ObjectTemplate.rb":"0cfd9438e4821cf2164b23d748b3227a8cffbe2fab5b7eb70832228ccb628ee0","src/bindings/ruby/templates/RecordTemplate.rb":"4aeff886928ca972e5dc9b799581b30c66a6f6dce446af3285dd3ed6b422dea9","src/bindings/ruby/templates/RustBufferBuilder.rb":"8da4e425b36dde4f171b238cbe57e02fb55e91a45a82134c1dccc0fc360733c0","src/bindings/ruby/templates/RustBufferStream.rb":"43ad2defc772fd24b68df0736533c26597ba007e89b6a5ba0d31fbe356648151","src/bindings/ruby/templates/RustBufferTemplate.rb":"405a32592cab145175b64e21398f83e6e0f16354552c0395479270e732910e08","src/bindings/ruby/templates/TopLevelFunctionTemplate.rb":"88213e7e25bef664da939c04dd5621f438af735ffcb4d2d0c24a529538630069","src/bindings/ruby/templates/macros.rb":"79d7d0e9af749dadbf242f37c0f86af7c616ea5318da127747def40f6cdb20d1","src/bindings/ruby/templates/wrapper.rb":"f82b41543546f8e5804cd0e1785f4735d9dd95383566d0e5ba1cd4d9e8c0578d","src/bindings/ruby/test.rs":"d19837119725233bd9971ca2dfc3256156071c64e6dfaf07ad2307432c055bbb","src/bindings/swift/gen_swift/callback_interface.rs":"6b51276350f506f96fefd0ae8cb3afdcd514e8a529d9e982afc68cbf68d74578","src/bindings/swift/gen_swift/compounds.rs":"1aba37cf2f438423a4ce476eea6a36f71f1d5daddbb77c88556bc3abde287ca7","src/bindings/swift/gen_swift/custom.rs":"bddb601b4ea8810ecbad01271d5ec0b3958999b09bc9382c83637dfd43451734","src/bindings/swift/gen_swift/enum_.rs":"87be67ec3394616368d9ef8e99b7f234c053b3bee9a7f9e6f2dff37f147c8837","src/bindings/swift/gen_swift/executor.rs":"ab672e2d05acbc2c4a839af22034aa557d5e69f1d9c913158310ea1e93851557","src/bindings/swift/gen_swift/external.rs":"321974136d58e649e60b2a3f70a369dce2d49f474f79579f8e0d66eb63d2d634","src/bindings/swift/gen_swift/miscellany.rs":"7fc2444596d76545ad82ee6c4bed64a29dd4a0438d50bfaafe511f41f6a0e409","src/bindings/swift/gen_swift/mod.rs":"41e4bf2fbe622d0dba85363455e287e00dc48e4043910cef35c30ce170acf52b","src/bindings/swift/gen_swift/object.rs":"2269f65a6b58a24bd08fedb133a38b37663bcf11d0586c50a67028022706a156","src/bindings/swift/gen_swift/primitives.rs":"c8346601008ac6a6d07f08ec7395182c45a4d86c163dc1d6d9c326c49f2acda1","src/bindings/swift/gen_swift/record.rs":"5ad98ab04a5d8178daf0956db819c87d26aae7bf968184e88d512e34c02feb90","src/bindings/swift/mod.rs":"26ba270cb7913661f3cee703038d1ea4a70bff64c3b31351d6bc77e67cdee20d","src/bindings/swift/templates/Async.swift":"84b9be2b5eca2dcfad7eee0cb8d34fec613a4bfdc8a7170b8d11575e457f567b","src/bindings/swift/templates/BooleanHelper.swift":"f02e391bed44ca0e03c66c4e6a1545caaae490fc72c4cf5935e66220082a8c30","src/bindings/swift/templates/BridgingHeaderTemplate.h":"3f468869e77b9293836822b8f2ac348716ab5d487f7b8fef1a87ec30ddafa8d5","src/bindings/swift/templates/CallbackInterfaceRuntime.swift":"2c71ac715ad0bca6f73559748453ba37ca242c90de38f76876989be05d21c49b","src/bindings/swift/templates/CallbackInterfaceTemplate.swift":"790f50b49d5b07dd44e8b215ed1fff02991c1f65aba9a9a9925275a544348813","src/bindings/swift/templates/CustomType.swift":"71520eb38a4be9035dca9e3e0402f386e7eaa79b28568bbc2f20d3fd53b4544d","src/bindings/swift/templates/DataHelper.swift":"df11547a2df57dcca0ff9cddc691bb5fa07d5ffd3d328d1c3b4443078008b111","src/bindings/swift/templates/DurationHelper.swift":"cbc41aaa58bda6c2313ede36a9f656a01a28f9c72aa1624e0e1c1da7b841ffb6","src/bindings/swift/templates/EnumTemplate.swift":"fe205dd28defea8ed6126a45b2a95240a920dfebda8927134a50c3b6d0d7e9d7","src/bindings/swift/templates/ErrorTemplate.swift":"3dddb278763b75b38294c1165522fa91078a951ce05c91fbdfde43b5a097f34f","src/bindings/swift/templates/Float32Helper.swift":"ea32538058c4b3c72b1cd2530ac00d0349fadab5e1bc617d33aae4c87692fc98","src/bindings/swift/templates/Float64Helper.swift":"e27e9424dc6e97b8cacc6ca4c796dd2d16dcfcb877e2f19c45eca03381a41e78","src/bindings/swift/templates/ForeignExecutorTemplate.swift":"205933825e691fec525286d263ea34d592cc462257764ee76325bf98cb3cd240","src/bindings/swift/templates/Helpers.swift":"a88fd909787b855998671e551cdb3284109e2fd2b2e7492b1c93c82aad0e9d35","src/bindings/swift/templates/Int16Helper.swift":"204906911813a3931436057c23032f4c4e39e023df90d641d6c6086aefe2f820","src/bindings/swift/templates/Int32Helper.swift":"0997f059c9c4edd3c41aee0bbad4aa2bda6d791a0d623ad8014d5aa6bdae718d","src/bindings/swift/templates/Int64Helper.swift":"bcf8c2deebb3ee9bce87735adc4bd100981989943b69f6a7fb499a9aec4c25d9","src/bindings/swift/templates/Int8Helper.swift":"ad1ec0fa213724933fa4dc4e2e304e13ac4722b774bfffac44793986b997dd33","src/bindings/swift/templates/MapTemplate.swift":"53971ec388417b02519f8deb8d66361ab4693eae77d116f6051cbea4738054ec","src/bindings/swift/templates/ModuleMapTemplate.modulemap":"99ad1e9bf550a21497296f9248ecd4385dd6d0b5892951d24cf990cdbf3eec2c","src/bindings/swift/templates/ObjectTemplate.swift":"4633572ac6d27a0f82f3b125c2136ad4fa126391c0b64b5db1bde4b04a77f807","src/bindings/swift/templates/OptionalTemplate.swift":"2376487ceadca3975f0e82ddf6ce61af8bbbf5b0592fa9cd977460f148d8c99d","src/bindings/swift/templates/RecordTemplate.swift":"16e0b98354b624a8922d7d384a005fa660a6a388d70381872ebbcd0de9fb78a4","src/bindings/swift/templates/RustBufferTemplate.swift":"f4422fdf0cb5b4db267d461f063dedc319ea1a5a13bae1b82c3f108ba8c658bb","src/bindings/swift/templates/SequenceTemplate.swift":"8425b279197582a94b4cf363ab0463907a68a624e24045720ef7df2bcacf3383","src/bindings/swift/templates/StringHelper.swift":"968b9b9b7fbe06a2ac2143016edaff3552e201652accb8d613b03645f0d24a90","src/bindings/swift/templates/TimestampHelper.swift":"82eece13aa186c8e3745c8ad2f1290639ca9689573018a2bdc5c75afbae58c26","src/bindings/swift/templates/TopLevelFunctionTemplate.swift":"ffe0287861e67dcad2be77f30c00c0a326ab59ffbd37409de3bafc969d49df26","src/bindings/swift/templates/Types.swift":"98c654bfc5d2d4ece965cfe15b00e7151b815e26bfb55abf17e799efffccc2c0","src/bindings/swift/templates/UInt16Helper.swift":"d6fba577324fc0e9e50329c968df99341de418011be126bd29702f8a94d87c02","src/bindings/swift/templates/UInt32Helper.swift":"5e0cf151a0c40098b3d96815ba3de23e15fe52f3c517577e1b0c7e7a4c12428f","src/bindings/swift/templates/UInt64Helper.swift":"17237b38d09ced8d2a8ff1ad9ca86873a19e417280e0e60f33d7063397ea4b7b","src/bindings/swift/templates/UInt8Helper.swift":"c4cb2ee4a78b54ca8d0013383c6b43e9ecd42776e3dc7d6e40086325d41714e5","src/bindings/swift/templates/macros.swift":"438091831b355b0ba6726dab7c17c0687ca58854c7799e946a8012e95a42f4ba","src/bindings/swift/templates/wrapper.swift":"d83a1b8ac3ffc761d4e560adae57d5ad275e0fd1bf3fdc35f3b3b3699317c6e6","src/bindings/swift/test.rs":"c15d19e7f324613e2dbd7995dffba875ed430919a0882f05e8e1f6cc8aea613c","src/interface/callbacks.rs":"34384f1a4e89cd30e2c35beab2bbd8cc0a9e3dd21ed0b390859610fbe209b16a","src/interface/enum_.rs":"3a4f9e77d80128444a8a226ef1e5ad7cd339f8d815aa7012554cf94c2e4edd98","src/interface/ffi.rs":"fa23a2e6fcd89d956523bd0aa5c946138ac629752f0be9085e09f78aade75fac","src/interface/function.rs":"835ee542cb19c67fb95502a790771e54deb712272baed8bdc3f6bfabd9c69d6f","src/interface/mod.rs":"5bd3b5a2dd91382089323a21d4d0419dd5621799add4786a4710df0afebcca69","src/interface/object.rs":"2364c4e7f887d6e4256d065f9ad049ffad19f92fe1c1d76789c33a22376bdd7f","src/interface/record.rs":"931dbaa8cb440debfcf14d51be3e6c517ba2c28655033ad2486e384a9bea25c4","src/interface/universe.rs":"66deaa33394e401e3005670731a0685068302fc01640655335a7cbe9fb4cf48e","src/lib.rs":"64a6239dbaa8196089600e7b0c577b43eefc4a9935893292298bf400fd77a03d","src/library_mode.rs":"89c9fc47db09ccb779fd2842d3c7e07b7863164bb5585eeaf959490ca5555438","src/macro_metadata/ci.rs":"07482b87bf5912277d114b24ad3e560cac94ca0087bd6ee9bee2c328d992bb18","src/macro_metadata/extract.rs":"a106a5b6ad8e5deba474646162b83ec4065796c58c60fc13dfbf1a72ed713833","src/macro_metadata/mod.rs":"bcb5e9a015510e9d74c288da928a4bfb8d80926a8ff85227c0eae8cdb2605519","src/scaffolding/mod.rs":"65749e72181c63edae55d49493aa49a1efece93c0a27deda230f5de4b9ba7d60","src/scaffolding/templates/CallbackInterfaceTemplate.rs":"0581f257c0eb7f0be922ef3ebf7892aafad15e9dc7865e53fc9396375f3188fb","src/scaffolding/templates/Checksums.rs":"ee926e840875c2e48e1d0cc5185c11f7a1ed3bde5264b07540812cb13c1d7481","src/scaffolding/templates/EnumTemplate.rs":"350cdcb4f23be6e6e2b442e0d4ea65549bb35a407bef1ac745a37a2e2b527fae","src/scaffolding/templates/ErrorTemplate.rs":"b93e8bc08d818fbf8976b766635ae192042b78c85f8086d4ee41ae03ab7f3b6f","src/scaffolding/templates/ExternalTypesTemplate.rs":"6cf8a89d9e6f1b6f5af4bc86e49454323fa4981846ec76d6661fcff41c348a8e","src/scaffolding/templates/ObjectTemplate.rs":"df614156bee529fd28905a6ad2270685e51736eade6af856edba5de6e9484141","src/scaffolding/templates/RecordTemplate.rs":"5b0f351739d5770f874fb7da56700c557df1d73ac219f3b18c4212d26fc422e0","src/scaffolding/templates/ReexportUniFFIScaffolding.rs":"aa8a1ffa98b6033707d965f90b5709474ed6bc79486fb47dacae8417fc056cf8","src/scaffolding/templates/TopLevelFunctionTemplate.rs":"96b99b38d074492673797737ddac0683c803a3908e03cc9b999d16f7a76ed178","src/scaffolding/templates/UdlMetadata.rs":"d7c50af1de92ef85630b385a910c7b29875502d622eb90da5541a7012b93d9e2","src/scaffolding/templates/macros.rs":"ea6bacd8dd9116ad739bdafe893d70407050f35e4a7ac8dd2c78b8ef34263e8e","src/scaffolding/templates/scaffolding_template.rs":"c8e18306a73ec5b764f665660fc5c91d498b63b6c3f489e524b2bae50f81f231"},"package":"fd992f2929a053829d5875af1eff2ee3d7a7001cb3b9a46cc7895f2caede6940"} \ No newline at end of file
diff --git a/third_party/rust/uniffi_bindgen/Cargo.toml b/third_party/rust/uniffi_bindgen/Cargo.toml
new file mode 100644
index 0000000000..9469a9cf25
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/Cargo.toml
@@ -0,0 +1,81 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2021"
+name = "uniffi_bindgen"
+version = "0.25.3"
+authors = ["Firefox Sync Team <sync-team@mozilla.com>"]
+description = "a multi-language bindings generator for rust (codegen and cli tooling)"
+homepage = "https://mozilla.github.io/uniffi-rs"
+documentation = "https://mozilla.github.io/uniffi-rs"
+keywords = [
+ "ffi",
+ "bindgen",
+]
+license = "MPL-2.0"
+repository = "https://github.com/mozilla/uniffi-rs"
+
+[dependencies.anyhow]
+version = "1"
+
+[dependencies.askama]
+version = "0.12"
+features = ["config"]
+default-features = false
+
+[dependencies.camino]
+version = "1.0.8"
+
+[dependencies.cargo_metadata]
+version = "0.15"
+
+[dependencies.clap]
+version = "4"
+features = [
+ "std",
+ "derive",
+]
+optional = true
+default-features = false
+
+[dependencies.fs-err]
+version = "2.7.0"
+
+[dependencies.glob]
+version = "0.3"
+
+[dependencies.goblin]
+version = "0.6"
+
+[dependencies.heck]
+version = "0.4"
+
+[dependencies.once_cell]
+version = "1.12"
+
+[dependencies.paste]
+version = "1.0"
+
+[dependencies.serde]
+version = "1"
+
+[dependencies.toml]
+version = "0.5"
+
+[dependencies.uniffi_meta]
+version = "=0.25.3"
+
+[dependencies.uniffi_testing]
+version = "=0.25.3"
+
+[dependencies.uniffi_udl]
+version = "=0.25.3"
diff --git a/third_party/rust/uniffi_bindgen/askama.toml b/third_party/rust/uniffi_bindgen/askama.toml
new file mode 100644
index 0000000000..a0aae5f41b
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/askama.toml
@@ -0,0 +1,21 @@
+[general]
+# Directories to search for templates, relative to the crate root.
+dirs = [ "src/scaffolding/templates", "src/bindings/kotlin/templates", "src/bindings/python/templates", "src/bindings/swift/templates", "src/bindings/ruby/templates" ]
+
+[[syntax]]
+name = "kt"
+
+[[syntax]]
+name = "py"
+
+[[syntax]]
+name = "swift"
+
+[[syntax]]
+name = "c"
+
+[[syntax]]
+name = "rs"
+
+[[syntax]]
+name = "rb"
diff --git a/third_party/rust/uniffi_bindgen/src/backend/config.rs b/third_party/rust/uniffi_bindgen/src/backend/config.rs
new file mode 100644
index 0000000000..616cde0656
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/backend/config.rs
@@ -0,0 +1,17 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use serde::{Deserialize, Serialize};
+
+/// Config value for template expressions
+///
+/// These are strings that support simple template substitution. `{}` gets replaced by a value.
+#[derive(Debug, Default, Clone, Serialize, Deserialize)]
+pub struct TemplateExpression(String);
+
+impl TemplateExpression {
+ pub fn render(&self, var: &str) -> String {
+ self.0.replace("{}", var)
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/backend/filters.rs b/third_party/rust/uniffi_bindgen/src/backend/filters.rs
new file mode 100644
index 0000000000..0d2da8cab2
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/backend/filters.rs
@@ -0,0 +1,76 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! Backend-agnostic askama filters
+
+use crate::interface::{
+ AsType, CallbackInterface, ComponentInterface, Enum, FfiType, Function, Object, Record,
+};
+use askama::Result;
+use std::fmt;
+
+// Need to define an error that implements std::error::Error, which neither String nor
+// anyhow::Error do.
+#[derive(Debug)]
+struct UniFFIError {
+ message: String,
+}
+
+impl UniFFIError {
+ fn new(message: String) -> Self {
+ Self { message }
+ }
+}
+
+impl fmt::Display for UniFFIError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{}", self.message)
+ }
+}
+
+impl std::error::Error for UniFFIError {}
+
+macro_rules! lookup_error {
+ ($($args:tt)*) => {
+ askama::Error::Custom(Box::new(UniFFIError::new(format!($($args)*))))
+ }
+}
+
+/// Get an Enum definition by name
+pub fn get_enum_definition<'a>(ci: &'a ComponentInterface, name: &str) -> Result<&'a Enum> {
+ ci.get_enum_definition(name)
+ .ok_or_else(|| lookup_error!("enum {name} not found"))
+}
+
+/// Get a Record definition by name
+pub fn get_record_definition<'a>(ci: &'a ComponentInterface, name: &str) -> Result<&'a Record> {
+ ci.get_record_definition(name)
+ .ok_or_else(|| lookup_error!("record {name} not found"))
+}
+
+/// Get a Function definition by name
+pub fn get_function_definition<'a>(ci: &'a ComponentInterface, name: &str) -> Result<&'a Function> {
+ ci.get_function_definition(name)
+ .ok_or_else(|| lookup_error!("function {name} not found"))
+}
+
+/// Get an Object definition by name
+pub fn get_object_definition<'a>(ci: &'a ComponentInterface, name: &str) -> Result<&'a Object> {
+ ci.get_object_definition(name)
+ .ok_or_else(|| lookup_error!("object {name} not found"))
+}
+
+/// Get an Callback Interface definition by name
+pub fn get_callback_interface_definition<'a>(
+ ci: &'a ComponentInterface,
+ name: &str,
+) -> Result<&'a CallbackInterface> {
+ ci.get_callback_interface_definition(name)
+ .ok_or_else(|| lookup_error!("callback interface {name} not found"))
+}
+
+/// Get the FfiType for a Type
+pub fn ffi_type(type_: &impl AsType) -> Result<FfiType, askama::Error> {
+ Ok(type_.as_type().into())
+}
diff --git a/third_party/rust/uniffi_bindgen/src/backend/mod.rs b/third_party/rust/uniffi_bindgen/src/backend/mod.rs
new file mode 100644
index 0000000000..6abcfc10d7
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/backend/mod.rs
@@ -0,0 +1,11 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+mod config;
+pub mod filters;
+mod types;
+
+pub use crate::interface::{Literal, Type};
+pub use config::TemplateExpression;
+pub use types::CodeType;
diff --git a/third_party/rust/uniffi_bindgen/src/backend/types.rs b/third_party/rust/uniffi_bindgen/src/backend/types.rs
new file mode 100644
index 0000000000..3f66fdfe01
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/backend/types.rs
@@ -0,0 +1,78 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! # Backend traits
+//!
+//! A trait to help format items.
+//!
+//! Each backend will have its own `filter` module, which is used by the askama templates.
+use super::Literal;
+use std::fmt::Debug;
+
+// XXX - Note that this trait is not used internally. It exists just to avoid an unnecessary
+// breaking change for external bindings which use this trait.
+// It is likely to be removed some time after 0.26.x.
+
+/// A Trait to help render types in a language specific format.
+pub trait CodeType: Debug {
+ /// The language specific label used to reference this type. This will be used in
+ /// method signatures and property declarations.
+ fn type_label(&self) -> String;
+
+ /// A representation of this type label that can be used as part of another
+ /// identifier. e.g. `read_foo()`, or `FooInternals`.
+ ///
+ /// This is especially useful when creating specialized objects or methods to deal
+ /// with this type only.
+ fn canonical_name(&self) -> String {
+ self.type_label()
+ }
+
+ fn literal(&self, _literal: &Literal) -> String {
+ unimplemented!("Unimplemented for {}", self.type_label())
+ }
+
+ /// Name of the FfiConverter
+ ///
+ /// This is the object that contains the lower, write, lift, and read methods for this type.
+ /// Depending on the binding this will either be a singleton or a class with static methods.
+ ///
+ /// This is the newer way of handling these methods and replaces the lower, write, lift, and
+ /// read CodeType methods. Currently only used by Kotlin, but the plan is to move other
+ /// backends to using this.
+ fn ffi_converter_name(&self) -> String {
+ format!("FfiConverter{}", self.canonical_name())
+ }
+
+ /// An expression for lowering a value into something we can pass over the FFI.
+ fn lower(&self) -> String {
+ format!("{}.lower", self.ffi_converter_name())
+ }
+
+ /// An expression for writing a value into a byte buffer.
+ fn write(&self) -> String {
+ format!("{}.write", self.ffi_converter_name())
+ }
+
+ /// An expression for lifting a value from something we received over the FFI.
+ fn lift(&self) -> String {
+ format!("{}.lift", self.ffi_converter_name())
+ }
+
+ /// An expression for reading a value from a byte buffer.
+ fn read(&self) -> String {
+ format!("{}.read", self.ffi_converter_name())
+ }
+
+ /// A list of imports that are needed if this type is in use.
+ /// Classes are imported exactly once.
+ fn imports(&self) -> Option<Vec<String>> {
+ None
+ }
+
+ /// Function to run at startup
+ fn initialization_fn(&self) -> Option<String> {
+ None
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/callback_interface.rs b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/callback_interface.rs
new file mode 100644
index 0000000000..e20020e87c
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/callback_interface.rs
@@ -0,0 +1,31 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use super::CodeType;
+use crate::ComponentInterface;
+
+#[derive(Debug)]
+pub struct CallbackInterfaceCodeType {
+ id: String,
+}
+
+impl CallbackInterfaceCodeType {
+ pub fn new(id: String) -> Self {
+ Self { id }
+ }
+}
+
+impl CodeType for CallbackInterfaceCodeType {
+ fn type_label(&self, ci: &ComponentInterface) -> String {
+ super::KotlinCodeOracle.class_name(ci, &self.id)
+ }
+
+ fn canonical_name(&self) -> String {
+ format!("Type{}", self.id)
+ }
+
+ fn initialization_fn(&self) -> Option<String> {
+ Some(format!("{}.register", self.ffi_converter_name()))
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/compounds.rs b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/compounds.rs
new file mode 100644
index 0000000000..4329f32f4c
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/compounds.rs
@@ -0,0 +1,98 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use super::{AsCodeType, CodeType};
+use crate::backend::{Literal, Type};
+use crate::ComponentInterface;
+use paste::paste;
+
+fn render_literal(literal: &Literal, inner: &Type, ci: &ComponentInterface) -> String {
+ match literal {
+ Literal::Null => "null".into(),
+ Literal::EmptySequence => "listOf()".into(),
+ Literal::EmptyMap => "mapOf()".into(),
+
+ // For optionals
+ _ => super::KotlinCodeOracle.find(inner).literal(literal, ci),
+ }
+}
+
+macro_rules! impl_code_type_for_compound {
+ ($T:ty, $type_label_pattern:literal, $canonical_name_pattern: literal) => {
+ paste! {
+ #[derive(Debug)]
+ pub struct $T {
+ inner: Type,
+ }
+
+ impl $T {
+ pub fn new(inner: Type) -> Self {
+ Self { inner }
+ }
+ fn inner(&self) -> &Type {
+ &self.inner
+ }
+ }
+
+ impl CodeType for $T {
+ fn type_label(&self, ci: &ComponentInterface) -> String {
+ format!($type_label_pattern, super::KotlinCodeOracle.find(self.inner()).type_label(ci))
+ }
+
+ fn canonical_name(&self) -> String {
+ format!($canonical_name_pattern, super::KotlinCodeOracle.find(self.inner()).canonical_name())
+ }
+
+ fn literal(&self, literal: &Literal, ci: &ComponentInterface) -> String {
+ render_literal(literal, self.inner(), ci)
+ }
+ }
+ }
+ }
+ }
+
+impl_code_type_for_compound!(OptionalCodeType, "{}?", "Optional{}");
+impl_code_type_for_compound!(SequenceCodeType, "List<{}>", "Sequence{}");
+
+#[derive(Debug)]
+pub struct MapCodeType {
+ key: Type,
+ value: Type,
+}
+
+impl MapCodeType {
+ pub fn new(key: Type, value: Type) -> Self {
+ Self { key, value }
+ }
+
+ fn key(&self) -> &Type {
+ &self.key
+ }
+
+ fn value(&self) -> &Type {
+ &self.value
+ }
+}
+
+impl CodeType for MapCodeType {
+ fn type_label(&self, ci: &ComponentInterface) -> String {
+ format!(
+ "Map<{}, {}>",
+ super::KotlinCodeOracle.find(self.key()).type_label(ci),
+ super::KotlinCodeOracle.find(self.value()).type_label(ci),
+ )
+ }
+
+ fn canonical_name(&self) -> String {
+ format!(
+ "Map{}{}",
+ self.key().as_codetype().canonical_name(),
+ self.value().as_codetype().canonical_name(),
+ )
+ }
+
+ fn literal(&self, literal: &Literal, ci: &ComponentInterface) -> String {
+ render_literal(literal, &self.value, ci)
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/custom.rs b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/custom.rs
new file mode 100644
index 0000000000..137cd0d8d9
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/custom.rs
@@ -0,0 +1,27 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use super::CodeType;
+use crate::ComponentInterface;
+
+#[derive(Debug)]
+pub struct CustomCodeType {
+ name: String,
+}
+
+impl CustomCodeType {
+ pub fn new(name: String) -> Self {
+ CustomCodeType { name }
+ }
+}
+
+impl CodeType for CustomCodeType {
+ fn type_label(&self, _ci: &ComponentInterface) -> String {
+ self.name.clone()
+ }
+
+ fn canonical_name(&self) -> String {
+ format!("Type{}", self.name)
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/enum_.rs b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/enum_.rs
new file mode 100644
index 0000000000..f5300c10ee
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/enum_.rs
@@ -0,0 +1,40 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use super::CodeType;
+use crate::backend::Literal;
+use crate::ComponentInterface;
+
+#[derive(Debug)]
+pub struct EnumCodeType {
+ id: String,
+}
+
+impl EnumCodeType {
+ pub fn new(id: String) -> Self {
+ Self { id }
+ }
+}
+
+impl CodeType for EnumCodeType {
+ fn type_label(&self, ci: &ComponentInterface) -> String {
+ super::KotlinCodeOracle.class_name(ci, &self.id)
+ }
+
+ fn canonical_name(&self) -> String {
+ format!("Type{}", self.id)
+ }
+
+ fn literal(&self, literal: &Literal, ci: &ComponentInterface) -> String {
+ if let Literal::Enum(v, _) = literal {
+ format!(
+ "{}.{}",
+ self.type_label(ci),
+ super::KotlinCodeOracle.enum_variant_name(v)
+ )
+ } else {
+ unreachable!();
+ }
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/executor.rs b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/executor.rs
new file mode 100644
index 0000000000..154e12a381
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/executor.rs
@@ -0,0 +1,24 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use super::CodeType;
+use crate::ComponentInterface;
+
+#[derive(Debug)]
+pub struct ForeignExecutorCodeType;
+
+impl CodeType for ForeignExecutorCodeType {
+ fn type_label(&self, _ci: &ComponentInterface) -> String {
+ // Kotlin uses a CoroutineScope for ForeignExecutor
+ "CoroutineScope".into()
+ }
+
+ fn canonical_name(&self) -> String {
+ "ForeignExecutor".into()
+ }
+
+ fn initialization_fn(&self) -> Option<String> {
+ Some("FfiConverterForeignExecutor.register".into())
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/external.rs b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/external.rs
new file mode 100644
index 0000000000..3ecf09d47f
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/external.rs
@@ -0,0 +1,27 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use super::CodeType;
+use crate::ComponentInterface;
+
+#[derive(Debug)]
+pub struct ExternalCodeType {
+ name: String,
+}
+
+impl ExternalCodeType {
+ pub fn new(name: String) -> Self {
+ Self { name }
+ }
+}
+
+impl CodeType for ExternalCodeType {
+ fn type_label(&self, _ci: &ComponentInterface) -> String {
+ self.name.clone()
+ }
+
+ fn canonical_name(&self) -> String {
+ format!("Type{}", self.name)
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/miscellany.rs b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/miscellany.rs
new file mode 100644
index 0000000000..17331ff511
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/miscellany.rs
@@ -0,0 +1,30 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use super::CodeType;
+use crate::ComponentInterface;
+use paste::paste;
+
+macro_rules! impl_code_type_for_miscellany {
+ ($T:ty, $class_name:literal, $canonical_name:literal) => {
+ paste! {
+ #[derive(Debug)]
+ pub struct $T;
+
+ impl CodeType for $T {
+ fn type_label(&self, _ci: &ComponentInterface) -> String {
+ $class_name.into()
+ }
+
+ fn canonical_name(&self) -> String {
+ $canonical_name.into()
+ }
+ }
+ }
+ };
+}
+
+impl_code_type_for_miscellany!(TimestampCodeType, "java.time.Instant", "Timestamp");
+
+impl_code_type_for_miscellany!(DurationCodeType, "java.time.Duration", "Duration");
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs
new file mode 100644
index 0000000000..1ed0575a9a
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs
@@ -0,0 +1,530 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::borrow::Borrow;
+use std::cell::RefCell;
+use std::collections::{BTreeSet, HashMap, HashSet};
+use std::fmt::Debug;
+
+use anyhow::{Context, Result};
+use askama::Template;
+use heck::{ToLowerCamelCase, ToShoutySnakeCase, ToUpperCamelCase};
+use serde::{Deserialize, Serialize};
+
+use crate::backend::TemplateExpression;
+use crate::interface::*;
+use crate::BindingsConfig;
+
+mod callback_interface;
+mod compounds;
+mod custom;
+mod enum_;
+mod executor;
+mod external;
+mod miscellany;
+mod object;
+mod primitives;
+mod record;
+mod variant;
+
+trait CodeType: Debug {
+ /// The language specific label used to reference this type. This will be used in
+ /// method signatures and property declarations.
+ fn type_label(&self, ci: &ComponentInterface) -> String;
+
+ /// A representation of this type label that can be used as part of another
+ /// identifier. e.g. `read_foo()`, or `FooInternals`.
+ ///
+ /// This is especially useful when creating specialized objects or methods to deal
+ /// with this type only.
+ fn canonical_name(&self) -> String;
+
+ fn literal(&self, _literal: &Literal, ci: &ComponentInterface) -> String {
+ unimplemented!("Unimplemented for {}", self.type_label(ci))
+ }
+
+ /// Name of the FfiConverter
+ ///
+ /// This is the object that contains the lower, write, lift, and read methods for this type.
+ /// Depending on the binding this will either be a singleton or a class with static methods.
+ ///
+ /// This is the newer way of handling these methods and replaces the lower, write, lift, and
+ /// read CodeType methods. Currently only used by Kotlin, but the plan is to move other
+ /// backends to using this.
+ fn ffi_converter_name(&self) -> String {
+ format!("FfiConverter{}", self.canonical_name())
+ }
+
+ /// A list of imports that are needed if this type is in use.
+ /// Classes are imported exactly once.
+ fn imports(&self) -> Option<Vec<String>> {
+ None
+ }
+
+ /// Function to run at startup
+ fn initialization_fn(&self) -> Option<String> {
+ None
+ }
+}
+
+// config options to customize the generated Kotlin.
+#[derive(Debug, Default, Clone, Serialize, Deserialize)]
+pub struct Config {
+ package_name: Option<String>,
+ cdylib_name: Option<String>,
+ #[serde(default)]
+ custom_types: HashMap<String, CustomTypeConfig>,
+ #[serde(default)]
+ external_packages: HashMap<String, String>,
+}
+
+#[derive(Debug, Default, Clone, Serialize, Deserialize)]
+pub struct CustomTypeConfig {
+ imports: Option<Vec<String>>,
+ type_name: Option<String>,
+ into_custom: TemplateExpression,
+ from_custom: TemplateExpression,
+}
+
+impl Config {
+ pub fn package_name(&self) -> String {
+ if let Some(package_name) = &self.package_name {
+ package_name.clone()
+ } else {
+ "uniffi".into()
+ }
+ }
+
+ pub fn cdylib_name(&self) -> String {
+ if let Some(cdylib_name) = &self.cdylib_name {
+ cdylib_name.clone()
+ } else {
+ "uniffi".into()
+ }
+ }
+}
+
+impl BindingsConfig for Config {
+ fn update_from_ci(&mut self, ci: &ComponentInterface) {
+ self.package_name
+ .get_or_insert_with(|| format!("uniffi.{}", ci.namespace()));
+ self.cdylib_name
+ .get_or_insert_with(|| format!("uniffi_{}", ci.namespace()));
+ }
+
+ fn update_from_cdylib_name(&mut self, cdylib_name: &str) {
+ self.cdylib_name
+ .get_or_insert_with(|| cdylib_name.to_string());
+ }
+
+ fn update_from_dependency_configs(&mut self, config_map: HashMap<&str, &Self>) {
+ for (crate_name, config) in config_map {
+ if !self.external_packages.contains_key(crate_name) {
+ self.external_packages
+ .insert(crate_name.to_string(), config.package_name());
+ }
+ }
+ }
+}
+
+// Generate kotlin bindings for the given ComponentInterface, as a string.
+pub fn generate_bindings(config: &Config, ci: &ComponentInterface) -> Result<String> {
+ KotlinWrapper::new(config.clone(), ci)
+ .render()
+ .context("failed to render kotlin bindings")
+}
+
+/// A struct to record a Kotlin import statement.
+#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
+pub enum ImportRequirement {
+ /// The name we are importing.
+ Import { name: String },
+ /// Import the name with the specified local name.
+ ImportAs { name: String, as_name: String },
+}
+
+impl ImportRequirement {
+ /// Render the Kotlin import statement.
+ fn render(&self) -> String {
+ match &self {
+ ImportRequirement::Import { name } => format!("import {name}"),
+ ImportRequirement::ImportAs { name, as_name } => {
+ format!("import {name} as {as_name}")
+ }
+ }
+ }
+}
+
+/// Renders Kotlin helper code for all types
+///
+/// This template is a bit different than others in that it stores internal state from the render
+/// process. Make sure to only call `render()` once.
+#[derive(Template)]
+#[template(syntax = "kt", escape = "none", path = "Types.kt")]
+pub struct TypeRenderer<'a> {
+ config: &'a Config,
+ ci: &'a ComponentInterface,
+ // Track included modules for the `include_once()` macro
+ include_once_names: RefCell<HashSet<String>>,
+ // Track imports added with the `add_import()` macro
+ imports: RefCell<BTreeSet<ImportRequirement>>,
+}
+
+impl<'a> TypeRenderer<'a> {
+ fn new(config: &'a Config, ci: &'a ComponentInterface) -> Self {
+ Self {
+ config,
+ ci,
+ include_once_names: RefCell::new(HashSet::new()),
+ imports: RefCell::new(BTreeSet::new()),
+ }
+ }
+
+ // Get the package name for an external type
+ fn external_type_package_name(&self, module_path: &str, namespace: &str) -> String {
+ // config overrides are keyed by the crate name, default fallback is the namespace.
+ let crate_name = module_path.split("::").next().unwrap();
+ match self.config.external_packages.get(crate_name) {
+ Some(name) => name.clone(),
+ // unreachable in library mode - all deps are in our config with correct namespace.
+ None => format!("uniffi.{namespace}"),
+ }
+ }
+
+ // The following methods are used by the `Types.kt` macros.
+
+ // Helper for the including a template, but only once.
+ //
+ // The first time this is called with a name it will return true, indicating that we should
+ // include the template. Subsequent calls will return false.
+ fn include_once_check(&self, name: &str) -> bool {
+ self.include_once_names
+ .borrow_mut()
+ .insert(name.to_string())
+ }
+
+ // Helper to add an import statement
+ //
+ // Call this inside your template to cause an import statement to be added at the top of the
+ // file. Imports will be sorted and de-deuped.
+ //
+ // Returns an empty string so that it can be used inside an askama `{{ }}` block.
+ fn add_import(&self, name: &str) -> &str {
+ self.imports.borrow_mut().insert(ImportRequirement::Import {
+ name: name.to_owned(),
+ });
+ ""
+ }
+
+ // Like add_import, but arranges for `import name as as_name`
+ fn add_import_as(&self, name: &str, as_name: &str) -> &str {
+ self.imports
+ .borrow_mut()
+ .insert(ImportRequirement::ImportAs {
+ name: name.to_owned(),
+ as_name: as_name.to_owned(),
+ });
+ ""
+ }
+}
+
+#[derive(Template)]
+#[template(syntax = "kt", escape = "none", path = "wrapper.kt")]
+pub struct KotlinWrapper<'a> {
+ config: Config,
+ ci: &'a ComponentInterface,
+ type_helper_code: String,
+ type_imports: BTreeSet<ImportRequirement>,
+ has_async_fns: bool,
+}
+
+impl<'a> KotlinWrapper<'a> {
+ pub fn new(config: Config, ci: &'a ComponentInterface) -> Self {
+ let type_renderer = TypeRenderer::new(&config, ci);
+ let type_helper_code = type_renderer.render().unwrap();
+ let type_imports = type_renderer.imports.into_inner();
+ Self {
+ config,
+ ci,
+ type_helper_code,
+ type_imports,
+ has_async_fns: ci.has_async_fns(),
+ }
+ }
+
+ pub fn initialization_fns(&self) -> Vec<String> {
+ self.ci
+ .iter_types()
+ .map(|t| KotlinCodeOracle.find(t))
+ .filter_map(|ct| ct.initialization_fn())
+ .chain(
+ self.has_async_fns
+ .then(|| "uniffiRustFutureContinuationCallback.register".into()),
+ )
+ .collect()
+ }
+
+ pub fn imports(&self) -> Vec<ImportRequirement> {
+ self.type_imports.iter().cloned().collect()
+ }
+}
+
+#[derive(Clone)]
+pub struct KotlinCodeOracle;
+
+impl KotlinCodeOracle {
+ fn find(&self, type_: &Type) -> Box<dyn CodeType> {
+ type_.clone().as_type().as_codetype()
+ }
+
+ /// Get the idiomatic Kotlin rendering of a class name (for enums, records, errors, etc).
+ fn class_name(&self, ci: &ComponentInterface, nm: &str) -> String {
+ let name = nm.to_string().to_upper_camel_case();
+ // fixup errors.
+ ci.is_name_used_as_error(nm)
+ .then(|| self.convert_error_suffix(&name))
+ .unwrap_or(name)
+ }
+
+ fn convert_error_suffix(&self, nm: &str) -> String {
+ match nm.strip_suffix("Error") {
+ None => nm.to_string(),
+ Some(stripped) => format!("{stripped}Exception"),
+ }
+ }
+
+ /// Get the idiomatic Kotlin rendering of a function name.
+ fn fn_name(&self, nm: &str) -> String {
+ format!("`{}`", nm.to_string().to_lower_camel_case())
+ }
+
+ /// Get the idiomatic Kotlin rendering of a variable name.
+ fn var_name(&self, nm: &str) -> String {
+ format!("`{}`", nm.to_string().to_lower_camel_case())
+ }
+
+ /// Get the idiomatic Kotlin rendering of an individual enum variant.
+ fn enum_variant_name(&self, nm: &str) -> String {
+ nm.to_string().to_shouty_snake_case()
+ }
+
+ fn ffi_type_label_by_value(ffi_type: &FfiType) -> String {
+ match ffi_type {
+ FfiType::RustBuffer(_) => format!("{}.ByValue", Self::ffi_type_label(ffi_type)),
+ _ => Self::ffi_type_label(ffi_type),
+ }
+ }
+
+ fn ffi_type_label(ffi_type: &FfiType) -> String {
+ match ffi_type {
+ // Note that unsigned integers in Kotlin are currently experimental, but java.nio.ByteBuffer does not
+ // support them yet. Thus, we use the signed variants to represent both signed and unsigned
+ // types from the component API.
+ FfiType::Int8 | FfiType::UInt8 => "Byte".to_string(),
+ FfiType::Int16 | FfiType::UInt16 => "Short".to_string(),
+ FfiType::Int32 | FfiType::UInt32 => "Int".to_string(),
+ FfiType::Int64 | FfiType::UInt64 => "Long".to_string(),
+ FfiType::Float32 => "Float".to_string(),
+ FfiType::Float64 => "Double".to_string(),
+ FfiType::RustArcPtr(_) => "Pointer".to_string(),
+ FfiType::RustBuffer(maybe_suffix) => {
+ format!("RustBuffer{}", maybe_suffix.as_deref().unwrap_or_default())
+ }
+ FfiType::ForeignBytes => "ForeignBytes.ByValue".to_string(),
+ FfiType::ForeignCallback => "ForeignCallback".to_string(),
+ FfiType::ForeignExecutorHandle => "USize".to_string(),
+ FfiType::ForeignExecutorCallback => "UniFfiForeignExecutorCallback".to_string(),
+ FfiType::RustFutureHandle => "Pointer".to_string(),
+ FfiType::RustFutureContinuationCallback => {
+ "UniFffiRustFutureContinuationCallbackType".to_string()
+ }
+ FfiType::RustFutureContinuationData => "USize".to_string(),
+ }
+ }
+}
+
+trait AsCodeType {
+ fn as_codetype(&self) -> Box<dyn CodeType>;
+}
+
+impl<T: AsType> AsCodeType for T {
+ fn as_codetype(&self) -> Box<dyn CodeType> {
+ // Map `Type` instances to a `Box<dyn CodeType>` for that type.
+ //
+ // There is a companion match in `templates/Types.kt` which performs a similar function for the
+ // template code.
+ //
+ // - When adding additional types here, make sure to also add a match arm to the `Types.kt` template.
+ // - To keep things manageable, let's try to limit ourselves to these 2 mega-matches
+ match self.as_type() {
+ Type::UInt8 => Box::new(primitives::UInt8CodeType),
+ Type::Int8 => Box::new(primitives::Int8CodeType),
+ Type::UInt16 => Box::new(primitives::UInt16CodeType),
+ Type::Int16 => Box::new(primitives::Int16CodeType),
+ Type::UInt32 => Box::new(primitives::UInt32CodeType),
+ Type::Int32 => Box::new(primitives::Int32CodeType),
+ Type::UInt64 => Box::new(primitives::UInt64CodeType),
+ Type::Int64 => Box::new(primitives::Int64CodeType),
+ Type::Float32 => Box::new(primitives::Float32CodeType),
+ Type::Float64 => Box::new(primitives::Float64CodeType),
+ Type::Boolean => Box::new(primitives::BooleanCodeType),
+ Type::String => Box::new(primitives::StringCodeType),
+ Type::Bytes => Box::new(primitives::BytesCodeType),
+
+ Type::Timestamp => Box::new(miscellany::TimestampCodeType),
+ Type::Duration => Box::new(miscellany::DurationCodeType),
+
+ Type::Enum { name, .. } => Box::new(enum_::EnumCodeType::new(name)),
+ Type::Object { name, .. } => Box::new(object::ObjectCodeType::new(name)),
+ Type::Record { name, .. } => Box::new(record::RecordCodeType::new(name)),
+ Type::CallbackInterface { name, .. } => {
+ Box::new(callback_interface::CallbackInterfaceCodeType::new(name))
+ }
+ Type::ForeignExecutor => Box::new(executor::ForeignExecutorCodeType),
+ Type::Optional { inner_type } => {
+ Box::new(compounds::OptionalCodeType::new(*inner_type))
+ }
+ Type::Sequence { inner_type } => {
+ Box::new(compounds::SequenceCodeType::new(*inner_type))
+ }
+ Type::Map {
+ key_type,
+ value_type,
+ } => Box::new(compounds::MapCodeType::new(*key_type, *value_type)),
+ Type::External { name, .. } => Box::new(external::ExternalCodeType::new(name)),
+ Type::Custom { name, .. } => Box::new(custom::CustomCodeType::new(name)),
+ }
+ }
+}
+
+mod filters {
+ use super::*;
+ pub use crate::backend::filters::*;
+
+ pub(super) fn type_name(
+ as_ct: &impl AsCodeType,
+ ci: &ComponentInterface,
+ ) -> Result<String, askama::Error> {
+ Ok(as_ct.as_codetype().type_label(ci))
+ }
+
+ pub(super) fn canonical_name(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
+ Ok(as_ct.as_codetype().canonical_name())
+ }
+
+ pub(super) fn ffi_converter_name(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
+ Ok(as_ct.as_codetype().ffi_converter_name())
+ }
+
+ pub(super) fn lower_fn(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
+ Ok(format!(
+ "{}.lower",
+ as_ct.as_codetype().ffi_converter_name()
+ ))
+ }
+
+ pub(super) fn allocation_size_fn(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
+ Ok(format!(
+ "{}.allocationSize",
+ as_ct.as_codetype().ffi_converter_name()
+ ))
+ }
+
+ pub(super) fn write_fn(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
+ Ok(format!(
+ "{}.write",
+ as_ct.as_codetype().ffi_converter_name()
+ ))
+ }
+
+ pub(super) fn lift_fn(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
+ Ok(format!("{}.lift", as_ct.as_codetype().ffi_converter_name()))
+ }
+
+ pub(super) fn read_fn(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
+ Ok(format!("{}.read", as_ct.as_codetype().ffi_converter_name()))
+ }
+
+ pub fn render_literal(
+ literal: &Literal,
+ as_ct: &impl AsType,
+ ci: &ComponentInterface,
+ ) -> Result<String, askama::Error> {
+ Ok(as_ct.as_codetype().literal(literal, ci))
+ }
+
+ pub fn ffi_type_name_by_value(type_: &FfiType) -> Result<String, askama::Error> {
+ Ok(KotlinCodeOracle::ffi_type_label_by_value(type_))
+ }
+
+ /// Get the idiomatic Kotlin rendering of a function name.
+ pub fn fn_name(nm: &str) -> Result<String, askama::Error> {
+ Ok(KotlinCodeOracle.fn_name(nm))
+ }
+
+ /// Get the idiomatic Kotlin rendering of a variable name.
+ pub fn var_name(nm: &str) -> Result<String, askama::Error> {
+ Ok(KotlinCodeOracle.var_name(nm))
+ }
+
+ /// Get a String representing the name used for an individual enum variant.
+ pub fn variant_name(v: &Variant) -> Result<String, askama::Error> {
+ Ok(KotlinCodeOracle.enum_variant_name(v.name()))
+ }
+
+ pub fn error_variant_name(v: &Variant) -> Result<String, askama::Error> {
+ let name = v.name().to_string().to_upper_camel_case();
+ Ok(KotlinCodeOracle.convert_error_suffix(&name))
+ }
+
+ pub fn async_poll(
+ callable: impl Callable,
+ ci: &ComponentInterface,
+ ) -> Result<String, askama::Error> {
+ let ffi_func = callable.ffi_rust_future_poll(ci);
+ Ok(format!(
+ "{{ future, continuation -> _UniFFILib.INSTANCE.{ffi_func}(future, continuation) }}"
+ ))
+ }
+
+ pub fn async_complete(
+ callable: impl Callable,
+ ci: &ComponentInterface,
+ ) -> Result<String, askama::Error> {
+ let ffi_func = callable.ffi_rust_future_complete(ci);
+ let call = format!("_UniFFILib.INSTANCE.{ffi_func}(future, continuation)");
+ let call = match callable.return_type() {
+ Some(Type::External {
+ kind: ExternalKind::DataClass,
+ name,
+ ..
+ }) => {
+ // Need to convert the RustBuffer from our package to the RustBuffer of the external package
+ let suffix = KotlinCodeOracle.class_name(ci, &name);
+ format!("{call}.let {{ RustBuffer{suffix}.create(it.capacity, it.len, it.data) }}")
+ }
+ _ => call,
+ };
+ Ok(format!("{{ future, continuation -> {call} }}"))
+ }
+
+ pub fn async_free(
+ callable: impl Callable,
+ ci: &ComponentInterface,
+ ) -> Result<String, askama::Error> {
+ let ffi_func = callable.ffi_rust_future_free(ci);
+ Ok(format!(
+ "{{ future -> _UniFFILib.INSTANCE.{ffi_func}(future) }}"
+ ))
+ }
+
+ /// Remove the "`" chars we put around function/variable names
+ ///
+ /// These are used to avoid name clashes with kotlin identifiers, but sometimes you want to
+ /// render the name unquoted. One example is the message property for errors where we want to
+ /// display the name for the user.
+ pub fn unquote(nm: &str) -> Result<String, askama::Error> {
+ Ok(nm.trim_matches('`').to_string())
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/object.rs b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/object.rs
new file mode 100644
index 0000000000..c39ae59cce
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/object.rs
@@ -0,0 +1,27 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use super::CodeType;
+use crate::ComponentInterface;
+
+#[derive(Debug)]
+pub struct ObjectCodeType {
+ id: String,
+}
+
+impl ObjectCodeType {
+ pub fn new(id: String) -> Self {
+ Self { id }
+ }
+}
+
+impl CodeType for ObjectCodeType {
+ fn type_label(&self, ci: &ComponentInterface) -> String {
+ super::KotlinCodeOracle.class_name(ci, &self.id)
+ }
+
+ fn canonical_name(&self) -> String {
+ format!("Type{}", self.id)
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/primitives.rs b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/primitives.rs
new file mode 100644
index 0000000000..22495fa209
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/primitives.rs
@@ -0,0 +1,86 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use super::CodeType;
+use crate::backend::Literal;
+use crate::interface::{ComponentInterface, Radix, Type};
+use paste::paste;
+
+fn render_literal(literal: &Literal, _ci: &ComponentInterface) -> String {
+ fn typed_number(type_: &Type, num_str: String) -> String {
+ match type_ {
+ // Bytes, Shorts and Ints can all be inferred from the type.
+ Type::Int8 | Type::Int16 | Type::Int32 => num_str,
+ Type::Int64 => format!("{num_str}L"),
+
+ Type::UInt8 | Type::UInt16 | Type::UInt32 => format!("{num_str}u"),
+ Type::UInt64 => format!("{num_str}uL"),
+
+ Type::Float32 => format!("{num_str}f"),
+ Type::Float64 => num_str,
+ _ => panic!("Unexpected literal: {num_str} is not a number"),
+ }
+ }
+
+ match literal {
+ Literal::Boolean(v) => format!("{v}"),
+ Literal::String(s) => format!("\"{s}\""),
+ Literal::Int(i, radix, type_) => typed_number(
+ type_,
+ match radix {
+ Radix::Octal => format!("{i:#x}"),
+ Radix::Decimal => format!("{i}"),
+ Radix::Hexadecimal => format!("{i:#x}"),
+ },
+ ),
+ Literal::UInt(i, radix, type_) => typed_number(
+ type_,
+ match radix {
+ Radix::Octal => format!("{i:#x}"),
+ Radix::Decimal => format!("{i}"),
+ Radix::Hexadecimal => format!("{i:#x}"),
+ },
+ ),
+ Literal::Float(string, type_) => typed_number(type_, string.clone()),
+
+ _ => unreachable!("Literal"),
+ }
+}
+
+macro_rules! impl_code_type_for_primitive {
+ ($T:ty, $class_name:literal) => {
+ paste! {
+ #[derive(Debug)]
+ pub struct $T;
+
+ impl CodeType for $T {
+ fn type_label(&self, _ci: &ComponentInterface) -> String {
+ $class_name.into()
+ }
+
+ fn canonical_name(&self) -> String {
+ $class_name.into()
+ }
+
+ fn literal(&self, literal: &Literal, ci: &ComponentInterface) -> String {
+ render_literal(&literal, ci)
+ }
+ }
+ }
+ };
+}
+
+impl_code_type_for_primitive!(BooleanCodeType, "Boolean");
+impl_code_type_for_primitive!(StringCodeType, "String");
+impl_code_type_for_primitive!(BytesCodeType, "ByteArray");
+impl_code_type_for_primitive!(Int8CodeType, "Byte");
+impl_code_type_for_primitive!(Int16CodeType, "Short");
+impl_code_type_for_primitive!(Int32CodeType, "Int");
+impl_code_type_for_primitive!(Int64CodeType, "Long");
+impl_code_type_for_primitive!(UInt8CodeType, "UByte");
+impl_code_type_for_primitive!(UInt16CodeType, "UShort");
+impl_code_type_for_primitive!(UInt32CodeType, "UInt");
+impl_code_type_for_primitive!(UInt64CodeType, "ULong");
+impl_code_type_for_primitive!(Float32CodeType, "Float");
+impl_code_type_for_primitive!(Float64CodeType, "Double");
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/record.rs b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/record.rs
new file mode 100644
index 0000000000..17781c2220
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/record.rs
@@ -0,0 +1,27 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use super::CodeType;
+use crate::ComponentInterface;
+
+#[derive(Debug)]
+pub struct RecordCodeType {
+ id: String,
+}
+
+impl RecordCodeType {
+ pub fn new(id: String) -> Self {
+ Self { id }
+ }
+}
+
+impl CodeType for RecordCodeType {
+ fn type_label(&self, ci: &ComponentInterface) -> String {
+ super::KotlinCodeOracle.class_name(ci, &self.id)
+ }
+
+ fn canonical_name(&self) -> String {
+ format!("Type{}", self.id)
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/variant.rs b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/variant.rs
new file mode 100644
index 0000000000..c7673882d9
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/variant.rs
@@ -0,0 +1,33 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use super::{AsCodeType, CodeType, KotlinCodeOracle};
+use crate::interface::{ComponentInterface, Variant};
+
+#[derive(Debug)]
+pub(super) struct VariantCodeType {
+ pub v: Variant,
+}
+
+impl CodeType for VariantCodeType {
+ fn type_label(&self, ci: &ComponentInterface) -> String {
+ KotlinCodeOracle.class_name(ci, self.v.name())
+ }
+
+ fn canonical_name(&self) -> String {
+ self.v.name().to_string()
+ }
+}
+
+impl AsCodeType for Variant {
+ fn as_codetype(&self) -> Box<dyn CodeType> {
+ Box::new(VariantCodeType { v: self.clone() })
+ }
+}
+
+impl AsCodeType for &Variant {
+ fn as_codetype(&self) -> Box<dyn CodeType> {
+ Box::new(VariantCodeType { v: (*self).clone() })
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/mod.rs b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/mod.rs
new file mode 100644
index 0000000000..466fe77879
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/mod.rs
@@ -0,0 +1,41 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use anyhow::Result;
+use camino::{Utf8Path, Utf8PathBuf};
+use fs_err as fs;
+use std::process::Command;
+
+pub mod gen_kotlin;
+pub use gen_kotlin::{generate_bindings, Config};
+mod test;
+
+use super::super::interface::ComponentInterface;
+pub use test::{run_script, run_test};
+
+pub fn write_bindings(
+ config: &Config,
+ ci: &ComponentInterface,
+ out_dir: &Utf8Path,
+ try_format_code: bool,
+) -> Result<()> {
+ let mut kt_file = full_bindings_path(config, out_dir);
+ fs::create_dir_all(&kt_file)?;
+ kt_file.push(format!("{}.kt", ci.namespace()));
+ fs::write(&kt_file, generate_bindings(config, ci)?)?;
+ if try_format_code {
+ if let Err(e) = Command::new("ktlint").arg("-F").arg(&kt_file).output() {
+ println!(
+ "Warning: Unable to auto-format {} using ktlint: {e:?}",
+ kt_file.file_name().unwrap(),
+ );
+ }
+ }
+ Ok(())
+}
+
+fn full_bindings_path(config: &Config, out_dir: &Utf8Path) -> Utf8PathBuf {
+ let package_path: Utf8PathBuf = config.package_name().split('.').collect();
+ Utf8PathBuf::from(out_dir).join(package_path)
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Async.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Async.kt
new file mode 100644
index 0000000000..c6a32655f2
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Async.kt
@@ -0,0 +1,44 @@
+// Async return type handlers
+
+internal const val UNIFFI_RUST_FUTURE_POLL_READY = 0.toShort()
+internal const val UNIFFI_RUST_FUTURE_POLL_MAYBE_READY = 1.toShort()
+
+internal val uniffiContinuationHandleMap = UniFfiHandleMap<CancellableContinuation<Short>>()
+
+// FFI type for Rust future continuations
+internal object uniffiRustFutureContinuationCallback: UniFffiRustFutureContinuationCallbackType {
+ override fun callback(continuationHandle: USize, pollResult: Short) {
+ uniffiContinuationHandleMap.remove(continuationHandle)?.resume(pollResult)
+ }
+
+ internal fun register(lib: _UniFFILib) {
+ lib.{{ ci.ffi_rust_future_continuation_callback_set().name() }}(this)
+ }
+}
+
+internal suspend fun<T, F, E: Exception> uniffiRustCallAsync(
+ rustFuture: Pointer,
+ pollFunc: (Pointer, USize) -> Unit,
+ completeFunc: (Pointer, RustCallStatus) -> F,
+ freeFunc: (Pointer) -> Unit,
+ liftFunc: (F) -> T,
+ errorHandler: CallStatusErrorHandler<E>
+): T {
+ try {
+ do {
+ val pollResult = suspendCancellableCoroutine<Short> { continuation ->
+ pollFunc(
+ rustFuture,
+ uniffiContinuationHandleMap.insert(continuation)
+ )
+ }
+ } while (pollResult != UNIFFI_RUST_FUTURE_POLL_READY);
+
+ return liftFunc(
+ rustCallWithError(errorHandler, { status -> completeFunc(rustFuture, status) })
+ )
+ } finally {
+ freeFunc(rustFuture)
+ }
+}
+
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/BooleanHelper.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/BooleanHelper.kt
new file mode 100644
index 0000000000..8cfa2ce000
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/BooleanHelper.kt
@@ -0,0 +1,19 @@
+public object FfiConverterBoolean: FfiConverter<Boolean, Byte> {
+ override fun lift(value: Byte): Boolean {
+ return value.toInt() != 0
+ }
+
+ override fun read(buf: ByteBuffer): Boolean {
+ return lift(buf.get())
+ }
+
+ override fun lower(value: Boolean): Byte {
+ return if (value) 1.toByte() else 0.toByte()
+ }
+
+ override fun allocationSize(value: Boolean) = 1
+
+ override fun write(value: Boolean, buf: ByteBuffer) {
+ buf.put(lower(value))
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ByteArrayHelper.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ByteArrayHelper.kt
new file mode 100644
index 0000000000..4840a199b4
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ByteArrayHelper.kt
@@ -0,0 +1,15 @@
+public object FfiConverterByteArray: FfiConverterRustBuffer<ByteArray> {
+ override fun read(buf: ByteBuffer): ByteArray {
+ val len = buf.getInt()
+ val byteArr = ByteArray(len)
+ buf.get(byteArr)
+ return byteArr
+ }
+ override fun allocationSize(value: ByteArray): Int {
+ return 4 + value.size
+ }
+ override fun write(value: ByteArray, buf: ByteBuffer) {
+ buf.putInt(value.size)
+ buf.put(value)
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/CallbackInterfaceRuntime.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/CallbackInterfaceRuntime.kt
new file mode 100644
index 0000000000..62a71e02f1
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/CallbackInterfaceRuntime.kt
@@ -0,0 +1,78 @@
+internal typealias Handle = Long
+internal class ConcurrentHandleMap<T>(
+ private val leftMap: MutableMap<Handle, T> = mutableMapOf(),
+ private val rightMap: MutableMap<T, Handle> = mutableMapOf()
+) {
+ private val lock = java.util.concurrent.locks.ReentrantLock()
+ private val currentHandle = AtomicLong(0L)
+ private val stride = 1L
+
+ fun insert(obj: T): Handle =
+ lock.withLock {
+ rightMap[obj] ?:
+ currentHandle.getAndAdd(stride)
+ .also { handle ->
+ leftMap[handle] = obj
+ rightMap[obj] = handle
+ }
+ }
+
+ fun get(handle: Handle) = lock.withLock {
+ leftMap[handle]
+ }
+
+ fun delete(handle: Handle) {
+ this.remove(handle)
+ }
+
+ fun remove(handle: Handle): T? =
+ lock.withLock {
+ leftMap.remove(handle)?.let { obj ->
+ rightMap.remove(obj)
+ obj
+ }
+ }
+}
+
+interface ForeignCallback : com.sun.jna.Callback {
+ public fun callback(handle: Handle, method: Int, argsData: Pointer, argsLen: Int, outBuf: RustBufferByReference): Int
+}
+
+// Magic number for the Rust proxy to call using the same mechanism as every other method,
+// to free the callback once it's dropped by Rust.
+internal const val IDX_CALLBACK_FREE = 0
+// Callback return codes
+internal const val UNIFFI_CALLBACK_SUCCESS = 0
+internal const val UNIFFI_CALLBACK_ERROR = 1
+internal const val UNIFFI_CALLBACK_UNEXPECTED_ERROR = 2
+
+public abstract class FfiConverterCallbackInterface<CallbackInterface>(
+ protected val foreignCallback: ForeignCallback
+): FfiConverter<CallbackInterface, Handle> {
+ private val handleMap = ConcurrentHandleMap<CallbackInterface>()
+
+ // Registers the foreign callback with the Rust side.
+ // This method is generated for each callback interface.
+ internal abstract fun register(lib: _UniFFILib)
+
+ fun drop(handle: Handle): RustBuffer.ByValue {
+ return handleMap.remove(handle).let { RustBuffer.ByValue() }
+ }
+
+ override fun lift(value: Handle): CallbackInterface {
+ return handleMap.get(value) ?: throw InternalException("No callback in handlemap; this is a Uniffi bug")
+ }
+
+ override fun read(buf: ByteBuffer) = lift(buf.getLong())
+
+ override fun lower(value: CallbackInterface) =
+ handleMap.insert(value).also {
+ assert(handleMap.get(it) === value) { "Handle map is not returning the object we just placed there. This is a bug in the HandleMap." }
+ }
+
+ override fun allocationSize(value: CallbackInterface) = 8
+
+ override fun write(value: CallbackInterface, buf: ByteBuffer) {
+ buf.putLong(lower(value))
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/CallbackInterfaceTemplate.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/CallbackInterfaceTemplate.kt
new file mode 100644
index 0000000000..5a29f0acc3
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/CallbackInterfaceTemplate.kt
@@ -0,0 +1,129 @@
+{%- let cbi = ci|get_callback_interface_definition(name) %}
+{%- let type_name = cbi|type_name(ci) %}
+{%- let foreign_callback = format!("ForeignCallback{}", canonical_type_name) %}
+
+{% if self.include_once_check("CallbackInterfaceRuntime.kt") %}{% include "CallbackInterfaceRuntime.kt" %}{% endif %}
+{{- self.add_import("java.util.concurrent.atomic.AtomicLong") }}
+{{- self.add_import("java.util.concurrent.locks.ReentrantLock") }}
+{{- self.add_import("kotlin.concurrent.withLock") }}
+
+// Declaration and FfiConverters for {{ type_name }} Callback Interface
+
+public interface {{ type_name }} {
+ {% for meth in cbi.methods() -%}
+ fun {{ meth.name()|fn_name }}({% call kt::arg_list_decl(meth) %})
+ {%- match meth.return_type() -%}
+ {%- when Some with (return_type) %}: {{ return_type|type_name(ci) -}}
+ {%- else -%}
+ {%- endmatch %}
+ {% endfor %}
+ companion object
+}
+
+// The ForeignCallback that is passed to Rust.
+internal class {{ foreign_callback }} : ForeignCallback {
+ @Suppress("TooGenericExceptionCaught")
+ override fun callback(handle: Handle, method: Int, argsData: Pointer, argsLen: Int, outBuf: RustBufferByReference): Int {
+ val cb = {{ ffi_converter_name }}.lift(handle)
+ return when (method) {
+ IDX_CALLBACK_FREE -> {
+ {{ ffi_converter_name }}.drop(handle)
+ // Successful return
+ // See docs of ForeignCallback in `uniffi_core/src/ffi/foreigncallbacks.rs`
+ UNIFFI_CALLBACK_SUCCESS
+ }
+ {% for meth in cbi.methods() -%}
+ {% let method_name = format!("invoke_{}", meth.name())|fn_name -%}
+ {{ loop.index }} -> {
+ // Call the method, write to outBuf and return a status code
+ // See docs of ForeignCallback in `uniffi_core/src/ffi/foreigncallbacks.rs` for info
+ try {
+ this.{{ method_name }}(cb, argsData, argsLen, outBuf)
+ } catch (e: Throwable) {
+ // Unexpected error
+ try {
+ // Try to serialize the error into a string
+ outBuf.setValue({{ Type::String.borrow()|ffi_converter_name }}.lower(e.toString()))
+ } catch (e: Throwable) {
+ // If that fails, then it's time to give up and just return
+ }
+ UNIFFI_CALLBACK_UNEXPECTED_ERROR
+ }
+ }
+ {% endfor %}
+ else -> {
+ // An unexpected error happened.
+ // See docs of ForeignCallback in `uniffi_core/src/ffi/foreigncallbacks.rs`
+ try {
+ // Try to serialize the error into a string
+ outBuf.setValue({{ Type::String.borrow()|ffi_converter_name }}.lower("Invalid Callback index"))
+ } catch (e: Throwable) {
+ // If that fails, then it's time to give up and just return
+ }
+ UNIFFI_CALLBACK_UNEXPECTED_ERROR
+ }
+ }
+ }
+
+ {% for meth in cbi.methods() -%}
+ {% let method_name = format!("invoke_{}", meth.name())|fn_name %}
+ @Suppress("UNUSED_PARAMETER")
+ private fun {{ method_name }}(kotlinCallbackInterface: {{ type_name }}, argsData: Pointer, argsLen: Int, outBuf: RustBufferByReference): Int {
+ {%- if meth.arguments().len() > 0 %}
+ val argsBuf = argsData.getByteBuffer(0, argsLen.toLong()).also {
+ it.order(ByteOrder.BIG_ENDIAN)
+ }
+ {%- endif %}
+
+ {%- match meth.return_type() %}
+ {%- when Some with (return_type) %}
+ fun makeCall() : Int {
+ val returnValue = kotlinCallbackInterface.{{ meth.name()|fn_name }}(
+ {%- for arg in meth.arguments() %}
+ {{ arg|read_fn }}(argsBuf)
+ {% if !loop.last %}, {% endif %}
+ {%- endfor %}
+ )
+ outBuf.setValue({{ return_type|ffi_converter_name }}.lowerIntoRustBuffer(returnValue))
+ return UNIFFI_CALLBACK_SUCCESS
+ }
+ {%- when None %}
+ fun makeCall() : Int {
+ kotlinCallbackInterface.{{ meth.name()|fn_name }}(
+ {%- for arg in meth.arguments() %}
+ {{ arg|read_fn }}(argsBuf)
+ {%- if !loop.last %}, {% endif %}
+ {%- endfor %}
+ )
+ return UNIFFI_CALLBACK_SUCCESS
+ }
+ {%- endmatch %}
+
+ {%- match meth.throws_type() %}
+ {%- when None %}
+ fun makeCallAndHandleError() : Int = makeCall()
+ {%- when Some(error_type) %}
+ fun makeCallAndHandleError() : Int = try {
+ makeCall()
+ } catch (e: {{ error_type|type_name(ci) }}) {
+ // Expected error, serialize it into outBuf
+ outBuf.setValue({{ error_type|ffi_converter_name }}.lowerIntoRustBuffer(e))
+ UNIFFI_CALLBACK_ERROR
+ }
+ {%- endmatch %}
+
+ return makeCallAndHandleError()
+ }
+ {% endfor %}
+}
+
+// The ffiConverter which transforms the Callbacks in to Handles to pass to Rust.
+public object {{ ffi_converter_name }}: FfiConverterCallbackInterface<{{ type_name }}>(
+ foreignCallback = {{ foreign_callback }}()
+) {
+ override fun register(lib: _UniFFILib) {
+ rustCall() { status ->
+ lib.{{ cbi.ffi_init_callback().name() }}(this.foreignCallback, status)
+ }
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/CustomTypeTemplate.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/CustomTypeTemplate.kt
new file mode 100644
index 0000000000..04150c5d78
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/CustomTypeTemplate.kt
@@ -0,0 +1,62 @@
+{%- match config.custom_types.get(name.as_str()) %}
+{%- when None %}
+{#- Define the type using typealiases to the builtin #}
+/**
+ * Typealias from the type name used in the UDL file to the builtin type. This
+ * is needed because the UDL type name is used in function/method signatures.
+ * It's also what we have an external type that references a custom type.
+ */
+public typealias {{ name }} = {{ builtin|type_name(ci) }}
+public typealias {{ ffi_converter_name }} = {{ builtin|ffi_converter_name }}
+
+{%- when Some with (config) %}
+
+{%- let ffi_type_name=builtin|ffi_type|ffi_type_name_by_value %}
+
+{# When the config specifies a different type name, create a typealias for it #}
+{%- match config.type_name %}
+{%- when Some(concrete_type_name) %}
+/**
+ * Typealias from the type name used in the UDL file to the custom type. This
+ * is needed because the UDL type name is used in function/method signatures.
+ * It's also what we have an external type that references a custom type.
+ */
+public typealias {{ name }} = {{ concrete_type_name }}
+{%- else %}
+{%- endmatch %}
+
+{%- match config.imports %}
+{%- when Some(imports) %}
+{%- for import_name in imports %}
+{{ self.add_import(import_name) }}
+{%- endfor %}
+{%- else %}
+{%- endmatch %}
+
+public object {{ ffi_converter_name }}: FfiConverter<{{ name }}, {{ ffi_type_name }}> {
+ override fun lift(value: {{ ffi_type_name }}): {{ name }} {
+ val builtinValue = {{ builtin|lift_fn }}(value)
+ return {{ config.into_custom.render("builtinValue") }}
+ }
+
+ override fun lower(value: {{ name }}): {{ ffi_type_name }} {
+ val builtinValue = {{ config.from_custom.render("value") }}
+ return {{ builtin|lower_fn }}(builtinValue)
+ }
+
+ override fun read(buf: ByteBuffer): {{ name }} {
+ val builtinValue = {{ builtin|read_fn }}(buf)
+ return {{ config.into_custom.render("builtinValue") }}
+ }
+
+ override fun allocationSize(value: {{ name }}): Int {
+ val builtinValue = {{ config.from_custom.render("value") }}
+ return {{ builtin|allocation_size_fn }}(builtinValue)
+ }
+
+ override fun write(value: {{ name }}, buf: ByteBuffer) {
+ val builtinValue = {{ config.from_custom.render("value") }}
+ {{ builtin|write_fn }}(builtinValue, buf)
+ }
+}
+{%- endmatch %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/DurationHelper.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/DurationHelper.kt
new file mode 100644
index 0000000000..4237c6f9a8
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/DurationHelper.kt
@@ -0,0 +1,36 @@
+public object FfiConverterDuration: FfiConverterRustBuffer<java.time.Duration> {
+ override fun read(buf: ByteBuffer): java.time.Duration {
+ // Type mismatch (should be u64) but we check for overflow/underflow below
+ val seconds = buf.getLong()
+ // Type mismatch (should be u32) but we check for overflow/underflow below
+ val nanoseconds = buf.getInt().toLong()
+ if (seconds < 0) {
+ throw java.time.DateTimeException("Duration exceeds minimum or maximum value supported by uniffi")
+ }
+ if (nanoseconds < 0) {
+ throw java.time.DateTimeException("Duration nanoseconds exceed minimum or maximum supported by uniffi")
+ }
+ return java.time.Duration.ofSeconds(seconds, nanoseconds)
+ }
+
+ // 8 bytes for seconds, 4 bytes for nanoseconds
+ override fun allocationSize(value: java.time.Duration) = 12
+
+ override fun write(value: java.time.Duration, buf: ByteBuffer) {
+ if (value.seconds < 0) {
+ // Rust does not support negative Durations
+ throw IllegalArgumentException("Invalid duration, must be non-negative")
+ }
+
+ if (value.nano < 0) {
+ // Java docs provide guarantee that nano will always be positive, so this should be impossible
+ // See: https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html
+ throw IllegalArgumentException("Invalid duration, nano value must be non-negative")
+ }
+
+ // Type mismatch (should be u64) but since Rust doesn't support negative durations we should be OK
+ buf.putLong(value.seconds)
+ // Type mismatch (should be u32) but since values will always be between 0 and 999,999,999 it should be OK
+ buf.putInt(value.nano)
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/EnumTemplate.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/EnumTemplate.kt
new file mode 100644
index 0000000000..d4c4a1684a
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/EnumTemplate.kt
@@ -0,0 +1,110 @@
+{#
+// Kotlin's `enum class` construct doesn't support variants with associated data,
+// but is a little nicer for consumers than its `sealed class` enum pattern.
+// So, we switch here, using `enum class` for enums with no associated data
+// and `sealed class` for the general case.
+#}
+
+{%- if e.is_flat() %}
+
+enum class {{ type_name }} {
+ {% for variant in e.variants() -%}
+ {{ variant|variant_name }}{% if loop.last %};{% else %},{% endif %}
+ {%- endfor %}
+ companion object
+}
+
+public object {{ e|ffi_converter_name }}: FfiConverterRustBuffer<{{ type_name }}> {
+ override fun read(buf: ByteBuffer) = try {
+ {{ type_name }}.values()[buf.getInt() - 1]
+ } catch (e: IndexOutOfBoundsException) {
+ throw RuntimeException("invalid enum value, something is very wrong!!", e)
+ }
+
+ override fun allocationSize(value: {{ type_name }}) = 4
+
+ override fun write(value: {{ type_name }}, buf: ByteBuffer) {
+ buf.putInt(value.ordinal + 1)
+ }
+}
+
+{% else %}
+
+sealed class {{ type_name }}{% if contains_object_references %}: Disposable {% endif %} {
+ {% for variant in e.variants() -%}
+ {% if !variant.has_fields() -%}
+ object {{ variant|type_name(ci) }} : {{ type_name }}()
+ {% else -%}
+ data class {{ variant|type_name(ci) }}(
+ {% for field in variant.fields() -%}
+ val {{ field.name()|var_name }}: {{ field|type_name(ci) }}{% if loop.last %}{% else %}, {% endif %}
+ {% endfor -%}
+ ) : {{ type_name }}() {
+ companion object
+ }
+ {%- endif %}
+ {% endfor %}
+
+ {% if contains_object_references %}
+ @Suppress("UNNECESSARY_SAFE_CALL") // codegen is much simpler if we unconditionally emit safe calls here
+ override fun destroy() {
+ when(this) {
+ {%- for variant in e.variants() %}
+ is {{ type_name }}.{{ variant|type_name(ci) }} -> {
+ {%- if variant.has_fields() %}
+ {% call kt::destroy_fields(variant) %}
+ {% else -%}
+ // Nothing to destroy
+ {%- endif %}
+ }
+ {%- endfor %}
+ }.let { /* this makes the `when` an expression, which ensures it is exhaustive */ }
+ }
+ {% endif %}
+ companion object
+}
+
+public object {{ e|ffi_converter_name }} : FfiConverterRustBuffer<{{ type_name }}>{
+ override fun read(buf: ByteBuffer): {{ type_name }} {
+ return when(buf.getInt()) {
+ {%- for variant in e.variants() %}
+ {{ loop.index }} -> {{ type_name }}.{{ variant|type_name(ci) }}{% if variant.has_fields() %}(
+ {% for field in variant.fields() -%}
+ {{ field|read_fn }}(buf),
+ {% endfor -%}
+ ){%- endif -%}
+ {%- endfor %}
+ else -> throw RuntimeException("invalid enum value, something is very wrong!!")
+ }
+ }
+
+ override fun allocationSize(value: {{ type_name }}) = when(value) {
+ {%- for variant in e.variants() %}
+ is {{ type_name }}.{{ variant|type_name(ci) }} -> {
+ // Add the size for the Int that specifies the variant plus the size needed for all fields
+ (
+ 4
+ {%- for field in variant.fields() %}
+ + {{ field|allocation_size_fn }}(value.{{ field.name()|var_name }})
+ {%- endfor %}
+ )
+ }
+ {%- endfor %}
+ }
+
+ override fun write(value: {{ type_name }}, buf: ByteBuffer) {
+ when(value) {
+ {%- for variant in e.variants() %}
+ is {{ type_name }}.{{ variant|type_name(ci) }} -> {
+ buf.putInt({{ loop.index }})
+ {%- for field in variant.fields() %}
+ {{ field|write_fn }}(value.{{ field.name()|var_name }}, buf)
+ {%- endfor %}
+ Unit
+ }
+ {%- endfor %}
+ }.let { /* this makes the `when` an expression, which ensures it is exhaustive */ }
+ }
+}
+
+{% endif %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ErrorTemplate.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ErrorTemplate.kt
new file mode 100644
index 0000000000..986db5424d
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ErrorTemplate.kt
@@ -0,0 +1,111 @@
+{%- let type_name = type_|type_name(ci) %}
+{%- let ffi_converter_name = type_|ffi_converter_name %}
+{%- let canonical_type_name = type_|canonical_name %}
+
+{% if e.is_flat() %}
+sealed class {{ type_name }}(message: String): Exception(message){% if contains_object_references %}, Disposable {% endif %} {
+ // Each variant is a nested class
+ // Flat enums carries a string error message, so no special implementation is necessary.
+ {% for variant in e.variants() -%}
+ class {{ variant|error_variant_name }}(message: String) : {{ type_name }}(message)
+ {% endfor %}
+
+ companion object ErrorHandler : CallStatusErrorHandler<{{ type_name }}> {
+ override fun lift(error_buf: RustBuffer.ByValue): {{ type_name }} = {{ ffi_converter_name }}.lift(error_buf)
+ }
+}
+{%- else %}
+sealed class {{ type_name }}: Exception(){% if contains_object_references %}, Disposable {% endif %} {
+ // Each variant is a nested class
+ {% for variant in e.variants() -%}
+ {%- let variant_name = variant|error_variant_name %}
+ class {{ variant_name }}(
+ {% for field in variant.fields() -%}
+ val {{ field.name()|var_name }}: {{ field|type_name(ci) }}{% if loop.last %}{% else %}, {% endif %}
+ {% endfor -%}
+ ) : {{ type_name }}() {
+ override val message
+ get() = "{%- for field in variant.fields() %}{{ field.name()|var_name|unquote }}=${ {{field.name()|var_name }} }{% if !loop.last %}, {% endif %}{% endfor %}"
+ }
+ {% endfor %}
+
+ companion object ErrorHandler : CallStatusErrorHandler<{{ type_name }}> {
+ override fun lift(error_buf: RustBuffer.ByValue): {{ type_name }} = {{ ffi_converter_name }}.lift(error_buf)
+ }
+
+ {% if contains_object_references %}
+ @Suppress("UNNECESSARY_SAFE_CALL") // codegen is much simpler if we unconditionally emit safe calls here
+ override fun destroy() {
+ when(this) {
+ {%- for variant in e.variants() %}
+ is {{ type_name }}.{{ variant|error_variant_name }} -> {
+ {%- if variant.has_fields() %}
+ {% call kt::destroy_fields(variant) %}
+ {% else -%}
+ // Nothing to destroy
+ {%- endif %}
+ }
+ {%- endfor %}
+ }.let { /* this makes the `when` an expression, which ensures it is exhaustive */ }
+ }
+ {% endif %}
+}
+{%- endif %}
+
+public object {{ e|ffi_converter_name }} : FfiConverterRustBuffer<{{ type_name }}> {
+ override fun read(buf: ByteBuffer): {{ type_name }} {
+ {% if e.is_flat() %}
+ return when(buf.getInt()) {
+ {%- for variant in e.variants() %}
+ {{ loop.index }} -> {{ type_name }}.{{ variant|error_variant_name }}({{ Type::String.borrow()|read_fn }}(buf))
+ {%- endfor %}
+ else -> throw RuntimeException("invalid error enum value, something is very wrong!!")
+ }
+ {% else %}
+
+ return when(buf.getInt()) {
+ {%- for variant in e.variants() %}
+ {{ loop.index }} -> {{ type_name }}.{{ variant|error_variant_name }}({% if variant.has_fields() %}
+ {% for field in variant.fields() -%}
+ {{ field|read_fn }}(buf),
+ {% endfor -%}
+ {%- endif -%})
+ {%- endfor %}
+ else -> throw RuntimeException("invalid error enum value, something is very wrong!!")
+ }
+ {%- endif %}
+ }
+
+ override fun allocationSize(value: {{ type_name }}): Int {
+ {%- if e.is_flat() %}
+ return 4
+ {%- else %}
+ return when(value) {
+ {%- for variant in e.variants() %}
+ is {{ type_name }}.{{ variant|error_variant_name }} -> (
+ // Add the size for the Int that specifies the variant plus the size needed for all fields
+ 4
+ {%- for field in variant.fields() %}
+ + {{ field|allocation_size_fn }}(value.{{ field.name()|var_name }})
+ {%- endfor %}
+ )
+ {%- endfor %}
+ }
+ {%- endif %}
+ }
+
+ override fun write(value: {{ type_name }}, buf: ByteBuffer) {
+ when(value) {
+ {%- for variant in e.variants() %}
+ is {{ type_name }}.{{ variant|error_variant_name }} -> {
+ buf.putInt({{ loop.index }})
+ {%- for field in variant.fields() %}
+ {{ field|write_fn }}(value.{{ field.name()|var_name }}, buf)
+ {%- endfor %}
+ Unit
+ }
+ {%- endfor %}
+ }.let { /* this makes the `when` an expression, which ensures it is exhaustive */ }
+ }
+
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ExternalTypeTemplate.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ExternalTypeTemplate.kt
new file mode 100644
index 0000000000..0fade7a0bc
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ExternalTypeTemplate.kt
@@ -0,0 +1,9 @@
+{%- let package_name=self.external_type_package_name(module_path, namespace) %}
+{%- let fully_qualified_type_name = "{}.{}"|format(package_name, name) %}
+{%- let fully_qualified_ffi_converter_name = "{}.FfiConverterType{}"|format(package_name, name) %}
+{%- let fully_qualified_rustbuffer_name = "{}.RustBuffer"|format(package_name) %}
+{%- let local_rustbuffer_name = "RustBuffer{}"|format(name) %}
+
+{{- self.add_import(fully_qualified_type_name) }}
+{{- self.add_import(fully_qualified_ffi_converter_name) }}
+{{ self.add_import_as(fully_qualified_rustbuffer_name, local_rustbuffer_name) }}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/FfiConverterTemplate.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/FfiConverterTemplate.kt
new file mode 100644
index 0000000000..3b2c9d225a
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/FfiConverterTemplate.kt
@@ -0,0 +1,71 @@
+// The FfiConverter interface handles converter types to and from the FFI
+//
+// All implementing objects should be public to support external types. When a
+// type is external we need to import it's FfiConverter.
+public interface FfiConverter<KotlinType, FfiType> {
+ // Convert an FFI type to a Kotlin type
+ fun lift(value: FfiType): KotlinType
+
+ // Convert an Kotlin type to an FFI type
+ fun lower(value: KotlinType): FfiType
+
+ // Read a Kotlin type from a `ByteBuffer`
+ fun read(buf: ByteBuffer): KotlinType
+
+ // Calculate bytes to allocate when creating a `RustBuffer`
+ //
+ // This must return at least as many bytes as the write() function will
+ // write. It can return more bytes than needed, for example when writing
+ // Strings we can't know the exact bytes needed until we the UTF-8
+ // encoding, so we pessimistically allocate the largest size possible (3
+ // bytes per codepoint). Allocating extra bytes is not really a big deal
+ // because the `RustBuffer` is short-lived.
+ fun allocationSize(value: KotlinType): Int
+
+ // Write a Kotlin type to a `ByteBuffer`
+ fun write(value: KotlinType, buf: ByteBuffer)
+
+ // Lower a value into a `RustBuffer`
+ //
+ // This method lowers a value into a `RustBuffer` rather than the normal
+ // FfiType. It's used by the callback interface code. Callback interface
+ // returns are always serialized into a `RustBuffer` regardless of their
+ // normal FFI type.
+ fun lowerIntoRustBuffer(value: KotlinType): RustBuffer.ByValue {
+ val rbuf = RustBuffer.alloc(allocationSize(value))
+ try {
+ val bbuf = rbuf.data!!.getByteBuffer(0, rbuf.capacity.toLong()).also {
+ it.order(ByteOrder.BIG_ENDIAN)
+ }
+ write(value, bbuf)
+ rbuf.writeField("len", bbuf.position())
+ return rbuf
+ } catch (e: Throwable) {
+ RustBuffer.free(rbuf)
+ throw e
+ }
+ }
+
+ // Lift a value from a `RustBuffer`.
+ //
+ // This here mostly because of the symmetry with `lowerIntoRustBuffer()`.
+ // It's currently only used by the `FfiConverterRustBuffer` class below.
+ fun liftFromRustBuffer(rbuf: RustBuffer.ByValue): KotlinType {
+ val byteBuf = rbuf.asByteBuffer()!!
+ try {
+ val item = read(byteBuf)
+ if (byteBuf.hasRemaining()) {
+ throw RuntimeException("junk remaining in buffer after lifting, something is very wrong!!")
+ }
+ return item
+ } finally {
+ RustBuffer.free(rbuf)
+ }
+ }
+}
+
+// FfiConverter that uses `RustBuffer` as the FfiType
+public interface FfiConverterRustBuffer<KotlinType>: FfiConverter<KotlinType, RustBuffer.ByValue> {
+ override fun lift(value: RustBuffer.ByValue) = liftFromRustBuffer(value)
+ override fun lower(value: KotlinType) = lowerIntoRustBuffer(value)
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Float32Helper.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Float32Helper.kt
new file mode 100644
index 0000000000..eafec5d122
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Float32Helper.kt
@@ -0,0 +1,19 @@
+public object FfiConverterFloat: FfiConverter<Float, Float> {
+ override fun lift(value: Float): Float {
+ return value
+ }
+
+ override fun read(buf: ByteBuffer): Float {
+ return buf.getFloat()
+ }
+
+ override fun lower(value: Float): Float {
+ return value
+ }
+
+ override fun allocationSize(value: Float) = 4
+
+ override fun write(value: Float, buf: ByteBuffer) {
+ buf.putFloat(value)
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Float64Helper.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Float64Helper.kt
new file mode 100644
index 0000000000..9fc2892c95
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Float64Helper.kt
@@ -0,0 +1,19 @@
+public object FfiConverterDouble: FfiConverter<Double, Double> {
+ override fun lift(value: Double): Double {
+ return value
+ }
+
+ override fun read(buf: ByteBuffer): Double {
+ return buf.getDouble()
+ }
+
+ override fun lower(value: Double): Double {
+ return value
+ }
+
+ override fun allocationSize(value: Double) = 8
+
+ override fun write(value: Double, buf: ByteBuffer) {
+ buf.putDouble(value)
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ForeignExecutorTemplate.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ForeignExecutorTemplate.kt
new file mode 100644
index 0000000000..3544b2f9e6
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ForeignExecutorTemplate.kt
@@ -0,0 +1,83 @@
+{{ self.add_import("kotlinx.coroutines.CoroutineScope") }}
+{{ self.add_import("kotlinx.coroutines.delay") }}
+{{ self.add_import("kotlinx.coroutines.isActive") }}
+{{ self.add_import("kotlinx.coroutines.launch") }}
+
+internal const val UNIFFI_RUST_TASK_CALLBACK_SUCCESS = 0.toByte()
+internal const val UNIFFI_RUST_TASK_CALLBACK_CANCELLED = 1.toByte()
+internal const val UNIFFI_FOREIGN_EXECUTOR_CALLBACK_SUCCESS = 0.toByte()
+internal const val UNIFFI_FOREIGN_EXECUTOR_CALLBACK_CANCELLED = 1.toByte()
+internal const val UNIFFI_FOREIGN_EXECUTOR_CALLBACK_ERROR = 2.toByte()
+
+// Callback function to execute a Rust task. The Kotlin code schedules these in a coroutine then
+// invokes them.
+internal interface UniFfiRustTaskCallback : com.sun.jna.Callback {
+ fun callback(rustTaskData: Pointer?, statusCode: Byte)
+}
+
+internal object UniFfiForeignExecutorCallback : com.sun.jna.Callback {
+ fun callback(handle: USize, delayMs: Int, rustTask: UniFfiRustTaskCallback?, rustTaskData: Pointer?) : Byte {
+ if (rustTask == null) {
+ FfiConverterForeignExecutor.drop(handle)
+ return UNIFFI_FOREIGN_EXECUTOR_CALLBACK_SUCCESS
+ } else {
+ val coroutineScope = FfiConverterForeignExecutor.lift(handle)
+ if (coroutineScope.isActive) {
+ val job = coroutineScope.launch {
+ if (delayMs > 0) {
+ delay(delayMs.toLong())
+ }
+ rustTask.callback(rustTaskData, UNIFFI_RUST_TASK_CALLBACK_SUCCESS)
+ }
+ job.invokeOnCompletion { cause ->
+ if (cause != null) {
+ rustTask.callback(rustTaskData, UNIFFI_RUST_TASK_CALLBACK_CANCELLED)
+ }
+ }
+ return UNIFFI_FOREIGN_EXECUTOR_CALLBACK_SUCCESS
+ } else {
+ return UNIFFI_FOREIGN_EXECUTOR_CALLBACK_CANCELLED
+ }
+ }
+ }
+}
+
+public object FfiConverterForeignExecutor: FfiConverter<CoroutineScope, USize> {
+ internal val handleMap = UniFfiHandleMap<CoroutineScope>()
+
+ internal fun drop(handle: USize) {
+ handleMap.remove(handle)
+ }
+
+ internal fun register(lib: _UniFFILib) {
+ {%- match ci.ffi_foreign_executor_callback_set() %}
+ {%- when Some with (fn) %}
+ lib.{{ fn.name() }}(UniFfiForeignExecutorCallback)
+ {%- when None %}
+ {#- No foreign executor, we don't set anything #}
+ {% endmatch %}
+ }
+
+ // Number of live handles, exposed so we can test the memory management
+ public fun handleCount() : Int {
+ return handleMap.size
+ }
+
+ override fun allocationSize(value: CoroutineScope) = USize.size
+
+ override fun lift(value: USize): CoroutineScope {
+ return handleMap.get(value) ?: throw RuntimeException("unknown handle in FfiConverterForeignExecutor.lift")
+ }
+
+ override fun read(buf: ByteBuffer): CoroutineScope {
+ return lift(USize.readFromBuffer(buf))
+ }
+
+ override fun lower(value: CoroutineScope): USize {
+ return handleMap.insert(value)
+ }
+
+ override fun write(value: CoroutineScope, buf: ByteBuffer) {
+ lower(value).writeToBuffer(buf)
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Helpers.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Helpers.kt
new file mode 100644
index 0000000000..382a5f7413
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Helpers.kt
@@ -0,0 +1,161 @@
+// A handful of classes and functions to support the generated data structures.
+// This would be a good candidate for isolating in its own ffi-support lib.
+// Error runtime.
+@Structure.FieldOrder("code", "error_buf")
+internal open class RustCallStatus : Structure() {
+ @JvmField var code: Byte = 0
+ @JvmField var error_buf: RustBuffer.ByValue = RustBuffer.ByValue()
+
+ class ByValue: RustCallStatus(), Structure.ByValue
+
+ fun isSuccess(): Boolean {
+ return code == 0.toByte()
+ }
+
+ fun isError(): Boolean {
+ return code == 1.toByte()
+ }
+
+ fun isPanic(): Boolean {
+ return code == 2.toByte()
+ }
+}
+
+class InternalException(message: String) : Exception(message)
+
+// Each top-level error class has a companion object that can lift the error from the call status's rust buffer
+interface CallStatusErrorHandler<E> {
+ fun lift(error_buf: RustBuffer.ByValue): E;
+}
+
+// Helpers for calling Rust
+// In practice we usually need to be synchronized to call this safely, so it doesn't
+// synchronize itself
+
+// Call a rust function that returns a Result<>. Pass in the Error class companion that corresponds to the Err
+private inline fun <U, E: Exception> rustCallWithError(errorHandler: CallStatusErrorHandler<E>, callback: (RustCallStatus) -> U): U {
+ var status = RustCallStatus();
+ val return_value = callback(status)
+ checkCallStatus(errorHandler, status)
+ return return_value
+}
+
+// Check RustCallStatus and throw an error if the call wasn't successful
+private fun<E: Exception> checkCallStatus(errorHandler: CallStatusErrorHandler<E>, status: RustCallStatus) {
+ if (status.isSuccess()) {
+ return
+ } else if (status.isError()) {
+ throw errorHandler.lift(status.error_buf)
+ } else if (status.isPanic()) {
+ // when the rust code sees a panic, it tries to construct a rustbuffer
+ // with the message. but if that code panics, then it just sends back
+ // an empty buffer.
+ if (status.error_buf.len > 0) {
+ throw InternalException({{ Type::String.borrow()|lift_fn }}(status.error_buf))
+ } else {
+ throw InternalException("Rust panic")
+ }
+ } else {
+ throw InternalException("Unknown rust call status: $status.code")
+ }
+}
+
+// CallStatusErrorHandler implementation for times when we don't expect a CALL_ERROR
+object NullCallStatusErrorHandler: CallStatusErrorHandler<InternalException> {
+ override fun lift(error_buf: RustBuffer.ByValue): InternalException {
+ RustBuffer.free(error_buf)
+ return InternalException("Unexpected CALL_ERROR")
+ }
+}
+
+// Call a rust function that returns a plain value
+private inline fun <U> rustCall(callback: (RustCallStatus) -> U): U {
+ return rustCallWithError(NullCallStatusErrorHandler, callback);
+}
+
+// IntegerType that matches Rust's `usize` / C's `size_t`
+public class USize(value: Long = 0) : IntegerType(Native.SIZE_T_SIZE, value, true) {
+ // This is needed to fill in the gaps of IntegerType's implementation of Number for Kotlin.
+ override fun toByte() = toInt().toByte()
+ // Needed until https://youtrack.jetbrains.com/issue/KT-47902 is fixed.
+ @Deprecated("`toInt().toChar()` is deprecated")
+ override fun toChar() = toInt().toChar()
+ override fun toShort() = toInt().toShort()
+
+ fun writeToBuffer(buf: ByteBuffer) {
+ // Make sure we always write usize integers using native byte-order, since they may be
+ // casted to pointer values
+ buf.order(ByteOrder.nativeOrder())
+ try {
+ when (Native.SIZE_T_SIZE) {
+ 4 -> buf.putInt(toInt())
+ 8 -> buf.putLong(toLong())
+ else -> throw RuntimeException("Invalid SIZE_T_SIZE: ${Native.SIZE_T_SIZE}")
+ }
+ } finally {
+ buf.order(ByteOrder.BIG_ENDIAN)
+ }
+ }
+
+ companion object {
+ val size: Int
+ get() = Native.SIZE_T_SIZE
+
+ fun readFromBuffer(buf: ByteBuffer) : USize {
+ // Make sure we always read usize integers using native byte-order, since they may be
+ // casted from pointer values
+ buf.order(ByteOrder.nativeOrder())
+ try {
+ return when (Native.SIZE_T_SIZE) {
+ 4 -> USize(buf.getInt().toLong())
+ 8 -> USize(buf.getLong())
+ else -> throw RuntimeException("Invalid SIZE_T_SIZE: ${Native.SIZE_T_SIZE}")
+ }
+ } finally {
+ buf.order(ByteOrder.BIG_ENDIAN)
+ }
+ }
+ }
+}
+
+
+// Map handles to objects
+//
+// This is used when the Rust code expects an opaque pointer to represent some foreign object.
+// Normally we would pass a pointer to the object, but JNA doesn't support getting a pointer from an
+// object reference , nor does it support leaking a reference to Rust.
+//
+// Instead, this class maps USize values to objects so that we can pass a pointer-sized type to
+// Rust when it needs an opaque pointer.
+//
+// TODO: refactor callbacks to use this class
+internal class UniFfiHandleMap<T: Any> {
+ private val map = ConcurrentHashMap<USize, T>()
+ // Use AtomicInteger for our counter, since we may be on a 32-bit system. 4 billion possible
+ // values seems like enough. If somehow we generate 4 billion handles, then this will wrap
+ // around back to zero and we can assume the first handle generated will have been dropped by
+ // then.
+ private val counter = java.util.concurrent.atomic.AtomicInteger(0)
+
+ val size: Int
+ get() = map.size
+
+ fun insert(obj: T): USize {
+ val handle = USize(counter.getAndAdd(1).toLong())
+ map.put(handle, obj)
+ return handle
+ }
+
+ fun get(handle: USize): T? {
+ return map.get(handle)
+ }
+
+ fun remove(handle: USize): T? {
+ return map.remove(handle)
+ }
+}
+
+// FFI type for Rust future continuations
+internal interface UniFffiRustFutureContinuationCallbackType : com.sun.jna.Callback {
+ fun callback(continuationHandle: USize, pollResult: Short);
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Int16Helper.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Int16Helper.kt
new file mode 100644
index 0000000000..75564276be
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Int16Helper.kt
@@ -0,0 +1,19 @@
+public object FfiConverterShort: FfiConverter<Short, Short> {
+ override fun lift(value: Short): Short {
+ return value
+ }
+
+ override fun read(buf: ByteBuffer): Short {
+ return buf.getShort()
+ }
+
+ override fun lower(value: Short): Short {
+ return value
+ }
+
+ override fun allocationSize(value: Short) = 2
+
+ override fun write(value: Short, buf: ByteBuffer) {
+ buf.putShort(value)
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Int32Helper.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Int32Helper.kt
new file mode 100644
index 0000000000..b7a8131c8b
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Int32Helper.kt
@@ -0,0 +1,19 @@
+public object FfiConverterInt: FfiConverter<Int, Int> {
+ override fun lift(value: Int): Int {
+ return value
+ }
+
+ override fun read(buf: ByteBuffer): Int {
+ return buf.getInt()
+ }
+
+ override fun lower(value: Int): Int {
+ return value
+ }
+
+ override fun allocationSize(value: Int) = 4
+
+ override fun write(value: Int, buf: ByteBuffer) {
+ buf.putInt(value)
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Int64Helper.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Int64Helper.kt
new file mode 100644
index 0000000000..601cfc7c2c
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Int64Helper.kt
@@ -0,0 +1,19 @@
+public object FfiConverterLong: FfiConverter<Long, Long> {
+ override fun lift(value: Long): Long {
+ return value
+ }
+
+ override fun read(buf: ByteBuffer): Long {
+ return buf.getLong()
+ }
+
+ override fun lower(value: Long): Long {
+ return value
+ }
+
+ override fun allocationSize(value: Long) = 8
+
+ override fun write(value: Long, buf: ByteBuffer) {
+ buf.putLong(value)
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Int8Helper.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Int8Helper.kt
new file mode 100644
index 0000000000..9237768dbf
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Int8Helper.kt
@@ -0,0 +1,19 @@
+public object FfiConverterByte: FfiConverter<Byte, Byte> {
+ override fun lift(value: Byte): Byte {
+ return value
+ }
+
+ override fun read(buf: ByteBuffer): Byte {
+ return buf.get()
+ }
+
+ override fun lower(value: Byte): Byte {
+ return value
+ }
+
+ override fun allocationSize(value: Byte) = 1
+
+ override fun write(value: Byte, buf: ByteBuffer) {
+ buf.put(value)
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/MapTemplate.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/MapTemplate.kt
new file mode 100644
index 0000000000..776c402727
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/MapTemplate.kt
@@ -0,0 +1,34 @@
+{%- let key_type_name = key_type|type_name(ci) %}
+{%- let value_type_name = value_type|type_name(ci) %}
+public object {{ ffi_converter_name }}: FfiConverterRustBuffer<Map<{{ key_type_name }}, {{ value_type_name }}>> {
+ override fun read(buf: ByteBuffer): Map<{{ key_type_name }}, {{ value_type_name }}> {
+ val len = buf.getInt()
+ return buildMap<{{ key_type_name }}, {{ value_type_name }}>(len) {
+ repeat(len) {
+ val k = {{ key_type|read_fn }}(buf)
+ val v = {{ value_type|read_fn }}(buf)
+ this[k] = v
+ }
+ }
+ }
+
+ override fun allocationSize(value: Map<{{ key_type_name }}, {{ value_type_name }}>): Int {
+ val spaceForMapSize = 4
+ val spaceForChildren = value.map { (k, v) ->
+ {{ key_type|allocation_size_fn }}(k) +
+ {{ value_type|allocation_size_fn }}(v)
+ }.sum()
+ return spaceForMapSize + spaceForChildren
+ }
+
+ override fun write(value: Map<{{ key_type_name }}, {{ value_type_name }}>, buf: ByteBuffer) {
+ buf.putInt(value.size)
+ // The parens on `(k, v)` here ensure we're calling the right method,
+ // which is important for compatibility with older android devices.
+ // Ref https://blog.danlew.net/2017/03/16/kotlin-puzzler-whose-line-is-it-anyways/
+ value.forEach { (k, v) ->
+ {{ key_type|write_fn }}(k, buf)
+ {{ value_type|write_fn }}(v, buf)
+ }
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/NamespaceLibraryTemplate.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/NamespaceLibraryTemplate.kt
new file mode 100644
index 0000000000..6a3aeada35
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/NamespaceLibraryTemplate.kt
@@ -0,0 +1,57 @@
+@Synchronized
+private fun findLibraryName(componentName: String): String {
+ val libOverride = System.getProperty("uniffi.component.$componentName.libraryOverride")
+ if (libOverride != null) {
+ return libOverride
+ }
+ return "{{ config.cdylib_name() }}"
+}
+
+private inline fun <reified Lib : Library> loadIndirect(
+ componentName: String
+): Lib {
+ return Native.load<Lib>(findLibraryName(componentName), Lib::class.java)
+}
+
+// A JNA Library to expose the extern-C FFI definitions.
+// This is an implementation detail which will be called internally by the public API.
+
+internal interface _UniFFILib : Library {
+ companion object {
+ internal val INSTANCE: _UniFFILib by lazy {
+ loadIndirect<_UniFFILib>(componentName = "{{ ci.namespace() }}")
+ .also { lib: _UniFFILib ->
+ uniffiCheckContractApiVersion(lib)
+ uniffiCheckApiChecksums(lib)
+ {% for fn in self.initialization_fns() -%}
+ {{ fn }}(lib)
+ {% endfor -%}
+ }
+ }
+ }
+
+ {% for func in ci.iter_ffi_function_definitions() -%}
+ fun {{ func.name() }}(
+ {%- call kt::arg_list_ffi_decl(func) %}
+ ): {% match func.return_type() %}{% when Some with (return_type) %}{{ return_type.borrow()|ffi_type_name_by_value }}{% when None %}Unit{% endmatch %}
+ {% endfor %}
+}
+
+private fun uniffiCheckContractApiVersion(lib: _UniFFILib) {
+ // Get the bindings contract version from our ComponentInterface
+ val bindings_contract_version = {{ ci.uniffi_contract_version() }}
+ // Get the scaffolding contract version by calling the into the dylib
+ val scaffolding_contract_version = lib.{{ ci.ffi_uniffi_contract_version().name() }}()
+ if (bindings_contract_version != scaffolding_contract_version) {
+ throw RuntimeException("UniFFI contract version mismatch: try cleaning and rebuilding your project")
+ }
+}
+
+@Suppress("UNUSED_PARAMETER")
+private fun uniffiCheckApiChecksums(lib: _UniFFILib) {
+ {%- for (name, expected_checksum) in ci.iter_checksums() %}
+ if (lib.{{ name }}() != {{ expected_checksum }}.toShort()) {
+ throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
+ }
+ {%- endfor %}
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ObjectRuntime.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ObjectRuntime.kt
new file mode 100644
index 0000000000..b9352c690f
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ObjectRuntime.kt
@@ -0,0 +1,161 @@
+// Interface implemented by anything that can contain an object reference.
+//
+// Such types expose a `destroy()` method that must be called to cleanly
+// dispose of the contained objects. Failure to call this method may result
+// in memory leaks.
+//
+// The easiest way to ensure this method is called is to use the `.use`
+// helper method to execute a block and destroy the object at the end.
+interface Disposable {
+ fun destroy()
+ companion object {
+ fun destroy(vararg args: Any?) {
+ args.filterIsInstance<Disposable>()
+ .forEach(Disposable::destroy)
+ }
+ }
+}
+
+inline fun <T : Disposable?, R> T.use(block: (T) -> R) =
+ try {
+ block(this)
+ } finally {
+ try {
+ // N.B. our implementation is on the nullable type `Disposable?`.
+ this?.destroy()
+ } catch (e: Throwable) {
+ // swallow
+ }
+ }
+
+// The base class for all UniFFI Object types.
+//
+// This class provides core operations for working with the Rust `Arc<T>` pointer to
+// the live Rust struct on the other side of the FFI.
+//
+// There's some subtlety here, because we have to be careful not to operate on a Rust
+// struct after it has been dropped, and because we must expose a public API for freeing
+// the Kotlin wrapper object in lieu of reliable finalizers. The core requirements are:
+//
+// * Each `FFIObject` instance holds an opaque pointer to the underlying Rust struct.
+// Method calls need to read this pointer from the object's state and pass it in to
+// the Rust FFI.
+//
+// * When an `FFIObject` is no longer needed, its pointer should be passed to a
+// special destructor function provided by the Rust FFI, which will drop the
+// underlying Rust struct.
+//
+// * Given an `FFIObject` instance, calling code is expected to call the special
+// `destroy` method in order to free it after use, either by calling it explicitly
+// or by using a higher-level helper like the `use` method. Failing to do so will
+// leak the underlying Rust struct.
+//
+// * We can't assume that calling code will do the right thing, and must be prepared
+// to handle Kotlin method calls executing concurrently with or even after a call to
+// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`.
+//
+// * We must never allow Rust code to operate on the underlying Rust struct after
+// the destructor has been called, and must never call the destructor more than once.
+// Doing so may trigger memory unsafety.
+//
+// If we try to implement this with mutual exclusion on access to the pointer, there is the
+// possibility of a race between a method call and a concurrent call to `destroy`:
+//
+// * Thread A starts a method call, reads the value of the pointer, but is interrupted
+// before it can pass the pointer over the FFI to Rust.
+// * Thread B calls `destroy` and frees the underlying Rust struct.
+// * Thread A resumes, passing the already-read pointer value to Rust and triggering
+// a use-after-free.
+//
+// One possible solution would be to use a `ReadWriteLock`, with each method call taking
+// a read lock (and thus allowed to run concurrently) and the special `destroy` method
+// taking a write lock (and thus blocking on live method calls). However, we aim not to
+// generate methods with any hidden blocking semantics, and a `destroy` method that might
+// block if called incorrectly seems to meet that bar.
+//
+// So, we achieve our goals by giving each `FFIObject` an associated `AtomicLong` counter to track
+// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy`
+// has been called. These are updated according to the following rules:
+//
+// * The initial value of the counter is 1, indicating a live object with no in-flight calls.
+// The initial value for the flag is false.
+//
+// * At the start of each method call, we atomically check the counter.
+// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted.
+// If it is nonzero them we atomically increment it by 1 and proceed with the method call.
+//
+// * At the end of each method call, we atomically decrement and check the counter.
+// If it has reached zero then we destroy the underlying Rust struct.
+//
+// * When `destroy` is called, we atomically flip the flag from false to true.
+// If the flag was already true we silently fail.
+// Otherwise we atomically decrement and check the counter.
+// If it has reached zero then we destroy the underlying Rust struct.
+//
+// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc<T>` works,
+// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`.
+//
+// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been
+// called *and* all in-flight method calls have completed, avoiding violating any of the expectations
+// of the underlying Rust code.
+//
+// In the future we may be able to replace some of this with automatic finalization logic, such as using
+// the new "Cleaner" functionaility in Java 9. The above scheme has been designed to work even if `destroy` is
+// invoked by garbage-collection machinery rather than by calling code (which by the way, it's apparently also
+// possible for the JVM to finalize an object while there is an in-flight call to one of its methods [1],
+// so there would still be some complexity here).
+//
+// Sigh...all of this for want of a robust finalization mechanism.
+//
+// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219
+//
+abstract class FFIObject(
+ protected val pointer: Pointer
+): Disposable, AutoCloseable {
+
+ private val wasDestroyed = AtomicBoolean(false)
+ private val callCounter = AtomicLong(1)
+
+ open protected fun freeRustArcPtr() {
+ // To be overridden in subclasses.
+ }
+
+ override fun destroy() {
+ // Only allow a single call to this method.
+ // TODO: maybe we should log a warning if called more than once?
+ if (this.wasDestroyed.compareAndSet(false, true)) {
+ // This decrement always matches the initial count of 1 given at creation time.
+ if (this.callCounter.decrementAndGet() == 0L) {
+ this.freeRustArcPtr()
+ }
+ }
+ }
+
+ @Synchronized
+ override fun close() {
+ this.destroy()
+ }
+
+ internal inline fun <R> callWithPointer(block: (ptr: Pointer) -> R): R {
+ // Check and increment the call counter, to keep the object alive.
+ // This needs a compare-and-set retry loop in case of concurrent updates.
+ do {
+ val c = this.callCounter.get()
+ if (c == 0L) {
+ throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed")
+ }
+ if (c == Long.MAX_VALUE) {
+ throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow")
+ }
+ } while (! this.callCounter.compareAndSet(c, c + 1L))
+ // Now we can safely do the method call without the pointer being freed concurrently.
+ try {
+ return block(this.pointer)
+ } finally {
+ // This decrement always matches the increment we performed above.
+ if (this.callCounter.decrementAndGet() == 0L) {
+ this.freeRustArcPtr()
+ }
+ }
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ObjectTemplate.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ObjectTemplate.kt
new file mode 100644
index 0000000000..8ce27a5d04
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ObjectTemplate.kt
@@ -0,0 +1,138 @@
+{%- let obj = ci|get_object_definition(name) %}
+{%- if self.include_once_check("ObjectRuntime.kt") %}{% include "ObjectRuntime.kt" %}{% endif %}
+{{- self.add_import("java.util.concurrent.atomic.AtomicLong") }}
+{{- self.add_import("java.util.concurrent.atomic.AtomicBoolean") }}
+
+public interface {{ type_name }}Interface {
+ {% for meth in obj.methods() -%}
+ {%- match meth.throws_type() -%}
+ {%- when Some with (throwable) -%}
+ @Throws({{ throwable|type_name(ci) }}::class)
+ {%- when None -%}
+ {%- endmatch %}
+ {% if meth.is_async() -%}
+ suspend fun {{ meth.name()|fn_name }}({% call kt::arg_list_decl(meth) %})
+ {%- else -%}
+ fun {{ meth.name()|fn_name }}({% call kt::arg_list_decl(meth) %})
+ {%- endif %}
+ {%- match meth.return_type() -%}
+ {%- when Some with (return_type) %}: {{ return_type|type_name(ci) -}}
+ {%- when None -%}
+ {%- endmatch -%}
+
+ {% endfor %}
+ companion object
+}
+
+class {{ type_name }}(
+ pointer: Pointer
+) : FFIObject(pointer), {{ type_name }}Interface {
+
+ {%- match obj.primary_constructor() %}
+ {%- when Some with (cons) %}
+ constructor({% call kt::arg_list_decl(cons) -%}) :
+ this({% call kt::to_ffi_call(cons) %})
+ {%- when None %}
+ {%- endmatch %}
+
+ /**
+ * Disconnect the object from the underlying Rust object.
+ *
+ * It can be called more than once, but once called, interacting with the object
+ * causes an `IllegalStateException`.
+ *
+ * Clients **must** call this method once done with the object, or cause a memory leak.
+ */
+ override protected fun freeRustArcPtr() {
+ rustCall() { status ->
+ _UniFFILib.INSTANCE.{{ obj.ffi_object_free().name() }}(this.pointer, status)
+ }
+ }
+
+ {% for meth in obj.methods() -%}
+ {%- match meth.throws_type() -%}
+ {%- when Some with (throwable) %}
+ @Throws({{ throwable|type_name(ci) }}::class)
+ {%- else -%}
+ {%- endmatch -%}
+ {%- if meth.is_async() %}
+ @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE")
+ override suspend fun {{ meth.name()|fn_name }}({%- call kt::arg_list_decl(meth) -%}){% match meth.return_type() %}{% when Some with (return_type) %} : {{ return_type|type_name(ci) }}{% when None %}{%- endmatch %} {
+ return uniffiRustCallAsync(
+ callWithPointer { thisPtr ->
+ _UniFFILib.INSTANCE.{{ meth.ffi_func().name() }}(
+ thisPtr,
+ {% call kt::arg_list_lowered(meth) %}
+ )
+ },
+ {{ meth|async_poll(ci) }},
+ {{ meth|async_complete(ci) }},
+ {{ meth|async_free(ci) }},
+ // lift function
+ {%- match meth.return_type() %}
+ {%- when Some(return_type) %}
+ { {{ return_type|lift_fn }}(it) },
+ {%- when None %}
+ { Unit },
+ {% endmatch %}
+ // Error FFI converter
+ {%- match meth.throws_type() %}
+ {%- when Some(e) %}
+ {{ e|type_name(ci) }}.ErrorHandler,
+ {%- when None %}
+ NullCallStatusErrorHandler,
+ {%- endmatch %}
+ )
+ }
+ {%- else -%}
+ {%- match meth.return_type() -%}
+ {%- when Some with (return_type) -%}
+ override fun {{ meth.name()|fn_name }}({% call kt::arg_list_protocol(meth) %}): {{ return_type|type_name(ci) }} =
+ callWithPointer {
+ {%- call kt::to_ffi_call_with_prefix("it", meth) %}
+ }.let {
+ {{ return_type|lift_fn }}(it)
+ }
+
+ {%- when None -%}
+ override fun {{ meth.name()|fn_name }}({% call kt::arg_list_protocol(meth) %}) =
+ callWithPointer {
+ {%- call kt::to_ffi_call_with_prefix("it", meth) %}
+ }
+ {% endmatch %}
+ {% endif %}
+ {% endfor %}
+
+ {% if !obj.alternate_constructors().is_empty() -%}
+ companion object {
+ {% for cons in obj.alternate_constructors() -%}
+ fun {{ cons.name()|fn_name }}({% call kt::arg_list_decl(cons) %}): {{ type_name }} =
+ {{ type_name }}({% call kt::to_ffi_call(cons) %})
+ {% endfor %}
+ }
+ {% else %}
+ companion object
+ {% endif %}
+}
+
+public object {{ obj|ffi_converter_name }}: FfiConverter<{{ type_name }}, Pointer> {
+ override fun lower(value: {{ type_name }}): Pointer = value.callWithPointer { it }
+
+ override fun lift(value: Pointer): {{ type_name }} {
+ return {{ type_name }}(value)
+ }
+
+ override fun read(buf: ByteBuffer): {{ type_name }} {
+ // The Rust code always writes pointers as 8 bytes, and will
+ // fail to compile if they don't fit.
+ return lift(Pointer(buf.getLong()))
+ }
+
+ override fun allocationSize(value: {{ type_name }}) = 8
+
+ override fun write(value: {{ type_name }}, buf: ByteBuffer) {
+ // The Rust code always expects pointers written as 8 bytes,
+ // and will fail to compile if they don't fit.
+ buf.putLong(Pointer.nativeValue(lower(value)))
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/OptionalTemplate.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/OptionalTemplate.kt
new file mode 100644
index 0000000000..56cb5f87a5
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/OptionalTemplate.kt
@@ -0,0 +1,27 @@
+{%- let inner_type_name = inner_type|type_name(ci) %}
+
+public object {{ ffi_converter_name }}: FfiConverterRustBuffer<{{ inner_type_name }}?> {
+ override fun read(buf: ByteBuffer): {{ inner_type_name }}? {
+ if (buf.get().toInt() == 0) {
+ return null
+ }
+ return {{ inner_type|read_fn }}(buf)
+ }
+
+ override fun allocationSize(value: {{ inner_type_name }}?): Int {
+ if (value == null) {
+ return 1
+ } else {
+ return 1 + {{ inner_type|allocation_size_fn }}(value)
+ }
+ }
+
+ override fun write(value: {{ inner_type_name }}?, buf: ByteBuffer) {
+ if (value == null) {
+ buf.put(0)
+ } else {
+ buf.put(1)
+ {{ inner_type|write_fn }}(value, buf)
+ }
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/RecordTemplate.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/RecordTemplate.kt
new file mode 100644
index 0000000000..b588ca1398
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/RecordTemplate.kt
@@ -0,0 +1,42 @@
+{%- let rec = ci|get_record_definition(name) %}
+
+data class {{ type_name }} (
+ {%- for field in rec.fields() %}
+ var {{ field.name()|var_name }}: {{ field|type_name(ci) -}}
+ {%- match field.default_value() %}
+ {%- when Some with(literal) %} = {{ literal|render_literal(field, ci) }}
+ {%- else %}
+ {%- endmatch -%}
+ {% if !loop.last %}, {% endif %}
+ {%- endfor %}
+) {% if contains_object_references %}: Disposable {% endif %}{
+ {% if contains_object_references %}
+ @Suppress("UNNECESSARY_SAFE_CALL") // codegen is much simpler if we unconditionally emit safe calls here
+ override fun destroy() {
+ {% call kt::destroy_fields(rec) %}
+ }
+ {% endif %}
+ companion object
+}
+
+public object {{ rec|ffi_converter_name }}: FfiConverterRustBuffer<{{ type_name }}> {
+ override fun read(buf: ByteBuffer): {{ type_name }} {
+ return {{ type_name }}(
+ {%- for field in rec.fields() %}
+ {{ field|read_fn }}(buf),
+ {%- endfor %}
+ )
+ }
+
+ override fun allocationSize(value: {{ type_name }}) = (
+ {%- for field in rec.fields() %}
+ {{ field|allocation_size_fn }}(value.{{ field.name()|var_name }}){% if !loop.last %} +{% endif%}
+ {%- endfor %}
+ )
+
+ override fun write(value: {{ type_name }}, buf: ByteBuffer) {
+ {%- for field in rec.fields() %}
+ {{ field|write_fn }}(value.{{ field.name()|var_name }}, buf)
+ {%- endfor %}
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/RustBufferTemplate.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/RustBufferTemplate.kt
new file mode 100644
index 0000000000..dfbea24074
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/RustBufferTemplate.kt
@@ -0,0 +1,87 @@
+// This is a helper for safely working with byte buffers returned from the Rust code.
+// A rust-owned buffer is represented by its capacity, its current length, and a
+// pointer to the underlying data.
+
+@Structure.FieldOrder("capacity", "len", "data")
+open class RustBuffer : Structure() {
+ @JvmField var capacity: Int = 0
+ @JvmField var len: Int = 0
+ @JvmField var data: Pointer? = null
+
+ class ByValue: RustBuffer(), Structure.ByValue
+ class ByReference: RustBuffer(), Structure.ByReference
+
+ companion object {
+ internal fun alloc(size: Int = 0) = rustCall() { status ->
+ _UniFFILib.INSTANCE.{{ ci.ffi_rustbuffer_alloc().name() }}(size, status)
+ }.also {
+ if(it.data == null) {
+ throw RuntimeException("RustBuffer.alloc() returned null data pointer (size=${size})")
+ }
+ }
+
+ internal fun create(capacity: Int, len: Int, data: Pointer?): RustBuffer.ByValue {
+ var buf = RustBuffer.ByValue()
+ buf.capacity = capacity
+ buf.len = len
+ buf.data = data
+ return buf
+ }
+
+ internal fun free(buf: RustBuffer.ByValue) = rustCall() { status ->
+ _UniFFILib.INSTANCE.{{ ci.ffi_rustbuffer_free().name() }}(buf, status)
+ }
+ }
+
+ @Suppress("TooGenericExceptionThrown")
+ fun asByteBuffer() =
+ this.data?.getByteBuffer(0, this.len.toLong())?.also {
+ it.order(ByteOrder.BIG_ENDIAN)
+ }
+}
+
+/**
+ * The equivalent of the `*mut RustBuffer` type.
+ * Required for callbacks taking in an out pointer.
+ *
+ * Size is the sum of all values in the struct.
+ */
+class RustBufferByReference : ByReference(16) {
+ /**
+ * Set the pointed-to `RustBuffer` to the given value.
+ */
+ fun setValue(value: RustBuffer.ByValue) {
+ // NOTE: The offsets are as they are in the C-like struct.
+ val pointer = getPointer()
+ pointer.setInt(0, value.capacity)
+ pointer.setInt(4, value.len)
+ pointer.setPointer(8, value.data)
+ }
+
+ /**
+ * Get a `RustBuffer.ByValue` from this reference.
+ */
+ fun getValue(): RustBuffer.ByValue {
+ val pointer = getPointer()
+ val value = RustBuffer.ByValue()
+ value.writeField("capacity", pointer.getInt(0))
+ value.writeField("len", pointer.getInt(4))
+ value.writeField("data", pointer.getPointer(8))
+
+ return value
+ }
+}
+
+// This is a helper for safely passing byte references into the rust code.
+// It's not actually used at the moment, because there aren't many things that you
+// can take a direct pointer to in the JVM, and if we're going to copy something
+// then we might as well copy it into a `RustBuffer`. But it's here for API
+// completeness.
+
+@Structure.FieldOrder("len", "data")
+open class ForeignBytes : Structure() {
+ @JvmField var len: Int = 0
+ @JvmField var data: Pointer? = null
+
+ class ByValue : ForeignBytes(), Structure.ByValue
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/SequenceTemplate.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/SequenceTemplate.kt
new file mode 100644
index 0000000000..876d1bc05e
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/SequenceTemplate.kt
@@ -0,0 +1,23 @@
+{%- let inner_type_name = inner_type|type_name(ci) %}
+
+public object {{ ffi_converter_name }}: FfiConverterRustBuffer<List<{{ inner_type_name }}>> {
+ override fun read(buf: ByteBuffer): List<{{ inner_type_name }}> {
+ val len = buf.getInt()
+ return List<{{ inner_type_name }}>(len) {
+ {{ inner_type|read_fn }}(buf)
+ }
+ }
+
+ override fun allocationSize(value: List<{{ inner_type_name }}>): Int {
+ val sizeForLength = 4
+ val sizeForItems = value.map { {{ inner_type|allocation_size_fn }}(it) }.sum()
+ return sizeForLength + sizeForItems
+ }
+
+ override fun write(value: List<{{ inner_type_name }}>, buf: ByteBuffer) {
+ buf.putInt(value.size)
+ value.forEach {
+ {{ inner_type|write_fn }}(it, buf)
+ }
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/StringHelper.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/StringHelper.kt
new file mode 100644
index 0000000000..68324be4f9
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/StringHelper.kt
@@ -0,0 +1,53 @@
+public object FfiConverterString: FfiConverter<String, RustBuffer.ByValue> {
+ // Note: we don't inherit from FfiConverterRustBuffer, because we use a
+ // special encoding when lowering/lifting. We can use `RustBuffer.len` to
+ // store our length and avoid writing it out to the buffer.
+ override fun lift(value: RustBuffer.ByValue): String {
+ try {
+ val byteArr = ByteArray(value.len)
+ value.asByteBuffer()!!.get(byteArr)
+ return byteArr.toString(Charsets.UTF_8)
+ } finally {
+ RustBuffer.free(value)
+ }
+ }
+
+ override fun read(buf: ByteBuffer): String {
+ val len = buf.getInt()
+ val byteArr = ByteArray(len)
+ buf.get(byteArr)
+ return byteArr.toString(Charsets.UTF_8)
+ }
+
+ fun toUtf8(value: String): ByteBuffer {
+ // Make sure we don't have invalid UTF-16, check for lone surrogates.
+ return Charsets.UTF_8.newEncoder().run {
+ onMalformedInput(CodingErrorAction.REPORT)
+ encode(CharBuffer.wrap(value))
+ }
+ }
+
+ override fun lower(value: String): RustBuffer.ByValue {
+ val byteBuf = toUtf8(value)
+ // Ideally we'd pass these bytes to `ffi_bytebuffer_from_bytes`, but doing so would require us
+ // to copy them into a JNA `Memory`. So we might as well directly copy them into a `RustBuffer`.
+ val rbuf = RustBuffer.alloc(byteBuf.limit())
+ rbuf.asByteBuffer()!!.put(byteBuf)
+ return rbuf
+ }
+
+ // We aren't sure exactly how many bytes our string will be once it's UTF-8
+ // encoded. Allocate 3 bytes per UTF-16 code unit which will always be
+ // enough.
+ override fun allocationSize(value: String): Int {
+ val sizeForLength = 4
+ val sizeForString = value.length * 3
+ return sizeForLength + sizeForString
+ }
+
+ override fun write(value: String, buf: ByteBuffer) {
+ val byteBuf = toUtf8(value)
+ buf.putInt(byteBuf.limit())
+ buf.put(byteBuf)
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/TimestampHelper.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/TimestampHelper.kt
new file mode 100644
index 0000000000..21069d7ce8
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/TimestampHelper.kt
@@ -0,0 +1,38 @@
+public object FfiConverterTimestamp: FfiConverterRustBuffer<java.time.Instant> {
+ override fun read(buf: ByteBuffer): java.time.Instant {
+ val seconds = buf.getLong()
+ // Type mismatch (should be u32) but we check for overflow/underflow below
+ val nanoseconds = buf.getInt().toLong()
+ if (nanoseconds < 0) {
+ throw java.time.DateTimeException("Instant nanoseconds exceed minimum or maximum supported by uniffi")
+ }
+ if (seconds >= 0) {
+ return java.time.Instant.EPOCH.plus(java.time.Duration.ofSeconds(seconds, nanoseconds))
+ } else {
+ return java.time.Instant.EPOCH.minus(java.time.Duration.ofSeconds(-seconds, nanoseconds))
+ }
+ }
+
+ // 8 bytes for seconds, 4 bytes for nanoseconds
+ override fun allocationSize(value: java.time.Instant) = 12
+
+ override fun write(value: java.time.Instant, buf: ByteBuffer) {
+ var epochOffset = java.time.Duration.between(java.time.Instant.EPOCH, value)
+
+ var sign = 1
+ if (epochOffset.isNegative()) {
+ sign = -1
+ epochOffset = epochOffset.negated()
+ }
+
+ if (epochOffset.nano < 0) {
+ // Java docs provide guarantee that nano will always be positive, so this should be impossible
+ // See: https://docs.oracle.com/javase/8/docs/api/java/time/Instant.html
+ throw IllegalArgumentException("Invalid timestamp, nano value must be non-negative")
+ }
+
+ buf.putLong(sign * epochOffset.seconds)
+ // Type mismatch (should be u32) but since values will always be between 0 and 999,999,999 it should be OK
+ buf.putInt(epochOffset.nano)
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFunctionTemplate.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFunctionTemplate.kt
new file mode 100644
index 0000000000..6a841d3484
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFunctionTemplate.kt
@@ -0,0 +1,51 @@
+{%- if func.is_async() %}
+{%- match func.throws_type() -%}
+{%- when Some with (throwable) %}
+@Throws({{ throwable|type_name(ci) }}::class)
+{%- else -%}
+{%- endmatch %}
+
+@Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE")
+suspend fun {{ func.name()|fn_name }}({%- call kt::arg_list_decl(func) -%}){% match func.return_type() %}{% when Some with (return_type) %} : {{ return_type|type_name(ci) }}{% when None %}{%- endmatch %} {
+ return uniffiRustCallAsync(
+ _UniFFILib.INSTANCE.{{ func.ffi_func().name() }}({% call kt::arg_list_lowered(func) %}),
+ {{ func|async_poll(ci) }},
+ {{ func|async_complete(ci) }},
+ {{ func|async_free(ci) }},
+ // lift function
+ {%- match func.return_type() %}
+ {%- when Some(return_type) %}
+ { {{ return_type|lift_fn }}(it) },
+ {%- when None %}
+ { Unit },
+ {% endmatch %}
+ // Error FFI converter
+ {%- match func.throws_type() %}
+ {%- when Some(e) %}
+ {{ e|type_name(ci) }}.ErrorHandler,
+ {%- when None %}
+ NullCallStatusErrorHandler,
+ {%- endmatch %}
+ )
+}
+
+{%- else %}
+{%- match func.throws_type() -%}
+{%- when Some with (throwable) %}
+@Throws({{ throwable|type_name(ci) }}::class)
+{%- else -%}
+{%- endmatch -%}
+
+{%- match func.return_type() -%}
+{%- when Some with (return_type) %}
+
+fun {{ func.name()|fn_name }}({%- call kt::arg_list_decl(func) -%}): {{ return_type|type_name(ci) }} {
+ return {{ return_type|lift_fn }}({% call kt::to_ffi_call(func) %})
+}
+{% when None %}
+
+fun {{ func.name()|fn_name }}({% call kt::arg_list_decl(func) %}) =
+ {% call kt::to_ffi_call(func) %}
+
+{% endmatch %}
+{%- endif %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Types.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Types.kt
new file mode 100644
index 0000000000..103d444ea3
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/Types.kt
@@ -0,0 +1,109 @@
+{%- import "macros.kt" as kt %}
+
+{%- for type_ in ci.iter_types() %}
+{%- let type_name = type_|type_name(ci) %}
+{%- let ffi_converter_name = type_|ffi_converter_name %}
+{%- let canonical_type_name = type_|canonical_name %}
+{%- let contains_object_references = ci.item_contains_object_references(type_) %}
+
+{#
+ # Map `Type` instances to an include statement for that type.
+ #
+ # There is a companion match in `KotlinCodeOracle::create_code_type()` which performs a similar function for the
+ # Rust code.
+ #
+ # - When adding additional types here, make sure to also add a match arm to that function.
+ # - To keep things manageable, let's try to limit ourselves to these 2 mega-matches
+ #}
+{%- match type_ %}
+
+{%- when Type::Boolean %}
+{%- include "BooleanHelper.kt" %}
+
+{%- when Type::Int8 %}
+{%- include "Int8Helper.kt" %}
+
+{%- when Type::Int16 %}
+{%- include "Int16Helper.kt" %}
+
+{%- when Type::Int32 %}
+{%- include "Int32Helper.kt" %}
+
+{%- when Type::Int64 %}
+{%- include "Int64Helper.kt" %}
+
+{%- when Type::UInt8 %}
+{%- include "UInt8Helper.kt" %}
+
+{%- when Type::UInt16 %}
+{%- include "UInt16Helper.kt" %}
+
+{%- when Type::UInt32 %}
+{%- include "UInt32Helper.kt" %}
+
+{%- when Type::UInt64 %}
+{%- include "UInt64Helper.kt" %}
+
+{%- when Type::Float32 %}
+{%- include "Float32Helper.kt" %}
+
+{%- when Type::Float64 %}
+{%- include "Float64Helper.kt" %}
+
+{%- when Type::String %}
+{%- include "StringHelper.kt" %}
+
+{%- when Type::Bytes %}
+{%- include "ByteArrayHelper.kt" %}
+
+{%- when Type::Enum { name, module_path } %}
+{%- let e = ci.get_enum_definition(name).unwrap() %}
+{%- if !ci.is_name_used_as_error(name) %}
+{% include "EnumTemplate.kt" %}
+{%- else %}
+{% include "ErrorTemplate.kt" %}
+{%- endif -%}
+
+{%- when Type::Object { module_path, name, imp } %}
+{% include "ObjectTemplate.kt" %}
+
+{%- when Type::Record { name, module_path } %}
+{% include "RecordTemplate.kt" %}
+
+{%- when Type::Optional { inner_type } %}
+{% include "OptionalTemplate.kt" %}
+
+{%- when Type::Sequence { inner_type } %}
+{% include "SequenceTemplate.kt" %}
+
+{%- when Type::Map { key_type, value_type } %}
+{% include "MapTemplate.kt" %}
+
+{%- when Type::CallbackInterface { module_path, name } %}
+{% include "CallbackInterfaceTemplate.kt" %}
+
+{%- when Type::ForeignExecutor %}
+{% include "ForeignExecutorTemplate.kt" %}
+
+{%- when Type::Timestamp %}
+{% include "TimestampHelper.kt" %}
+
+{%- when Type::Duration %}
+{% include "DurationHelper.kt" %}
+
+{%- when Type::Custom { module_path, name, builtin } %}
+{% include "CustomTypeTemplate.kt" %}
+
+{%- when Type::External { module_path, name, namespace, kind, tagged } %}
+{% include "ExternalTypeTemplate.kt" %}
+
+{%- else %}
+{%- endmatch %}
+{%- endfor %}
+
+{%- if ci.has_async_fns() %}
+{# Import types needed for async support #}
+{{ self.add_import("kotlin.coroutines.resume") }}
+{{ self.add_import("kotlinx.coroutines.suspendCancellableCoroutine") }}
+{{ self.add_import("kotlinx.coroutines.CancellableContinuation") }}
+{%- endif %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/UInt16Helper.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/UInt16Helper.kt
new file mode 100644
index 0000000000..279a8fa91b
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/UInt16Helper.kt
@@ -0,0 +1,19 @@
+public object FfiConverterUShort: FfiConverter<UShort, Short> {
+ override fun lift(value: Short): UShort {
+ return value.toUShort()
+ }
+
+ override fun read(buf: ByteBuffer): UShort {
+ return lift(buf.getShort())
+ }
+
+ override fun lower(value: UShort): Short {
+ return value.toShort()
+ }
+
+ override fun allocationSize(value: UShort) = 2
+
+ override fun write(value: UShort, buf: ByteBuffer) {
+ buf.putShort(value.toShort())
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/UInt32Helper.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/UInt32Helper.kt
new file mode 100644
index 0000000000..da7b5b28d6
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/UInt32Helper.kt
@@ -0,0 +1,19 @@
+public object FfiConverterUInt: FfiConverter<UInt, Int> {
+ override fun lift(value: Int): UInt {
+ return value.toUInt()
+ }
+
+ override fun read(buf: ByteBuffer): UInt {
+ return lift(buf.getInt())
+ }
+
+ override fun lower(value: UInt): Int {
+ return value.toInt()
+ }
+
+ override fun allocationSize(value: UInt) = 4
+
+ override fun write(value: UInt, buf: ByteBuffer) {
+ buf.putInt(value.toInt())
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/UInt64Helper.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/UInt64Helper.kt
new file mode 100644
index 0000000000..44d27ad36b
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/UInt64Helper.kt
@@ -0,0 +1,19 @@
+public object FfiConverterULong: FfiConverter<ULong, Long> {
+ override fun lift(value: Long): ULong {
+ return value.toULong()
+ }
+
+ override fun read(buf: ByteBuffer): ULong {
+ return lift(buf.getLong())
+ }
+
+ override fun lower(value: ULong): Long {
+ return value.toLong()
+ }
+
+ override fun allocationSize(value: ULong) = 8
+
+ override fun write(value: ULong, buf: ByteBuffer) {
+ buf.putLong(value.toLong())
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/UInt8Helper.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/UInt8Helper.kt
new file mode 100644
index 0000000000..b6d176603e
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/UInt8Helper.kt
@@ -0,0 +1,19 @@
+public object FfiConverterUByte: FfiConverter<UByte, Byte> {
+ override fun lift(value: Byte): UByte {
+ return value.toUByte()
+ }
+
+ override fun read(buf: ByteBuffer): UByte {
+ return lift(buf.get())
+ }
+
+ override fun lower(value: UByte): Byte {
+ return value.toByte()
+ }
+
+ override fun allocationSize(value: UByte) = 1
+
+ override fun write(value: UByte, buf: ByteBuffer) {
+ buf.put(value.toByte())
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/macros.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/macros.kt
new file mode 100644
index 0000000000..6a95d6a66d
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/macros.kt
@@ -0,0 +1,77 @@
+{#
+// Template to call into rust. Used in several places.
+// Variable names in `arg_list_decl` should match up with arg lists
+// passed to rust via `arg_list_lowered`
+#}
+
+{%- macro to_ffi_call(func) -%}
+ {%- match func.throws_type() %}
+ {%- when Some with (e) %}
+ rustCallWithError({{ e|type_name(ci) }})
+ {%- else %}
+ rustCall()
+ {%- endmatch %} { _status ->
+ _UniFFILib.INSTANCE.{{ func.ffi_func().name() }}({% call arg_list_lowered(func) -%} _status)
+}
+{%- endmacro -%}
+
+{%- macro to_ffi_call_with_prefix(prefix, func) %}
+ {%- match func.throws_type() %}
+ {%- when Some with (e) %}
+ rustCallWithError({{ e|type_name(ci) }})
+ {%- else %}
+ rustCall()
+ {%- endmatch %} { _status ->
+ _UniFFILib.INSTANCE.{{ func.ffi_func().name() }}(
+ {{- prefix }},
+ {% call arg_list_lowered(func) %}
+ _status)
+}
+{%- endmacro %}
+
+{%- macro arg_list_lowered(func) %}
+ {%- for arg in func.arguments() %}
+ {{- arg|lower_fn }}({{ arg.name()|var_name }}),
+ {%- endfor %}
+{%- endmacro -%}
+
+{#-
+// Arglist as used in kotlin declarations of methods, functions and constructors.
+// Note the var_name and type_name filters.
+-#}
+
+{% macro arg_list_decl(func) %}
+ {%- for arg in func.arguments() -%}
+ {{ arg.name()|var_name }}: {{ arg|type_name(ci) }}
+ {%- match arg.default_value() %}
+ {%- when Some with(literal) %} = {{ literal|render_literal(arg, ci) }}
+ {%- else %}
+ {%- endmatch %}
+ {%- if !loop.last %}, {% endif -%}
+ {%- endfor %}
+{%- endmacro %}
+
+{% macro arg_list_protocol(func) %}
+ {%- for arg in func.arguments() -%}
+ {{ arg.name()|var_name }}: {{ arg|type_name(ci) }}
+ {%- if !loop.last %}, {% endif -%}
+ {%- endfor %}
+{%- endmacro %}
+{#-
+// Arglist as used in the _UniFFILib function declarations.
+// Note unfiltered name but ffi_type_name filters.
+-#}
+{%- macro arg_list_ffi_decl(func) %}
+ {%- for arg in func.arguments() %}
+ {{- arg.name()|var_name }}: {{ arg.type_().borrow()|ffi_type_name_by_value -}},
+ {%- endfor %}
+ {%- if func.has_rust_call_status_arg() %}_uniffi_out_err: RustCallStatus, {% endif %}
+{%- endmacro -%}
+
+// Macro for destroying fields
+{%- macro destroy_fields(member) %}
+ Disposable.destroy(
+ {%- for field in member.fields() %}
+ this.{{ field.name()|var_name }}{%- if !loop.last %}, {% endif -%}
+ {% endfor -%})
+{%- endmacro -%}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/wrapper.kt b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/wrapper.kt
new file mode 100644
index 0000000000..9ee4229018
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/wrapper.kt
@@ -0,0 +1,57 @@
+// This file was autogenerated by some hot garbage in the `uniffi` crate.
+// Trust me, you don't want to mess with it!
+
+@file:Suppress("NAME_SHADOWING")
+
+package {{ config.package_name() }};
+
+// Common helper code.
+//
+// Ideally this would live in a separate .kt file where it can be unittested etc
+// in isolation, and perhaps even published as a re-useable package.
+//
+// However, it's important that the details of how this helper code works (e.g. the
+// way that different builtin types are passed across the FFI) exactly match what's
+// expected by the Rust code on the other side of the interface. In practice right
+// now that means coming from the exact some version of `uniffi` that was used to
+// compile the Rust component. The easiest way to ensure this is to bundle the Kotlin
+// helpers directly inline like we're doing here.
+
+import com.sun.jna.Library
+import com.sun.jna.IntegerType
+import com.sun.jna.Native
+import com.sun.jna.Pointer
+import com.sun.jna.Structure
+import com.sun.jna.Callback
+import com.sun.jna.ptr.*
+import java.nio.ByteBuffer
+import java.nio.ByteOrder
+import java.nio.CharBuffer
+import java.nio.charset.CodingErrorAction
+import java.util.concurrent.ConcurrentHashMap
+
+{%- for req in self.imports() %}
+{{ req.render() }}
+{%- endfor %}
+
+{% include "RustBufferTemplate.kt" %}
+{% include "FfiConverterTemplate.kt" %}
+{% include "Helpers.kt" %}
+
+// Contains loading, initialization code,
+// and the FFI Function declarations in a com.sun.jna.Library.
+{% include "NamespaceLibraryTemplate.kt" %}
+
+// Async support
+{%- if ci.has_async_fns() %}
+{% include "Async.kt" %}
+{%- endif %}
+
+// Public interface members begin here.
+{{ type_helper_code }}
+
+{%- for func in ci.function_definitions() %}
+{%- include "TopLevelFunctionTemplate.kt" %}
+{%- endfor %}
+
+{% import "macros.kt" as kt %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/test.rs b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/test.rs
new file mode 100644
index 0000000000..7b78540741
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/test.rs
@@ -0,0 +1,131 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use crate::{
+ bindings::{RunScriptOptions, TargetLanguage},
+ library_mode::generate_bindings,
+};
+use anyhow::{bail, Context, Result};
+use camino::{Utf8Path, Utf8PathBuf};
+use std::env;
+use std::process::Command;
+use uniffi_testing::UniFFITestHelper;
+
+/// Run Kotlin tests for a UniFFI test fixture
+pub fn run_test(tmp_dir: &str, fixture_name: &str, script_file: &str) -> Result<()> {
+ run_script(
+ tmp_dir,
+ fixture_name,
+ script_file,
+ vec![],
+ &RunScriptOptions::default(),
+ )
+}
+
+/// Run a Kotlin script
+///
+/// This function will set things up so that the script can import the UniFFI bindings for a crate
+pub fn run_script(
+ tmp_dir: &str,
+ crate_name: &str,
+ script_file: &str,
+ args: Vec<String>,
+ options: &RunScriptOptions,
+) -> Result<()> {
+ let script_path = Utf8Path::new(".").join(script_file);
+ let test_helper = UniFFITestHelper::new(crate_name)?;
+ let out_dir = test_helper.create_out_dir(tmp_dir, &script_path)?;
+ let cdylib_path = test_helper.copy_cdylib_to_out_dir(&out_dir)?;
+ generate_bindings(
+ &cdylib_path,
+ None,
+ &[TargetLanguage::Kotlin],
+ &out_dir,
+ false,
+ )?;
+ let jar_file = build_jar(crate_name, &out_dir, options)?;
+
+ let mut command = kotlinc_command(options);
+ command
+ .arg("-classpath")
+ .arg(calc_classpath(vec![&out_dir, &jar_file]))
+ // Enable runtime assertions, for easy testing etc.
+ .arg("-J-ea")
+ // Our test scripts should not produce any warnings.
+ .arg("-Werror")
+ .arg("-script")
+ .arg(script_path)
+ .args(if args.is_empty() {
+ vec![]
+ } else {
+ std::iter::once(String::from("--")).chain(args).collect()
+ });
+
+ let status = command
+ .spawn()
+ .context("Failed to spawn `kotlinc` to run Kotlin script")?
+ .wait()
+ .context("Failed to wait for `kotlinc` when running Kotlin script")?;
+ if !status.success() {
+ anyhow::bail!("running `kotlinc` failed")
+ }
+ Ok(())
+}
+
+/// Generate kotlin bindings for the given namespace, then use the kotlin
+/// command-line tools to compile them into a .jar file.
+fn build_jar(
+ crate_name: &str,
+ out_dir: &Utf8Path,
+ options: &RunScriptOptions,
+) -> Result<Utf8PathBuf> {
+ let mut jar_file = Utf8PathBuf::from(out_dir);
+ jar_file.push(format!("{crate_name}.jar"));
+ let sources = glob::glob(out_dir.join("**/*.kt").as_str())?
+ .flatten()
+ .map(|p| String::from(p.to_string_lossy()))
+ .collect::<Vec<String>>();
+ if sources.is_empty() {
+ bail!("No kotlin sources found in {out_dir}")
+ }
+
+ let mut command = kotlinc_command(options);
+ command
+ // Our generated bindings should not produce any warnings; fail tests if they do.
+ .arg("-Werror")
+ .arg("-d")
+ .arg(&jar_file)
+ .arg("-classpath")
+ .arg(calc_classpath(vec![]))
+ .args(sources);
+
+ let status = command
+ .spawn()
+ .context("Failed to spawn `kotlinc` to compile the bindings")?
+ .wait()
+ .context("Failed to wait for `kotlinc` when compiling the bindings")?;
+ if !status.success() {
+ bail!("running `kotlinc` failed")
+ }
+ Ok(jar_file)
+}
+
+fn kotlinc_command(options: &RunScriptOptions) -> Command {
+ let mut command = Command::new("kotlinc");
+ if !options.show_compiler_messages {
+ command.arg("-nowarn");
+ }
+ command
+}
+
+fn calc_classpath(extra_paths: Vec<&Utf8Path>) -> String {
+ extra_paths
+ .into_iter()
+ .map(|p| p.to_string())
+ // Add the system classpath as a component, using the fact that env::var returns an Option,
+ // which implement Iterator
+ .chain(env::var("CLASSPATH"))
+ .collect::<Vec<String>>()
+ .join(":")
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/mod.rs b/third_party/rust/uniffi_bindgen/src/bindings/mod.rs
new file mode 100644
index 0000000000..d39202bcf2
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/mod.rs
@@ -0,0 +1,125 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! Generate foreign language bindings for a uniffi component.
+//!
+//! This module contains all the code for generating foreign language bindings,
+//! along with some helpers for executing foreign language scripts or tests.
+
+use anyhow::{bail, Result};
+use camino::Utf8Path;
+use serde::{Deserialize, Serialize};
+use std::fmt;
+
+use crate::interface::ComponentInterface;
+
+pub mod kotlin;
+pub mod python;
+pub mod ruby;
+pub mod swift;
+
+/// Enumeration of all foreign language targets currently supported by this crate.
+///
+/// The functions in this module will delegate to a language-specific backend based
+/// on the provided `TargetLanguage`. For convenience of calling code we also provide
+/// a few `TryFrom` implementations to help guess the correct target language from
+/// e.g. a file extension of command-line argument.
+#[derive(Copy, Clone, Eq, PartialEq, Hash)]
+#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
+pub enum TargetLanguage {
+ Kotlin,
+ Swift,
+ Python,
+ Ruby,
+}
+
+impl fmt::Display for TargetLanguage {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ Self::Kotlin => write!(f, "kotlin"),
+ Self::Swift => write!(f, "swift"),
+ Self::Python => write!(f, "python"),
+ Self::Ruby => write!(f, "ruby"),
+ }
+ }
+}
+
+/// Mode for the `run_script` function defined for each language
+#[derive(Clone, Debug)]
+pub struct RunScriptOptions {
+ pub show_compiler_messages: bool,
+}
+
+impl Default for RunScriptOptions {
+ fn default() -> Self {
+ Self {
+ show_compiler_messages: true,
+ }
+ }
+}
+
+impl TryFrom<&str> for TargetLanguage {
+ type Error = anyhow::Error;
+ fn try_from(value: &str) -> Result<Self> {
+ Ok(match value.to_ascii_lowercase().as_str() {
+ "kotlin" | "kt" | "kts" => TargetLanguage::Kotlin,
+ "swift" => TargetLanguage::Swift,
+ "python" | "py" => TargetLanguage::Python,
+ "ruby" | "rb" => TargetLanguage::Ruby,
+ _ => bail!("Unknown or unsupported target language: \"{value}\""),
+ })
+ }
+}
+
+impl TryFrom<&std::ffi::OsStr> for TargetLanguage {
+ type Error = anyhow::Error;
+ fn try_from(value: &std::ffi::OsStr) -> Result<Self> {
+ match value.to_str() {
+ None => bail!("Unreadable target language"),
+ Some(s) => s.try_into(),
+ }
+ }
+}
+
+impl TryFrom<String> for TargetLanguage {
+ type Error = anyhow::Error;
+ fn try_from(value: String) -> Result<Self> {
+ TryFrom::try_from(value.as_str())
+ }
+}
+
+#[derive(Debug, Clone, Default, Serialize, Deserialize)]
+pub struct Config {
+ #[serde(default)]
+ pub(crate) kotlin: kotlin::Config,
+ #[serde(default)]
+ pub(crate) swift: swift::Config,
+ #[serde(default)]
+ pub(crate) python: python::Config,
+ #[serde(default)]
+ pub(crate) ruby: ruby::Config,
+}
+
+/// Generate foreign language bindings from a compiled `uniffi` library.
+pub fn write_bindings(
+ config: &Config,
+ ci: &ComponentInterface,
+ out_dir: &Utf8Path,
+ language: TargetLanguage,
+ try_format_code: bool,
+) -> Result<()> {
+ match language {
+ TargetLanguage::Kotlin => {
+ kotlin::write_bindings(&config.kotlin, ci, out_dir, try_format_code)?
+ }
+ TargetLanguage::Swift => {
+ swift::write_bindings(&config.swift, ci, out_dir, try_format_code)?
+ }
+ TargetLanguage::Python => {
+ python::write_bindings(&config.python, ci, out_dir, try_format_code)?
+ }
+ TargetLanguage::Ruby => ruby::write_bindings(&config.ruby, ci, out_dir, try_format_code)?,
+ }
+ Ok(())
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/callback_interface.rs b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/callback_interface.rs
new file mode 100644
index 0000000000..9c93965e35
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/callback_interface.rs
@@ -0,0 +1,31 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use super::CodeType;
+use crate::backend::Literal;
+
+#[derive(Debug)]
+pub struct CallbackInterfaceCodeType {
+ id: String,
+}
+
+impl CallbackInterfaceCodeType {
+ pub fn new(id: String) -> Self {
+ Self { id }
+ }
+}
+
+impl CodeType for CallbackInterfaceCodeType {
+ fn type_label(&self) -> String {
+ super::PythonCodeOracle.class_name(&self.id)
+ }
+
+ fn canonical_name(&self) -> String {
+ format!("CallbackInterface{}", self.id)
+ }
+
+ fn literal(&self, _literal: &Literal) -> String {
+ unreachable!();
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/compounds.rs b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/compounds.rs
new file mode 100644
index 0000000000..b91bcbe18f
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/compounds.rs
@@ -0,0 +1,108 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use super::CodeType;
+use crate::backend::{Literal, Type};
+
+#[derive(Debug)]
+pub struct OptionalCodeType {
+ inner: Type,
+}
+
+impl OptionalCodeType {
+ pub fn new(inner: Type) -> Self {
+ Self { inner }
+ }
+}
+
+impl CodeType for OptionalCodeType {
+ fn type_label(&self) -> String {
+ format!(
+ "typing.Optional[{}]",
+ super::PythonCodeOracle.find(&self.inner).type_label()
+ )
+ }
+
+ fn canonical_name(&self) -> String {
+ format!(
+ "Optional{}",
+ super::PythonCodeOracle.find(&self.inner).canonical_name(),
+ )
+ }
+
+ fn literal(&self, literal: &Literal) -> String {
+ match literal {
+ Literal::Null => "None".into(),
+ _ => super::PythonCodeOracle.find(&self.inner).literal(literal),
+ }
+ }
+}
+
+#[derive(Debug)]
+pub struct SequenceCodeType {
+ inner: Type,
+}
+
+impl SequenceCodeType {
+ pub fn new(inner: Type) -> Self {
+ Self { inner }
+ }
+}
+
+impl CodeType for SequenceCodeType {
+ fn type_label(&self) -> String {
+ // Python 3.8 and below do not support `list[T]`
+ format!(
+ "typing.List[{}]",
+ super::PythonCodeOracle.find(&self.inner).type_label()
+ )
+ }
+
+ fn canonical_name(&self) -> String {
+ format!(
+ "Sequence{}",
+ super::PythonCodeOracle.find(&self.inner).canonical_name(),
+ )
+ }
+
+ fn literal(&self, literal: &Literal) -> String {
+ match literal {
+ Literal::EmptySequence => "[]".into(),
+ _ => unimplemented!(),
+ }
+ }
+}
+
+#[derive(Debug)]
+pub struct MapCodeType {
+ key: Type,
+ value: Type,
+}
+
+impl MapCodeType {
+ pub fn new(key: Type, value: Type) -> Self {
+ Self { key, value }
+ }
+}
+
+impl CodeType for MapCodeType {
+ fn type_label(&self) -> String {
+ "dict".to_string()
+ }
+
+ fn canonical_name(&self) -> String {
+ format!(
+ "Map{}{}",
+ super::PythonCodeOracle.find(&self.key).canonical_name(),
+ super::PythonCodeOracle.find(&self.value).canonical_name(),
+ )
+ }
+
+ fn literal(&self, literal: &Literal) -> String {
+ match literal {
+ Literal::EmptyMap => "{}".into(),
+ _ => unimplemented!(),
+ }
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/custom.rs b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/custom.rs
new file mode 100644
index 0000000000..f735899f3d
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/custom.rs
@@ -0,0 +1,26 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use super::CodeType;
+
+#[derive(Debug)]
+pub struct CustomCodeType {
+ name: String,
+}
+
+impl CustomCodeType {
+ pub fn new(name: String) -> Self {
+ Self { name }
+ }
+}
+
+impl CodeType for CustomCodeType {
+ fn type_label(&self) -> String {
+ self.name.clone()
+ }
+
+ fn canonical_name(&self) -> String {
+ format!("Type{}", self.name)
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/enum_.rs b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/enum_.rs
new file mode 100644
index 0000000000..83ce177e07
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/enum_.rs
@@ -0,0 +1,39 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use super::CodeType;
+use crate::backend::Literal;
+
+#[derive(Debug)]
+pub struct EnumCodeType {
+ id: String,
+}
+
+impl EnumCodeType {
+ pub fn new(id: String) -> Self {
+ Self { id }
+ }
+}
+
+impl CodeType for EnumCodeType {
+ fn type_label(&self) -> String {
+ super::PythonCodeOracle.class_name(&self.id)
+ }
+
+ fn canonical_name(&self) -> String {
+ format!("Type{}", self.id)
+ }
+
+ fn literal(&self, literal: &Literal) -> String {
+ if let Literal::Enum(v, _) = literal {
+ format!(
+ "{}.{}",
+ self.type_label(),
+ super::PythonCodeOracle.enum_variant_name(v)
+ )
+ } else {
+ unreachable!();
+ }
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/error.rs b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/error.rs
new file mode 100644
index 0000000000..aa1c0db75e
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/error.rs
@@ -0,0 +1,31 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use crate::backend::{ Literal};
+use super::CodeType;
+
+#[derive(Debug)]
+pub struct ErrorCodeType {
+ id: String,
+}
+
+impl ErrorCodeType {
+ pub fn new(id: String) -> Self {
+ Self { id }
+ }
+}
+
+impl CodeType for ErrorCodeType {
+ fn type_label(&self) -> String {
+ super::PythonCodeOracle.class_name(&self.id)
+ }
+
+ fn canonical_name(&self) -> String {
+ format!("Type{}", self.id)
+ }
+
+ fn literal(&self, _literal: &Literal) -> String {
+ unreachable!();
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/executor.rs b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/executor.rs
new file mode 100644
index 0000000000..be3ba1d791
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/executor.rs
@@ -0,0 +1,18 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use super::CodeType;
+
+#[derive(Debug)]
+pub struct ForeignExecutorCodeType;
+
+impl CodeType for ForeignExecutorCodeType {
+ fn type_label(&self) -> String {
+ "asyncio.BaseEventLoop".into()
+ }
+
+ fn canonical_name(&self) -> String {
+ "ForeignExecutor".into()
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/external.rs b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/external.rs
new file mode 100644
index 0000000000..0d19c4bb3c
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/external.rs
@@ -0,0 +1,26 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use super::CodeType;
+
+#[derive(Debug)]
+pub struct ExternalCodeType {
+ name: String,
+}
+
+impl ExternalCodeType {
+ pub fn new(name: String) -> Self {
+ Self { name }
+ }
+}
+
+impl CodeType for ExternalCodeType {
+ fn type_label(&self) -> String {
+ self.name.clone()
+ }
+
+ fn canonical_name(&self) -> String {
+ format!("Type{}", self.name)
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/miscellany.rs b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/miscellany.rs
new file mode 100644
index 0000000000..07ff5cd0d7
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/miscellany.rs
@@ -0,0 +1,34 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use super::CodeType;
+use crate::backend::Literal;
+use paste::paste;
+
+macro_rules! impl_code_type_for_miscellany {
+ ($T:ty, $canonical_name:literal) => {
+ paste! {
+ #[derive(Debug)]
+ pub struct $T;
+
+ impl CodeType for $T {
+ fn type_label(&self) -> String {
+ format!("{}", $canonical_name)
+ }
+
+ fn canonical_name(&self) -> String {
+ format!("{}", $canonical_name)
+ }
+
+ fn literal(&self, _literal: &Literal) -> String {
+ unreachable!()
+ }
+ }
+ }
+ };
+}
+
+impl_code_type_for_miscellany!(TimestampCodeType, "Timestamp");
+
+impl_code_type_for_miscellany!(DurationCodeType, "Duration");
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/mod.rs b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/mod.rs
new file mode 100644
index 0000000000..8178fcc102
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/mod.rs
@@ -0,0 +1,474 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use anyhow::{Context, Result};
+use askama::Template;
+use heck::{ToShoutySnakeCase, ToSnakeCase, ToUpperCamelCase};
+use once_cell::sync::Lazy;
+use serde::{Deserialize, Serialize};
+use std::borrow::Borrow;
+use std::cell::RefCell;
+use std::collections::{BTreeSet, HashMap, HashSet};
+use std::fmt::Debug;
+
+use crate::backend::TemplateExpression;
+use crate::interface::*;
+use crate::BindingsConfig;
+
+mod callback_interface;
+mod compounds;
+mod custom;
+mod enum_;
+mod executor;
+mod external;
+mod miscellany;
+mod object;
+mod primitives;
+mod record;
+
+/// A trait tor the implementation.
+trait CodeType: Debug {
+ /// The language specific label used to reference this type. This will be used in
+ /// method signatures and property declarations.
+ fn type_label(&self) -> String;
+
+ /// A representation of this type label that can be used as part of another
+ /// identifier. e.g. `read_foo()`, or `FooInternals`.
+ ///
+ /// This is especially useful when creating specialized objects or methods to deal
+ /// with this type only.
+ fn canonical_name(&self) -> String {
+ self.type_label()
+ }
+
+ fn literal(&self, _literal: &Literal) -> String {
+ unimplemented!("Unimplemented for {}", self.type_label())
+ }
+
+ /// Name of the FfiConverter
+ ///
+ /// This is the object that contains the lower, write, lift, and read methods for this type.
+ fn ffi_converter_name(&self) -> String {
+ format!("FfiConverter{}", self.canonical_name())
+ }
+
+ /// A list of imports that are needed if this type is in use.
+ /// Classes are imported exactly once.
+ fn imports(&self) -> Option<Vec<String>> {
+ None
+ }
+
+ /// Function to run at startup
+ fn initialization_fn(&self) -> Option<String> {
+ None
+ }
+}
+
+// Taken from Python's `keyword.py` module.
+static KEYWORDS: Lazy<HashSet<String>> = Lazy::new(|| {
+ let kwlist = vec![
+ "False",
+ "None",
+ "True",
+ "__peg_parser__",
+ "and",
+ "as",
+ "assert",
+ "async",
+ "await",
+ "break",
+ "class",
+ "continue",
+ "def",
+ "del",
+ "elif",
+ "else",
+ "except",
+ "finally",
+ "for",
+ "from",
+ "global",
+ "if",
+ "import",
+ "in",
+ "is",
+ "lambda",
+ "nonlocal",
+ "not",
+ "or",
+ "pass",
+ "raise",
+ "return",
+ "try",
+ "while",
+ "with",
+ "yield",
+ ];
+ HashSet::from_iter(kwlist.into_iter().map(|s| s.to_string()))
+});
+
+// Config options to customize the generated python.
+#[derive(Debug, Clone, Default, Serialize, Deserialize)]
+pub struct Config {
+ cdylib_name: Option<String>,
+ #[serde(default)]
+ custom_types: HashMap<String, CustomTypeConfig>,
+}
+
+#[derive(Debug, Clone, Default, Serialize, Deserialize)]
+pub struct CustomTypeConfig {
+ // This `CustomTypeConfig` doesn't have a `type_name` like the others -- which is why we have
+ // separate structs rather than a shared one.
+ imports: Option<Vec<String>>,
+ into_custom: TemplateExpression,
+ from_custom: TemplateExpression,
+}
+
+impl Config {
+ pub fn cdylib_name(&self) -> String {
+ if let Some(cdylib_name) = &self.cdylib_name {
+ cdylib_name.clone()
+ } else {
+ "uniffi".into()
+ }
+ }
+}
+
+impl BindingsConfig for Config {
+ fn update_from_ci(&mut self, ci: &ComponentInterface) {
+ self.cdylib_name
+ .get_or_insert_with(|| format!("uniffi_{}", ci.namespace()));
+ }
+
+ fn update_from_cdylib_name(&mut self, cdylib_name: &str) {
+ self.cdylib_name
+ .get_or_insert_with(|| cdylib_name.to_string());
+ }
+
+ fn update_from_dependency_configs(&mut self, _config_map: HashMap<&str, &Self>) {}
+}
+
+// Generate python bindings for the given ComponentInterface, as a string.
+pub fn generate_python_bindings(config: &Config, ci: &ComponentInterface) -> Result<String> {
+ PythonWrapper::new(config.clone(), ci)
+ .render()
+ .context("failed to render python bindings")
+}
+
+/// A struct to record a Python import statement.
+#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
+pub enum ImportRequirement {
+ /// A simple module import.
+ Module { mod_name: String },
+ /// A single symbol from a module.
+ Symbol {
+ mod_name: String,
+ symbol_name: String,
+ },
+ /// A single symbol from a module with the specified local name.
+ SymbolAs {
+ mod_name: String,
+ symbol_name: String,
+ as_name: String,
+ },
+}
+
+impl ImportRequirement {
+ /// Render the Python import statement.
+ fn render(&self) -> String {
+ match &self {
+ ImportRequirement::Module { mod_name } => format!("import {mod_name}"),
+ ImportRequirement::Symbol {
+ mod_name,
+ symbol_name,
+ } => format!("from {mod_name} import {symbol_name}"),
+ ImportRequirement::SymbolAs {
+ mod_name,
+ symbol_name,
+ as_name,
+ } => format!("from {mod_name} import {symbol_name} as {as_name}"),
+ }
+ }
+}
+
+/// Renders Python helper code for all types
+///
+/// This template is a bit different than others in that it stores internal state from the render
+/// process. Make sure to only call `render()` once.
+#[derive(Template)]
+#[template(syntax = "py", escape = "none", path = "Types.py")]
+pub struct TypeRenderer<'a> {
+ python_config: &'a Config,
+ ci: &'a ComponentInterface,
+ // Track included modules for the `include_once()` macro
+ include_once_names: RefCell<HashSet<String>>,
+ // Track imports added with the `add_import()` macro
+ imports: RefCell<BTreeSet<ImportRequirement>>,
+}
+
+impl<'a> TypeRenderer<'a> {
+ fn new(python_config: &'a Config, ci: &'a ComponentInterface) -> Self {
+ Self {
+ python_config,
+ ci,
+ include_once_names: RefCell::new(HashSet::new()),
+ imports: RefCell::new(BTreeSet::new()),
+ }
+ }
+
+ // The following methods are used by the `Types.py` macros.
+
+ // Helper for the including a template, but only once.
+ //
+ // The first time this is called with a name it will return true, indicating that we should
+ // include the template. Subsequent calls will return false.
+ fn include_once_check(&self, name: &str) -> bool {
+ self.include_once_names
+ .borrow_mut()
+ .insert(name.to_string())
+ }
+
+ // Helper to add an import statement
+ //
+ // Call this inside your template to cause an import statement to be added at the top of the
+ // file. Imports will be sorted and de-deuped.
+ //
+ // Returns an empty string so that it can be used inside an askama `{{ }}` block.
+ fn add_import(&self, name: &str) -> &str {
+ self.imports.borrow_mut().insert(ImportRequirement::Module {
+ mod_name: name.to_owned(),
+ });
+ ""
+ }
+
+ // Like add_import, but arranges for `from module import name`.
+ fn add_import_of(&self, mod_name: &str, name: &str) -> &str {
+ self.imports.borrow_mut().insert(ImportRequirement::Symbol {
+ mod_name: mod_name.to_owned(),
+ symbol_name: name.to_owned(),
+ });
+ ""
+ }
+
+ // Like add_import, but arranges for `from module import name as other`.
+ fn add_import_of_as(&self, mod_name: &str, symbol_name: &str, as_name: &str) -> &str {
+ self.imports
+ .borrow_mut()
+ .insert(ImportRequirement::SymbolAs {
+ mod_name: mod_name.to_owned(),
+ symbol_name: symbol_name.to_owned(),
+ as_name: as_name.to_owned(),
+ });
+ ""
+ }
+}
+
+#[derive(Template)]
+#[template(syntax = "py", escape = "none", path = "wrapper.py")]
+pub struct PythonWrapper<'a> {
+ ci: &'a ComponentInterface,
+ config: Config,
+ type_helper_code: String,
+ type_imports: BTreeSet<ImportRequirement>,
+}
+impl<'a> PythonWrapper<'a> {
+ pub fn new(config: Config, ci: &'a ComponentInterface) -> Self {
+ let type_renderer = TypeRenderer::new(&config, ci);
+ let type_helper_code = type_renderer.render().unwrap();
+ let type_imports = type_renderer.imports.into_inner();
+ Self {
+ config,
+ ci,
+ type_helper_code,
+ type_imports,
+ }
+ }
+
+ pub fn imports(&self) -> Vec<ImportRequirement> {
+ self.type_imports.iter().cloned().collect()
+ }
+}
+
+fn fixup_keyword(name: String) -> String {
+ if KEYWORDS.contains(&name) {
+ format!("_{name}")
+ } else {
+ name
+ }
+}
+
+#[derive(Clone, Default)]
+pub struct PythonCodeOracle;
+
+impl PythonCodeOracle {
+ fn find(&self, type_: &Type) -> Box<dyn CodeType> {
+ type_.clone().as_type().as_codetype()
+ }
+
+ /// Get the idiomatic Python rendering of a class name (for enums, records, errors, etc).
+ fn class_name(&self, nm: &str) -> String {
+ fixup_keyword(nm.to_string().to_upper_camel_case())
+ }
+
+ /// Get the idiomatic Python rendering of a function name.
+ fn fn_name(&self, nm: &str) -> String {
+ fixup_keyword(nm.to_string().to_snake_case())
+ }
+
+ /// Get the idiomatic Python rendering of a variable name.
+ fn var_name(&self, nm: &str) -> String {
+ fixup_keyword(nm.to_string().to_snake_case())
+ }
+
+ /// Get the idiomatic Python rendering of an individual enum variant.
+ fn enum_variant_name(&self, nm: &str) -> String {
+ fixup_keyword(nm.to_string().to_shouty_snake_case())
+ }
+
+ fn ffi_type_label(ffi_type: &FfiType) -> String {
+ match ffi_type {
+ FfiType::Int8 => "ctypes.c_int8".to_string(),
+ FfiType::UInt8 => "ctypes.c_uint8".to_string(),
+ FfiType::Int16 => "ctypes.c_int16".to_string(),
+ FfiType::UInt16 => "ctypes.c_uint16".to_string(),
+ FfiType::Int32 => "ctypes.c_int32".to_string(),
+ FfiType::UInt32 => "ctypes.c_uint32".to_string(),
+ FfiType::Int64 => "ctypes.c_int64".to_string(),
+ FfiType::UInt64 => "ctypes.c_uint64".to_string(),
+ FfiType::Float32 => "ctypes.c_float".to_string(),
+ FfiType::Float64 => "ctypes.c_double".to_string(),
+ FfiType::RustArcPtr(_) => "ctypes.c_void_p".to_string(),
+ FfiType::RustBuffer(maybe_suffix) => match maybe_suffix {
+ Some(suffix) => format!("_UniffiRustBuffer{suffix}"),
+ None => "_UniffiRustBuffer".to_string(),
+ },
+ FfiType::ForeignBytes => "_UniffiForeignBytes".to_string(),
+ FfiType::ForeignCallback => "_UNIFFI_FOREIGN_CALLBACK_T".to_string(),
+ // Pointer to an `asyncio.EventLoop` instance
+ FfiType::ForeignExecutorHandle => "ctypes.c_size_t".to_string(),
+ FfiType::ForeignExecutorCallback => "_UNIFFI_FOREIGN_EXECUTOR_CALLBACK_T".to_string(),
+ FfiType::RustFutureHandle => "ctypes.c_void_p".to_string(),
+ FfiType::RustFutureContinuationCallback => "_UNIFFI_FUTURE_CONTINUATION_T".to_string(),
+ FfiType::RustFutureContinuationData => "ctypes.c_size_t".to_string(),
+ }
+ }
+}
+
+trait AsCodeType {
+ fn as_codetype(&self) -> Box<dyn CodeType>;
+}
+
+impl<T: AsType> AsCodeType for T {
+ fn as_codetype(&self) -> Box<dyn CodeType> {
+ // Map `Type` instances to a `Box<dyn CodeType>` for that type.
+ //
+ // There is a companion match in `templates/Types.py` which performs a similar function for the
+ // template code.
+ //
+ // - When adding additional types here, make sure to also add a match arm to the `Types.py` template.
+ // - To keep things manageable, let's try to limit ourselves to these 2 mega-matches
+ match self.as_type() {
+ Type::UInt8 => Box::new(primitives::UInt8CodeType),
+ Type::Int8 => Box::new(primitives::Int8CodeType),
+ Type::UInt16 => Box::new(primitives::UInt16CodeType),
+ Type::Int16 => Box::new(primitives::Int16CodeType),
+ Type::UInt32 => Box::new(primitives::UInt32CodeType),
+ Type::Int32 => Box::new(primitives::Int32CodeType),
+ Type::UInt64 => Box::new(primitives::UInt64CodeType),
+ Type::Int64 => Box::new(primitives::Int64CodeType),
+ Type::Float32 => Box::new(primitives::Float32CodeType),
+ Type::Float64 => Box::new(primitives::Float64CodeType),
+ Type::Boolean => Box::new(primitives::BooleanCodeType),
+ Type::String => Box::new(primitives::StringCodeType),
+ Type::Bytes => Box::new(primitives::BytesCodeType),
+
+ Type::Timestamp => Box::new(miscellany::TimestampCodeType),
+ Type::Duration => Box::new(miscellany::DurationCodeType),
+
+ Type::Enum { name, .. } => Box::new(enum_::EnumCodeType::new(name)),
+ Type::Object { name, .. } => Box::new(object::ObjectCodeType::new(name)),
+ Type::Record { name, .. } => Box::new(record::RecordCodeType::new(name)),
+ Type::CallbackInterface { name, .. } => {
+ Box::new(callback_interface::CallbackInterfaceCodeType::new(name))
+ }
+ Type::ForeignExecutor => Box::new(executor::ForeignExecutorCodeType),
+ Type::Optional { inner_type } => {
+ Box::new(compounds::OptionalCodeType::new(*inner_type))
+ }
+ Type::Sequence { inner_type } => {
+ Box::new(compounds::SequenceCodeType::new(*inner_type))
+ }
+ Type::Map {
+ key_type,
+ value_type,
+ } => Box::new(compounds::MapCodeType::new(*key_type, *value_type)),
+ Type::External { name, .. } => Box::new(external::ExternalCodeType::new(name)),
+ Type::Custom { name, .. } => Box::new(custom::CustomCodeType::new(name)),
+ }
+ }
+}
+
+pub mod filters {
+ use super::*;
+ pub use crate::backend::filters::*;
+
+ pub(super) fn type_name(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
+ Ok(as_ct.as_codetype().type_label())
+ }
+
+ pub(super) fn ffi_converter_name(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
+ Ok(String::from("_Uniffi") + &as_ct.as_codetype().ffi_converter_name()[3..])
+ }
+
+ pub(super) fn canonical_name(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
+ Ok(as_ct.as_codetype().canonical_name())
+ }
+
+ pub(super) fn lift_fn(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
+ Ok(format!("{}.lift", ffi_converter_name(as_ct)?))
+ }
+
+ pub(super) fn lower_fn(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
+ Ok(format!("{}.lower", ffi_converter_name(as_ct)?))
+ }
+
+ pub(super) fn read_fn(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
+ Ok(format!("{}.read", ffi_converter_name(as_ct)?))
+ }
+
+ pub(super) fn write_fn(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
+ Ok(format!("{}.write", ffi_converter_name(as_ct)?))
+ }
+
+ pub(super) fn literal_py(
+ literal: &Literal,
+ as_ct: &impl AsCodeType,
+ ) -> Result<String, askama::Error> {
+ Ok(as_ct.as_codetype().literal(literal))
+ }
+
+ pub fn ffi_type_name(type_: &FfiType) -> Result<String, askama::Error> {
+ Ok(PythonCodeOracle::ffi_type_label(type_))
+ }
+
+ /// Get the idiomatic Python rendering of a class name (for enums, records, errors, etc).
+ pub fn class_name(nm: &str) -> Result<String, askama::Error> {
+ Ok(PythonCodeOracle.class_name(nm))
+ }
+
+ /// Get the idiomatic Python rendering of a function name.
+ pub fn fn_name(nm: &str) -> Result<String, askama::Error> {
+ Ok(PythonCodeOracle.fn_name(nm))
+ }
+
+ /// Get the idiomatic Python rendering of a variable name.
+ pub fn var_name(nm: &str) -> Result<String, askama::Error> {
+ Ok(PythonCodeOracle.var_name(nm))
+ }
+
+ /// Get the idiomatic Python rendering of an individual enum variant.
+ pub fn enum_variant_py(nm: &str) -> Result<String, askama::Error> {
+ Ok(PythonCodeOracle.enum_variant_name(nm))
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/object.rs b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/object.rs
new file mode 100644
index 0000000000..1165bb0e54
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/object.rs
@@ -0,0 +1,31 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use super::CodeType;
+use crate::backend::Literal;
+
+#[derive(Debug)]
+pub struct ObjectCodeType {
+ id: String,
+}
+
+impl ObjectCodeType {
+ pub fn new(id: String) -> Self {
+ Self { id }
+ }
+}
+
+impl CodeType for ObjectCodeType {
+ fn type_label(&self) -> String {
+ super::PythonCodeOracle.class_name(&self.id)
+ }
+
+ fn canonical_name(&self) -> String {
+ format!("Type{}", self.id)
+ }
+
+ fn literal(&self, _literal: &Literal) -> String {
+ unreachable!();
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/primitives.rs b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/primitives.rs
new file mode 100644
index 0000000000..4b3edecad4
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/primitives.rs
@@ -0,0 +1,71 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use super::CodeType;
+use crate::backend::Literal;
+use crate::interface::Radix;
+use paste::paste;
+
+fn render_literal(literal: &Literal) -> String {
+ match literal {
+ Literal::Boolean(v) => {
+ if *v {
+ "True".into()
+ } else {
+ "False".into()
+ }
+ }
+ Literal::String(s) => format!("\"{s}\""),
+ // https://docs.python.org/3/reference/lexical_analysis.html#integer-literals
+ Literal::Int(i, radix, _) => match radix {
+ Radix::Octal => format!("int(0o{i:o})"),
+ Radix::Decimal => format!("{i}"),
+ Radix::Hexadecimal => format!("{i:#x}"),
+ },
+ Literal::UInt(i, radix, _) => match radix {
+ Radix::Octal => format!("0o{i:o}"),
+ Radix::Decimal => format!("{i}"),
+ Radix::Hexadecimal => format!("{i:#x}"),
+ },
+ Literal::Float(string, _type_) => string.clone(),
+
+ _ => unreachable!("Literal"),
+ }
+}
+
+macro_rules! impl_code_type_for_primitive {
+ ($T:ty, $python_name:literal, $canonical_name:literal) => {
+ paste! {
+ #[derive(Debug)]
+ pub struct $T;
+ impl CodeType for $T {
+ fn type_label(&self) -> String {
+ $python_name.into()
+ }
+
+ fn canonical_name(&self) -> String {
+ $canonical_name.into()
+ }
+
+ fn literal(&self, literal: &Literal) -> String {
+ render_literal(&literal)
+ }
+ }
+ }
+ };
+}
+
+impl_code_type_for_primitive!(BooleanCodeType, "bool", "Bool");
+impl_code_type_for_primitive!(StringCodeType, "str", "String");
+impl_code_type_for_primitive!(BytesCodeType, "bytes", "Bytes");
+impl_code_type_for_primitive!(Int8CodeType, "int", "Int8");
+impl_code_type_for_primitive!(Int16CodeType, "int", "Int16");
+impl_code_type_for_primitive!(Int32CodeType, "int", "Int32");
+impl_code_type_for_primitive!(Int64CodeType, "int", "Int64");
+impl_code_type_for_primitive!(UInt8CodeType, "int", "UInt8");
+impl_code_type_for_primitive!(UInt16CodeType, "int", "UInt16");
+impl_code_type_for_primitive!(UInt32CodeType, "int", "UInt32");
+impl_code_type_for_primitive!(UInt64CodeType, "int", "UInt64");
+impl_code_type_for_primitive!(Float32CodeType, "float", "Float");
+impl_code_type_for_primitive!(Float64CodeType, "float", "Double");
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/record.rs b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/record.rs
new file mode 100644
index 0000000000..df00f98e8b
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/record.rs
@@ -0,0 +1,31 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use super::CodeType;
+use crate::backend::Literal;
+
+#[derive(Debug)]
+pub struct RecordCodeType {
+ id: String,
+}
+
+impl RecordCodeType {
+ pub fn new(id: String) -> Self {
+ Self { id }
+ }
+}
+
+impl CodeType for RecordCodeType {
+ fn type_label(&self) -> String {
+ super::PythonCodeOracle.class_name(&self.id)
+ }
+
+ fn canonical_name(&self) -> String {
+ format!("Type{}", self.id)
+ }
+
+ fn literal(&self, _literal: &Literal) -> String {
+ unreachable!();
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/mod.rs b/third_party/rust/uniffi_bindgen/src/bindings/python/mod.rs
new file mode 100644
index 0000000000..4b9dc8f609
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/mod.rs
@@ -0,0 +1,37 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::process::Command;
+
+use anyhow::Result;
+use camino::Utf8Path;
+use fs_err as fs;
+
+pub mod gen_python;
+mod test;
+use super::super::interface::ComponentInterface;
+pub use gen_python::{generate_python_bindings, Config};
+pub use test::{run_script, run_test};
+
+// Generate python bindings for the given ComponentInterface, in the given output directory.
+pub fn write_bindings(
+ config: &Config,
+ ci: &ComponentInterface,
+ out_dir: &Utf8Path,
+ try_format_code: bool,
+) -> Result<()> {
+ let py_file = out_dir.join(format!("{}.py", ci.namespace()));
+ fs::write(&py_file, generate_python_bindings(config, ci)?)?;
+
+ if try_format_code {
+ if let Err(e) = Command::new("yapf").arg(&py_file).output() {
+ println!(
+ "Warning: Unable to auto-format {} using yapf: {e:?}",
+ py_file.file_name().unwrap(),
+ )
+ }
+ }
+
+ Ok(())
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Async.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Async.py
new file mode 100644
index 0000000000..82aa534b46
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Async.py
@@ -0,0 +1,40 @@
+# RustFuturePoll values
+_UNIFFI_RUST_FUTURE_POLL_READY = 0
+_UNIFFI_RUST_FUTURE_POLL_MAYBE_READY = 1
+
+# Stores futures for _uniffi_continuation_callback
+_UniffiContinuationPointerManager = _UniffiPointerManager()
+
+# Continuation callback for async functions
+# lift the return value or error and resolve the future, causing the async function to resume.
+@_UNIFFI_FUTURE_CONTINUATION_T
+def _uniffi_continuation_callback(future_ptr, poll_code):
+ (eventloop, future) = _UniffiContinuationPointerManager.release_pointer(future_ptr)
+ eventloop.call_soon_threadsafe(_uniffi_set_future_result, future, poll_code)
+
+def _uniffi_set_future_result(future, poll_code):
+ if not future.cancelled():
+ future.set_result(poll_code)
+
+async def _uniffi_rust_call_async(rust_future, ffi_poll, ffi_complete, ffi_free, lift_func, error_ffi_converter):
+ try:
+ eventloop = asyncio.get_running_loop()
+
+ # Loop and poll until we see a _UNIFFI_RUST_FUTURE_POLL_READY value
+ while True:
+ future = eventloop.create_future()
+ ffi_poll(
+ rust_future,
+ _UniffiContinuationPointerManager.new_pointer((eventloop, future)),
+ )
+ poll_code = await future
+ if poll_code == _UNIFFI_RUST_FUTURE_POLL_READY:
+ break
+
+ return lift_func(
+ _rust_call_with_error(error_ffi_converter, ffi_complete, rust_future)
+ )
+ finally:
+ ffi_free(rust_future)
+
+_UniffiLib.{{ ci.ffi_rust_future_continuation_callback_set().name() }}(_uniffi_continuation_callback)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/BooleanHelper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/BooleanHelper.py
new file mode 100644
index 0000000000..6775e9e132
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/BooleanHelper.py
@@ -0,0 +1,16 @@
+class _UniffiConverterBool(_UniffiConverterPrimitive):
+ @classmethod
+ def check(cls, value):
+ return not not value
+
+ @classmethod
+ def read(cls, buf):
+ return cls.lift(buf.read_u8())
+
+ @classmethod
+ def write_unchecked(cls, value, buf):
+ buf.write_u8(value)
+
+ @staticmethod
+ def lift(value):
+ return value != 0
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/BytesHelper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/BytesHelper.py
new file mode 100644
index 0000000000..196b5b29fa
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/BytesHelper.py
@@ -0,0 +1,16 @@
+class _UniffiConverterBytes(_UniffiConverterRustBuffer):
+ @staticmethod
+ def read(buf):
+ size = buf.read_i32()
+ if size < 0:
+ raise InternalError("Unexpected negative byte string length")
+ return buf.read(size)
+
+ @staticmethod
+ def write(value, buf):
+ try:
+ memoryview(value)
+ except TypeError:
+ raise TypeError("a bytes-like object is required, not {!r}".format(type(value).__name__))
+ buf.write_i32(len(value))
+ buf.write(value)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceRuntime.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceRuntime.py
new file mode 100644
index 0000000000..0fe2ab8dc0
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceRuntime.py
@@ -0,0 +1,77 @@
+import threading
+
+class ConcurrentHandleMap:
+ """
+ A map where inserting, getting and removing data is synchronized with a lock.
+ """
+
+ def __init__(self):
+ # type Handle = int
+ self._left_map = {} # type: Dict[Handle, Any]
+ self._right_map = {} # type: Dict[Any, Handle]
+
+ self._lock = threading.Lock()
+ self._current_handle = 0
+ self._stride = 1
+
+
+ def insert(self, obj):
+ with self._lock:
+ if obj in self._right_map:
+ return self._right_map[obj]
+ else:
+ handle = self._current_handle
+ self._current_handle += self._stride
+ self._left_map[handle] = obj
+ self._right_map[obj] = handle
+ return handle
+
+ def get(self, handle):
+ with self._lock:
+ return self._left_map.get(handle)
+
+ def remove(self, handle):
+ with self._lock:
+ if handle in self._left_map:
+ obj = self._left_map.pop(handle)
+ del self._right_map[obj]
+ return obj
+
+# Magic number for the Rust proxy to call using the same mechanism as every other method,
+# to free the callback once it's dropped by Rust.
+IDX_CALLBACK_FREE = 0
+# Return codes for callback calls
+_UNIFFI_CALLBACK_SUCCESS = 0
+_UNIFFI_CALLBACK_ERROR = 1
+_UNIFFI_CALLBACK_UNEXPECTED_ERROR = 2
+
+class _UniffiConverterCallbackInterface:
+ _handle_map = ConcurrentHandleMap()
+
+ def __init__(self, cb):
+ self._foreign_callback = cb
+
+ def drop(self, handle):
+ self.__class__._handle_map.remove(handle)
+
+ @classmethod
+ def lift(cls, handle):
+ obj = cls._handle_map.get(handle)
+ if not obj:
+ raise InternalError("The object in the handle map has been dropped already")
+
+ return obj
+
+ @classmethod
+ def read(cls, buf):
+ handle = buf.read_u64()
+ cls.lift(handle)
+
+ @classmethod
+ def lower(cls, cb):
+ handle = cls._handle_map.insert(cb)
+ return handle
+
+ @classmethod
+ def write(cls, cb, buf):
+ buf.write_u64(cls.lower(cb))
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceTemplate.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceTemplate.py
new file mode 100644
index 0000000000..e0e926757a
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceTemplate.py
@@ -0,0 +1,105 @@
+{%- let cbi = ci|get_callback_interface_definition(id) %}
+{%- let foreign_callback = format!("foreignCallback{}", canonical_type_name) %}
+
+{% if self.include_once_check("CallbackInterfaceRuntime.py") %}{% include "CallbackInterfaceRuntime.py" %}{% endif %}
+
+# Declaration and _UniffiConverters for {{ type_name }} Callback Interface
+
+class {{ type_name }}:
+ {% for meth in cbi.methods() -%}
+ def {{ meth.name()|fn_name }}(self, {% call py::arg_list_decl(meth) %}):
+ raise NotImplementedError
+
+ {% endfor %}
+
+def py_{{ foreign_callback }}(handle, method, args_data, args_len, buf_ptr):
+ {% for meth in cbi.methods() -%}
+ {% let method_name = format!("invoke_{}", meth.name())|fn_name %}
+ def {{ method_name }}(python_callback, args_stream, buf_ptr):
+ {#- Unpacking args from the _UniffiRustBuffer #}
+ def makeCall():
+ {#- Calling the concrete callback object #}
+ {%- if meth.arguments().len() != 0 -%}
+ return python_callback.{{ meth.name()|fn_name }}(
+ {% for arg in meth.arguments() -%}
+ {{ arg|read_fn }}(args_stream)
+ {%- if !loop.last %}, {% endif %}
+ {% endfor -%}
+ )
+ {%- else %}
+ return python_callback.{{ meth.name()|fn_name }}()
+ {%- endif %}
+
+ def makeCallAndHandleReturn():
+ {%- match meth.return_type() %}
+ {%- when Some(return_type) %}
+ rval = makeCall()
+ with _UniffiRustBuffer.alloc_with_builder() as builder:
+ {{ return_type|write_fn }}(rval, builder)
+ buf_ptr[0] = builder.finalize()
+ {%- when None %}
+ makeCall()
+ {%- endmatch %}
+ return _UNIFFI_CALLBACK_SUCCESS
+
+ {%- match meth.throws_type() %}
+ {%- when None %}
+ return makeCallAndHandleReturn()
+ {%- when Some(err) %}
+ try:
+ return makeCallAndHandleReturn()
+ except {{ err|type_name }} as e:
+ # Catch errors declared in the UDL file
+ with _UniffiRustBuffer.alloc_with_builder() as builder:
+ {{ err|write_fn }}(e, builder)
+ buf_ptr[0] = builder.finalize()
+ return _UNIFFI_CALLBACK_ERROR
+ {%- endmatch %}
+
+ {% endfor %}
+
+ cb = {{ ffi_converter_name }}.lift(handle)
+ if not cb:
+ raise InternalError("No callback in handlemap; this is a uniffi bug")
+
+ if method == IDX_CALLBACK_FREE:
+ {{ ffi_converter_name }}.drop(handle)
+ # Successfull return
+ # See docs of ForeignCallback in `uniffi_core/src/ffi/foreigncallbacks.rs`
+ return _UNIFFI_CALLBACK_SUCCESS
+
+ {% for meth in cbi.methods() -%}
+ {% let method_name = format!("invoke_{}", meth.name())|fn_name -%}
+ if method == {{ loop.index }}:
+ # Call the method and handle any errors
+ # See docs of ForeignCallback in `uniffi_core/src/ffi/foreigncallbacks.rs` for details
+ try:
+ return {{ method_name }}(cb, _UniffiRustBufferStream(args_data, args_len), buf_ptr)
+ except BaseException as e:
+ # Catch unexpected errors
+ try:
+ # Try to serialize the exception into a String
+ buf_ptr[0] = {{ Type::String.borrow()|lower_fn }}(repr(e))
+ except:
+ # If that fails, just give up
+ pass
+ return _UNIFFI_CALLBACK_UNEXPECTED_ERROR
+ {% endfor %}
+
+ # This should never happen, because an out of bounds method index won't
+ # ever be used. Once we can catch errors, we should return an InternalException.
+ # https://github.com/mozilla/uniffi-rs/issues/351
+
+ # An unexpected error happened.
+ # See docs of ForeignCallback in `uniffi_core/src/ffi/foreigncallbacks.rs`
+ return _UNIFFI_CALLBACK_UNEXPECTED_ERROR
+
+# We need to keep this function reference alive:
+# if they get GC'd while in use then UniFFI internals could attempt to call a function
+# that is in freed memory.
+# That would be...uh...bad. Yeah, that's the word. Bad.
+{{ foreign_callback }} = _UNIFFI_FOREIGN_CALLBACK_T(py_{{ foreign_callback }})
+_rust_call(lambda err: _UniffiLib.{{ cbi.ffi_init_callback().name() }}({{ foreign_callback }}, err))
+
+# The _UniffiConverter which transforms the Callbacks in to Handles to pass to Rust.
+{{ ffi_converter_name }} = _UniffiConverterCallbackInterface({{ foreign_callback }})
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/CustomType.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/CustomType.py
new file mode 100644
index 0000000000..5be6155b84
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/CustomType.py
@@ -0,0 +1,58 @@
+{%- match python_config.custom_types.get(name.as_str()) %}
+{% when None %}
+{#- No custom type config, just forward all methods to our builtin type #}
+# Type alias
+{{ name }} = {{ builtin|type_name }}
+
+class _UniffiConverterType{{ name }}:
+ @staticmethod
+ def write(value, buf):
+ {{ builtin|ffi_converter_name }}.write(value, buf)
+
+ @staticmethod
+ def read(buf):
+ return {{ builtin|ffi_converter_name }}.read(buf)
+
+ @staticmethod
+ def lift(value):
+ return {{ builtin|ffi_converter_name }}.lift(value)
+
+ @staticmethod
+ def lower(value):
+ return {{ builtin|ffi_converter_name }}.lower(value)
+
+{%- when Some(config) %}
+
+{%- match config.imports %}
+{%- when Some(imports) %}
+{%- for import_name in imports %}
+{{ self.add_import(import_name) }}
+{%- endfor %}
+{%- else %}
+{%- endmatch %}
+
+# Type alias
+{{ name }} = {{ builtin|type_name }}
+
+{#- Custom type config supplied, use it to convert the builtin type #}
+class _UniffiConverterType{{ name }}:
+ @staticmethod
+ def write(value, buf):
+ builtin_value = {{ config.from_custom.render("value") }}
+ {{ builtin|write_fn }}(builtin_value, buf)
+
+ @staticmethod
+ def read(buf):
+ builtin_value = {{ builtin|read_fn }}(buf)
+ return {{ config.into_custom.render("builtin_value") }}
+
+ @staticmethod
+ def lift(value):
+ builtin_value = {{ builtin|lift_fn }}(value)
+ return {{ config.into_custom.render("builtin_value") }}
+
+ @staticmethod
+ def lower(value):
+ builtin_value = {{ config.from_custom.render("value") }}
+ return {{ builtin|lower_fn }}(builtin_value)
+{%- endmatch %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/DurationHelper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/DurationHelper.py
new file mode 100644
index 0000000000..10974e009d
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/DurationHelper.py
@@ -0,0 +1,21 @@
+# The Duration type.
+Duration = datetime.timedelta
+
+# There is a loss of precision when converting from Rust durations,
+# which are accurate to the nanosecond,
+# to Python durations, which are only accurate to the microsecond.
+class _UniffiConverterDuration(_UniffiConverterRustBuffer):
+ @staticmethod
+ def read(buf):
+ seconds = buf.read_u64()
+ microseconds = buf.read_u32() / 1.0e3
+ return datetime.timedelta(seconds=seconds, microseconds=microseconds)
+
+ @staticmethod
+ def write(value, buf):
+ seconds = value.seconds + value.days * 24 * 3600
+ nanoseconds = value.microseconds * 1000
+ if seconds < 0:
+ raise ValueError("Invalid duration, must be non-negative")
+ buf.write_i64(seconds)
+ buf.write_u32(nanoseconds)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py
new file mode 100644
index 0000000000..84d089baf9
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py
@@ -0,0 +1,97 @@
+{#
+# Python has a built-in `enum` module which is nice to use, but doesn't support
+# variants with associated data. So, we switch here, and generate a stdlib `enum`
+# when none of the variants have associated data, or a generic nested-class
+# construct when they do.
+#}
+{% if e.is_flat() %}
+
+class {{ type_name }}(enum.Enum):
+ {% for variant in e.variants() -%}
+ {{ variant.name()|enum_variant_py }} = {{ loop.index }}
+ {% endfor %}
+{% else %}
+
+class {{ type_name }}:
+ def __init__(self):
+ raise RuntimeError("{{ type_name }} cannot be instantiated directly")
+
+ # Each enum variant is a nested class of the enum itself.
+ {% for variant in e.variants() -%}
+ class {{ variant.name()|enum_variant_py }}:
+ {% for field in variant.fields() %}
+ {{- field.name()|var_name }}: "{{- field|type_name }}";
+ {%- endfor %}
+
+ @typing.no_type_check
+ def __init__(self,{% for field in variant.fields() %}{{ field.name()|var_name }}: "{{- field|type_name }}"{% if loop.last %}{% else %}, {% endif %}{% endfor %}):
+ {% if variant.has_fields() %}
+ {%- for field in variant.fields() %}
+ self.{{ field.name()|var_name }} = {{ field.name()|var_name }}
+ {%- endfor %}
+ {% else %}
+ pass
+ {% endif %}
+
+ def __str__(self):
+ return "{{ type_name }}.{{ variant.name()|enum_variant_py }}({% for field in variant.fields() %}{{ field.name()|var_name }}={}{% if loop.last %}{% else %}, {% endif %}{% endfor %})".format({% for field in variant.fields() %}self.{{ field.name()|var_name }}{% if loop.last %}{% else %}, {% endif %}{% endfor %})
+
+ def __eq__(self, other):
+ if not other.is_{{ variant.name()|var_name }}():
+ return False
+ {%- for field in variant.fields() %}
+ if self.{{ field.name()|var_name }} != other.{{ field.name()|var_name }}:
+ return False
+ {%- endfor %}
+ return True
+ {% endfor %}
+
+ # For each variant, we have an `is_NAME` method for easily checking
+ # whether an instance is that variant.
+ {% for variant in e.variants() -%}
+ def is_{{ variant.name()|var_name }}(self) -> bool:
+ return isinstance(self, {{ type_name }}.{{ variant.name()|enum_variant_py }})
+ {% endfor %}
+
+# Now, a little trick - we make each nested variant class be a subclass of the main
+# enum class, so that method calls and instance checks etc will work intuitively.
+# We might be able to do this a little more neatly with a metaclass, but this'll do.
+{% for variant in e.variants() -%}
+{{ type_name }}.{{ variant.name()|enum_variant_py }} = type("{{ type_name }}.{{ variant.name()|enum_variant_py }}", ({{ type_name }}.{{variant.name()|enum_variant_py}}, {{ type_name }},), {}) # type: ignore
+{% endfor %}
+
+{% endif %}
+
+class {{ ffi_converter_name }}(_UniffiConverterRustBuffer):
+ @staticmethod
+ def read(buf):
+ variant = buf.read_i32()
+
+ {%- for variant in e.variants() %}
+ if variant == {{ loop.index }}:
+ {%- if e.is_flat() %}
+ return {{ type_name }}.{{variant.name()|enum_variant_py}}
+ {%- else %}
+ return {{ type_name }}.{{variant.name()|enum_variant_py}}(
+ {%- for field in variant.fields() %}
+ {{ field|read_fn }}(buf),
+ {%- endfor %}
+ )
+ {%- endif %}
+ {%- endfor %}
+ raise InternalError("Raw enum value doesn't match any cases")
+
+ def write(value, buf):
+ {%- for variant in e.variants() %}
+ {%- if e.is_flat() %}
+ if value == {{ type_name }}.{{ variant.name()|enum_variant_py }}:
+ buf.write_i32({{ loop.index }})
+ {%- else %}
+ if value.is_{{ variant.name()|var_name }}():
+ buf.write_i32({{ loop.index }})
+ {%- for field in variant.fields() %}
+ {{ field|write_fn }}(value.{{ field.name()|var_name }}, buf)
+ {%- endfor %}
+ {%- endif %}
+ {%- endfor %}
+
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ErrorTemplate.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ErrorTemplate.py
new file mode 100644
index 0000000000..26a1e6452a
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ErrorTemplate.py
@@ -0,0 +1,70 @@
+# {{ type_name }}
+# We want to define each variant as a nested class that's also a subclass,
+# which is tricky in Python. To accomplish this we're going to create each
+# class separately, then manually add the child classes to the base class's
+# __dict__. All of this happens in dummy class to avoid polluting the module
+# namespace.
+class {{ type_name }}(Exception):
+ pass
+
+_UniffiTemp{{ type_name }} = {{ type_name }}
+
+class {{ type_name }}: # type: ignore
+ {%- for variant in e.variants() -%}
+ {%- let variant_type_name = variant.name()|class_name -%}
+ {%- if e.is_flat() %}
+ class {{ variant_type_name }}(_UniffiTemp{{ type_name }}):
+ def __repr__(self):
+ return "{{ type_name }}.{{ variant_type_name }}({})".format(repr(str(self)))
+ {%- else %}
+ class {{ variant_type_name }}(_UniffiTemp{{ type_name }}):
+ def __init__(self{% for field in variant.fields() %}, {{ field.name()|var_name }}{% endfor %}):
+ {%- if variant.has_fields() %}
+ super().__init__(", ".join([
+ {%- for field in variant.fields() %}
+ "{{ field.name()|var_name }}={!r}".format({{ field.name()|var_name }}),
+ {%- endfor %}
+ ]))
+ {%- for field in variant.fields() %}
+ self.{{ field.name()|var_name }} = {{ field.name()|var_name }}
+ {%- endfor %}
+ {%- else %}
+ pass
+ {%- endif %}
+ def __repr__(self):
+ return "{{ type_name }}.{{ variant_type_name }}({})".format(str(self))
+ {%- endif %}
+ _UniffiTemp{{ type_name }}.{{ variant_type_name }} = {{ variant_type_name }} # type: ignore
+ {%- endfor %}
+
+{{ type_name }} = _UniffiTemp{{ type_name }} # type: ignore
+del _UniffiTemp{{ type_name }}
+
+
+class {{ ffi_converter_name }}(_UniffiConverterRustBuffer):
+ @staticmethod
+ def read(buf):
+ variant = buf.read_i32()
+ {%- for variant in e.variants() %}
+ if variant == {{ loop.index }}:
+ return {{ type_name }}.{{ variant.name()|class_name }}(
+ {%- if e.is_flat() %}
+ {{ Type::String.borrow()|read_fn }}(buf),
+ {%- else %}
+ {%- for field in variant.fields() %}
+ {{ field.name()|var_name }}={{ field|read_fn }}(buf),
+ {%- endfor %}
+ {%- endif %}
+ )
+ {%- endfor %}
+ raise InternalError("Raw enum value doesn't match any cases")
+
+ @staticmethod
+ def write(value, buf):
+ {%- for variant in e.variants() %}
+ if isinstance(value, {{ type_name }}.{{ variant.name()|class_name }}):
+ buf.write_i32({{ loop.index }})
+ {%- for field in variant.fields() %}
+ {{ field|write_fn }}(value.{{ field.name()|var_name }}, buf)
+ {%- endfor %}
+ {%- endfor %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ExternalTemplate.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ExternalTemplate.py
new file mode 100644
index 0000000000..71e05e8b06
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ExternalTemplate.py
@@ -0,0 +1,9 @@
+{%- let ns = namespace|fn_name %}
+
+# External type {{name}} is in namespace "{{namespace}}", crate {{module_path}}
+{%- let ffi_converter_name = "_UniffiConverterType{}"|format(name) %}
+{{ self.add_import_of(ns, ffi_converter_name) }}
+{{ self.add_import_of(ns, name) }} {#- import the type alias itself -#}
+
+{%- let rustbuffer_local_name = "_UniffiRustBuffer{}"|format(name) %}
+{{ self.add_import_of_as(ns, "_UniffiRustBuffer", rustbuffer_local_name) }}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Float32Helper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Float32Helper.py
new file mode 100644
index 0000000000..a52107a638
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Float32Helper.py
@@ -0,0 +1,8 @@
+class _UniffiConverterFloat(_UniffiConverterPrimitiveFloat):
+ @staticmethod
+ def read(buf):
+ return buf.read_float()
+
+ @staticmethod
+ def write_unchecked(value, buf):
+ buf.write_float(value)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Float64Helper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Float64Helper.py
new file mode 100644
index 0000000000..772f5080e9
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Float64Helper.py
@@ -0,0 +1,8 @@
+class _UniffiConverterDouble(_UniffiConverterPrimitiveFloat):
+ @staticmethod
+ def read(buf):
+ return buf.read_double()
+
+ @staticmethod
+ def write_unchecked(value, buf):
+ buf.write_double(value)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ForeignExecutorTemplate.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ForeignExecutorTemplate.py
new file mode 100644
index 0000000000..6a6932fed0
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ForeignExecutorTemplate.py
@@ -0,0 +1,63 @@
+# FFI code for the ForeignExecutor type
+
+{{ self.add_import("asyncio") }}
+
+_UNIFFI_RUST_TASK_CALLBACK_SUCCESS = 0
+_UNIFFI_RUST_TASK_CALLBACK_CANCELLED = 1
+_UNIFFI_FOREIGN_EXECUTOR_CALLBACK_SUCCESS = 0
+_UNIFFI_FOREIGN_EXECUTOR_CALLBACK_CANCELED = 1
+_UNIFFI_FOREIGN_EXECUTOR_CALLBACK_ERROR = 2
+
+class {{ ffi_converter_name }}:
+ _pointer_manager = _UniffiPointerManager()
+
+ @classmethod
+ def lower(cls, eventloop):
+ if not isinstance(eventloop, asyncio.BaseEventLoop):
+ raise TypeError("_uniffi_executor_callback: Expected EventLoop instance")
+ return cls._pointer_manager.new_pointer(eventloop)
+
+ @classmethod
+ def write(cls, eventloop, buf):
+ buf.write_c_size_t(cls.lower(eventloop))
+
+ @classmethod
+ def read(cls, buf):
+ return cls.lift(buf.read_c_size_t())
+
+ @classmethod
+ def lift(cls, value):
+ return cls._pointer_manager.lookup(value)
+
+@_UNIFFI_FOREIGN_EXECUTOR_CALLBACK_T
+def _uniffi_executor_callback(eventloop_address, delay, task_ptr, task_data):
+ if task_ptr is None:
+ {{ ffi_converter_name }}._pointer_manager.release_pointer(eventloop_address)
+ return _UNIFFI_FOREIGN_EXECUTOR_CALLBACK_SUCCESS
+ else:
+ eventloop = {{ ffi_converter_name }}._pointer_manager.lookup(eventloop_address)
+ if eventloop.is_closed():
+ return _UNIFFI_FOREIGN_EXECUTOR_CALLBACK_CANCELED
+
+ callback = _UNIFFI_RUST_TASK(task_ptr)
+ # FIXME: there's no easy way to get a callback when an eventloop is closed. This means that
+ # if eventloop is called before the `call_soon_threadsafe()` calls are invoked, the call
+ # will never happen and we will probably leak a resource.
+ if delay == 0:
+ # This can be called from any thread, so make sure to use `call_soon_threadsafe'
+ eventloop.call_soon_threadsafe(callback, task_data,
+ _UNIFFI_FOREIGN_EXECUTOR_CALLBACK_SUCCESS)
+ else:
+ # For delayed tasks, we use `call_soon_threadsafe()` + `call_later()` to make the
+ # operation threadsafe
+ eventloop.call_soon_threadsafe(eventloop.call_later, delay / 1000.0, callback,
+ task_data, _UNIFFI_FOREIGN_EXECUTOR_CALLBACK_SUCCESS)
+ return _UNIFFI_FOREIGN_EXECUTOR_CALLBACK_SUCCESS
+
+# Register the callback with the scaffolding
+{%- match ci.ffi_foreign_executor_callback_set() %}
+{%- when Some with (fn) %}
+_UniffiLib.{{ fn.name() }}(_uniffi_executor_callback)
+{%- when None %}
+{#- No foreign executor, we don't set anything #}
+{% endmatch %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Helpers.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Helpers.py
new file mode 100644
index 0000000000..dca962f176
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Helpers.py
@@ -0,0 +1,75 @@
+# A handful of classes and functions to support the generated data structures.
+# This would be a good candidate for isolating in its own ffi-support lib.
+
+class InternalError(Exception):
+ pass
+
+class _UniffiRustCallStatus(ctypes.Structure):
+ """
+ Error runtime.
+ """
+ _fields_ = [
+ ("code", ctypes.c_int8),
+ ("error_buf", _UniffiRustBuffer),
+ ]
+
+ # These match the values from the uniffi::rustcalls module
+ CALL_SUCCESS = 0
+ CALL_ERROR = 1
+ CALL_PANIC = 2
+
+ def __str__(self):
+ if self.code == _UniffiRustCallStatus.CALL_SUCCESS:
+ return "_UniffiRustCallStatus(CALL_SUCCESS)"
+ elif self.code == _UniffiRustCallStatus.CALL_ERROR:
+ return "_UniffiRustCallStatus(CALL_ERROR)"
+ elif self.code == _UniffiRustCallStatus.CALL_PANIC:
+ return "_UniffiRustCallStatus(CALL_PANIC)"
+ else:
+ return "_UniffiRustCallStatus(<invalid code>)"
+
+def _rust_call(fn, *args):
+ # Call a rust function
+ return _rust_call_with_error(None, fn, *args)
+
+def _rust_call_with_error(error_ffi_converter, fn, *args):
+ # Call a rust function and handle any errors
+ #
+ # This function is used for rust calls that return Result<> and therefore can set the CALL_ERROR status code.
+ # error_ffi_converter must be set to the _UniffiConverter for the error class that corresponds to the result.
+ call_status = _UniffiRustCallStatus(code=_UniffiRustCallStatus.CALL_SUCCESS, error_buf=_UniffiRustBuffer(0, 0, None))
+
+ args_with_error = args + (ctypes.byref(call_status),)
+ result = fn(*args_with_error)
+ _uniffi_check_call_status(error_ffi_converter, call_status)
+ return result
+
+def _uniffi_check_call_status(error_ffi_converter, call_status):
+ if call_status.code == _UniffiRustCallStatus.CALL_SUCCESS:
+ pass
+ elif call_status.code == _UniffiRustCallStatus.CALL_ERROR:
+ if error_ffi_converter is None:
+ call_status.error_buf.free()
+ raise InternalError("_rust_call_with_error: CALL_ERROR, but error_ffi_converter is None")
+ else:
+ raise error_ffi_converter.lift(call_status.error_buf)
+ elif call_status.code == _UniffiRustCallStatus.CALL_PANIC:
+ # When the rust code sees a panic, it tries to construct a _UniffiRustBuffer
+ # with the message. But if that code panics, then it just sends back
+ # an empty buffer.
+ if call_status.error_buf.len > 0:
+ msg = _UniffiConverterString.lift(call_status.error_buf)
+ else:
+ msg = "Unknown rust panic"
+ raise InternalError(msg)
+ else:
+ raise InternalError("Invalid _UniffiRustCallStatus code: {}".format(
+ call_status.code))
+
+# A function pointer for a callback as defined by UniFFI.
+# Rust definition `fn(handle: u64, method: u32, args: _UniffiRustBuffer, buf_ptr: *mut _UniffiRustBuffer) -> int`
+_UNIFFI_FOREIGN_CALLBACK_T = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_ulonglong, ctypes.c_ulong, ctypes.POINTER(ctypes.c_char), ctypes.c_int, ctypes.POINTER(_UniffiRustBuffer))
+
+# UniFFI future continuation
+_UNIFFI_FUTURE_CONTINUATION_T = ctypes.CFUNCTYPE(None, ctypes.c_size_t, ctypes.c_int8)
+
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int16Helper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int16Helper.py
new file mode 100644
index 0000000000..99f19dc1c0
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int16Helper.py
@@ -0,0 +1,12 @@
+class _UniffiConverterInt16(_UniffiConverterPrimitiveInt):
+ CLASS_NAME = "i16"
+ VALUE_MIN = -2**15
+ VALUE_MAX = 2**15
+
+ @staticmethod
+ def read(buf):
+ return buf.read_i16()
+
+ @staticmethod
+ def write_unchecked(value, buf):
+ buf.write_i16(value)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int32Helper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int32Helper.py
new file mode 100644
index 0000000000..3b142c8749
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int32Helper.py
@@ -0,0 +1,12 @@
+class _UniffiConverterInt32(_UniffiConverterPrimitiveInt):
+ CLASS_NAME = "i32"
+ VALUE_MIN = -2**31
+ VALUE_MAX = 2**31
+
+ @staticmethod
+ def read(buf):
+ return buf.read_i32()
+
+ @staticmethod
+ def write_unchecked(value, buf):
+ buf.write_i32(value)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int64Helper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int64Helper.py
new file mode 100644
index 0000000000..6e94379cbf
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int64Helper.py
@@ -0,0 +1,12 @@
+class _UniffiConverterInt64(_UniffiConverterPrimitiveInt):
+ CLASS_NAME = "i64"
+ VALUE_MIN = -2**63
+ VALUE_MAX = 2**63
+
+ @staticmethod
+ def read(buf):
+ return buf.read_i64()
+
+ @staticmethod
+ def write_unchecked(value, buf):
+ buf.write_i64(value)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int8Helper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int8Helper.py
new file mode 100644
index 0000000000..732530e3cb
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int8Helper.py
@@ -0,0 +1,12 @@
+class _UniffiConverterInt8(_UniffiConverterPrimitiveInt):
+ CLASS_NAME = "i8"
+ VALUE_MIN = -2**7
+ VALUE_MAX = 2**7
+
+ @staticmethod
+ def read(buf):
+ return buf.read_i8()
+
+ @staticmethod
+ def write_unchecked(value, buf):
+ buf.write_i8(value)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/MapTemplate.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/MapTemplate.py
new file mode 100644
index 0000000000..387227ed09
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/MapTemplate.py
@@ -0,0 +1,27 @@
+{%- let key_ffi_converter = key_type|ffi_converter_name %}
+{%- let value_ffi_converter = value_type|ffi_converter_name %}
+
+class {{ ffi_converter_name }}(_UniffiConverterRustBuffer):
+ @classmethod
+ def write(cls, items, buf):
+ buf.write_i32(len(items))
+ for (key, value) in items.items():
+ {{ key_ffi_converter }}.write(key, buf)
+ {{ value_ffi_converter }}.write(value, buf)
+
+ @classmethod
+ def read(cls, buf):
+ count = buf.read_i32()
+ if count < 0:
+ raise InternalError("Unexpected negative map size")
+
+ # It would be nice to use a dict comprehension,
+ # but in Python 3.7 and before the evaluation order is not according to spec,
+ # so we we're reading the value before the key.
+ # This loop makes the order explicit: first reading the key, then the value.
+ d = {}
+ for i in range(count):
+ key = {{ key_ffi_converter }}.read(buf)
+ val = {{ value_ffi_converter }}.read(buf)
+ d[key] = val
+ return d
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/NamespaceLibraryTemplate.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/NamespaceLibraryTemplate.py
new file mode 100644
index 0000000000..fac6cd5564
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/NamespaceLibraryTemplate.py
@@ -0,0 +1,83 @@
+# Define some ctypes FFI types that we use in the library
+
+"""
+ctypes type for the foreign executor callback. This is a built-in interface for scheduling
+tasks
+
+Args:
+ executor: opaque c_size_t value representing the eventloop
+ delay: delay in ms
+ task: function pointer to the task callback
+ task_data: void pointer to the task callback data
+
+Normally we should call task(task_data) after the detail.
+However, when task is NULL this indicates that Rust has dropped the ForeignExecutor and we should
+decrease the EventLoop refcount.
+"""
+_UNIFFI_FOREIGN_EXECUTOR_CALLBACK_T = ctypes.CFUNCTYPE(ctypes.c_int8, ctypes.c_size_t, ctypes.c_uint32, ctypes.c_void_p, ctypes.c_void_p)
+
+"""
+Function pointer for a Rust task, which a callback function that takes a opaque pointer
+"""
+_UNIFFI_RUST_TASK = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_int8)
+
+def _uniffi_future_callback_t(return_type):
+ """
+ Factory function to create callback function types for async functions
+ """
+ return ctypes.CFUNCTYPE(None, ctypes.c_size_t, return_type, _UniffiRustCallStatus)
+
+def _uniffi_load_indirect():
+ """
+ This is how we find and load the dynamic library provided by the component.
+ For now we just look it up by name.
+ """
+ if sys.platform == "darwin":
+ libname = "lib{}.dylib"
+ elif sys.platform.startswith("win"):
+ # As of python3.8, ctypes does not seem to search $PATH when loading DLLs.
+ # We could use `os.add_dll_directory` to configure the search path, but
+ # it doesn't feel right to mess with application-wide settings. Let's
+ # assume that the `.dll` is next to the `.py` file and load by full path.
+ libname = os.path.join(
+ os.path.dirname(__file__),
+ "{}.dll",
+ )
+ else:
+ # Anything else must be an ELF platform - Linux, *BSD, Solaris/illumos
+ libname = "lib{}.so"
+
+ libname = libname.format("{{ config.cdylib_name() }}")
+ path = os.path.join(os.path.dirname(__file__), libname)
+ lib = ctypes.cdll.LoadLibrary(path)
+ return lib
+
+def _uniffi_check_contract_api_version(lib):
+ # Get the bindings contract version from our ComponentInterface
+ bindings_contract_version = {{ ci.uniffi_contract_version() }}
+ # Get the scaffolding contract version by calling the into the dylib
+ scaffolding_contract_version = lib.{{ ci.ffi_uniffi_contract_version().name() }}()
+ if bindings_contract_version != scaffolding_contract_version:
+ raise InternalError("UniFFI contract version mismatch: try cleaning and rebuilding your project")
+
+def _uniffi_check_api_checksums(lib):
+ {%- for (name, expected_checksum) in ci.iter_checksums() %}
+ if lib.{{ name }}() != {{ expected_checksum }}:
+ raise InternalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
+ {%- else %}
+ pass
+ {%- endfor %}
+
+# A ctypes library to expose the extern-C FFI definitions.
+# This is an implementation detail which will be called internally by the public API.
+
+_UniffiLib = _uniffi_load_indirect()
+{%- for func in ci.iter_ffi_function_definitions() %}
+_UniffiLib.{{ func.name() }}.argtypes = (
+ {%- call py::arg_list_ffi_decl(func) -%}
+)
+_UniffiLib.{{ func.name() }}.restype = {% match func.return_type() %}{% when Some with (type_) %}{{ type_|ffi_type_name }}{% when None %}None{% endmatch %}
+{%- endfor %}
+{# Ensure to call the contract verification only after we defined all functions. -#}
+_uniffi_check_contract_api_version(_UniffiLib)
+_uniffi_check_api_checksums(_UniffiLib)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py
new file mode 100644
index 0000000000..7e98f7c46f
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py
@@ -0,0 +1,87 @@
+{%- let obj = ci|get_object_definition(name) %}
+
+class {{ type_name }}:
+ _pointer: ctypes.c_void_p
+
+{%- match obj.primary_constructor() %}
+{%- when Some with (cons) %}
+ def __init__(self, {% call py::arg_list_decl(cons) -%}):
+ {%- call py::setup_args_extra_indent(cons) %}
+ self._pointer = {% call py::to_ffi_call(cons) %}
+{%- when None %}
+{%- endmatch %}
+
+ def __del__(self):
+ # In case of partial initialization of instances.
+ pointer = getattr(self, "_pointer", None)
+ if pointer is not None:
+ _rust_call(_UniffiLib.{{ obj.ffi_object_free().name() }}, pointer)
+
+ # Used by alternative constructors or any methods which return this type.
+ @classmethod
+ def _make_instance_(cls, pointer):
+ # Lightly yucky way to bypass the usual __init__ logic
+ # and just create a new instance with the required pointer.
+ inst = cls.__new__(cls)
+ inst._pointer = pointer
+ return inst
+
+{%- for cons in obj.alternate_constructors() %}
+
+ @classmethod
+ def {{ cons.name()|fn_name }}(cls, {% call py::arg_list_decl(cons) %}):
+ {%- call py::setup_args_extra_indent(cons) %}
+ # Call the (fallible) function before creating any half-baked object instances.
+ pointer = {% call py::to_ffi_call(cons) %}
+ return cls._make_instance_(pointer)
+{% endfor %}
+
+{%- for meth in obj.methods() -%}
+ {%- call py::method_decl(meth.name()|fn_name, meth) %}
+{% endfor %}
+
+{%- for tm in obj.uniffi_traits() -%}
+{%- match tm %}
+{%- when UniffiTrait::Debug { fmt } %}
+ {%- call py::method_decl("__repr__", fmt) %}
+{%- when UniffiTrait::Display { fmt } %}
+ {%- call py::method_decl("__str__", fmt) %}
+{%- when UniffiTrait::Eq { eq, ne } %}
+ def __eq__(self, other: object) -> {{ eq.return_type().unwrap()|type_name }}:
+ if not isinstance(other, {{ type_name }}):
+ return NotImplemented
+
+ return {{ eq.return_type().unwrap()|lift_fn }}({% call py::to_ffi_call_with_prefix("self._pointer", eq) %})
+
+ def __ne__(self, other: object) -> {{ ne.return_type().unwrap()|type_name }}:
+ if not isinstance(other, {{ type_name }}):
+ return NotImplemented
+
+ return {{ ne.return_type().unwrap()|lift_fn }}({% call py::to_ffi_call_with_prefix("self._pointer", ne) %})
+{%- when UniffiTrait::Hash { hash } %}
+ {%- call py::method_decl("__hash__", hash) %}
+{% endmatch %}
+{% endfor %}
+
+
+class {{ ffi_converter_name }}:
+ @classmethod
+ def read(cls, buf):
+ ptr = buf.read_u64()
+ if ptr == 0:
+ raise InternalError("Raw pointer value was null")
+ return cls.lift(ptr)
+
+ @classmethod
+ def write(cls, value, buf):
+ if not isinstance(value, {{ type_name }}):
+ raise TypeError("Expected {{ type_name }} instance, {} found".format(type(value).__name__))
+ buf.write_u64(cls.lower(value))
+
+ @staticmethod
+ def lift(value):
+ return {{ type_name }}._make_instance_(value)
+
+ @staticmethod
+ def lower(value):
+ return value._pointer
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/OptionalTemplate.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/OptionalTemplate.py
new file mode 100644
index 0000000000..e406c51d49
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/OptionalTemplate.py
@@ -0,0 +1,21 @@
+{%- let inner_ffi_converter = inner_type|ffi_converter_name %}
+
+class {{ ffi_converter_name }}(_UniffiConverterRustBuffer):
+ @classmethod
+ def write(cls, value, buf):
+ if value is None:
+ buf.write_u8(0)
+ return
+
+ buf.write_u8(1)
+ {{ inner_ffi_converter }}.write(value, buf)
+
+ @classmethod
+ def read(cls, buf):
+ flag = buf.read_u8()
+ if flag == 0:
+ return None
+ elif flag == 1:
+ return {{ inner_ffi_converter }}.read(buf)
+ else:
+ raise InternalError("Unexpected flag byte for optional type")
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/PointerManager.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/PointerManager.py
new file mode 100644
index 0000000000..23aa28eab4
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/PointerManager.py
@@ -0,0 +1,68 @@
+class _UniffiPointerManagerCPython:
+ """
+ Manage giving out pointers to Python objects on CPython
+
+ This class is used to generate opaque pointers that reference Python objects to pass to Rust.
+ It assumes a CPython platform. See _UniffiPointerManagerGeneral for the alternative.
+ """
+
+ def new_pointer(self, obj):
+ """
+ Get a pointer for an object as a ctypes.c_size_t instance
+
+ Each call to new_pointer() must be balanced with exactly one call to release_pointer()
+
+ This returns a ctypes.c_size_t. This is always the same size as a pointer and can be
+ interchanged with pointers for FFI function arguments and return values.
+ """
+ # IncRef the object since we're going to pass a pointer to Rust
+ ctypes.pythonapi.Py_IncRef(ctypes.py_object(obj))
+ # id() is the object address on CPython
+ # (https://docs.python.org/3/library/functions.html#id)
+ return id(obj)
+
+ def release_pointer(self, address):
+ py_obj = ctypes.cast(address, ctypes.py_object)
+ obj = py_obj.value
+ ctypes.pythonapi.Py_DecRef(py_obj)
+ return obj
+
+ def lookup(self, address):
+ return ctypes.cast(address, ctypes.py_object).value
+
+class _UniffiPointerManagerGeneral:
+ """
+ Manage giving out pointers to Python objects on non-CPython platforms
+
+ This has the same API as _UniffiPointerManagerCPython, but doesn't assume we're running on
+ CPython and is slightly slower.
+
+ Instead of using real pointers, it maps integer values to objects and returns the keys as
+ c_size_t values.
+ """
+
+ def __init__(self):
+ self._map = {}
+ self._lock = threading.Lock()
+ self._current_handle = 0
+
+ def new_pointer(self, obj):
+ with self._lock:
+ handle = self._current_handle
+ self._current_handle += 1
+ self._map[handle] = obj
+ return handle
+
+ def release_pointer(self, handle):
+ with self._lock:
+ return self._map.pop(handle)
+
+ def lookup(self, handle):
+ with self._lock:
+ return self._map[handle]
+
+# Pick an pointer manager implementation based on the platform
+if platform.python_implementation() == 'CPython':
+ _UniffiPointerManager = _UniffiPointerManagerCPython # type: ignore
+else:
+ _UniffiPointerManager = _UniffiPointerManagerGeneral # type: ignore
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py
new file mode 100644
index 0000000000..99a30e120f
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py
@@ -0,0 +1,49 @@
+{%- let rec = ci|get_record_definition(name) %}
+class {{ type_name }}:
+ {% for field in rec.fields() %}
+ {{- field.name()|var_name }}: "{{- field|type_name }}";
+ {%- endfor %}
+
+ @typing.no_type_check
+ def __init__(self, {% for field in rec.fields() %}
+ {{- field.name()|var_name }}: "{{- field|type_name }}"
+ {%- if field.default_value().is_some() %} = _DEFAULT{% endif %}
+ {%- if !loop.last %}, {% endif %}
+ {%- endfor %}):
+ {%- for field in rec.fields() %}
+ {%- let field_name = field.name()|var_name %}
+ {%- match field.default_value() %}
+ {%- when None %}
+ self.{{ field_name }} = {{ field_name }}
+ {%- when Some with(literal) %}
+ if {{ field_name }} is _DEFAULT:
+ self.{{ field_name }} = {{ literal|literal_py(field) }}
+ else:
+ self.{{ field_name }} = {{ field_name }}
+ {%- endmatch %}
+ {%- endfor %}
+
+ def __str__(self):
+ return "{{ type_name }}({% for field in rec.fields() %}{{ field.name()|var_name }}={}{% if loop.last %}{% else %}, {% endif %}{% endfor %})".format({% for field in rec.fields() %}self.{{ field.name()|var_name }}{% if loop.last %}{% else %}, {% endif %}{% endfor %})
+
+ def __eq__(self, other):
+ {%- for field in rec.fields() %}
+ if self.{{ field.name()|var_name }} != other.{{ field.name()|var_name }}:
+ return False
+ {%- endfor %}
+ return True
+
+class {{ ffi_converter_name }}(_UniffiConverterRustBuffer):
+ @staticmethod
+ def read(buf):
+ return {{ type_name }}(
+ {%- for field in rec.fields() %}
+ {{ field.name()|var_name }}={{ field|read_fn }}(buf),
+ {%- endfor %}
+ )
+
+ @staticmethod
+ def write(value, buf):
+ {%- for field in rec.fields() %}
+ {{ field|write_fn }}(value.{{ field.name()|var_name }}, buf)
+ {%- endfor %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/RustBufferHelper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/RustBufferHelper.py
new file mode 100644
index 0000000000..daabd5b4b9
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/RustBufferHelper.py
@@ -0,0 +1,59 @@
+# Types conforming to `_UniffiConverterPrimitive` pass themselves directly over the FFI.
+class _UniffiConverterPrimitive:
+ @classmethod
+ def check(cls, value):
+ return value
+
+ @classmethod
+ def lift(cls, value):
+ return value
+
+ @classmethod
+ def lower(cls, value):
+ return cls.lowerUnchecked(cls.check(value))
+
+ @classmethod
+ def lowerUnchecked(cls, value):
+ return value
+
+ @classmethod
+ def write(cls, value, buf):
+ cls.write_unchecked(cls.check(value), buf)
+
+class _UniffiConverterPrimitiveInt(_UniffiConverterPrimitive):
+ @classmethod
+ def check(cls, value):
+ try:
+ value = value.__index__()
+ except Exception:
+ raise TypeError("'{}' object cannot be interpreted as an integer".format(type(value).__name__))
+ if not isinstance(value, int):
+ raise TypeError("__index__ returned non-int (type {})".format(type(value).__name__))
+ if not cls.VALUE_MIN <= value < cls.VALUE_MAX:
+ raise ValueError("{} requires {} <= value < {}".format(cls.CLASS_NAME, cls.VALUE_MIN, cls.VALUE_MAX))
+ return super().check(value)
+
+class _UniffiConverterPrimitiveFloat(_UniffiConverterPrimitive):
+ @classmethod
+ def check(cls, value):
+ try:
+ value = value.__float__()
+ except Exception:
+ raise TypeError("must be real number, not {}".format(type(value).__name__))
+ if not isinstance(value, float):
+ raise TypeError("__float__ returned non-float (type {})".format(type(value).__name__))
+ return super().check(value)
+
+# Helper class for wrapper types that will always go through a _UniffiRustBuffer.
+# Classes should inherit from this and implement the `read` and `write` static methods.
+class _UniffiConverterRustBuffer:
+ @classmethod
+ def lift(cls, rbuf):
+ with rbuf.consume_with_stream() as stream:
+ return cls.read(stream)
+
+ @classmethod
+ def lower(cls, value):
+ with _UniffiRustBuffer.alloc_with_builder() as builder:
+ cls.write(value, builder)
+ return builder.finalize()
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/RustBufferTemplate.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/RustBufferTemplate.py
new file mode 100644
index 0000000000..c317a632fc
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/RustBufferTemplate.py
@@ -0,0 +1,211 @@
+
+class _UniffiRustBuffer(ctypes.Structure):
+ _fields_ = [
+ ("capacity", ctypes.c_int32),
+ ("len", ctypes.c_int32),
+ ("data", ctypes.POINTER(ctypes.c_char)),
+ ]
+
+ @staticmethod
+ def alloc(size):
+ return _rust_call(_UniffiLib.{{ ci.ffi_rustbuffer_alloc().name() }}, size)
+
+ @staticmethod
+ def reserve(rbuf, additional):
+ return _rust_call(_UniffiLib.{{ ci.ffi_rustbuffer_reserve().name() }}, rbuf, additional)
+
+ def free(self):
+ return _rust_call(_UniffiLib.{{ ci.ffi_rustbuffer_free().name() }}, self)
+
+ def __str__(self):
+ return "_UniffiRustBuffer(capacity={}, len={}, data={})".format(
+ self.capacity,
+ self.len,
+ self.data[0:self.len]
+ )
+
+ @contextlib.contextmanager
+ def alloc_with_builder(*args):
+ """Context-manger to allocate a buffer using a _UniffiRustBufferBuilder.
+
+ The allocated buffer will be automatically freed if an error occurs, ensuring that
+ we don't accidentally leak it.
+ """
+ builder = _UniffiRustBufferBuilder()
+ try:
+ yield builder
+ except:
+ builder.discard()
+ raise
+
+ @contextlib.contextmanager
+ def consume_with_stream(self):
+ """Context-manager to consume a buffer using a _UniffiRustBufferStream.
+
+ The _UniffiRustBuffer will be freed once the context-manager exits, ensuring that we don't
+ leak it even if an error occurs.
+ """
+ try:
+ s = _UniffiRustBufferStream.from_rust_buffer(self)
+ yield s
+ if s.remaining() != 0:
+ raise RuntimeError("junk data left in buffer at end of consume_with_stream")
+ finally:
+ self.free()
+
+ @contextlib.contextmanager
+ def read_with_stream(self):
+ """Context-manager to read a buffer using a _UniffiRustBufferStream.
+
+ This is like consume_with_stream, but doesn't free the buffer afterwards.
+ It should only be used with borrowed `_UniffiRustBuffer` data.
+ """
+ s = _UniffiRustBufferStream.from_rust_buffer(self)
+ yield s
+ if s.remaining() != 0:
+ raise RuntimeError("junk data left in buffer at end of read_with_stream")
+
+class _UniffiForeignBytes(ctypes.Structure):
+ _fields_ = [
+ ("len", ctypes.c_int32),
+ ("data", ctypes.POINTER(ctypes.c_char)),
+ ]
+
+ def __str__(self):
+ return "_UniffiForeignBytes(len={}, data={})".format(self.len, self.data[0:self.len])
+
+
+class _UniffiRustBufferStream:
+ """
+ Helper for structured reading of bytes from a _UniffiRustBuffer
+ """
+
+ def __init__(self, data, len):
+ self.data = data
+ self.len = len
+ self.offset = 0
+
+ @classmethod
+ def from_rust_buffer(cls, buf):
+ return cls(buf.data, buf.len)
+
+ def remaining(self):
+ return self.len - self.offset
+
+ def _unpack_from(self, size, format):
+ if self.offset + size > self.len:
+ raise InternalError("read past end of rust buffer")
+ value = struct.unpack(format, self.data[self.offset:self.offset+size])[0]
+ self.offset += size
+ return value
+
+ def read(self, size):
+ if self.offset + size > self.len:
+ raise InternalError("read past end of rust buffer")
+ data = self.data[self.offset:self.offset+size]
+ self.offset += size
+ return data
+
+ def read_i8(self):
+ return self._unpack_from(1, ">b")
+
+ def read_u8(self):
+ return self._unpack_from(1, ">B")
+
+ def read_i16(self):
+ return self._unpack_from(2, ">h")
+
+ def read_u16(self):
+ return self._unpack_from(2, ">H")
+
+ def read_i32(self):
+ return self._unpack_from(4, ">i")
+
+ def read_u32(self):
+ return self._unpack_from(4, ">I")
+
+ def read_i64(self):
+ return self._unpack_from(8, ">q")
+
+ def read_u64(self):
+ return self._unpack_from(8, ">Q")
+
+ def read_float(self):
+ v = self._unpack_from(4, ">f")
+ return v
+
+ def read_double(self):
+ return self._unpack_from(8, ">d")
+
+ def read_c_size_t(self):
+ return self._unpack_from(ctypes.sizeof(ctypes.c_size_t) , "@N")
+
+class _UniffiRustBufferBuilder:
+ """
+ Helper for structured writing of bytes into a _UniffiRustBuffer.
+ """
+
+ def __init__(self):
+ self.rbuf = _UniffiRustBuffer.alloc(16)
+ self.rbuf.len = 0
+
+ def finalize(self):
+ rbuf = self.rbuf
+ self.rbuf = None
+ return rbuf
+
+ def discard(self):
+ if self.rbuf is not None:
+ rbuf = self.finalize()
+ rbuf.free()
+
+ @contextlib.contextmanager
+ def _reserve(self, num_bytes):
+ if self.rbuf.len + num_bytes > self.rbuf.capacity:
+ self.rbuf = _UniffiRustBuffer.reserve(self.rbuf, num_bytes)
+ yield None
+ self.rbuf.len += num_bytes
+
+ def _pack_into(self, size, format, value):
+ with self._reserve(size):
+ # XXX TODO: I feel like I should be able to use `struct.pack_into` here but can't figure it out.
+ for i, byte in enumerate(struct.pack(format, value)):
+ self.rbuf.data[self.rbuf.len + i] = byte
+
+ def write(self, value):
+ with self._reserve(len(value)):
+ for i, byte in enumerate(value):
+ self.rbuf.data[self.rbuf.len + i] = byte
+
+ def write_i8(self, v):
+ self._pack_into(1, ">b", v)
+
+ def write_u8(self, v):
+ self._pack_into(1, ">B", v)
+
+ def write_i16(self, v):
+ self._pack_into(2, ">h", v)
+
+ def write_u16(self, v):
+ self._pack_into(2, ">H", v)
+
+ def write_i32(self, v):
+ self._pack_into(4, ">i", v)
+
+ def write_u32(self, v):
+ self._pack_into(4, ">I", v)
+
+ def write_i64(self, v):
+ self._pack_into(8, ">q", v)
+
+ def write_u64(self, v):
+ self._pack_into(8, ">Q", v)
+
+ def write_float(self, v):
+ self._pack_into(4, ">f", v)
+
+ def write_double(self, v):
+ self._pack_into(8, ">d", v)
+
+ def write_c_size_t(self, v):
+ self._pack_into(ctypes.sizeof(ctypes.c_size_t) , "@N", v)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/SequenceTemplate.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/SequenceTemplate.py
new file mode 100644
index 0000000000..3c9f5a4596
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/SequenceTemplate.py
@@ -0,0 +1,19 @@
+{%- let inner_ffi_converter = inner_type|ffi_converter_name %}
+
+class {{ ffi_converter_name}}(_UniffiConverterRustBuffer):
+ @classmethod
+ def write(cls, value, buf):
+ items = len(value)
+ buf.write_i32(items)
+ for item in value:
+ {{ inner_ffi_converter }}.write(item, buf)
+
+ @classmethod
+ def read(cls, buf):
+ count = buf.read_i32()
+ if count < 0:
+ raise InternalError("Unexpected negative sequence length")
+
+ return [
+ {{ inner_ffi_converter }}.read(buf) for i in range(count)
+ ]
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/StringHelper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/StringHelper.py
new file mode 100644
index 0000000000..40890b6abc
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/StringHelper.py
@@ -0,0 +1,33 @@
+class _UniffiConverterString:
+ @staticmethod
+ def check(value):
+ if not isinstance(value, str):
+ raise TypeError("argument must be str, not {}".format(type(value).__name__))
+ return value
+
+ @staticmethod
+ def read(buf):
+ size = buf.read_i32()
+ if size < 0:
+ raise InternalError("Unexpected negative string length")
+ utf8_bytes = buf.read(size)
+ return utf8_bytes.decode("utf-8")
+
+ @staticmethod
+ def write(value, buf):
+ value = _UniffiConverterString.check(value)
+ utf8_bytes = value.encode("utf-8")
+ buf.write_i32(len(utf8_bytes))
+ buf.write(utf8_bytes)
+
+ @staticmethod
+ def lift(buf):
+ with buf.consume_with_stream() as stream:
+ return stream.read(stream.remaining()).decode("utf-8")
+
+ @staticmethod
+ def lower(value):
+ value = _UniffiConverterString.check(value)
+ with _UniffiRustBuffer.alloc_with_builder() as builder:
+ builder.write(value.encode("utf-8"))
+ return builder.finalize()
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/TimestampHelper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/TimestampHelper.py
new file mode 100644
index 0000000000..8402f6095d
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/TimestampHelper.py
@@ -0,0 +1,32 @@
+# The Timestamp type.
+Timestamp = datetime.datetime
+
+# There is a loss of precision when converting from Rust timestamps,
+# which are accurate to the nanosecond,
+# to Python datetimes, which have a variable precision due to the use of float as representation.
+class _UniffiConverterTimestamp(_UniffiConverterRustBuffer):
+ @staticmethod
+ def read(buf):
+ seconds = buf.read_i64()
+ microseconds = buf.read_u32() / 1000
+ # Use fromtimestamp(0) then add the seconds using a timedelta. This
+ # ensures that we get OverflowError rather than ValueError when
+ # seconds is too large.
+ if seconds >= 0:
+ return datetime.datetime.fromtimestamp(0, tz=datetime.timezone.utc) + datetime.timedelta(seconds=seconds, microseconds=microseconds)
+ else:
+ return datetime.datetime.fromtimestamp(0, tz=datetime.timezone.utc) - datetime.timedelta(seconds=-seconds, microseconds=microseconds)
+
+ @staticmethod
+ def write(value, buf):
+ if value >= datetime.datetime.fromtimestamp(0, datetime.timezone.utc):
+ sign = 1
+ delta = value - datetime.datetime.fromtimestamp(0, datetime.timezone.utc)
+ else:
+ sign = -1
+ delta = datetime.datetime.fromtimestamp(0, datetime.timezone.utc) - value
+
+ seconds = delta.seconds + delta.days * 24 * 3600
+ nanoseconds = delta.microseconds * 1000
+ buf.write_i64(sign * seconds)
+ buf.write_u32(nanoseconds)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/TopLevelFunctionTemplate.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/TopLevelFunctionTemplate.py
new file mode 100644
index 0000000000..f258b60a1c
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/TopLevelFunctionTemplate.py
@@ -0,0 +1,38 @@
+{%- if func.is_async() %}
+
+def {{ func.name()|fn_name }}({%- call py::arg_list_decl(func) -%}):
+ return _uniffi_rust_call_async(
+ _UniffiLib.{{ func.ffi_func().name() }}({% call py::arg_list_lowered(func) %}),
+ _UniffiLib.{{func.ffi_rust_future_poll(ci) }},
+ _UniffiLib.{{func.ffi_rust_future_complete(ci) }},
+ _UniffiLib.{{func.ffi_rust_future_free(ci) }},
+ # lift function
+ {%- match func.return_type() %}
+ {%- when Some(return_type) %}
+ {{ return_type|lift_fn }},
+ {%- when None %}
+ lambda val: None,
+ {% endmatch %}
+ # Error FFI converter
+ {%- match func.throws_type() %}
+ {%- when Some(e) %}
+ {{ e|ffi_converter_name }},
+ {%- when None %}
+ None,
+ {%- endmatch %}
+ )
+
+{%- else %}
+{%- match func.return_type() -%}
+{%- when Some with (return_type) %}
+
+def {{ func.name()|fn_name }}({%- call py::arg_list_decl(func) -%}) -> "{{ return_type|type_name }}":
+ {%- call py::setup_args(func) %}
+ return {{ return_type|lift_fn }}({% call py::to_ffi_call(func) %})
+{% when None %}
+
+def {{ func.name()|fn_name }}({%- call py::arg_list_decl(func) -%}):
+ {%- call py::setup_args(func) %}
+ {% call py::to_ffi_call(func) %}
+{% endmatch %}
+{%- endif %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Types.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Types.py
new file mode 100644
index 0000000000..5e05314c37
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Types.py
@@ -0,0 +1,102 @@
+{%- import "macros.py" as py %}
+
+{%- for type_ in ci.iter_types() %}
+{%- let type_name = type_|type_name %}
+{%- let ffi_converter_name = type_|ffi_converter_name %}
+{%- let canonical_type_name = type_|canonical_name %}
+
+{#
+ # Map `Type` instances to an include statement for that type.
+ #
+ # There is a companion match in `PythonCodeOracle::create_code_type()` which performs a similar function for the
+ # Rust code.
+ #
+ # - When adding additional types here, make sure to also add a match arm to that function.
+ # - To keep things manageable, let's try to limit ourselves to these 2 mega-matches
+ #}
+{%- match type_ %}
+
+{%- when Type::Boolean %}
+{%- include "BooleanHelper.py" %}
+
+{%- when Type::Int8 %}
+{%- include "Int8Helper.py" %}
+
+{%- when Type::Int16 %}
+{%- include "Int16Helper.py" %}
+
+{%- when Type::Int32 %}
+{%- include "Int32Helper.py" %}
+
+{%- when Type::Int64 %}
+{%- include "Int64Helper.py" %}
+
+{%- when Type::UInt8 %}
+{%- include "UInt8Helper.py" %}
+
+{%- when Type::UInt16 %}
+{%- include "UInt16Helper.py" %}
+
+{%- when Type::UInt32 %}
+{%- include "UInt32Helper.py" %}
+
+{%- when Type::UInt64 %}
+{%- include "UInt64Helper.py" %}
+
+{%- when Type::Float32 %}
+{%- include "Float32Helper.py" %}
+
+{%- when Type::Float64 %}
+{%- include "Float64Helper.py" %}
+
+{%- when Type::String %}
+{%- include "StringHelper.py" %}
+
+{%- when Type::Bytes %}
+{%- include "BytesHelper.py" %}
+
+{%- when Type::Enum { name, module_path } %}
+{%- let e = ci.get_enum_definition(name).unwrap() %}
+{# For enums, there are either an error *or* an enum, they can't be both. #}
+{%- if ci.is_name_used_as_error(name) %}
+{%- include "ErrorTemplate.py" %}
+{%- else %}
+{%- include "EnumTemplate.py" %}
+{% endif %}
+
+{%- when Type::Record { name, module_path } %}
+{%- include "RecordTemplate.py" %}
+
+{%- when Type::Object { name, module_path, imp } %}
+{%- include "ObjectTemplate.py" %}
+
+{%- when Type::Timestamp %}
+{%- include "TimestampHelper.py" %}
+
+{%- when Type::Duration %}
+{%- include "DurationHelper.py" %}
+
+{%- when Type::Optional { inner_type } %}
+{%- include "OptionalTemplate.py" %}
+
+{%- when Type::Sequence { inner_type } %}
+{%- include "SequenceTemplate.py" %}
+
+{%- when Type::Map { key_type, value_type } %}
+{%- include "MapTemplate.py" %}
+
+{%- when Type::CallbackInterface { name: id, module_path } %}
+{%- include "CallbackInterfaceTemplate.py" %}
+
+{%- when Type::Custom { name, module_path, builtin } %}
+{%- include "CustomType.py" %}
+
+{%- when Type::External { name, module_path, namespace, kind, tagged } %}
+{%- include "ExternalTemplate.py" %}
+
+{%- when Type::ForeignExecutor %}
+{%- include "ForeignExecutorTemplate.py" %}
+
+{%- else %}
+{%- endmatch %}
+{%- endfor %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt16Helper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt16Helper.py
new file mode 100644
index 0000000000..081c6731ce
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt16Helper.py
@@ -0,0 +1,12 @@
+class _UniffiConverterUInt16(_UniffiConverterPrimitiveInt):
+ CLASS_NAME = "u16"
+ VALUE_MIN = 0
+ VALUE_MAX = 2**16
+
+ @staticmethod
+ def read(buf):
+ return buf.read_u16()
+
+ @staticmethod
+ def write_unchecked(value, buf):
+ buf.write_u16(value)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt32Helper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt32Helper.py
new file mode 100644
index 0000000000..b80e75177d
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt32Helper.py
@@ -0,0 +1,12 @@
+class _UniffiConverterUInt32(_UniffiConverterPrimitiveInt):
+ CLASS_NAME = "u32"
+ VALUE_MIN = 0
+ VALUE_MAX = 2**32
+
+ @staticmethod
+ def read(buf):
+ return buf.read_u32()
+
+ @staticmethod
+ def write_unchecked(value, buf):
+ buf.write_u32(value)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt64Helper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt64Helper.py
new file mode 100644
index 0000000000..4b87e58547
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt64Helper.py
@@ -0,0 +1,12 @@
+class _UniffiConverterUInt64(_UniffiConverterPrimitiveInt):
+ CLASS_NAME = "u64"
+ VALUE_MIN = 0
+ VALUE_MAX = 2**64
+
+ @staticmethod
+ def read(buf):
+ return buf.read_u64()
+
+ @staticmethod
+ def write_unchecked(value, buf):
+ buf.write_u64(value)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt8Helper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt8Helper.py
new file mode 100644
index 0000000000..33026706f2
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt8Helper.py
@@ -0,0 +1,12 @@
+class _UniffiConverterUInt8(_UniffiConverterPrimitiveInt):
+ CLASS_NAME = "u8"
+ VALUE_MIN = 0
+ VALUE_MAX = 2**8
+
+ @staticmethod
+ def read(buf):
+ return buf.read_u8()
+
+ @staticmethod
+ def write_unchecked(value, buf):
+ buf.write_u8(value)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/macros.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/macros.py
new file mode 100644
index 0000000000..ef3b1bb94d
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/macros.py
@@ -0,0 +1,147 @@
+{#
+// Template to call into rust. Used in several places.
+// Variable names in `arg_list_decl` should match up with arg lists
+// passed to rust via `arg_list_lowered`
+#}
+
+{%- macro to_ffi_call(func) -%}
+ {%- match func.throws_type() -%}
+ {%- when Some with (e) -%}
+_rust_call_with_error({{ e|ffi_converter_name }},
+ {%- else -%}
+_rust_call(
+ {%- endmatch -%}
+ _UniffiLib.{{ func.ffi_func().name() }},
+ {%- call arg_list_lowered(func) -%}
+)
+{%- endmacro -%}
+
+{%- macro to_ffi_call_with_prefix(prefix, func) -%}
+ {%- match func.throws_type() -%}
+ {%- when Some with (e) -%}
+_rust_call_with_error(
+ {{ e|ffi_converter_name }},
+ {%- else -%}
+_rust_call(
+ {%- endmatch -%}
+ _UniffiLib.{{ func.ffi_func().name() }},
+ {{- prefix }},
+ {%- call arg_list_lowered(func) -%}
+)
+{%- endmacro -%}
+
+{%- macro arg_list_lowered(func) %}
+ {%- for arg in func.arguments() %}
+ {{ arg|lower_fn }}({{ arg.name()|var_name }})
+ {%- if !loop.last %},{% endif %}
+ {%- endfor %}
+{%- endmacro -%}
+
+{#-
+// Arglist as used in Python declarations of methods, functions and constructors.
+// Note the var_name and type_name filters.
+-#}
+
+{% macro arg_list_decl(func) %}
+ {%- for arg in func.arguments() -%}
+ {{ arg.name()|var_name }}
+ {%- match arg.default_value() %}
+ {%- when Some with(literal) %}: "typing.Union[object, {{ arg|type_name -}}]" = _DEFAULT
+ {%- else %}: "{{ arg|type_name -}}"
+ {%- endmatch %}
+ {%- if !loop.last %},{% endif -%}
+ {%- endfor %}
+{%- endmacro %}
+
+{#-
+// Arglist as used in the _UniffiLib function declarations.
+// Note unfiltered name but ffi_type_name filters.
+-#}
+{%- macro arg_list_ffi_decl(func) %}
+ {%- for arg in func.arguments() %}
+ {{ arg.type_().borrow()|ffi_type_name }},
+ {%- endfor %}
+ {%- if func.has_rust_call_status_arg() %}
+ ctypes.POINTER(_UniffiRustCallStatus),{% endif %}
+{% endmacro -%}
+
+{#
+ # Setup function arguments by initializing default values.
+ #}
+{%- macro setup_args(func) %}
+ {%- for arg in func.arguments() %}
+ {%- match arg.default_value() %}
+ {%- when None %}
+ {%- when Some with(literal) %}
+ if {{ arg.name()|var_name }} is _DEFAULT:
+ {{ arg.name()|var_name }} = {{ literal|literal_py(arg.as_type().borrow()) }}
+ {%- endmatch %}
+ {% endfor -%}
+{%- endmacro -%}
+
+{#
+ # Exactly the same thing as `setup_args()` but with an extra 4 spaces of
+ # indent so that it works with object methods.
+ #}
+{%- macro setup_args_extra_indent(func) %}
+ {%- for arg in func.arguments() %}
+ {%- match arg.default_value() %}
+ {%- when None %}
+ {%- when Some with(literal) %}
+ if {{ arg.name()|var_name }} is _DEFAULT:
+ {{ arg.name()|var_name }} = {{ literal|literal_py(arg.as_type().borrow()) }}
+ {%- endmatch %}
+ {% endfor -%}
+{%- endmacro -%}
+
+{#
+ # Macro to call methods
+ #}
+{%- macro method_decl(py_method_name, meth) %}
+{% if meth.is_async() %}
+
+ def {{ py_method_name }}(self, {% call arg_list_decl(meth) %}):
+ {%- call setup_args_extra_indent(meth) %}
+ return _uniffi_rust_call_async(
+ _UniffiLib.{{ meth.ffi_func().name() }}(
+ self._pointer, {% call arg_list_lowered(meth) %}
+ ),
+ _UniffiLib.{{ meth.ffi_rust_future_poll(ci) }},
+ _UniffiLib.{{ meth.ffi_rust_future_complete(ci) }},
+ _UniffiLib.{{ meth.ffi_rust_future_free(ci) }},
+ # lift function
+ {%- match meth.return_type() %}
+ {%- when Some(return_type) %}
+ {{ return_type|lift_fn }},
+ {%- when None %}
+ lambda val: None,
+ {% endmatch %}
+ # Error FFI converter
+ {%- match meth.throws_type() %}
+ {%- when Some(e) %}
+ {{ e|ffi_converter_name }},
+ {%- when None %}
+ None,
+ {%- endmatch %}
+ )
+
+{%- else -%}
+{%- match meth.return_type() %}
+
+{%- when Some with (return_type) %}
+
+ def {{ py_method_name }}(self, {% call arg_list_decl(meth) %}) -> "{{ return_type|type_name }}":
+ {%- call setup_args_extra_indent(meth) %}
+ return {{ return_type|lift_fn }}(
+ {% call to_ffi_call_with_prefix("self._pointer", meth) %}
+ )
+
+{%- when None %}
+
+ def {{ py_method_name }}(self, {% call arg_list_decl(meth) %}):
+ {%- call setup_args_extra_indent(meth) %}
+ {% call to_ffi_call_with_prefix("self._pointer", meth) %}
+{% endmatch %}
+{% endif %}
+
+{% endmacro %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/wrapper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/wrapper.py
new file mode 100644
index 0000000000..24c3290ff7
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/wrapper.py
@@ -0,0 +1,74 @@
+# This file was autogenerated by some hot garbage in the `uniffi` crate.
+# Trust me, you don't want to mess with it!
+
+# Common helper code.
+#
+# Ideally this would live in a separate .py file where it can be unittested etc
+# in isolation, and perhaps even published as a re-useable package.
+#
+# However, it's important that the details of how this helper code works (e.g. the
+# way that different builtin types are passed across the FFI) exactly match what's
+# expected by the rust code on the other side of the interface. In practice right
+# now that means coming from the exact some version of `uniffi` that was used to
+# compile the rust component. The easiest way to ensure this is to bundle the Python
+# helpers directly inline like we're doing here.
+
+import os
+import sys
+import ctypes
+import enum
+import struct
+import contextlib
+import datetime
+import typing
+{%- if ci.has_async_fns() %}
+import asyncio
+{%- endif %}
+import platform
+{%- for req in self.imports() %}
+{{ req.render() }}
+{%- endfor %}
+
+# Used for default argument values
+_DEFAULT = object()
+
+{% include "RustBufferTemplate.py" %}
+{% include "Helpers.py" %}
+{% include "PointerManager.py" %}
+{% include "RustBufferHelper.py" %}
+
+# Contains loading, initialization code, and the FFI Function declarations.
+{% include "NamespaceLibraryTemplate.py" %}
+
+# Async support
+{%- if ci.has_async_fns() %}
+{%- include "Async.py" %}
+{%- endif %}
+
+# Public interface members begin here.
+{{ type_helper_code }}
+
+{%- for func in ci.function_definitions() %}
+{%- include "TopLevelFunctionTemplate.py" %}
+{%- endfor %}
+
+__all__ = [
+ "InternalError",
+ {%- for e in ci.enum_definitions() %}
+ "{{ e|type_name }}",
+ {%- endfor %}
+ {%- for record in ci.record_definitions() %}
+ "{{ record|type_name }}",
+ {%- endfor %}
+ {%- for func in ci.function_definitions() %}
+ "{{ func.name()|fn_name }}",
+ {%- endfor %}
+ {%- for obj in ci.object_definitions() %}
+ "{{ obj|type_name }}",
+ {%- endfor %}
+ {%- for c in ci.callback_interface_definitions() %}
+ "{{ c.name()|class_name }}",
+ {%- endfor %}
+]
+
+{% import "macros.py" as py %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/test.rs b/third_party/rust/uniffi_bindgen/src/bindings/python/test.rs
new file mode 100644
index 0000000000..0fcf09996f
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/test.rs
@@ -0,0 +1,69 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use crate::{
+ bindings::{RunScriptOptions, TargetLanguage},
+ library_mode::generate_bindings,
+};
+use anyhow::{Context, Result};
+use camino::Utf8Path;
+use std::env;
+use std::ffi::OsString;
+use std::process::Command;
+use uniffi_testing::UniFFITestHelper;
+
+/// Run Python tests for a UniFFI test fixture
+pub fn run_test(tmp_dir: &str, fixture_name: &str, script_file: &str) -> Result<()> {
+ run_script(
+ tmp_dir,
+ fixture_name,
+ script_file,
+ vec![],
+ &RunScriptOptions::default(),
+ )
+}
+
+/// Run a Python script
+///
+/// This function will set things up so that the script can import the UniFFI bindings for a crate
+pub fn run_script(
+ tmp_dir: &str,
+ crate_name: &str,
+ script_file: &str,
+ args: Vec<String>,
+ _options: &RunScriptOptions,
+) -> Result<()> {
+ let script_path = Utf8Path::new(".").join(script_file).canonicalize_utf8()?;
+ let test_helper = UniFFITestHelper::new(crate_name)?;
+ let out_dir = test_helper.create_out_dir(tmp_dir, &script_path)?;
+ let cdylib_path = test_helper.copy_cdylib_to_out_dir(&out_dir)?;
+ generate_bindings(
+ &cdylib_path,
+ None,
+ &[TargetLanguage::Python],
+ &out_dir,
+ false,
+ )?;
+
+ let pythonpath = env::var_os("PYTHONPATH").unwrap_or_else(|| OsString::from(""));
+ let pythonpath = env::join_paths(
+ env::split_paths(&pythonpath).chain(vec![out_dir.to_path_buf().into_std_path_buf()]),
+ )?;
+
+ let mut command = Command::new("python3");
+ command
+ .current_dir(out_dir)
+ .env("PYTHONPATH", pythonpath)
+ .arg(script_path)
+ .args(args);
+ let status = command
+ .spawn()
+ .context("Failed to spawn `python3` when running script")?
+ .wait()
+ .context("Failed to wait for `python3` when running script")?;
+ if !status.success() {
+ anyhow::bail!("running `python3` failed");
+ }
+ Ok(())
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/ruby/gen_ruby/mod.rs b/third_party/rust/uniffi_bindgen/src/bindings/ruby/gen_ruby/mod.rs
new file mode 100644
index 0000000000..1f1bf8e299
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/ruby/gen_ruby/mod.rs
@@ -0,0 +1,375 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use anyhow::Result;
+use askama::Template;
+use heck::{ToShoutySnakeCase, ToSnakeCase, ToUpperCamelCase};
+use serde::{Deserialize, Serialize};
+use std::borrow::Borrow;
+use std::collections::HashMap;
+
+use crate::interface::*;
+use crate::BindingsConfig;
+
+const RESERVED_WORDS: &[&str] = &[
+ "alias", "and", "BEGIN", "begin", "break", "case", "class", "def", "defined?", "do", "else",
+ "elsif", "END", "end", "ensure", "false", "for", "if", "module", "next", "nil", "not", "or",
+ "redo", "rescue", "retry", "return", "self", "super", "then", "true", "undef", "unless",
+ "until", "when", "while", "yield", "__FILE__", "__LINE__",
+];
+
+fn is_reserved_word(word: &str) -> bool {
+ RESERVED_WORDS.contains(&word)
+}
+
+/// Get the canonical, unique-within-this-component name for a type.
+///
+/// When generating helper code for foreign language bindings, it's sometimes useful to be
+/// able to name a particular type in order to e.g. call a helper function that is specific
+/// to that type. We support this by defining a naming convention where each type gets a
+/// unique canonical name, constructed recursively from the names of its component types (if any).
+pub fn canonical_name(t: &Type) -> String {
+ match t {
+ // Builtin primitive types, with plain old names.
+ Type::Int8 => "i8".into(),
+ Type::UInt8 => "u8".into(),
+ Type::Int16 => "i16".into(),
+ Type::UInt16 => "u16".into(),
+ Type::Int32 => "i32".into(),
+ Type::UInt32 => "u32".into(),
+ Type::Int64 => "i64".into(),
+ Type::UInt64 => "u64".into(),
+ Type::Float32 => "f32".into(),
+ Type::Float64 => "f64".into(),
+ Type::String => "string".into(),
+ Type::Bytes => "bytes".into(),
+ Type::Boolean => "bool".into(),
+ // API defined types.
+ // Note that these all get unique names, and the parser ensures that the names do not
+ // conflict with a builtin type. We add a prefix to the name to guard against pathological
+ // cases like a record named `SequenceRecord` interfering with `sequence<Record>`.
+ // However, types that support importing all end up with the same prefix of "Type", so
+ // that the import handling code knows how to find the remote reference.
+ Type::Object { name, .. } => format!("Type{name}"),
+ Type::Enum { name, .. } => format!("Type{name}"),
+ Type::Record { name, .. } => format!("Type{name}"),
+ Type::CallbackInterface { name, .. } => format!("CallbackInterface{name}"),
+ Type::Timestamp => "Timestamp".into(),
+ Type::Duration => "Duration".into(),
+ Type::ForeignExecutor => "ForeignExecutor".into(),
+ // Recursive types.
+ // These add a prefix to the name of the underlying type.
+ // The component API definition cannot give names to recursive types, so as long as the
+ // prefixes we add here are all unique amongst themselves, then we have no chance of
+ // acccidentally generating name collisions.
+ Type::Optional { inner_type } => format!("Optional{}", canonical_name(inner_type)),
+ Type::Sequence { inner_type } => format!("Sequence{}", canonical_name(inner_type)),
+ Type::Map {
+ key_type,
+ value_type,
+ } => format!(
+ "Map{}{}",
+ canonical_name(key_type).to_upper_camel_case(),
+ canonical_name(value_type).to_upper_camel_case()
+ ),
+ // A type that exists externally.
+ Type::External { name, .. } | Type::Custom { name, .. } => format!("Type{name}"),
+ }
+}
+
+// Some config options for it the caller wants to customize the generated ruby.
+// Note that this can only be used to control details of the ruby *that do not affect the underlying component*,
+// since the details of the underlying component are entirely determined by the `ComponentInterface`.
+#[derive(Debug, Clone, Default, Serialize, Deserialize)]
+pub struct Config {
+ cdylib_name: Option<String>,
+ cdylib_path: Option<String>,
+}
+
+impl Config {
+ pub fn cdylib_name(&self) -> String {
+ self.cdylib_name
+ .clone()
+ .unwrap_or_else(|| "uniffi".to_string())
+ }
+
+ pub fn custom_cdylib_path(&self) -> bool {
+ self.cdylib_path.is_some()
+ }
+
+ pub fn cdylib_path(&self) -> String {
+ self.cdylib_path.clone().unwrap_or_default()
+ }
+}
+
+impl BindingsConfig for Config {
+ fn update_from_ci(&mut self, ci: &ComponentInterface) {
+ self.cdylib_name
+ .get_or_insert_with(|| format!("uniffi_{}", ci.namespace()));
+ }
+
+ fn update_from_cdylib_name(&mut self, cdylib_name: &str) {
+ self.cdylib_name
+ .get_or_insert_with(|| cdylib_name.to_string());
+ }
+
+ fn update_from_dependency_configs(&mut self, _config_map: HashMap<&str, &Self>) {}
+}
+
+#[derive(Template)]
+#[template(syntax = "rb", escape = "none", path = "wrapper.rb")]
+pub struct RubyWrapper<'a> {
+ config: Config,
+ ci: &'a ComponentInterface,
+ canonical_name: &'a dyn Fn(&Type) -> String,
+}
+impl<'a> RubyWrapper<'a> {
+ pub fn new(config: Config, ci: &'a ComponentInterface) -> Self {
+ Self {
+ config,
+ ci,
+ canonical_name: &canonical_name,
+ }
+ }
+}
+
+mod filters {
+ use super::*;
+ pub use crate::backend::filters::*;
+
+ pub fn type_ffi(type_: &FfiType) -> Result<String, askama::Error> {
+ Ok(match type_ {
+ FfiType::Int8 => ":int8".to_string(),
+ FfiType::UInt8 => ":uint8".to_string(),
+ FfiType::Int16 => ":int16".to_string(),
+ FfiType::UInt16 => ":uint16".to_string(),
+ FfiType::Int32 => ":int32".to_string(),
+ FfiType::UInt32 => ":uint32".to_string(),
+ FfiType::Int64 => ":int64".to_string(),
+ FfiType::UInt64 => ":uint64".to_string(),
+ FfiType::Float32 => ":float".to_string(),
+ FfiType::Float64 => ":double".to_string(),
+ FfiType::RustArcPtr(_) => ":pointer".to_string(),
+ FfiType::RustBuffer(_) => "RustBuffer.by_value".to_string(),
+ FfiType::ForeignBytes => "ForeignBytes".to_string(),
+ FfiType::ForeignCallback => unimplemented!("Callback interfaces are not implemented"),
+ FfiType::ForeignExecutorCallback => {
+ unimplemented!("Foreign executors are not implemented")
+ }
+ FfiType::ForeignExecutorHandle => {
+ unimplemented!("Foreign executors are not implemented")
+ }
+ FfiType::RustFutureHandle
+ | FfiType::RustFutureContinuationCallback
+ | FfiType::RustFutureContinuationData => {
+ unimplemented!("Async functions are not implemented")
+ }
+ })
+ }
+
+ pub fn literal_rb(literal: &Literal) -> Result<String, askama::Error> {
+ Ok(match literal {
+ Literal::Boolean(v) => {
+ if *v {
+ "true".into()
+ } else {
+ "false".into()
+ }
+ }
+ // use the double-quote form to match with the other languages, and quote escapes.
+ Literal::String(s) => format!("\"{s}\""),
+ Literal::Null => "nil".into(),
+ Literal::EmptySequence => "[]".into(),
+ Literal::EmptyMap => "{}".into(),
+ Literal::Enum(v, type_) => match type_ {
+ Type::Enum { name, .. } => {
+ format!("{}::{}", class_name_rb(name)?, enum_name_rb(v)?)
+ }
+ _ => panic!("Unexpected type in enum literal: {type_:?}"),
+ },
+ // https://docs.ruby-lang.org/en/2.0.0/syntax/literals_rdoc.html
+ Literal::Int(i, radix, _) => match radix {
+ Radix::Octal => format!("0o{i:o}"),
+ Radix::Decimal => format!("{i}"),
+ Radix::Hexadecimal => format!("{i:#x}"),
+ },
+ Literal::UInt(i, radix, _) => match radix {
+ Radix::Octal => format!("0o{i:o}"),
+ Radix::Decimal => format!("{i}"),
+ Radix::Hexadecimal => format!("{i:#x}"),
+ },
+ Literal::Float(string, _type_) => string.clone(),
+ })
+ }
+
+ pub fn class_name_rb(nm: &str) -> Result<String, askama::Error> {
+ Ok(nm.to_string().to_upper_camel_case())
+ }
+
+ pub fn fn_name_rb(nm: &str) -> Result<String, askama::Error> {
+ Ok(nm.to_string().to_snake_case())
+ }
+
+ pub fn var_name_rb(nm: &str) -> Result<String, askama::Error> {
+ let nm = nm.to_string();
+ let prefix = if is_reserved_word(&nm) { "_" } else { "" };
+
+ Ok(format!("{prefix}{}", nm.to_snake_case()))
+ }
+
+ pub fn enum_name_rb(nm: &str) -> Result<String, askama::Error> {
+ Ok(nm.to_string().to_shouty_snake_case())
+ }
+
+ pub fn coerce_rb(nm: &str, ns: &str, type_: &Type) -> Result<String, askama::Error> {
+ Ok(match type_ {
+ Type::Int8 => format!("{ns}::uniffi_in_range({nm}, \"i8\", -2**7, 2**7)"),
+ Type::Int16 => format!("{ns}::uniffi_in_range({nm}, \"i16\", -2**15, 2**15)"),
+ Type::Int32 => format!("{ns}::uniffi_in_range({nm}, \"i32\", -2**31, 2**31)"),
+ Type::Int64 => format!("{ns}::uniffi_in_range({nm}, \"i64\", -2**63, 2**63)"),
+ Type::UInt8 => format!("{ns}::uniffi_in_range({nm}, \"u8\", 0, 2**8)"),
+ Type::UInt16 => format!("{ns}::uniffi_in_range({nm}, \"u16\", 0, 2**16)"),
+ Type::UInt32 => format!("{ns}::uniffi_in_range({nm}, \"u32\", 0, 2**32)"),
+ Type::UInt64 => format!("{ns}::uniffi_in_range({nm}, \"u64\", 0, 2**64)"),
+ Type::Float32 | Type::Float64 => nm.to_string(),
+ Type::Boolean => format!("{nm} ? true : false"),
+ Type::Object { .. } | Type::Enum { .. } | Type::Record { .. } => nm.to_string(),
+ Type::String => format!("{ns}::uniffi_utf8({nm})"),
+ Type::Bytes => format!("{ns}::uniffi_bytes({nm})"),
+ Type::Timestamp | Type::Duration => nm.to_string(),
+ Type::CallbackInterface { .. } => {
+ panic!("No support for coercing callback interfaces yet")
+ }
+ Type::Optional { inner_type: t } => format!("({nm} ? {} : nil)", coerce_rb(nm, ns, t)?),
+ Type::Sequence { inner_type: t } => {
+ let coerce_code = coerce_rb("v", ns, t)?;
+ if coerce_code == "v" {
+ nm.to_string()
+ } else {
+ format!("{nm}.map {{ |v| {coerce_code} }}")
+ }
+ }
+ Type::Map { value_type: t, .. } => {
+ let k_coerce_code = coerce_rb("k", ns, &Type::String)?;
+ let v_coerce_code = coerce_rb("v", ns, t)?;
+
+ if k_coerce_code == "k" && v_coerce_code == "v" {
+ nm.to_string()
+ } else {
+ format!(
+ "{nm}.each.with_object({{}}) {{ |(k, v), res| res[{k_coerce_code}] = {v_coerce_code} }}"
+ )
+ }
+ }
+ Type::External { .. } => panic!("No support for external types, yet"),
+ Type::Custom { .. } => panic!("No support for custom types, yet"),
+ Type::ForeignExecutor => unimplemented!("Foreign executors are not implemented"),
+ })
+ }
+
+ pub fn lower_rb(nm: &str, type_: &Type) -> Result<String, askama::Error> {
+ Ok(match type_ {
+ Type::Int8
+ | Type::UInt8
+ | Type::Int16
+ | Type::UInt16
+ | Type::Int32
+ | Type::UInt32
+ | Type::Int64
+ | Type::UInt64
+ | Type::Float32
+ | Type::Float64 => nm.to_string(),
+ Type::Boolean => format!("({nm} ? 1 : 0)"),
+ Type::String => format!("RustBuffer.allocFromString({nm})"),
+ Type::Bytes => format!("RustBuffer.allocFromBytes({nm})"),
+ Type::Object { name, .. } => format!("({}._uniffi_lower {nm})", class_name_rb(name)?),
+ Type::CallbackInterface { .. } => {
+ panic!("No support for lowering callback interfaces yet")
+ }
+ Type::Enum { .. }
+ | Type::Record { .. }
+ | Type::Optional { .. }
+ | Type::Sequence { .. }
+ | Type::Timestamp
+ | Type::Duration
+ | Type::Map { .. } => format!(
+ "RustBuffer.alloc_from_{}({})",
+ class_name_rb(&canonical_name(type_))?,
+ nm
+ ),
+ Type::External { .. } => panic!("No support for lowering external types, yet"),
+ Type::Custom { .. } => panic!("No support for lowering custom types, yet"),
+ Type::ForeignExecutor => unimplemented!("Foreign executors are not implemented"),
+ })
+ }
+
+ pub fn lift_rb(nm: &str, type_: &Type) -> Result<String, askama::Error> {
+ Ok(match type_ {
+ Type::Int8
+ | Type::UInt8
+ | Type::Int16
+ | Type::UInt16
+ | Type::Int32
+ | Type::UInt32
+ | Type::Int64
+ | Type::UInt64 => format!("{nm}.to_i"),
+ Type::Float32 | Type::Float64 => format!("{nm}.to_f"),
+ Type::Boolean => format!("1 == {nm}"),
+ Type::String => format!("{nm}.consumeIntoString"),
+ Type::Bytes => format!("{nm}.consumeIntoBytes"),
+ Type::Object { name, .. } => format!("{}._uniffi_allocate({nm})", class_name_rb(name)?),
+ Type::CallbackInterface { .. } => {
+ panic!("No support for lifting callback interfaces, yet")
+ }
+ Type::Enum { .. } => {
+ format!(
+ "{}.consumeInto{}",
+ nm,
+ class_name_rb(&canonical_name(type_))?
+ )
+ }
+ Type::Record { .. }
+ | Type::Optional { .. }
+ | Type::Sequence { .. }
+ | Type::Timestamp
+ | Type::Duration
+ | Type::Map { .. } => format!(
+ "{}.consumeInto{}",
+ nm,
+ class_name_rb(&canonical_name(type_))?
+ ),
+ Type::External { .. } => panic!("No support for lifting external types, yet"),
+ Type::Custom { .. } => panic!("No support for lifting custom types, yet"),
+ Type::ForeignExecutor => unimplemented!("Foreign executors are not implemented"),
+ })
+ }
+}
+
+#[cfg(test)]
+mod test_type {
+ use super::*;
+
+ #[test]
+ fn test_canonical_names() {
+ // Non-exhaustive, but gives a bit of a flavour of what we want.
+ assert_eq!(canonical_name(&Type::UInt8), "u8");
+ assert_eq!(canonical_name(&Type::String), "string");
+ assert_eq!(canonical_name(&Type::Bytes), "bytes");
+ assert_eq!(
+ canonical_name(&Type::Optional {
+ inner_type: Box::new(Type::Sequence {
+ inner_type: Box::new(Type::Object {
+ module_path: "anything".to_string(),
+ name: "Example".into(),
+ imp: ObjectImpl::Struct,
+ })
+ })
+ }),
+ "OptionalSequenceTypeExample"
+ );
+ }
+}
+
+#[cfg(test)]
+mod tests;
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/ruby/gen_ruby/tests.rs b/third_party/rust/uniffi_bindgen/src/bindings/ruby/gen_ruby/tests.rs
new file mode 100644
index 0000000000..9ae5d1816f
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/ruby/gen_ruby/tests.rs
@@ -0,0 +1,47 @@
+use super::{is_reserved_word, Config};
+
+#[test]
+fn when_reserved_word() {
+ assert!(is_reserved_word("end"));
+}
+
+#[test]
+fn when_not_reserved_word() {
+ assert!(!is_reserved_word("ruby"));
+}
+
+#[test]
+fn cdylib_name() {
+ let config = Config {
+ cdylib_name: None,
+ cdylib_path: None,
+ };
+
+ assert_eq!("uniffi", config.cdylib_name());
+
+ let config = Config {
+ cdylib_name: Some("todolist".to_string()),
+ cdylib_path: None,
+ };
+
+ assert_eq!("todolist", config.cdylib_name());
+}
+
+#[test]
+fn cdylib_path() {
+ let config = Config {
+ cdylib_name: None,
+ cdylib_path: None,
+ };
+
+ assert_eq!("", config.cdylib_path());
+ assert!(!config.custom_cdylib_path());
+
+ let config = Config {
+ cdylib_name: None,
+ cdylib_path: Some("/foo/bar".to_string()),
+ };
+
+ assert_eq!("/foo/bar", config.cdylib_path());
+ assert!(config.custom_cdylib_path());
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/ruby/mod.rs b/third_party/rust/uniffi_bindgen/src/bindings/ruby/mod.rs
new file mode 100644
index 0000000000..6db89b9f0b
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/ruby/mod.rs
@@ -0,0 +1,48 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::process::Command;
+
+use anyhow::{Context, Result};
+use camino::Utf8Path;
+use fs_err as fs;
+
+pub mod gen_ruby;
+mod test;
+pub use gen_ruby::{Config, RubyWrapper};
+pub use test::{run_test, test_script_command};
+
+use super::super::interface::ComponentInterface;
+
+// Generate ruby bindings for the given ComponentInterface, in the given output directory.
+
+pub fn write_bindings(
+ config: &Config,
+ ci: &ComponentInterface,
+ out_dir: &Utf8Path,
+ try_format_code: bool,
+) -> Result<()> {
+ let rb_file = out_dir.join(format!("{}.rb", ci.namespace()));
+ fs::write(&rb_file, generate_ruby_bindings(config, ci)?)?;
+
+ if try_format_code {
+ if let Err(e) = Command::new("rubocop").arg("-A").arg(&rb_file).output() {
+ println!(
+ "Warning: Unable to auto-format {} using rubocop: {e:?}",
+ rb_file.file_name().unwrap(),
+ )
+ }
+ }
+
+ Ok(())
+}
+
+// Generate ruby bindings for the given ComponentInterface, as a string.
+
+pub fn generate_ruby_bindings(config: &Config, ci: &ComponentInterface) -> Result<String> {
+ use askama::Template;
+ RubyWrapper::new(config.clone(), ci)
+ .render()
+ .context("failed to render ruby bindings")
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/EnumTemplate.rb b/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/EnumTemplate.rb
new file mode 100644
index 0000000000..23b701f6a7
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/EnumTemplate.rb
@@ -0,0 +1,59 @@
+{% if e.is_flat() %}
+
+class {{ e.name()|class_name_rb }}
+ {% for variant in e.variants() -%}
+ {{ variant.name()|enum_name_rb }} = {{ loop.index }}
+ {% endfor %}
+end
+
+{% else %}
+
+class {{ e.name()|class_name_rb }}
+ def initialize
+ raise RuntimeError, '{{ e.name()|class_name_rb }} cannot be instantiated directly'
+ end
+
+ # Each enum variant is a nested class of the enum itself.
+ {% for variant in e.variants() -%}
+ class {{ variant.name()|enum_name_rb }}
+ {% if variant.has_fields() %}
+ attr_reader {% for field in variant.fields() %}:{{ field.name()|var_name_rb }}{% if loop.last %}{% else %}, {% endif %}{%- endfor %}
+ {% endif %}
+ def initialize({% for field in variant.fields() %}{{ field.name()|var_name_rb }}{% if loop.last %}{% else %}, {% endif %}{% endfor %})
+ {% if variant.has_fields() %}
+ {%- for field in variant.fields() %}
+ @{{ field.name()|var_name_rb }} = {{ field.name()|var_name_rb }}
+ {%- endfor %}
+ {% else %}
+ {% endif %}
+ end
+
+ def to_s
+ "{{ e.name()|class_name_rb }}::{{ variant.name()|enum_name_rb }}({% for field in variant.fields() %}{{ field.name() }}=#{@{{ field.name() }}}{% if loop.last %}{% else %}, {% endif %}{% endfor %})"
+ end
+
+ def ==(other)
+ if !other.{{ variant.name()|var_name_rb }}?
+ return false
+ end
+ {%- for field in variant.fields() %}
+ if @{{ field.name()|var_name_rb }} != other.{{ field.name()|var_name_rb }}
+ return false
+ end
+ {%- endfor %}
+
+ true
+ end
+
+ # For each variant, we have an `NAME?` method for easily checking
+ # whether an instance is that variant.
+ {% for variant in e.variants() %}
+ def {{ variant.name()|var_name_rb }}?
+ instance_of? {{ e.name()|class_name_rb }}::{{ variant.name()|enum_name_rb }}
+ end
+ {% endfor %}
+ end
+ {% endfor %}
+end
+
+{% endif %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/ErrorTemplate.rb b/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/ErrorTemplate.rb
new file mode 100644
index 0000000000..a7e26370c8
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/ErrorTemplate.rb
@@ -0,0 +1,121 @@
+class RustCallStatus < FFI::Struct
+ layout :code, :int8,
+ :error_buf, RustBuffer
+
+ def code
+ self[:code]
+ end
+
+ def error_buf
+ self[:error_buf]
+ end
+
+ def to_s
+ "RustCallStatus(code=#{self[:code]})"
+ end
+end
+
+# These match the values from the uniffi::rustcalls module
+CALL_SUCCESS = 0
+CALL_ERROR = 1
+CALL_PANIC = 2
+{%- for e in ci.enum_definitions() %}
+{% if ci.is_name_used_as_error(e.name()) %}
+{% if e.is_flat() %}
+class {{ e.name()|class_name_rb }}
+ {%- for variant in e.variants() %}
+ {{ variant.name()|class_name_rb }} = Class.new StandardError
+ {%- endfor %}
+{% else %}
+module {{ e.name()|class_name_rb }}
+ {%- for variant in e.variants() %}
+ class {{ variant.name()|class_name_rb }} < StandardError
+ def initialize({% for field in variant.fields() %}{{ field.name()|var_name_rb }}{% if !loop.last %}, {% endif %}{% endfor %})
+ {%- for field in variant.fields() %}
+ @{{ field.name()|var_name_rb }} = {{ field.name()|var_name_rb }}
+ {%- endfor %}
+ super()
+ end
+ {%- if variant.has_fields() %}
+
+ attr_reader {% for field in variant.fields() %}:{{ field.name()|var_name_rb }}{% if !loop.last %}, {% endif %}{% endfor %}
+ {% endif %}
+
+ def to_s
+ "#{self.class.name}({% for field in variant.fields() %}{{ field.name()|var_name_rb }}=#{@{{ field.name()|var_name_rb }}.inspect}{% if !loop.last %}, {% endif %}{% endfor %})"
+ end
+ end
+ {%- endfor %}
+{% endif %}
+end
+{% endif %}
+{%- endfor %}
+
+# Map error modules to the RustBuffer method name that reads them
+ERROR_MODULE_TO_READER_METHOD = {
+{%- for e in ci.enum_definitions() %}
+{% if ci.is_name_used_as_error(e.name()) %}
+{%- let typ=ci.get_type(e.name()).unwrap() %}
+{%- let canonical_type_name = canonical_name(typ.borrow()).borrow()|class_name_rb %}
+ {{ e.name()|class_name_rb }} => :read{{ canonical_type_name }},
+{% endif %}
+{%- endfor %}
+}
+
+private_constant :ERROR_MODULE_TO_READER_METHOD, :CALL_SUCCESS, :CALL_ERROR, :CALL_PANIC,
+ :RustCallStatus
+
+def self.consume_buffer_into_error(error_module, rust_buffer)
+ rust_buffer.consumeWithStream do |stream|
+ reader_method = ERROR_MODULE_TO_READER_METHOD[error_module]
+ return stream.send(reader_method)
+ end
+end
+
+class InternalError < StandardError
+end
+
+def self.rust_call(fn_name, *args)
+ # Call a rust function
+ rust_call_with_error(nil, fn_name, *args)
+end
+
+def self.rust_call_with_error(error_module, fn_name, *args)
+ # Call a rust function and handle errors
+ #
+ # Use this when the rust function returns a Result<>. error_module must be the error_module that corresponds to that Result.
+
+
+ # Note: RustCallStatus.new zeroes out the struct, which is exactly what we
+ # want to pass to Rust (code=0, error_buf=RustBuffer(len=0, capacity=0,
+ # data=NULL))
+ status = RustCallStatus.new
+ args << status
+
+ result = UniFFILib.public_send(fn_name, *args)
+
+ case status.code
+ when CALL_SUCCESS
+ result
+ when CALL_ERROR
+ if error_module.nil?
+ status.error_buf.free
+ raise InternalError, "CALL_ERROR with no error_module set"
+ else
+ raise consume_buffer_into_error(error_module, status.error_buf)
+ end
+ when CALL_PANIC
+ # When the rust code sees a panic, it tries to construct a RustBuffer
+ # with the message. But if that code panics, then it just sends back
+ # an empty buffer.
+ if status.error_buf.len > 0
+ raise InternalError, status.error_buf.consumeIntoString()
+ else
+ raise InternalError, "Rust panic"
+ end
+ else
+ raise InternalError, "Unknown call status: #{status.code}"
+ end
+end
+
+private_class_method :consume_buffer_into_error
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/Helpers.rb b/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/Helpers.rb
new file mode 100644
index 0000000000..49fa247a77
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/Helpers.rb
@@ -0,0 +1,18 @@
+def self.uniffi_in_range(i, type_name, min, max)
+ raise TypeError, "no implicit conversion of #{i} into Integer" unless i.respond_to?(:to_int)
+ i = i.to_int
+ raise RangeError, "#{type_name} requires #{min} <= value < #{max}" unless (min <= i && i < max)
+ i
+end
+
+def self.uniffi_utf8(v)
+ raise TypeError, "no implicit conversion of #{v} into String" unless v.respond_to?(:to_str)
+ v = v.to_str.encode(Encoding::UTF_8)
+ raise Encoding::InvalidByteSequenceError, "not a valid UTF-8 encoded string" unless v.valid_encoding?
+ v
+end
+
+def self.uniffi_bytes(v)
+ raise TypeError, "no implicit conversion of #{v} into String" unless v.respond_to?(:to_str)
+ v.to_str
+end
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/NamespaceLibraryTemplate.rb b/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/NamespaceLibraryTemplate.rb
new file mode 100644
index 0000000000..8536fc322e
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/NamespaceLibraryTemplate.rb
@@ -0,0 +1,17 @@
+# This is how we find and load the dynamic library provided by the component.
+# For now we just look it up by name.
+module UniFFILib
+ extend FFI::Library
+
+ {% if config.custom_cdylib_path() %}
+ ffi_lib {{ config.cdylib_path() }}
+ {% else %}
+ ffi_lib '{{ config.cdylib_name() }}'
+ {% endif %}
+
+ {% for func in ci.iter_ffi_function_definitions_non_async() -%}
+ attach_function :{{ func.name() }},
+ {%- call rb::arg_list_ffi_decl(func) %},
+ {% match func.return_type() %}{% when Some with (type_) %}{{ type_|type_ffi }}{% when None %}:void{% endmatch %}
+ {% endfor %}
+end
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/ObjectTemplate.rb b/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/ObjectTemplate.rb
new file mode 100644
index 0000000000..677c5c729b
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/ObjectTemplate.rb
@@ -0,0 +1,73 @@
+class {{ obj.name()|class_name_rb }}
+
+ # A private helper for initializing instances of the class from a raw pointer,
+ # bypassing any initialization logic and ensuring they are GC'd properly.
+ def self._uniffi_allocate(pointer)
+ pointer.autorelease = false
+ inst = allocate
+ inst.instance_variable_set :@pointer, pointer
+ ObjectSpace.define_finalizer(inst, _uniffi_define_finalizer_by_pointer(pointer, inst.object_id))
+ return inst
+ end
+
+ # A private helper for registering an object finalizer.
+ # N.B. it's important that this does not capture a reference
+ # to the actual instance, only its underlying pointer.
+ def self._uniffi_define_finalizer_by_pointer(pointer, object_id)
+ Proc.new do |_id|
+ {{ ci.namespace()|class_name_rb }}.rust_call(
+ :{{ obj.ffi_object_free().name() }},
+ pointer
+ )
+ end
+ end
+
+ # A private helper for lowering instances into a raw pointer.
+ # This does an explicit typecheck, because accidentally lowering a different type of
+ # object in a place where this type is expected, could lead to memory unsafety.
+ def self._uniffi_lower(inst)
+ if not inst.is_a? self
+ raise TypeError.new "Expected a {{ obj.name()|class_name_rb }} instance, got #{inst}"
+ end
+ return inst.instance_variable_get :@pointer
+ end
+
+ {%- match obj.primary_constructor() %}
+ {%- when Some with (cons) %}
+ def initialize({% call rb::arg_list_decl(cons) -%})
+ {%- call rb::coerce_args_extra_indent(cons) %}
+ pointer = {% call rb::to_ffi_call(cons) %}
+ @pointer = pointer
+ ObjectSpace.define_finalizer(self, self.class._uniffi_define_finalizer_by_pointer(pointer, self.object_id))
+ end
+ {%- when None %}
+ {%- endmatch %}
+
+ {% for cons in obj.alternate_constructors() -%}
+ def self.{{ cons.name()|fn_name_rb }}({% call rb::arg_list_decl(cons) %})
+ {%- call rb::coerce_args_extra_indent(cons) %}
+ # Call the (fallible) function before creating any half-baked object instances.
+ # Lightly yucky way to bypass the usual "initialize" logic
+ # and just create a new instance with the required pointer.
+ return _uniffi_allocate({% call rb::to_ffi_call(cons) %})
+ end
+ {% endfor %}
+
+ {% for meth in obj.methods() -%}
+ {%- match meth.return_type() -%}
+
+ {%- when Some with (return_type) -%}
+ def {{ meth.name()|fn_name_rb }}({% call rb::arg_list_decl(meth) %})
+ {%- call rb::coerce_args_extra_indent(meth) %}
+ result = {% call rb::to_ffi_call_with_prefix("@pointer", meth) %}
+ return {{ "result"|lift_rb(return_type) }}
+ end
+
+ {%- when None -%}
+ def {{ meth.name()|fn_name_rb }}({% call rb::arg_list_decl(meth) %})
+ {%- call rb::coerce_args_extra_indent(meth) %}
+ {% call rb::to_ffi_call_with_prefix("@pointer", meth) %}
+ end
+ {% endmatch %}
+ {% endfor %}
+end
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/RecordTemplate.rb b/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/RecordTemplate.rb
new file mode 100644
index 0000000000..c940b31060
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/RecordTemplate.rb
@@ -0,0 +1,20 @@
+# Record type {{ rec.name() }}
+class {{ rec.name()|class_name_rb }}
+ attr_reader {% for field in rec.fields() %}:{{ field.name()|var_name_rb }}{% if loop.last %}{% else %}, {% endif %}{%- endfor %}
+
+ def initialize({% for field in rec.fields() %}{{ field.name()|var_name_rb }}{% if loop.last %}{% else %}, {% endif %}{% endfor %})
+ {%- for field in rec.fields() %}
+ @{{ field.name()|var_name_rb }} = {{ field.name()|var_name_rb }}
+ {%- endfor %}
+ end
+
+ def ==(other)
+ {%- for field in rec.fields() %}
+ if @{{ field.name()|var_name_rb }} != other.{{ field.name()|var_name_rb }}
+ return false
+ end
+ {%- endfor %}
+
+ true
+ end
+end
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/RustBufferBuilder.rb b/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/RustBufferBuilder.rb
new file mode 100644
index 0000000000..8749139116
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/RustBufferBuilder.rb
@@ -0,0 +1,264 @@
+
+# Helper for structured writing of values into a RustBuffer.
+class RustBufferBuilder
+ def initialize
+ @rust_buf = RustBuffer.alloc 16
+ @rust_buf.len = 0
+ end
+
+ def finalize
+ rbuf = @rust_buf
+
+ @rust_buf = nil
+
+ rbuf
+ end
+
+ def discard
+ return if @rust_buf.nil?
+
+ rbuf = finalize
+ rbuf.free
+ end
+
+ def write(value)
+ reserve(value.bytes.size) do
+ @rust_buf.data.put_array_of_char @rust_buf.len, value.bytes
+ end
+ end
+
+ {% for typ in ci.iter_types() -%}
+ {%- let canonical_type_name = canonical_name(typ).borrow()|class_name_rb -%}
+ {%- match typ -%}
+
+ {% when Type::Int8 -%}
+
+ def write_I8(v)
+ v = {{ ci.namespace()|class_name_rb }}::uniffi_in_range(v, "i8", -2**7, 2**7)
+ pack_into(1, 'c', v)
+ end
+
+ {% when Type::UInt8 -%}
+
+ def write_U8(v)
+ v = {{ ci.namespace()|class_name_rb }}::uniffi_in_range(v, "u8", 0, 2**8)
+ pack_into(1, 'c', v)
+ end
+
+ {% when Type::Int16 -%}
+
+ def write_I16(v)
+ v = {{ ci.namespace()|class_name_rb }}::uniffi_in_range(v, "i16", -2**15, 2**15)
+ pack_into(2, 's>', v)
+ end
+
+ {% when Type::UInt16 -%}
+
+ def write_U16(v)
+ v = {{ ci.namespace()|class_name_rb }}::uniffi_in_range(v, "u16", 0, 2**16)
+ pack_into(2, 'S>', v)
+ end
+
+ {% when Type::Int32 -%}
+
+ def write_I32(v)
+ v = {{ ci.namespace()|class_name_rb }}::uniffi_in_range(v, "i32", -2**31, 2**31)
+ pack_into(4, 'l>', v)
+ end
+
+ {% when Type::UInt32 -%}
+
+ def write_U32(v)
+ v = {{ ci.namespace()|class_name_rb }}::uniffi_in_range(v, "u32", 0, 2**32)
+ pack_into(4, 'L>', v)
+ end
+
+ {% when Type::Int64 -%}
+
+ def write_I64(v)
+ v = {{ ci.namespace()|class_name_rb }}::uniffi_in_range(v, "i64", -2**63, 2**63)
+ pack_into(8, 'q>', v)
+ end
+
+ {% when Type::UInt64 -%}
+
+ def write_U64(v)
+ v = {{ ci.namespace()|class_name_rb }}::uniffi_in_range(v, "u64", 0, 2**64)
+ pack_into(8, 'Q>', v)
+ end
+
+ {% when Type::Float32 -%}
+
+ def write_F32(v)
+ pack_into(4, 'g', v)
+ end
+
+ {% when Type::Float64 -%}
+
+ def write_F64(v)
+ pack_into(8, 'G', v)
+ end
+
+ {% when Type::Boolean -%}
+
+ def write_Bool(v)
+ pack_into(1, 'c', v ? 1 : 0)
+ end
+
+ {% when Type::String -%}
+
+ def write_String(v)
+ v = {{ ci.namespace()|class_name_rb }}::uniffi_utf8(v)
+ pack_into 4, 'l>', v.bytes.size
+ write v
+ end
+
+ {% when Type::Bytes -%}
+
+ def write_Bytes(v)
+ v = {{ ci.namespace()|class_name_rb }}::uniffi_bytes(v)
+ pack_into 4, 'l>', v.bytes.size
+ write v
+ end
+
+ {% when Type::Timestamp -%}
+ # The Timestamp type.
+ ONE_SECOND_IN_NANOSECONDS = 10**9
+
+ def write_{{ canonical_type_name }}(v)
+ seconds = v.tv_sec
+ nanoseconds = v.tv_nsec
+
+ # UniFFi conventions assume that nanoseconds part has to represent nanoseconds portion of
+ # duration between epoch and the timestamp moment. Ruby `Time#tv_nsec` returns the number of
+ # nanoseconds for the subsecond part, which is sort of opposite to "duration" meaning.
+ # Hence we need to convert value returned by `Time#tv_nsec` back and forth with the following
+ # logic:
+ if seconds < 0 && nanoseconds != 0
+ # In order to get duration nsec we shift by 1 second:
+ nanoseconds = ONE_SECOND_IN_NANOSECONDS - nanoseconds
+
+ # Then we compensate 1 second shift:
+ seconds += 1
+ end
+
+ pack_into 8, 'q>', seconds
+ pack_into 4, 'L>', nanoseconds
+ end
+
+ {% when Type::Duration -%}
+ # The Duration type.
+
+ def write_{{ canonical_type_name }}(v)
+ seconds = v.tv_sec
+ nanoseconds = v.tv_nsec
+
+ raise ArgumentError, 'Invalid duration, must be non-negative' if seconds < 0
+
+ pack_into 8, 'Q>', seconds
+ pack_into 4, 'L>', nanoseconds
+ end
+
+ {% when Type::Object with { name: object_name, module_path, imp } -%}
+ # The Object type {{ object_name }}.
+
+ def write_{{ canonical_type_name }}(obj)
+ pointer = {{ object_name|class_name_rb}}._uniffi_lower obj
+ pack_into(8, 'Q>', pointer.address)
+ end
+
+ {% when Type::Enum { name: enum_name, module_path } -%}
+ {% if !ci.is_name_used_as_error(enum_name) %}
+ {%- let e = ci|get_enum_definition(enum_name) -%}
+ # The Enum type {{ enum_name }}.
+
+ def write_{{ canonical_type_name }}(v)
+ {%- if e.is_flat() %}
+ pack_into(4, 'l>', v)
+ {%- else -%}
+ {%- for variant in e.variants() %}
+ if v.{{ variant.name()|var_name_rb }}?
+ pack_into(4, 'l>', {{ loop.index }})
+ {%- for field in variant.fields() %}
+ self.write_{{ canonical_name(field.as_type().borrow()).borrow()|class_name_rb }}(v.{{ field.name() }})
+ {%- endfor %}
+ end
+ {%- endfor %}
+ {%- endif %}
+ end
+ {% endif %}
+
+ {% when Type::Record { name: record_name, module_path } -%}
+ {%- let rec = ci|get_record_definition(record_name) -%}
+ # The Record type {{ record_name }}.
+
+ def write_{{ canonical_type_name }}(v)
+ {%- for field in rec.fields() %}
+ self.write_{{ canonical_name(field.as_type().borrow()).borrow()|class_name_rb }}(v.{{ field.name()|var_name_rb }})
+ {%- endfor %}
+ end
+
+ {% when Type::Optional { inner_type } -%}
+ # The Optional<T> type for {{ canonical_name(inner_type) }}.
+
+ def write_{{ canonical_type_name }}(v)
+ if v.nil?
+ pack_into(1, 'c', 0)
+ else
+ pack_into(1, 'c', 1)
+ self.write_{{ canonical_name(inner_type).borrow()|class_name_rb }}(v)
+ end
+ end
+
+ {% when Type::Sequence { inner_type } -%}
+ # The Sequence<T> type for {{ canonical_name(inner_type) }}.
+
+ def write_{{ canonical_type_name }}(items)
+ pack_into(4, 'l>', items.size)
+
+ items.each do |item|
+ self.write_{{ canonical_name(inner_type).borrow()|class_name_rb }}(item)
+ end
+ end
+
+ {% when Type::Map { key_type: k, value_type: inner_type } -%}
+ # The Map<T> type for {{ canonical_name(inner_type) }}.
+
+ def write_{{ canonical_type_name }}(items)
+ pack_into(4, 'l>', items.size)
+
+ items.each do |k, v|
+ write_String(k)
+ self.write_{{ canonical_name(inner_type).borrow()|class_name_rb }}(v)
+ end
+ end
+
+ {%- else -%}
+ # This type is not yet supported in the Ruby backend.
+ def write_{{ canonical_type_name }}(v)
+ raise InternalError('RustBufferStream.write() not implemented yet for {{ canonical_type_name }}')
+ end
+
+ {%- endmatch -%}
+ {%- endfor %}
+
+ private
+
+ def reserve(num_bytes)
+ if @rust_buf.len + num_bytes > @rust_buf.capacity
+ @rust_buf = RustBuffer.reserve(@rust_buf, num_bytes)
+ end
+
+ yield
+
+ @rust_buf.len += num_bytes
+ end
+
+ def pack_into(size, format, value)
+ reserve(size) do
+ @rust_buf.data.put_array_of_char @rust_buf.len, [value].pack(format).bytes
+ end
+ end
+end
+
+private_constant :RustBufferBuilder
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/RustBufferStream.rb b/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/RustBufferStream.rb
new file mode 100644
index 0000000000..b085dddf15
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/RustBufferStream.rb
@@ -0,0 +1,315 @@
+
+# Helper for structured reading of values from a RustBuffer.
+class RustBufferStream
+
+ def initialize(rbuf)
+ @rbuf = rbuf
+ @offset = 0
+ end
+
+ def remaining
+ @rbuf.len - @offset
+ end
+
+ def read(size)
+ raise InternalError, 'read past end of rust buffer' if @offset + size > @rbuf.len
+
+ data = @rbuf.data.get_bytes @offset, size
+
+ @offset += size
+
+ data
+ end
+
+ {% for typ in ci.iter_types() -%}
+ {%- let canonical_type_name = canonical_name(typ).borrow()|class_name_rb -%}
+ {%- match typ -%}
+
+ {% when Type::Int8 -%}
+
+ def readI8
+ unpack_from 1, 'c'
+ end
+
+ {% when Type::UInt8 -%}
+
+ def readU8
+ unpack_from 1, 'c'
+ end
+
+ {% when Type::Int16 -%}
+
+ def readI16
+ unpack_from 2, 's>'
+ end
+
+ {% when Type::UInt16 -%}
+
+ def readU16
+ unpack_from 2, 'S>'
+ end
+
+ {% when Type::Int32 -%}
+
+ def readI32
+ unpack_from 4, 'l>'
+ end
+
+ {% when Type::UInt32 -%}
+
+ def readU32
+ unpack_from 4, 'L>'
+ end
+
+ {% when Type::Int64 -%}
+
+ def readI64
+ unpack_from 8, 'q>'
+ end
+
+ {% when Type::UInt64 -%}
+
+ def readU64
+ unpack_from 8, 'Q>'
+ end
+
+ {% when Type::Float32 -%}
+
+ def readF32
+ unpack_from 4, 'g'
+ end
+
+ {% when Type::Float64 -%}
+
+ def readF64
+ unpack_from 8, 'G'
+ end
+
+ {% when Type::Boolean -%}
+
+ def readBool
+ v = unpack_from 1, 'c'
+
+ return false if v == 0
+ return true if v == 1
+
+ raise InternalError, 'Unexpected byte for Boolean type'
+ end
+
+ {% when Type::String -%}
+
+ def readString
+ size = unpack_from 4, 'l>'
+
+ raise InternalError, 'Unexpected negative string length' if size.negative?
+
+ read(size).force_encoding(Encoding::UTF_8)
+ end
+
+ {% when Type::Bytes -%}
+
+ def readBytes
+ size = unpack_from 4, 'l>'
+
+ raise InternalError, 'Unexpected negative byte string length' if size.negative?
+
+ read(size).force_encoding(Encoding::BINARY)
+ end
+
+ {% when Type::Timestamp -%}
+ # The Timestamp type.
+ ONE_SECOND_IN_NANOSECONDS = 10**9
+
+ def read{{ canonical_type_name }}
+ seconds = unpack_from 8, 'q>'
+ nanoseconds = unpack_from 4, 'L>'
+
+ # UniFFi conventions assume that nanoseconds part has to represent nanoseconds portion of
+ # duration between epoch and the timestamp moment. Ruby `Time#tv_nsec` returns the number of
+ # nanoseconds for the subsecond part, which is sort of opposite to "duration" meaning.
+ # Hence we need to convert value returned by `Time#tv_nsec` back and forth with the following
+ # logic:
+ if seconds < 0 && nanoseconds != 0
+ # In order to get duration nsec we shift by 1 second:
+ nanoseconds = ONE_SECOND_IN_NANOSECONDS - nanoseconds
+
+ # Then we compensate 1 second shift:
+ seconds -= 1
+ end
+
+ Time.at(seconds, nanoseconds, :nanosecond, in: '+00:00').utc
+ end
+
+ {% when Type::Duration -%}
+ # The Duration type.
+
+ def read{{ canonical_type_name }}
+ seconds = unpack_from 8, 'q>'
+ nanoseconds = unpack_from 4, 'L>'
+
+ Time.at(seconds, nanoseconds, :nanosecond, in: '+00:00').utc
+ end
+
+ {% when Type::Object with { name: object_name, module_path, imp } -%}
+ # The Object type {{ object_name }}.
+
+ def read{{ canonical_type_name }}
+ pointer = FFI::Pointer.new unpack_from 8, 'Q>'
+ return {{ object_name|class_name_rb }}._uniffi_allocate(pointer)
+ end
+
+ {% when Type::Enum { name, module_path } -%}
+ {%- let e = ci|get_enum_definition(name) -%}
+ {% if !ci.is_name_used_as_error(name) %}
+ {% let enum_name = name %}
+ # The Enum type {{ enum_name }}.
+
+ def read{{ canonical_type_name }}
+ variant = unpack_from 4, 'l>'
+ {% if e.is_flat() -%}
+ {%- for variant in e.variants() %}
+ if variant == {{ loop.index }}
+ return {{ enum_name|class_name_rb }}::{{ variant.name()|enum_name_rb }}
+ end
+ {%- endfor %}
+
+ raise InternalError, 'Unexpected variant tag for {{ canonical_type_name }}'
+ {%- else -%}
+ {%- for variant in e.variants() %}
+ if variant == {{ loop.index }}
+ {%- if variant.has_fields() %}
+ return {{ enum_name|class_name_rb }}::{{ variant.name()|enum_name_rb }}.new(
+ {%- for field in variant.fields() %}
+ self.read{{ canonical_name(field.as_type().borrow()).borrow()|class_name_rb }}(){% if loop.last %}{% else %},{% endif %}
+ {%- endfor %}
+ )
+ {%- else %}
+ return {{ enum_name|class_name_rb }}::{{ variant.name()|enum_name_rb }}.new
+ {% endif %}
+ end
+ {%- endfor %}
+ raise InternalError, 'Unexpected variant tag for {{ canonical_type_name }}'
+ {%- endif %}
+ end
+
+ {% else %}
+
+ {% let error_name = name %}
+
+ # The Error type {{ error_name }}
+
+ def read{{ canonical_type_name }}
+ variant = unpack_from 4, 'l>'
+ {% if e.is_flat() -%}
+ {%- for variant in e.variants() %}
+ if variant == {{ loop.index }}
+ return {{ error_name|class_name_rb }}::{{ variant.name()|class_name_rb }}.new(
+ readString()
+ )
+ end
+ {%- endfor %}
+
+ raise InternalError, 'Unexpected variant tag for {{ canonical_type_name }}'
+ {%- else -%}
+ {%- for variant in e.variants() %}
+ if variant == {{ loop.index }}
+ {%- if variant.has_fields() %}
+ return {{ error_name|class_name_rb }}::{{ variant.name()|class_name_rb }}.new(
+ {%- for field in variant.fields() %}
+ read{{ canonical_name(field.as_type().borrow()).borrow()|class_name_rb }}(){% if loop.last %}{% else %},{% endif %}
+ {%- endfor %}
+ )
+ {%- else %}
+ return {{ error_name|class_name_rb }}::{{ variant.name()|class_name_rb }}.new
+ {%- endif %}
+ end
+ {%- endfor %}
+
+ raise InternalError, 'Unexpected variant tag for {{ canonical_type_name }}'
+ {%- endif %}
+ end
+ {% endif %}
+
+ {% when Type::Record { name: record_name, module_path } -%}
+ {%- let rec = ci|get_record_definition(record_name) -%}
+ # The Record type {{ record_name }}.
+
+ def read{{ canonical_type_name }}
+ {{ rec.name()|class_name_rb }}.new(
+ {%- for field in rec.fields() %}
+ read{{ canonical_name(field.as_type().borrow()).borrow()|class_name_rb }}{% if loop.last %}{% else %},{% endif %}
+ {%- endfor %}
+ )
+ end
+
+ {% when Type::Optional { inner_type } -%}
+ # The Optional<T> type for {{ canonical_name(inner_type) }}.
+
+ def read{{ canonical_type_name }}
+ flag = unpack_from 1, 'c'
+
+ if flag == 0
+ return nil
+ elsif flag == 1
+ return read{{ canonical_name(inner_type).borrow()|class_name_rb }}
+ else
+ raise InternalError, 'Unexpected flag byte for {{ canonical_type_name }}'
+ end
+ end
+
+ {% when Type::Sequence { inner_type } -%}
+ # The Sequence<T> type for {{ canonical_name(inner_type) }}.
+
+ def read{{ canonical_type_name }}
+ count = unpack_from 4, 'l>'
+
+ raise InternalError, 'Unexpected negative sequence length' if count.negative?
+
+ items = []
+
+ count.times do
+ items.append read{{ canonical_name(inner_type).borrow()|class_name_rb }}
+ end
+
+ items
+ end
+
+ {% when Type::Map { key_type: k, value_type: inner_type } -%}
+ # The Map<T> type for {{ canonical_name(inner_type) }}.
+
+ def read{{ canonical_type_name }}
+ count = unpack_from 4, 'l>'
+ raise InternalError, 'Unexpected negative map size' if count.negative?
+
+ items = {}
+ count.times do
+ key = readString
+ items[key] = read{{ canonical_name(inner_type).borrow()|class_name_rb }}
+ end
+
+ items
+ end
+ {%- else -%}
+ # This type is not yet supported in the Ruby backend.
+ def read{{ canonical_type_name }}
+ raise InternalError, 'RustBufferStream.read not implemented yet for {{ canonical_type_name }}'
+ end
+
+ {%- endmatch -%}
+ {%- endfor %}
+
+ def unpack_from(size, format)
+ raise InternalError, 'read past end of rust buffer' if @offset + size > @rbuf.len
+
+ value = @rbuf.data.get_bytes(@offset, size).unpack format
+
+ @offset += size
+
+ # TODO: verify this
+ raise 'more than one element!!!' if value.size > 1
+
+ value[0]
+ end
+end
+
+private_constant :RustBufferStream
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/RustBufferTemplate.rb b/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/RustBufferTemplate.rb
new file mode 100644
index 0000000000..0194c9666d
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/RustBufferTemplate.rb
@@ -0,0 +1,236 @@
+class RustBuffer < FFI::Struct
+ layout :capacity, :int32,
+ :len, :int32,
+ :data, :pointer
+
+ def self.alloc(size)
+ return {{ ci.namespace()|class_name_rb }}.rust_call(:{{ ci.ffi_rustbuffer_alloc().name() }}, size)
+ end
+
+ def self.reserve(rbuf, additional)
+ return {{ ci.namespace()|class_name_rb }}.rust_call(:{{ ci.ffi_rustbuffer_reserve().name() }}, rbuf, additional)
+ end
+
+ def free
+ {{ ci.namespace()|class_name_rb }}.rust_call(:{{ ci.ffi_rustbuffer_free().name() }}, self)
+ end
+
+ def capacity
+ self[:capacity]
+ end
+
+ def len
+ self[:len]
+ end
+
+ def len=(value)
+ self[:len] = value
+ end
+
+ def data
+ self[:data]
+ end
+
+ def to_s
+ "RustBuffer(capacity=#{capacity}, len=#{len}, data=#{data.read_bytes len})"
+ end
+
+ # The allocated buffer will be automatically freed if an error occurs, ensuring that
+ # we don't accidentally leak it.
+ def self.allocWithBuilder
+ builder = RustBufferBuilder.new
+
+ begin
+ yield builder
+ rescue => e
+ builder.discard
+ raise e
+ end
+ end
+
+ # The RustBuffer will be freed once the context-manager exits, ensuring that we don't
+ # leak it even if an error occurs.
+ def consumeWithStream
+ stream = RustBufferStream.new self
+
+ yield stream
+
+ raise RuntimeError, 'junk data left in buffer after consuming' if stream.remaining != 0
+ ensure
+ free
+ end
+
+ {%- for typ in ci.iter_types() -%}
+ {%- let canonical_type_name = canonical_name(typ) -%}
+ {%- match typ -%}
+
+ {% when Type::String -%}
+ # The primitive String type.
+
+ def self.allocFromString(value)
+ RustBuffer.allocWithBuilder do |builder|
+ builder.write value.encode('utf-8')
+ return builder.finalize
+ end
+ end
+
+ def consumeIntoString
+ consumeWithStream do |stream|
+ return stream.read(stream.remaining).force_encoding(Encoding::UTF_8)
+ end
+ end
+
+ {% when Type::Bytes -%}
+ # The primitive Bytes type.
+
+ def self.allocFromBytes(value)
+ RustBuffer.allocWithBuilder do |builder|
+ builder.write_Bytes(value)
+ return builder.finalize
+ end
+ end
+
+ def consumeIntoBytes
+ consumeWithStream do |stream|
+ return stream.readBytes
+ end
+ end
+
+ {% when Type::Timestamp -%}
+ def self.alloc_from_{{ canonical_type_name }}(v)
+ RustBuffer.allocWithBuilder do |builder|
+ builder.write_{{ canonical_type_name }}(v)
+ return builder.finalize
+ end
+ end
+
+ def consumeInto{{ canonical_type_name }}
+ consumeWithStream do |stream|
+ return stream.read{{ canonical_type_name }}
+ end
+ end
+
+ {% when Type::Duration -%}
+ def self.alloc_from_{{ canonical_type_name }}(v)
+ RustBuffer.allocWithBuilder do |builder|
+ builder.write_{{ canonical_type_name }}(v)
+ return builder.finalize
+ end
+ end
+
+ def consumeInto{{ canonical_type_name }}
+ consumeWithStream do |stream|
+ return stream.read{{ canonical_type_name }}
+ end
+ end
+
+ {% when Type::Record { name: record_name, module_path } -%}
+ {%- let rec = ci|get_record_definition(record_name) -%}
+ # The Record type {{ record_name }}.
+
+ def self.alloc_from_{{ canonical_type_name }}(v)
+ RustBuffer.allocWithBuilder do |builder|
+ builder.write_{{ canonical_type_name }}(v)
+ return builder.finalize
+ end
+ end
+
+ def consumeInto{{ canonical_type_name }}
+ consumeWithStream do |stream|
+ return stream.read{{ canonical_type_name }}
+ end
+ end
+
+ {% when Type::Enum { name: enum_name, module_path } -%}
+ {% if !ci.is_name_used_as_error(enum_name) %}
+ {%- let e = ci|get_enum_definition(enum_name) -%}
+ # The Enum type {{ enum_name }}.
+
+ def self.alloc_from_{{ canonical_type_name }}(v)
+ RustBuffer.allocWithBuilder do |builder|
+ builder.write_{{ canonical_type_name }}(v)
+ return builder.finalize
+ end
+ end
+
+ def consumeInto{{ canonical_type_name }}
+ consumeWithStream do |stream|
+ return stream.read{{ canonical_type_name }}
+ end
+ end
+ {% endif %}
+
+ {% when Type::Optional { inner_type } -%}
+ # The Optional<T> type for {{ canonical_name(inner_type) }}.
+
+ def self.alloc_from_{{ canonical_type_name }}(v)
+ RustBuffer.allocWithBuilder do |builder|
+ builder.write_{{ canonical_type_name }}(v)
+ return builder.finalize()
+ end
+ end
+
+ def consumeInto{{ canonical_type_name }}
+ consumeWithStream do |stream|
+ return stream.read{{ canonical_type_name }}
+ end
+ end
+
+ {% when Type::Sequence { inner_type } -%}
+ # The Sequence<T> type for {{ canonical_name(inner_type) }}.
+
+ def self.alloc_from_{{ canonical_type_name }}(v)
+ RustBuffer.allocWithBuilder do |builder|
+ builder.write_{{ canonical_type_name }}(v)
+ return builder.finalize()
+ end
+ end
+
+ def consumeInto{{ canonical_type_name }}
+ consumeWithStream do |stream|
+ return stream.read{{ canonical_type_name }}
+ end
+ end
+
+ {% when Type::Map { key_type: k, value_type: inner_type } -%}
+ # The Map<T> type for {{ canonical_name(inner_type) }}.
+
+ def self.alloc_from_{{ canonical_type_name }}(v)
+ RustBuffer.allocWithBuilder do |builder|
+ builder.write_{{ canonical_type_name }}(v)
+ return builder.finalize
+ end
+ end
+
+ def consumeInto{{ canonical_type_name }}
+ consumeWithStream do |stream|
+ return stream.read{{ canonical_type_name }}
+ end
+ end
+
+ {%- else -%}
+ {#- No code emitted for types that don't lower into a RustBuffer -#}
+ {%- endmatch -%}
+ {%- endfor %}
+end
+
+module UniFFILib
+ class ForeignBytes < FFI::Struct
+ layout :len, :int32,
+ :data, :pointer
+
+ def len
+ self[:len]
+ end
+
+ def data
+ self[:data]
+ end
+
+ def to_s
+ "ForeignBytes(len=#{len}, data=#{data.read_bytes(len)})"
+ end
+ end
+end
+
+private_constant :UniFFILib
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/TopLevelFunctionTemplate.rb b/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/TopLevelFunctionTemplate.rb
new file mode 100644
index 0000000000..13214cf31b
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/TopLevelFunctionTemplate.rb
@@ -0,0 +1,16 @@
+{%- match func.return_type() -%}
+{%- when Some with (return_type) %}
+
+def self.{{ func.name()|fn_name_rb }}({%- call rb::arg_list_decl(func) -%})
+ {%- call rb::coerce_args(func) %}
+ result = {% call rb::to_ffi_call(func) %}
+ return {{ "result"|lift_rb(return_type) }}
+end
+
+{% when None %}
+
+def self.{{ func.name()|fn_name_rb }}({%- call rb::arg_list_decl(func) -%})
+ {%- call rb::coerce_args(func) %}
+ {% call rb::to_ffi_call(func) %}
+end
+{% endmatch %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/macros.rb b/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/macros.rb
new file mode 100644
index 0000000000..8dc3e5e613
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/macros.rb
@@ -0,0 +1,73 @@
+{#
+// Template to call into rust. Used in several places.
+// Variable names in `arg_list_decl` should match up with arg lists
+// passed to rust via `_arg_list_ffi_call` (we use `var_name_rb` in `lower_rb`)
+#}
+
+{%- macro to_ffi_call(func) -%}
+ {%- match func.throws_name() -%}
+ {%- when Some with (e) -%}
+ {{ ci.namespace()|class_name_rb }}.rust_call_with_error({{ e|class_name_rb }},
+ {%- else -%}
+ {{ ci.namespace()|class_name_rb }}.rust_call(
+ {%- endmatch -%}
+ :{{ func.ffi_func().name() }},
+ {%- call _arg_list_ffi_call(func) -%}
+)
+{%- endmacro -%}
+
+{%- macro to_ffi_call_with_prefix(prefix, func) -%}
+ {%- match func.throws_name() -%}
+ {%- when Some with (e) -%}
+ {{ ci.namespace()|class_name_rb }}.rust_call_with_error({{ e|class_name_rb }},
+ {%- else -%}
+ {{ ci.namespace()|class_name_rb }}.rust_call(
+ {%- endmatch -%}
+ :{{ func.ffi_func().name() }},
+ {{- prefix }},
+ {%- call _arg_list_ffi_call(func) -%}
+)
+{%- endmacro -%}
+
+{%- macro _arg_list_ffi_call(func) %}
+ {%- for arg in func.arguments() %}
+ {{- arg.name()|lower_rb(arg.as_type().borrow()) }}
+ {%- if !loop.last %},{% endif %}
+ {%- endfor %}
+{%- endmacro -%}
+
+{#-
+// Arglist as used in Ruby declarations of methods, functions and constructors.
+// Note the var_name_rb and type_rb filters.
+-#}
+
+{% macro arg_list_decl(func) %}
+ {%- for arg in func.arguments() -%}
+ {{ arg.name()|var_name_rb }}
+ {%- match arg.default_value() %}
+ {%- when Some with(literal) %} = {{ literal|literal_rb }}
+ {%- else %}
+ {%- endmatch %}
+ {%- if !loop.last %}, {% endif -%}
+ {%- endfor %}
+{%- endmacro %}
+
+{#-
+// Arglist as used in the UniFFILib function declarations.
+// Note unfiltered name but type_ffi filters.
+-#}
+{%- macro arg_list_ffi_decl(func) %}
+ [{%- for arg in func.arguments() -%}{{ arg.type_().borrow()|type_ffi }}, {% endfor -%} RustCallStatus.by_ref]
+{%- endmacro -%}
+
+{%- macro coerce_args(func) %}
+ {%- for arg in func.arguments() %}
+ {{ arg.name() }} = {{ arg.name()|coerce_rb(ci.namespace()|class_name_rb, arg.as_type().borrow()) -}}
+ {% endfor -%}
+{%- endmacro -%}
+
+{%- macro coerce_args_extra_indent(func) %}
+ {%- for arg in func.arguments() %}
+ {{ arg.name() }} = {{ arg.name()|coerce_rb(ci.namespace()|class_name_rb, arg.as_type().borrow()) }}
+ {%- endfor %}
+{%- endmacro -%}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/wrapper.rb b/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/wrapper.rb
new file mode 100644
index 0000000000..e3631b68de
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/ruby/templates/wrapper.rb
@@ -0,0 +1,52 @@
+# This file was autogenerated by some hot garbage in the `uniffi` crate.
+# Trust me, you don't want to mess with it!
+
+# Common helper code.
+#
+# Ideally this would live in a separate .rb file where it can be unittested etc
+# in isolation, and perhaps even published as a re-useable package.
+#
+# However, it's important that the details of how this helper code works (e.g. the
+# way that different builtin types are passed across the FFI) exactly match what's
+# expected by the rust code on the other side of the interface. In practice right
+# now that means coming from the exact some version of `uniffi` that was used to
+# compile the rust component. The easiest way to ensure this is to bundle the Ruby
+# helpers directly inline like we're doing here.
+
+require 'ffi'
+
+
+module {{ ci.namespace()|class_name_rb }}
+ {% include "Helpers.rb" %}
+
+ {% include "RustBufferTemplate.rb" %}
+ {% include "RustBufferStream.rb" %}
+ {% include "RustBufferBuilder.rb" %}
+
+ # Error definitions
+ {% include "ErrorTemplate.rb" %}
+
+ {% include "NamespaceLibraryTemplate.rb" %}
+
+ # Public interface members begin here.
+
+ {% for e in ci.enum_definitions() %}
+ {% if !ci.is_name_used_as_error(e.name()) %}
+ {% include "EnumTemplate.rb" %}
+ {% endif %}
+ {%- endfor -%}
+
+ {%- for rec in ci.record_definitions() %}
+ {% include "RecordTemplate.rb" %}
+ {% endfor %}
+
+ {% for func in ci.function_definitions() %}
+ {% include "TopLevelFunctionTemplate.rb" %}
+ {% endfor %}
+
+ {% for obj in ci.object_definitions() %}
+ {% include "ObjectTemplate.rb" %}
+ {% endfor %}
+end
+
+{% import "macros.rb" as rb %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/ruby/test.rs b/third_party/rust/uniffi_bindgen/src/bindings/ruby/test.rs
new file mode 100644
index 0000000000..03da37d567
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/ruby/test.rs
@@ -0,0 +1,52 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use crate::bindings::TargetLanguage;
+use crate::library_mode::generate_bindings;
+use anyhow::{bail, Context, Result};
+use camino::Utf8Path;
+use std::env;
+use std::ffi::OsString;
+use std::process::{Command, Stdio};
+use uniffi_testing::UniFFITestHelper;
+
+/// Run Ruby tests for a UniFFI test fixture
+pub fn run_test(tmp_dir: &str, fixture_name: &str, script_file: &str) -> Result<()> {
+ let status = test_script_command(tmp_dir, fixture_name, script_file)?
+ .spawn()
+ .context("Failed to spawn `ruby` when running script")?
+ .wait()
+ .context("Failed to wait for `ruby` when running script")?;
+ if !status.success() {
+ bail!("running `ruby` failed");
+ }
+ Ok(())
+}
+
+/// Create a `Command` instance that runs a test script
+pub fn test_script_command(
+ tmp_dir: &str,
+ fixture_name: &str,
+ script_file: &str,
+) -> Result<Command> {
+ let script_path = Utf8Path::new(".").join(script_file).canonicalize_utf8()?;
+ let test_helper = UniFFITestHelper::new(fixture_name)?;
+ let out_dir = test_helper.create_out_dir(tmp_dir, &script_path)?;
+ let cdylib_path = test_helper.copy_cdylib_to_out_dir(&out_dir)?;
+ generate_bindings(&cdylib_path, None, &[TargetLanguage::Ruby], &out_dir, false)?;
+
+ let rubypath = env::var_os("RUBYLIB").unwrap_or_else(|| OsString::from(""));
+ let rubypath = env::join_paths(
+ env::split_paths(&rubypath).chain(vec![out_dir.to_path_buf().into_std_path_buf()]),
+ )?;
+
+ let mut command = Command::new("ruby");
+ command
+ .current_dir(out_dir)
+ .env("RUBYLIB", rubypath)
+ .arg(script_path)
+ .stderr(Stdio::inherit())
+ .stdout(Stdio::inherit());
+ Ok(command)
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/callback_interface.rs b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/callback_interface.rs
new file mode 100644
index 0000000000..5d8b37e0af
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/callback_interface.rs
@@ -0,0 +1,26 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use super::CodeType;
+
+#[derive(Debug)]
+pub struct CallbackInterfaceCodeType {
+ id: String,
+}
+
+impl CallbackInterfaceCodeType {
+ pub fn new(id: String) -> Self {
+ Self { id }
+ }
+}
+
+impl CodeType for CallbackInterfaceCodeType {
+ fn type_label(&self) -> String {
+ super::SwiftCodeOracle.class_name(&self.id)
+ }
+
+ fn canonical_name(&self) -> String {
+ format!("CallbackInterface{}", self.type_label())
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/compounds.rs b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/compounds.rs
new file mode 100644
index 0000000000..8e6dddf3f9
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/compounds.rs
@@ -0,0 +1,108 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use super::CodeType;
+use crate::backend::{Literal, Type};
+
+#[derive(Debug)]
+pub struct OptionalCodeType {
+ inner: Type,
+}
+
+impl OptionalCodeType {
+ pub fn new(inner: Type) -> Self {
+ Self { inner }
+ }
+}
+
+impl CodeType for OptionalCodeType {
+ fn type_label(&self) -> String {
+ format!("{}?", super::SwiftCodeOracle.find(&self.inner).type_label())
+ }
+
+ fn canonical_name(&self) -> String {
+ format!(
+ "Option{}",
+ super::SwiftCodeOracle.find(&self.inner).canonical_name()
+ )
+ }
+
+ fn literal(&self, literal: &Literal) -> String {
+ match literal {
+ Literal::Null => "nil".into(),
+ _ => super::SwiftCodeOracle.find(&self.inner).literal(literal),
+ }
+ }
+}
+
+#[derive(Debug)]
+pub struct SequenceCodeType {
+ inner: Type,
+}
+
+impl SequenceCodeType {
+ pub fn new(inner: Type) -> Self {
+ Self { inner }
+ }
+}
+
+impl CodeType for SequenceCodeType {
+ fn type_label(&self) -> String {
+ format!(
+ "[{}]",
+ super::SwiftCodeOracle.find(&self.inner).type_label()
+ )
+ }
+
+ fn canonical_name(&self) -> String {
+ format!(
+ "Sequence{}",
+ super::SwiftCodeOracle.find(&self.inner).canonical_name()
+ )
+ }
+
+ fn literal(&self, literal: &Literal) -> String {
+ match literal {
+ Literal::EmptySequence => "[]".into(),
+ _ => unreachable!(),
+ }
+ }
+}
+
+#[derive(Debug)]
+pub struct MapCodeType {
+ key: Type,
+ value: Type,
+}
+
+impl MapCodeType {
+ pub fn new(key: Type, value: Type) -> Self {
+ Self { key, value }
+ }
+}
+
+impl CodeType for MapCodeType {
+ fn type_label(&self) -> String {
+ format!(
+ "[{}: {}]",
+ super::SwiftCodeOracle.find(&self.key).type_label(),
+ super::SwiftCodeOracle.find(&self.value).type_label()
+ )
+ }
+
+ fn canonical_name(&self) -> String {
+ format!(
+ "Dictionary{}{}",
+ super::SwiftCodeOracle.find(&self.key).canonical_name(),
+ super::SwiftCodeOracle.find(&self.value).canonical_name()
+ )
+ }
+
+ fn literal(&self, literal: &Literal) -> String {
+ match literal {
+ Literal::EmptyMap => "[:]".into(),
+ _ => unreachable!(),
+ }
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/custom.rs b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/custom.rs
new file mode 100644
index 0000000000..f4591b6eb6
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/custom.rs
@@ -0,0 +1,26 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use super::CodeType;
+
+#[derive(Debug)]
+pub struct CustomCodeType {
+ name: String,
+}
+
+impl CustomCodeType {
+ pub fn new(name: String) -> Self {
+ CustomCodeType { name }
+ }
+}
+
+impl CodeType for CustomCodeType {
+ fn type_label(&self) -> String {
+ self.name.clone()
+ }
+
+ fn canonical_name(&self) -> String {
+ format!("Type{}", self.name)
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/enum_.rs b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/enum_.rs
new file mode 100644
index 0000000000..14377ed9de
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/enum_.rs
@@ -0,0 +1,35 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use super::CodeType;
+use crate::backend::Literal;
+
+#[derive(Debug)]
+pub struct EnumCodeType {
+ id: String,
+}
+
+impl EnumCodeType {
+ pub fn new(id: String) -> Self {
+ Self { id }
+ }
+}
+
+impl CodeType for EnumCodeType {
+ fn type_label(&self) -> String {
+ super::SwiftCodeOracle.class_name(&self.id)
+ }
+
+ fn canonical_name(&self) -> String {
+ format!("Type{}", self.id)
+ }
+
+ fn literal(&self, literal: &Literal) -> String {
+ if let Literal::Enum(v, _) = literal {
+ format!(".{}", super::SwiftCodeOracle.enum_variant_name(v))
+ } else {
+ unreachable!();
+ }
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/executor.rs b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/executor.rs
new file mode 100644
index 0000000000..b488b004cf
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/executor.rs
@@ -0,0 +1,23 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use super::CodeType;
+
+#[derive(Debug)]
+pub struct ForeignExecutorCodeType;
+
+impl CodeType for ForeignExecutorCodeType {
+ fn type_label(&self) -> String {
+ // On Swift, we define a struct to represent a ForeignExecutor
+ "UniFfiForeignExecutor".into()
+ }
+
+ fn canonical_name(&self) -> String {
+ "ForeignExecutor".into()
+ }
+
+ fn initialization_fn(&self) -> Option<String> {
+ Some("uniffiInitForeignExecutor".into())
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/external.rs b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/external.rs
new file mode 100644
index 0000000000..0b6728ba84
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/external.rs
@@ -0,0 +1,36 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use super::CodeType;
+
+#[derive(Debug)]
+pub struct ExternalCodeType {
+ name: String,
+}
+
+impl ExternalCodeType {
+ pub fn new(name: String) -> Self {
+ ExternalCodeType { name }
+ }
+}
+
+impl CodeType for ExternalCodeType {
+ fn type_label(&self) -> String {
+ self.name.clone()
+ }
+
+ fn canonical_name(&self) -> String {
+ format!("Type{}", self.name)
+ }
+
+ // lower and lift need to call public function which were generated for
+ // the original types.
+ fn lower(&self) -> String {
+ format!("{}_lower", self.ffi_converter_name())
+ }
+
+ fn lift(&self) -> String {
+ format!("{}_lift", self.ffi_converter_name())
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/miscellany.rs b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/miscellany.rs
new file mode 100644
index 0000000000..c45091c80a
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/miscellany.rs
@@ -0,0 +1,31 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use super::CodeType;
+
+#[derive(Debug)]
+pub struct TimestampCodeType;
+
+impl CodeType for TimestampCodeType {
+ fn type_label(&self) -> String {
+ "Date".into()
+ }
+
+ fn canonical_name(&self) -> String {
+ "Timestamp".into()
+ }
+}
+
+#[derive(Debug)]
+pub struct DurationCodeType;
+
+impl CodeType for DurationCodeType {
+ fn type_label(&self) -> String {
+ "TimeInterval".into()
+ }
+
+ fn canonical_name(&self) -> String {
+ "Duration".into()
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/mod.rs b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/mod.rs
new file mode 100644
index 0000000000..12db4afc66
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/mod.rs
@@ -0,0 +1,688 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use once_cell::sync::Lazy;
+use std::borrow::Borrow;
+use std::cell::RefCell;
+use std::collections::{BTreeSet, HashMap, HashSet};
+use std::fmt::Debug;
+
+use anyhow::{Context, Result};
+use askama::Template;
+use heck::{ToLowerCamelCase, ToUpperCamelCase};
+use serde::{Deserialize, Serialize};
+
+use super::Bindings;
+use crate::backend::TemplateExpression;
+use crate::interface::*;
+use crate::BindingsConfig;
+
+mod callback_interface;
+mod compounds;
+mod custom;
+mod enum_;
+mod executor;
+mod external;
+mod miscellany;
+mod object;
+mod primitives;
+mod record;
+
+/// A trait tor the implementation.
+trait CodeType: Debug {
+ /// The language specific label used to reference this type. This will be used in
+ /// method signatures and property declarations.
+ fn type_label(&self) -> String;
+
+ /// A representation of this type label that can be used as part of another
+ /// identifier. e.g. `read_foo()`, or `FooInternals`.
+ ///
+ /// This is especially useful when creating specialized objects or methods to deal
+ /// with this type only.
+ fn canonical_name(&self) -> String {
+ self.type_label()
+ }
+
+ fn literal(&self, _literal: &Literal) -> String {
+ unimplemented!("Unimplemented for {}", self.type_label())
+ }
+
+ /// Name of the FfiConverter
+ ///
+ /// This is the object that contains the lower, write, lift, and read methods for this type.
+ fn ffi_converter_name(&self) -> String {
+ format!("FfiConverter{}", self.canonical_name())
+ }
+
+ // XXX - the below should be removed and replace with the ffi_converter_name reference in the template.
+ /// An expression for lowering a value into something we can pass over the FFI.
+ fn lower(&self) -> String {
+ format!("{}.lower", self.ffi_converter_name())
+ }
+
+ /// An expression for writing a value into a byte buffer.
+ fn write(&self) -> String {
+ format!("{}.write", self.ffi_converter_name())
+ }
+
+ /// An expression for lifting a value from something we received over the FFI.
+ fn lift(&self) -> String {
+ format!("{}.lift", self.ffi_converter_name())
+ }
+
+ /// An expression for reading a value from a byte buffer.
+ fn read(&self) -> String {
+ format!("{}.read", self.ffi_converter_name())
+ }
+
+ /// A list of imports that are needed if this type is in use.
+ /// Classes are imported exactly once.
+ fn imports(&self) -> Option<Vec<String>> {
+ None
+ }
+
+ /// Function to run at startup
+ fn initialization_fn(&self) -> Option<String> {
+ None
+ }
+}
+
+/// From <https://docs.swift.org/swift-book/documentation/the-swift-programming-language/lexicalstructure/#Keywords-and-Punctuation>
+static KEYWORDS: Lazy<HashSet<String>> = Lazy::new(|| {
+ [
+ // Keywords used in declarations:
+ "associatedtype",
+ "class",
+ "deinit",
+ "enum",
+ "extension",
+ "fileprivate",
+ "func",
+ "import",
+ "init",
+ "inout",
+ "internal",
+ "let",
+ "open",
+ "operator",
+ "private",
+ "precedencegroup",
+ "protocol",
+ "public",
+ "rethrows",
+ "static",
+ "struct",
+ "subscript",
+ "typealias",
+ "var",
+ // Keywords used in statements:
+ "break",
+ "case",
+ "catch",
+ "continue",
+ "default",
+ "defer",
+ "do",
+ "else",
+ "fallthrough",
+ "for",
+ "guard",
+ "if",
+ "in",
+ "repeat",
+ "return",
+ "throw",
+ "switch",
+ "where",
+ "while",
+ // Keywords used in expressions and types:
+ "Any",
+ "as",
+ "await",
+ "catch",
+ "false",
+ "is",
+ "nil",
+ "rethrows",
+ "self",
+ "Self",
+ "super",
+ "throw",
+ "throws",
+ "true",
+ "try",
+ ]
+ .iter()
+ .map(ToString::to_string)
+ .collect::<HashSet<_>>()
+});
+
+/// Quote a name for use in a context where keywords must be quoted
+pub fn quote_general_keyword(nm: String) -> String {
+ if KEYWORDS.contains(&nm) {
+ format!("`{nm}`")
+ } else {
+ nm
+ }
+}
+
+/// Per <https://docs.swift.org/swift-book/documentation/the-swift-programming-language/lexicalstructure/#Keywords-and-Punctuation> subset of keywords which need quoting in arg context.
+static ARG_KEYWORDS: Lazy<HashSet<String>> = Lazy::new(|| {
+ ["inout", "var", "let"]
+ .iter()
+ .map(ToString::to_string)
+ .collect::<HashSet<_>>()
+});
+
+/// Quote a name for use in arg context where fewer keywords must be quoted
+pub fn quote_arg_keyword(nm: String) -> String {
+ if ARG_KEYWORDS.contains(&nm) {
+ format!("`{nm}`")
+ } else {
+ nm
+ }
+}
+
+/// Config options for the caller to customize the generated Swift.
+///
+/// Note that this can only be used to control details of the Swift *that do not affect the underlying component*,
+/// since the details of the underlying component are entirely determined by the `ComponentInterface`.
+#[derive(Debug, Clone, Default, Serialize, Deserialize)]
+pub struct Config {
+ cdylib_name: Option<String>,
+ module_name: Option<String>,
+ ffi_module_name: Option<String>,
+ ffi_module_filename: Option<String>,
+ generate_module_map: Option<bool>,
+ omit_argument_labels: Option<bool>,
+ #[serde(default)]
+ custom_types: HashMap<String, CustomTypeConfig>,
+}
+
+#[derive(Debug, Default, Clone, Serialize, Deserialize)]
+pub struct CustomTypeConfig {
+ imports: Option<Vec<String>>,
+ type_name: Option<String>,
+ into_custom: TemplateExpression,
+ from_custom: TemplateExpression,
+}
+
+impl Config {
+ /// The name of the Swift module containing the high-level foreign-language bindings.
+ pub fn module_name(&self) -> String {
+ match self.module_name.as_ref() {
+ Some(name) => name.clone(),
+ None => "uniffi".into(),
+ }
+ }
+
+ /// The name of the lower-level C module containing the FFI declarations.
+ pub fn ffi_module_name(&self) -> String {
+ match self.ffi_module_name.as_ref() {
+ Some(name) => name.clone(),
+ None => format!("{}FFI", self.module_name()),
+ }
+ }
+
+ /// The filename stem for the lower-level C module containing the FFI declarations.
+ pub fn ffi_module_filename(&self) -> String {
+ match self.ffi_module_filename.as_ref() {
+ Some(name) => name.clone(),
+ None => self.ffi_module_name(),
+ }
+ }
+
+ /// The name of the `.modulemap` file for the lower-level C module with FFI declarations.
+ pub fn modulemap_filename(&self) -> String {
+ format!("{}.modulemap", self.ffi_module_filename())
+ }
+
+ /// The name of the `.h` file for the lower-level C module with FFI declarations.
+ pub fn header_filename(&self) -> String {
+ format!("{}.h", self.ffi_module_filename())
+ }
+
+ /// The name of the compiled Rust library containing the FFI implementation.
+ pub fn cdylib_name(&self) -> String {
+ if let Some(cdylib_name) = &self.cdylib_name {
+ cdylib_name.clone()
+ } else {
+ "uniffi".into()
+ }
+ }
+
+ /// Whether to generate a `.modulemap` file for the lower-level C module with FFI declarations.
+ pub fn generate_module_map(&self) -> bool {
+ self.generate_module_map.unwrap_or(true)
+ }
+
+ /// Whether to omit argument labels in Swift function definitions.
+ pub fn omit_argument_labels(&self) -> bool {
+ self.omit_argument_labels.unwrap_or(false)
+ }
+}
+
+impl BindingsConfig for Config {
+ fn update_from_ci(&mut self, ci: &ComponentInterface) {
+ self.module_name
+ .get_or_insert_with(|| ci.namespace().into());
+ self.cdylib_name
+ .get_or_insert_with(|| format!("uniffi_{}", ci.namespace()));
+ }
+
+ fn update_from_cdylib_name(&mut self, cdylib_name: &str) {
+ self.cdylib_name
+ .get_or_insert_with(|| cdylib_name.to_string());
+ }
+
+ fn update_from_dependency_configs(&mut self, _config_map: HashMap<&str, &Self>) {}
+}
+
+/// Generate UniFFI component bindings for Swift, as strings in memory.
+///
+pub fn generate_bindings(config: &Config, ci: &ComponentInterface) -> Result<Bindings> {
+ let header = BridgingHeader::new(config, ci)
+ .render()
+ .context("failed to render Swift bridging header")?;
+ let library = SwiftWrapper::new(config.clone(), ci)
+ .render()
+ .context("failed to render Swift library")?;
+ let modulemap = if config.generate_module_map() {
+ Some(
+ ModuleMap::new(config, ci)
+ .render()
+ .context("failed to render Swift modulemap")?,
+ )
+ } else {
+ None
+ };
+ Ok(Bindings {
+ library,
+ header,
+ modulemap,
+ })
+}
+
+/// Renders Swift helper code for all types
+///
+/// This template is a bit different than others in that it stores internal state from the render
+/// process. Make sure to only call `render()` once.
+#[derive(Template)]
+#[template(syntax = "swift", escape = "none", path = "Types.swift")]
+pub struct TypeRenderer<'a> {
+ config: &'a Config,
+ ci: &'a ComponentInterface,
+ // Track included modules for the `include_once()` macro
+ include_once_names: RefCell<HashSet<String>>,
+ // Track imports added with the `add_import()` macro
+ imports: RefCell<BTreeSet<String>>,
+}
+
+impl<'a> TypeRenderer<'a> {
+ fn new(config: &'a Config, ci: &'a ComponentInterface) -> Self {
+ Self {
+ config,
+ ci,
+ include_once_names: RefCell::new(HashSet::new()),
+ imports: RefCell::new(BTreeSet::new()),
+ }
+ }
+
+ // The following methods are used by the `Types.swift` macros.
+
+ // Helper for the including a template, but only once.
+ //
+ // The first time this is called with a name it will return true, indicating that we should
+ // include the template. Subsequent calls will return false.
+ fn include_once_check(&self, name: &str) -> bool {
+ self.include_once_names
+ .borrow_mut()
+ .insert(name.to_string())
+ }
+
+ // Helper to add an import statement
+ //
+ // Call this inside your template to cause an import statement to be added at the top of the
+ // file. Imports will be sorted and de-deuped.
+ //
+ // Returns an empty string so that it can be used inside an askama `{{ }}` block.
+ fn add_import(&self, name: &str) -> &str {
+ self.imports.borrow_mut().insert(name.to_owned());
+ ""
+ }
+}
+
+/// Template for generating the `.h` file that defines the low-level C FFI.
+///
+/// This file defines only the low-level structs and functions that are exposed
+/// by the compiled Rust code. It gets wrapped into a higher-level API by the
+/// code from [`SwiftWrapper`].
+#[derive(Template)]
+#[template(syntax = "c", escape = "none", path = "BridgingHeaderTemplate.h")]
+pub struct BridgingHeader<'config, 'ci> {
+ _config: &'config Config,
+ ci: &'ci ComponentInterface,
+}
+
+impl<'config, 'ci> BridgingHeader<'config, 'ci> {
+ pub fn new(config: &'config Config, ci: &'ci ComponentInterface) -> Self {
+ Self {
+ _config: config,
+ ci,
+ }
+ }
+}
+
+/// Template for generating the `.modulemap` file that exposes the low-level C FFI.
+///
+/// This file defines how the low-level C FFI from [`BridgingHeader`] gets exposed
+/// as a Swift module that can be called by other Swift code. In our case, its only
+/// job is to define the *name* of the Swift module that will contain the FFI functions
+/// so that it can be imported by the higher-level code in from [`SwiftWrapper`].
+#[derive(Template)]
+#[template(syntax = "c", escape = "none", path = "ModuleMapTemplate.modulemap")]
+pub struct ModuleMap<'config, 'ci> {
+ config: &'config Config,
+ _ci: &'ci ComponentInterface,
+}
+
+impl<'config, 'ci> ModuleMap<'config, 'ci> {
+ pub fn new(config: &'config Config, _ci: &'ci ComponentInterface) -> Self {
+ Self { config, _ci }
+ }
+}
+
+#[derive(Template)]
+#[template(syntax = "swift", escape = "none", path = "wrapper.swift")]
+pub struct SwiftWrapper<'a> {
+ config: Config,
+ ci: &'a ComponentInterface,
+ type_helper_code: String,
+ type_imports: BTreeSet<String>,
+ has_async_fns: bool,
+}
+impl<'a> SwiftWrapper<'a> {
+ pub fn new(config: Config, ci: &'a ComponentInterface) -> Self {
+ let type_renderer = TypeRenderer::new(&config, ci);
+ let type_helper_code = type_renderer.render().unwrap();
+ let type_imports = type_renderer.imports.into_inner();
+ Self {
+ config,
+ ci,
+ type_helper_code,
+ type_imports,
+ has_async_fns: ci.has_async_fns(),
+ }
+ }
+
+ pub fn imports(&self) -> Vec<String> {
+ self.type_imports.iter().cloned().collect()
+ }
+
+ pub fn initialization_fns(&self) -> Vec<String> {
+ self.ci
+ .iter_types()
+ .map(|t| SwiftCodeOracle.find(t))
+ .filter_map(|ct| ct.initialization_fn())
+ .chain(
+ self.has_async_fns
+ .then(|| "uniffiInitContinuationCallback".into()),
+ )
+ .collect()
+ }
+}
+
+#[derive(Clone)]
+pub struct SwiftCodeOracle;
+
+impl SwiftCodeOracle {
+ // Map `Type` instances to a `Box<dyn CodeType>` for that type.
+ //
+ // There is a companion match in `templates/Types.swift` which performs a similar function for the
+ // template code.
+ //
+ // - When adding additional types here, make sure to also add a match arm to the `Types.swift` template.
+ // - To keep things manageable, let's try to limit ourselves to these 2 mega-matches
+ fn create_code_type(&self, type_: Type) -> Box<dyn CodeType> {
+ match type_ {
+ Type::UInt8 => Box::new(primitives::UInt8CodeType),
+ Type::Int8 => Box::new(primitives::Int8CodeType),
+ Type::UInt16 => Box::new(primitives::UInt16CodeType),
+ Type::Int16 => Box::new(primitives::Int16CodeType),
+ Type::UInt32 => Box::new(primitives::UInt32CodeType),
+ Type::Int32 => Box::new(primitives::Int32CodeType),
+ Type::UInt64 => Box::new(primitives::UInt64CodeType),
+ Type::Int64 => Box::new(primitives::Int64CodeType),
+ Type::Float32 => Box::new(primitives::Float32CodeType),
+ Type::Float64 => Box::new(primitives::Float64CodeType),
+ Type::Boolean => Box::new(primitives::BooleanCodeType),
+ Type::String => Box::new(primitives::StringCodeType),
+ Type::Bytes => Box::new(primitives::BytesCodeType),
+
+ Type::Timestamp => Box::new(miscellany::TimestampCodeType),
+ Type::Duration => Box::new(miscellany::DurationCodeType),
+
+ Type::Enum { name, .. } => Box::new(enum_::EnumCodeType::new(name)),
+ Type::Object { name, .. } => Box::new(object::ObjectCodeType::new(name)),
+ Type::Record { name, .. } => Box::new(record::RecordCodeType::new(name)),
+ Type::CallbackInterface { name, .. } => {
+ Box::new(callback_interface::CallbackInterfaceCodeType::new(name))
+ }
+ Type::ForeignExecutor => Box::new(executor::ForeignExecutorCodeType),
+ Type::Optional { inner_type } => {
+ Box::new(compounds::OptionalCodeType::new(*inner_type))
+ }
+ Type::Sequence { inner_type } => {
+ Box::new(compounds::SequenceCodeType::new(*inner_type))
+ }
+ Type::Map {
+ key_type,
+ value_type,
+ } => Box::new(compounds::MapCodeType::new(*key_type, *value_type)),
+ Type::External { name, .. } => Box::new(external::ExternalCodeType::new(name)),
+ Type::Custom { name, .. } => Box::new(custom::CustomCodeType::new(name)),
+ }
+ }
+
+ fn find(&self, type_: &Type) -> Box<dyn CodeType> {
+ self.create_code_type(type_.clone())
+ }
+
+ /// Get the idiomatic Swift rendering of a class name (for enums, records, errors, etc).
+ fn class_name(&self, nm: &str) -> String {
+ nm.to_string().to_upper_camel_case()
+ }
+
+ /// Get the idiomatic Swift rendering of a function name.
+ fn fn_name(&self, nm: &str) -> String {
+ nm.to_string().to_lower_camel_case()
+ }
+
+ /// Get the idiomatic Swift rendering of a variable name.
+ fn var_name(&self, nm: &str) -> String {
+ nm.to_string().to_lower_camel_case()
+ }
+
+ /// Get the idiomatic Swift rendering of an individual enum variant.
+ fn enum_variant_name(&self, nm: &str) -> String {
+ nm.to_string().to_lower_camel_case()
+ }
+
+ fn ffi_type_label_raw(&self, ffi_type: &FfiType) -> String {
+ match ffi_type {
+ FfiType::Int8 => "Int8".into(),
+ FfiType::UInt8 => "UInt8".into(),
+ FfiType::Int16 => "Int16".into(),
+ FfiType::UInt16 => "UInt16".into(),
+ FfiType::Int32 => "Int32".into(),
+ FfiType::UInt32 => "UInt32".into(),
+ FfiType::Int64 => "Int64".into(),
+ FfiType::UInt64 => "UInt64".into(),
+ FfiType::Float32 => "Float".into(),
+ FfiType::Float64 => "Double".into(),
+ FfiType::RustArcPtr(_) => "UnsafeMutableRawPointer".into(),
+ FfiType::RustBuffer(_) => "RustBuffer".into(),
+ FfiType::ForeignBytes => "ForeignBytes".into(),
+ FfiType::ForeignCallback => "ForeignCallback".into(),
+ FfiType::ForeignExecutorHandle => "Int".into(),
+ FfiType::ForeignExecutorCallback => "ForeignExecutorCallback".into(),
+ FfiType::RustFutureContinuationCallback => "UniFfiRustFutureContinuation".into(),
+ FfiType::RustFutureHandle | FfiType::RustFutureContinuationData => {
+ "UnsafeMutableRawPointer".into()
+ }
+ }
+ }
+
+ fn ffi_type_label(&self, ffi_type: &FfiType) -> String {
+ match ffi_type {
+ FfiType::ForeignCallback
+ | FfiType::ForeignExecutorCallback
+ | FfiType::RustFutureHandle
+ | FfiType::RustFutureContinuationCallback
+ | FfiType::RustFutureContinuationData => {
+ format!("{} _Nonnull", self.ffi_type_label_raw(ffi_type))
+ }
+ _ => self.ffi_type_label_raw(ffi_type),
+ }
+ }
+
+ fn ffi_canonical_name(&self, ffi_type: &FfiType) -> String {
+ self.ffi_type_label_raw(ffi_type)
+ }
+}
+
+pub mod filters {
+ use super::*;
+ pub use crate::backend::filters::*;
+
+ fn oracle() -> &'static SwiftCodeOracle {
+ &SwiftCodeOracle
+ }
+
+ pub fn type_name(as_type: &impl AsType) -> Result<String, askama::Error> {
+ Ok(oracle().find(&as_type.as_type()).type_label())
+ }
+
+ pub fn canonical_name(as_type: &impl AsType) -> Result<String, askama::Error> {
+ Ok(oracle().find(&as_type.as_type()).canonical_name())
+ }
+
+ pub fn ffi_converter_name(as_type: &impl AsType) -> Result<String, askama::Error> {
+ Ok(oracle().find(&as_type.as_type()).ffi_converter_name())
+ }
+
+ pub fn lower_fn(as_type: &impl AsType) -> Result<String, askama::Error> {
+ Ok(oracle().find(&as_type.as_type()).lower())
+ }
+
+ pub fn write_fn(as_type: &impl AsType) -> Result<String, askama::Error> {
+ Ok(oracle().find(&as_type.as_type()).write())
+ }
+
+ pub fn lift_fn(as_type: &impl AsType) -> Result<String, askama::Error> {
+ Ok(oracle().find(&as_type.as_type()).lift())
+ }
+
+ pub fn read_fn(as_type: &impl AsType) -> Result<String, askama::Error> {
+ Ok(oracle().find(&as_type.as_type()).read())
+ }
+
+ pub fn literal_swift(
+ literal: &Literal,
+ as_type: &impl AsType,
+ ) -> Result<String, askama::Error> {
+ Ok(oracle().find(&as_type.as_type()).literal(literal))
+ }
+
+ /// Get the Swift type for an FFIType
+ pub fn ffi_type_name(ffi_type: &FfiType) -> Result<String, askama::Error> {
+ Ok(oracle().ffi_type_label(ffi_type))
+ }
+
+ pub fn ffi_canonical_name(ffi_type: &FfiType) -> Result<String, askama::Error> {
+ Ok(oracle().ffi_canonical_name(ffi_type))
+ }
+
+ /// Like `ffi_type_name`, but used in `BridgingHeaderTemplate.h` which uses a slightly different
+ /// names.
+ pub fn header_ffi_type_name(ffi_type: &FfiType) -> Result<String, askama::Error> {
+ Ok(match ffi_type {
+ FfiType::Int8 => "int8_t".into(),
+ FfiType::UInt8 => "uint8_t".into(),
+ FfiType::Int16 => "int16_t".into(),
+ FfiType::UInt16 => "uint16_t".into(),
+ FfiType::Int32 => "int32_t".into(),
+ FfiType::UInt32 => "uint32_t".into(),
+ FfiType::Int64 => "int64_t".into(),
+ FfiType::UInt64 => "uint64_t".into(),
+ FfiType::Float32 => "float".into(),
+ FfiType::Float64 => "double".into(),
+ FfiType::RustArcPtr(_) => "void*_Nonnull".into(),
+ FfiType::RustBuffer(_) => "RustBuffer".into(),
+ FfiType::ForeignBytes => "ForeignBytes".into(),
+ FfiType::ForeignCallback => "ForeignCallback _Nonnull".into(),
+ FfiType::ForeignExecutorCallback => "UniFfiForeignExecutorCallback _Nonnull".into(),
+ FfiType::ForeignExecutorHandle => "size_t".into(),
+ FfiType::RustFutureContinuationCallback => {
+ "UniFfiRustFutureContinuation _Nonnull".into()
+ }
+ FfiType::RustFutureHandle | FfiType::RustFutureContinuationData => {
+ "void* _Nonnull".into()
+ }
+ })
+ }
+
+ /// Get the idiomatic Swift rendering of a class name (for enums, records, errors, etc).
+ pub fn class_name(nm: &str) -> Result<String, askama::Error> {
+ Ok(oracle().class_name(nm))
+ }
+
+ /// Get the idiomatic Swift rendering of a function name.
+ pub fn fn_name(nm: &str) -> Result<String, askama::Error> {
+ Ok(quote_general_keyword(oracle().fn_name(nm)))
+ }
+
+ /// Get the idiomatic Swift rendering of a variable name.
+ pub fn var_name(nm: &str) -> Result<String, askama::Error> {
+ Ok(quote_general_keyword(oracle().var_name(nm)))
+ }
+
+ /// Get the idiomatic Swift rendering of an arguments name.
+ /// This is the same as the var name but quoting is not required.
+ pub fn arg_name(nm: &str) -> Result<String, askama::Error> {
+ Ok(quote_arg_keyword(oracle().var_name(nm)))
+ }
+
+ /// Get the idiomatic Swift rendering of an individual enum variant, quoted if it is a keyword (for use in e.g. declarations)
+ pub fn enum_variant_swift_quoted(nm: &str) -> Result<String, askama::Error> {
+ Ok(quote_general_keyword(oracle().enum_variant_name(nm)))
+ }
+
+ /// Get the idiomatic Swift rendering of an individual enum variant, for contexts (for use in non-declaration contexts where quoting is not needed)
+ pub fn enum_variant_swift(nm: &str) -> Result<String, askama::Error> {
+ Ok(oracle().enum_variant_name(nm))
+ }
+
+ pub fn error_handler(result: &ResultType) -> Result<String, askama::Error> {
+ Ok(match &result.throws_type {
+ Some(t) => format!("{}.lift", ffi_converter_name(t)?),
+ None => "nil".into(),
+ })
+ }
+
+ /// Name of the callback function to handle an async result
+ pub fn future_callback(result: &ResultType) -> Result<String, askama::Error> {
+ Ok(format!(
+ "uniffiFutureCallbackHandler{}{}",
+ match &result.return_type {
+ Some(t) => SwiftCodeOracle.find(t).canonical_name(),
+ None => "Void".into(),
+ },
+ match &result.throws_type {
+ Some(t) => SwiftCodeOracle.find(t).canonical_name(),
+ None => "".into(),
+ }
+ ))
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/object.rs b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/object.rs
new file mode 100644
index 0000000000..ea140c998d
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/object.rs
@@ -0,0 +1,26 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use super::CodeType;
+
+#[derive(Debug)]
+pub struct ObjectCodeType {
+ id: String,
+}
+
+impl ObjectCodeType {
+ pub fn new(id: String) -> Self {
+ Self { id }
+ }
+}
+
+impl CodeType for ObjectCodeType {
+ fn type_label(&self) -> String {
+ super::SwiftCodeOracle.class_name(&self.id)
+ }
+
+ fn canonical_name(&self) -> String {
+ format!("Type{}", self.id)
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/primitives.rs b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/primitives.rs
new file mode 100644
index 0000000000..86424658a3
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/primitives.rs
@@ -0,0 +1,91 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use super::CodeType;
+use crate::backend::Literal;
+use crate::interface::{Radix, Type};
+use paste::paste;
+
+fn render_literal(literal: &Literal) -> String {
+ fn typed_number(type_: &Type, num_str: String) -> String {
+ match type_ {
+ // special case Int32.
+ Type::Int32 => num_str,
+ // otherwise use constructor e.g. UInt8(x)
+ Type::Int8
+ | Type::UInt8
+ | Type::Int16
+ | Type::UInt16
+ | Type::UInt32
+ | Type::Int64
+ | Type::UInt64
+ | Type::Float32
+ | Type::Float64 =>
+ // XXX we should pass in the codetype itself.
+ {
+ format!(
+ "{}({num_str})",
+ super::SwiftCodeOracle.find(type_).type_label()
+ )
+ }
+ _ => panic!("Unexpected literal: {num_str} is not a number"),
+ }
+ }
+
+ match literal {
+ Literal::Boolean(v) => format!("{v}"),
+ Literal::String(s) => format!("\"{s}\""),
+ Literal::Int(i, radix, type_) => typed_number(
+ type_,
+ match radix {
+ Radix::Octal => format!("0o{i:o}"),
+ Radix::Decimal => format!("{i}"),
+ Radix::Hexadecimal => format!("{i:#x}"),
+ },
+ ),
+ Literal::UInt(i, radix, type_) => typed_number(
+ type_,
+ match radix {
+ Radix::Octal => format!("0o{i:o}"),
+ Radix::Decimal => format!("{i}"),
+ Radix::Hexadecimal => format!("{i:#x}"),
+ },
+ ),
+ Literal::Float(string, type_) => typed_number(type_, string.clone()),
+ _ => unreachable!("Literal"),
+ }
+}
+
+macro_rules! impl_code_type_for_primitive {
+ ($T:ty, $class_name:literal) => {
+ paste! {
+ #[derive(Debug)]
+ pub struct $T;
+
+ impl CodeType for $T {
+ fn type_label(&self) -> String {
+ $class_name.into()
+ }
+
+ fn literal(&self, literal: &Literal) -> String {
+ render_literal(&literal)
+ }
+ }
+ }
+ };
+}
+
+impl_code_type_for_primitive!(BooleanCodeType, "Bool");
+impl_code_type_for_primitive!(StringCodeType, "String");
+impl_code_type_for_primitive!(BytesCodeType, "Data");
+impl_code_type_for_primitive!(Int8CodeType, "Int8");
+impl_code_type_for_primitive!(Int16CodeType, "Int16");
+impl_code_type_for_primitive!(Int32CodeType, "Int32");
+impl_code_type_for_primitive!(Int64CodeType, "Int64");
+impl_code_type_for_primitive!(UInt8CodeType, "UInt8");
+impl_code_type_for_primitive!(UInt16CodeType, "UInt16");
+impl_code_type_for_primitive!(UInt32CodeType, "UInt32");
+impl_code_type_for_primitive!(UInt64CodeType, "UInt64");
+impl_code_type_for_primitive!(Float32CodeType, "Float");
+impl_code_type_for_primitive!(Float64CodeType, "Double");
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/record.rs b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/record.rs
new file mode 100644
index 0000000000..401109011f
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/record.rs
@@ -0,0 +1,26 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use super::CodeType;
+
+#[derive(Debug)]
+pub struct RecordCodeType {
+ id: String,
+}
+
+impl RecordCodeType {
+ pub fn new(id: String) -> Self {
+ Self { id }
+ }
+}
+
+impl CodeType for RecordCodeType {
+ fn type_label(&self) -> String {
+ super::SwiftCodeOracle.class_name(&self.id)
+ }
+
+ fn canonical_name(&self) -> String {
+ format!("Type{}", self.id)
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/mod.rs b/third_party/rust/uniffi_bindgen/src/bindings/swift/mod.rs
new file mode 100644
index 0000000000..bf17f38a4e
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/mod.rs
@@ -0,0 +1,97 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! # Swift bindings backend for UniFFI
+//!
+//! This module generates Swift bindings from a [`ComponentInterface`] definition,
+//! using Swift's builtin support for loading C header files.
+//!
+//! Conceptually, the generated bindings are split into two Swift modules, one for the low-level
+//! C FFI layer and one for the higher-level Swift bindings. For a UniFFI component named "example"
+//! we generate:
+//!
+//! * A C header file `exampleFFI.h` declaring the low-level structs and functions for calling
+//! into Rust, along with a corresponding `exampleFFI.modulemap` to expose them to Swift.
+//!
+//! * A Swift source file `example.swift` that imports the `exampleFFI` module and wraps it
+//! to provide the higher-level Swift API.
+//!
+//! Most of the concepts in a [`ComponentInterface`] have an obvious counterpart in Swift,
+//! with the details documented in inline comments where appropriate.
+//!
+//! To handle lifting/lowering/serializing types across the FFI boundary, the Swift code
+//! defines a `protocol ViaFfi` that is analogous to the `uniffi::ViaFfi` Rust trait.
+//! Each type that can traverse the FFI conforms to the `ViaFfi` protocol, which specifies:
+//!
+//! * The corresponding low-level type.
+//! * How to lift from and lower into into that type.
+//! * How to read from and write into a byte buffer.
+//!
+
+use std::process::Command;
+
+use anyhow::Result;
+use camino::Utf8Path;
+use fs_err as fs;
+
+pub mod gen_swift;
+pub use gen_swift::{generate_bindings, Config};
+mod test;
+
+use super::super::interface::ComponentInterface;
+pub use test::{run_script, run_test};
+
+/// The Swift bindings generated from a [`ComponentInterface`].
+///
+pub struct Bindings {
+ /// The contents of the generated `.swift` file, as a string.
+ library: String,
+ /// The contents of the generated `.h` file, as a string.
+ header: String,
+ /// The contents of the generated `.modulemap` file, as a string.
+ modulemap: Option<String>,
+}
+
+/// Write UniFFI component bindings for Swift as files on disk.
+///
+/// Unlike other target languages, binding to Rust code from Swift involves more than just
+/// generating a `.swift` file. We also need to produce a `.h` file with the C-level API
+/// declarations, and a `.modulemap` file to tell Swift how to use it.
+pub fn write_bindings(
+ config: &Config,
+ ci: &ComponentInterface,
+ out_dir: &Utf8Path,
+ try_format_code: bool,
+) -> Result<()> {
+ let Bindings {
+ header,
+ library,
+ modulemap,
+ } = generate_bindings(config, ci)?;
+
+ let source_file = out_dir.join(format!("{}.swift", config.module_name()));
+ fs::write(&source_file, library)?;
+
+ let header_file = out_dir.join(config.header_filename());
+ fs::write(header_file, header)?;
+
+ if let Some(modulemap) = modulemap {
+ let modulemap_file = out_dir.join(config.modulemap_filename());
+ fs::write(modulemap_file, modulemap)?;
+ }
+
+ if try_format_code {
+ if let Err(e) = Command::new("swiftformat")
+ .arg(source_file.as_str())
+ .output()
+ {
+ println!(
+ "Warning: Unable to auto-format {} using swiftformat: {e:?}",
+ source_file.file_name().unwrap(),
+ );
+ }
+ }
+
+ Ok(())
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Async.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Async.swift
new file mode 100644
index 0000000000..695208861d
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Async.swift
@@ -0,0 +1,62 @@
+private let UNIFFI_RUST_FUTURE_POLL_READY: Int8 = 0
+private let UNIFFI_RUST_FUTURE_POLL_MAYBE_READY: Int8 = 1
+
+fileprivate func uniffiRustCallAsync<F, T>(
+ rustFutureFunc: () -> UnsafeMutableRawPointer,
+ pollFunc: (UnsafeMutableRawPointer, UnsafeMutableRawPointer) -> (),
+ completeFunc: (UnsafeMutableRawPointer, UnsafeMutablePointer<RustCallStatus>) -> F,
+ freeFunc: (UnsafeMutableRawPointer) -> (),
+ liftFunc: (F) throws -> T,
+ errorHandler: ((RustBuffer) throws -> Error)?
+) async throws -> T {
+ // Make sure to call uniffiEnsureInitialized() since future creation doesn't have a
+ // RustCallStatus param, so doesn't use makeRustCall()
+ uniffiEnsureInitialized()
+ let rustFuture = rustFutureFunc()
+ defer {
+ freeFunc(rustFuture)
+ }
+ var pollResult: Int8;
+ repeat {
+ pollResult = await withUnsafeContinuation {
+ pollFunc(rustFuture, ContinuationHolder($0).toOpaque())
+ }
+ } while pollResult != UNIFFI_RUST_FUTURE_POLL_READY
+
+ return try liftFunc(makeRustCall(
+ { completeFunc(rustFuture, $0) },
+ errorHandler: errorHandler
+ ))
+}
+
+// Callback handlers for an async calls. These are invoked by Rust when the future is ready. They
+// lift the return value or error and resume the suspended function.
+fileprivate func uniffiFutureContinuationCallback(ptr: UnsafeMutableRawPointer, pollResult: Int8) {
+ ContinuationHolder.fromOpaque(ptr).resume(pollResult)
+}
+
+// Wraps UnsafeContinuation in a class so that we can use reference counting when passing it across
+// the FFI
+fileprivate class ContinuationHolder {
+ let continuation: UnsafeContinuation<Int8, Never>
+
+ init(_ continuation: UnsafeContinuation<Int8, Never>) {
+ self.continuation = continuation
+ }
+
+ func resume(_ pollResult: Int8) {
+ self.continuation.resume(returning: pollResult)
+ }
+
+ func toOpaque() -> UnsafeMutableRawPointer {
+ return Unmanaged<ContinuationHolder>.passRetained(self).toOpaque()
+ }
+
+ static func fromOpaque(_ ptr: UnsafeRawPointer) -> ContinuationHolder {
+ return Unmanaged<ContinuationHolder>.fromOpaque(ptr).takeRetainedValue()
+ }
+}
+
+fileprivate func uniffiInitContinuationCallback() {
+ {{ ci.ffi_rust_future_continuation_callback_set().name() }}(uniffiFutureContinuationCallback)
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/BooleanHelper.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/BooleanHelper.swift
new file mode 100644
index 0000000000..465e519628
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/BooleanHelper.swift
@@ -0,0 +1,20 @@
+fileprivate struct FfiConverterBool : FfiConverter {
+ typealias FfiType = Int8
+ typealias SwiftType = Bool
+
+ public static func lift(_ value: Int8) throws -> Bool {
+ return value != 0
+ }
+
+ public static func lower(_ value: Bool) -> Int8 {
+ return value ? 1 : 0
+ }
+
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Bool {
+ return try lift(readInt(&buf))
+ }
+
+ public static func write(_ value: Bool, into buf: inout [UInt8]) {
+ writeInt(&buf, lower(value))
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/BridgingHeaderTemplate.h b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/BridgingHeaderTemplate.h
new file mode 100644
index 0000000000..87698e359f
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/BridgingHeaderTemplate.h
@@ -0,0 +1,79 @@
+// This file was autogenerated by some hot garbage in the `uniffi` crate.
+// Trust me, you don't want to mess with it!
+
+#pragma once
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+// The following structs are used to implement the lowest level
+// of the FFI, and thus useful to multiple uniffied crates.
+// We ensure they are declared exactly once, with a header guard, UNIFFI_SHARED_H.
+#ifdef UNIFFI_SHARED_H
+ // We also try to prevent mixing versions of shared uniffi header structs.
+ // If you add anything to the #else block, you must increment the version suffix in UNIFFI_SHARED_HEADER_V4
+ #ifndef UNIFFI_SHARED_HEADER_V4
+ #error Combining helper code from multiple versions of uniffi is not supported
+ #endif // ndef UNIFFI_SHARED_HEADER_V4
+#else
+#define UNIFFI_SHARED_H
+#define UNIFFI_SHARED_HEADER_V4
+// ⚠️ Attention: If you change this #else block (ending in `#endif // def UNIFFI_SHARED_H`) you *must* ⚠️
+// ⚠️ increment the version suffix in all instances of UNIFFI_SHARED_HEADER_V4 in this file. ⚠️
+
+typedef struct RustBuffer
+{
+ int32_t capacity;
+ int32_t len;
+ uint8_t *_Nullable data;
+} RustBuffer;
+
+typedef int32_t (*ForeignCallback)(uint64_t, int32_t, const uint8_t *_Nonnull, int32_t, RustBuffer *_Nonnull);
+
+// Task defined in Rust that Swift executes
+typedef void (*UniFfiRustTaskCallback)(const void * _Nullable, int8_t);
+
+// Callback to execute Rust tasks using a Swift Task
+//
+// Args:
+// executor: ForeignExecutor lowered into a size_t value
+// delay: Delay in MS
+// task: UniFfiRustTaskCallback to call
+// task_data: data to pass the task callback
+typedef int8_t (*UniFfiForeignExecutorCallback)(size_t, uint32_t, UniFfiRustTaskCallback _Nullable, const void * _Nullable);
+
+typedef struct ForeignBytes
+{
+ int32_t len;
+ const uint8_t *_Nullable data;
+} ForeignBytes;
+
+// Error definitions
+typedef struct RustCallStatus {
+ int8_t code;
+ RustBuffer errorBuf;
+} RustCallStatus;
+
+// ⚠️ Attention: If you change this #else block (ending in `#endif // def UNIFFI_SHARED_H`) you *must* ⚠️
+// ⚠️ increment the version suffix in all instances of UNIFFI_SHARED_HEADER_V4 in this file. ⚠️
+#endif // def UNIFFI_SHARED_H
+
+// Continuation callback for UniFFI Futures
+typedef void (*UniFfiRustFutureContinuation)(void * _Nonnull, int8_t);
+
+// Scaffolding functions
+{%- for func in ci.iter_ffi_function_definitions() %}
+{% match func.return_type() -%}{%- when Some with (type_) %}{{ type_|header_ffi_type_name }}{% when None %}void{% endmatch %} {{ func.name() }}(
+ {%- if func.arguments().len() > 0 %}
+ {%- for arg in func.arguments() %}
+ {{- arg.type_().borrow()|header_ffi_type_name }} {{ arg.name() -}}{% if !loop.last || func.has_rust_call_status_arg() %}, {% endif %}
+ {%- endfor %}
+ {%- if func.has_rust_call_status_arg() %}RustCallStatus *_Nonnull out_status{% endif %}
+ {%- else %}
+ {%- if func.has_rust_call_status_arg() %}RustCallStatus *_Nonnull out_status{%- else %}void{% endif %}
+ {% endif %}
+);
+{%- endfor %}
+
+{% import "macros.swift" as swift %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/CallbackInterfaceRuntime.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/CallbackInterfaceRuntime.swift
new file mode 100644
index 0000000000..9ae62d1667
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/CallbackInterfaceRuntime.swift
@@ -0,0 +1,64 @@
+fileprivate extension NSLock {
+ func withLock<T>(f: () throws -> T) rethrows -> T {
+ self.lock()
+ defer { self.unlock() }
+ return try f()
+ }
+}
+
+fileprivate typealias UniFFICallbackHandle = UInt64
+fileprivate class UniFFICallbackHandleMap<T> {
+ private var leftMap: [UniFFICallbackHandle: T] = [:]
+ private var counter: [UniFFICallbackHandle: UInt64] = [:]
+ private var rightMap: [ObjectIdentifier: UniFFICallbackHandle] = [:]
+
+ private let lock = NSLock()
+ private var currentHandle: UniFFICallbackHandle = 0
+ private let stride: UniFFICallbackHandle = 1
+
+ func insert(obj: T) -> UniFFICallbackHandle {
+ lock.withLock {
+ let id = ObjectIdentifier(obj as AnyObject)
+ let handle = rightMap[id] ?? {
+ currentHandle += stride
+ let handle = currentHandle
+ leftMap[handle] = obj
+ rightMap[id] = handle
+ return handle
+ }()
+ counter[handle] = (counter[handle] ?? 0) + 1
+ return handle
+ }
+ }
+
+ func get(handle: UniFFICallbackHandle) -> T? {
+ lock.withLock {
+ leftMap[handle]
+ }
+ }
+
+ func delete(handle: UniFFICallbackHandle) {
+ remove(handle: handle)
+ }
+
+ @discardableResult
+ func remove(handle: UniFFICallbackHandle) -> T? {
+ lock.withLock {
+ defer { counter[handle] = (counter[handle] ?? 1) - 1 }
+ guard counter[handle] == 1 else { return leftMap[handle] }
+ let obj = leftMap.removeValue(forKey: handle)
+ if let obj = obj {
+ rightMap.removeValue(forKey: ObjectIdentifier(obj as AnyObject))
+ }
+ return obj
+ }
+ }
+}
+
+// Magic number for the Rust proxy to call using the same mechanism as every other method,
+// to free the callback once it's dropped by Rust.
+private let IDX_CALLBACK_FREE: Int32 = 0
+// Callback return codes
+private let UNIFFI_CALLBACK_SUCCESS: Int32 = 0
+private let UNIFFI_CALLBACK_ERROR: Int32 = 1
+private let UNIFFI_CALLBACK_UNEXPECTED_ERROR: Int32 = 2
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/CallbackInterfaceTemplate.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/CallbackInterfaceTemplate.swift
new file mode 100644
index 0000000000..aec8ded930
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/CallbackInterfaceTemplate.swift
@@ -0,0 +1,150 @@
+{%- let cbi = ci|get_callback_interface_definition(name) %}
+{%- let foreign_callback = format!("foreignCallback{}", canonical_type_name) %}
+{%- if self.include_once_check("CallbackInterfaceRuntime.swift") %}{%- include "CallbackInterfaceRuntime.swift" %}{%- endif %}
+
+// Declaration and FfiConverters for {{ type_name }} Callback Interface
+
+public protocol {{ type_name }} : AnyObject {
+ {% for meth in cbi.methods() -%}
+ func {{ meth.name()|fn_name }}({% call swift::arg_list_protocol(meth) %}) {% call swift::throws(meth) -%}
+ {%- match meth.return_type() -%}
+ {%- when Some with (return_type) %} -> {{ return_type|type_name -}}
+ {%- else -%}
+ {%- endmatch %}
+ {% endfor %}
+}
+
+// The ForeignCallback that is passed to Rust.
+fileprivate let {{ foreign_callback }} : ForeignCallback =
+ { (handle: UniFFICallbackHandle, method: Int32, argsData: UnsafePointer<UInt8>, argsLen: Int32, out_buf: UnsafeMutablePointer<RustBuffer>) -> Int32 in
+ {% for meth in cbi.methods() -%}
+ {%- let method_name = format!("invoke_{}", meth.name())|fn_name %}
+
+ func {{ method_name }}(_ swiftCallbackInterface: {{ type_name }}, _ argsData: UnsafePointer<UInt8>, _ argsLen: Int32, _ out_buf: UnsafeMutablePointer<RustBuffer>) throws -> Int32 {
+ {%- if meth.arguments().len() > 0 %}
+ var reader = createReader(data: Data(bytes: argsData, count: Int(argsLen)))
+ {%- endif %}
+
+ {%- match meth.return_type() %}
+ {%- when Some(return_type) %}
+ func makeCall() throws -> Int32 {
+ let result = {% if meth.throws() %} try{% endif %} swiftCallbackInterface.{{ meth.name()|fn_name }}(
+ {% for arg in meth.arguments() -%}
+ {% if !config.omit_argument_labels() %}{{ arg.name()|var_name }}: {% endif %} try {{ arg|read_fn }}(from: &reader)
+ {%- if !loop.last %}, {% endif %}
+ {% endfor -%}
+ )
+ var writer = [UInt8]()
+ {{ return_type|write_fn }}(result, into: &writer)
+ out_buf.pointee = RustBuffer(bytes: writer)
+ return UNIFFI_CALLBACK_SUCCESS
+ }
+ {%- when None %}
+ func makeCall() throws -> Int32 {
+ try swiftCallbackInterface.{{ meth.name()|fn_name }}(
+ {% for arg in meth.arguments() -%}
+ {% if !config.omit_argument_labels() %}{{ arg.name()|var_name }}: {% endif %} try {{ arg|read_fn }}(from: &reader)
+ {%- if !loop.last %}, {% endif %}
+ {% endfor -%}
+ )
+ return UNIFFI_CALLBACK_SUCCESS
+ }
+ {%- endmatch %}
+
+ {%- match meth.throws_type() %}
+ {%- when None %}
+ return try makeCall()
+ {%- when Some(error_type) %}
+ do {
+ return try makeCall()
+ } catch let error as {{ error_type|type_name }} {
+ out_buf.pointee = {{ error_type|lower_fn }}(error)
+ return UNIFFI_CALLBACK_ERROR
+ }
+ {%- endmatch %}
+ }
+ {%- endfor %}
+
+
+ switch method {
+ case IDX_CALLBACK_FREE:
+ {{ ffi_converter_name }}.drop(handle: handle)
+ // Sucessful return
+ // See docs of ForeignCallback in `uniffi_core/src/ffi/foreigncallbacks.rs`
+ return UNIFFI_CALLBACK_SUCCESS
+ {% for meth in cbi.methods() -%}
+ {% let method_name = format!("invoke_{}", meth.name())|fn_name -%}
+ case {{ loop.index }}:
+ let cb: {{ cbi|type_name }}
+ do {
+ cb = try {{ ffi_converter_name }}.lift(handle)
+ } catch {
+ out_buf.pointee = {{ Type::String.borrow()|lower_fn }}("{{ cbi.name() }}: Invalid handle")
+ return UNIFFI_CALLBACK_UNEXPECTED_ERROR
+ }
+ do {
+ return try {{ method_name }}(cb, argsData, argsLen, out_buf)
+ } catch let error {
+ out_buf.pointee = {{ Type::String.borrow()|lower_fn }}(String(describing: error))
+ return UNIFFI_CALLBACK_UNEXPECTED_ERROR
+ }
+ {% endfor %}
+ // This should never happen, because an out of bounds method index won't
+ // ever be used. Once we can catch errors, we should return an InternalError.
+ // https://github.com/mozilla/uniffi-rs/issues/351
+ default:
+ // An unexpected error happened.
+ // See docs of ForeignCallback in `uniffi_core/src/ffi/foreigncallbacks.rs`
+ return UNIFFI_CALLBACK_UNEXPECTED_ERROR
+ }
+}
+
+// FfiConverter protocol for callback interfaces
+fileprivate struct {{ ffi_converter_name }} {
+ private static let initCallbackOnce: () = {
+ // Swift ensures this initializer code will once run once, even when accessed by multiple threads.
+ try! rustCall { (err: UnsafeMutablePointer<RustCallStatus>) in
+ {{ cbi.ffi_init_callback().name() }}({{ foreign_callback }}, err)
+ }
+ }()
+
+ private static func ensureCallbackinitialized() {
+ _ = initCallbackOnce
+ }
+
+ static func drop(handle: UniFFICallbackHandle) {
+ handleMap.remove(handle: handle)
+ }
+
+ private static var handleMap = UniFFICallbackHandleMap<{{ type_name }}>()
+}
+
+extension {{ ffi_converter_name }} : FfiConverter {
+ typealias SwiftType = {{ type_name }}
+ // We can use Handle as the FfiType because it's a typealias to UInt64
+ typealias FfiType = UniFFICallbackHandle
+
+ public static func lift(_ handle: UniFFICallbackHandle) throws -> SwiftType {
+ ensureCallbackinitialized();
+ guard let callback = handleMap.get(handle: handle) else {
+ throw UniffiInternalError.unexpectedStaleHandle
+ }
+ return callback
+ }
+
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType {
+ ensureCallbackinitialized();
+ let handle: UniFFICallbackHandle = try readInt(&buf)
+ return try lift(handle)
+ }
+
+ public static func lower(_ v: SwiftType) -> UniFFICallbackHandle {
+ ensureCallbackinitialized();
+ return handleMap.insert(obj: v)
+ }
+
+ public static func write(_ v: SwiftType, into buf: inout [UInt8]) {
+ ensureCallbackinitialized();
+ writeInt(&buf, lower(v))
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/CustomType.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/CustomType.swift
new file mode 100644
index 0000000000..504cadfc1d
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/CustomType.swift
@@ -0,0 +1,86 @@
+{%- let ffi_type_name=builtin|ffi_type|ffi_type_name %}
+{%- match config.custom_types.get(name.as_str()) %}
+{%- when None %}
+{#- No config, just forward all methods to our builtin type #}
+/**
+ * Typealias from the type name used in the UDL file to the builtin type. This
+ * is needed because the UDL type name is used in function/method signatures.
+ */
+public typealias {{ name }} = {{ builtin|type_name }}
+public struct FfiConverterType{{ name }}: FfiConverter {
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> {{ name }} {
+ return try {{ builtin|read_fn }}(from: &buf)
+ }
+
+ public static func write(_ value: {{ name }}, into buf: inout [UInt8]) {
+ return {{ builtin|write_fn }}(value, into: &buf)
+ }
+
+ public static func lift(_ value: {{ ffi_type_name }}) throws -> {{ name }} {
+ return try {{ builtin|lift_fn }}(value)
+ }
+
+ public static func lower(_ value: {{ name }}) -> {{ ffi_type_name }} {
+ return {{ builtin|lower_fn }}(value)
+ }
+}
+
+{%- when Some with (config) %}
+
+{# When the config specifies a different type name, create a typealias for it #}
+{%- match config.type_name %}
+{%- when Some with (concrete_type_name) %}
+/**
+ * Typealias from the type name used in the UDL file to the custom type. This
+ * is needed because the UDL type name is used in function/method signatures.
+ */
+public typealias {{ name }} = {{ concrete_type_name }}
+{%- else %}
+{%- endmatch %}
+
+{%- match config.imports %}
+{%- when Some(imports) %}
+{%- for import_name in imports %}
+{{ self.add_import(import_name) }}
+{%- endfor %}
+{%- else %}
+{%- endmatch %}
+
+public struct FfiConverterType{{ name }}: FfiConverter {
+ {#- Custom type config supplied, use it to convert the builtin type #}
+
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> {{ name }} {
+ let builtinValue = try {{ builtin|read_fn }}(from: &buf)
+ return {{ config.into_custom.render("builtinValue") }}
+ }
+
+ public static func write(_ value: {{ name }}, into buf: inout [UInt8]) {
+ let builtinValue = {{ config.from_custom.render("value") }}
+ return {{ builtin|write_fn }}(builtinValue, into: &buf)
+ }
+
+ public static func lift(_ value: {{ ffi_type_name }}) throws -> {{ name }} {
+ let builtinValue = try {{ builtin|lift_fn }}(value)
+ return {{ config.into_custom.render("builtinValue") }}
+ }
+
+ public static func lower(_ value: {{ name }}) -> {{ ffi_type_name }} {
+ let builtinValue = {{ config.from_custom.render("value") }}
+ return {{ builtin|lower_fn }}(builtinValue)
+ }
+}
+
+{%- endmatch %}
+
+{#
+We always write these public functions just incase the type is used as
+an external type by another crate.
+#}
+public func FfiConverterType{{ name }}_lift(_ value: {{ ffi_type_name }}) throws -> {{ name }} {
+ return try FfiConverterType{{ name }}.lift(value)
+}
+
+public func FfiConverterType{{ name }}_lower(_ value: {{ name }}) -> {{ ffi_type_name }} {
+ return FfiConverterType{{ name }}.lower(value)
+}
+
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/DataHelper.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/DataHelper.swift
new file mode 100644
index 0000000000..7db240bf9c
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/DataHelper.swift
@@ -0,0 +1,14 @@
+fileprivate struct FfiConverterData: FfiConverterRustBuffer {
+ typealias SwiftType = Data
+
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Data {
+ let len: Int32 = try readInt(&buf)
+ return Data(try readBytes(&buf, count: Int(len)))
+ }
+
+ public static func write(_ value: Data, into buf: inout [UInt8]) {
+ let len = Int32(value.count)
+ writeInt(&buf, len)
+ writeBytes(&buf, value)
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/DurationHelper.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/DurationHelper.swift
new file mode 100644
index 0000000000..c2aa49e9d1
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/DurationHelper.swift
@@ -0,0 +1,24 @@
+fileprivate struct FfiConverterDuration: FfiConverterRustBuffer {
+ typealias SwiftType = TimeInterval
+
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TimeInterval {
+ let seconds: UInt64 = try readInt(&buf)
+ let nanoseconds: UInt32 = try readInt(&buf)
+ return Double(seconds) + (Double(nanoseconds) / 1.0e9)
+ }
+
+ public static func write(_ value: TimeInterval, into buf: inout [UInt8]) {
+ if value.rounded(.down) > Double(Int64.max) {
+ fatalError("Duration overflow, exceeds max bounds supported by Uniffi")
+ }
+
+ if value < 0 {
+ fatalError("Invalid duration, must be non-negative")
+ }
+
+ let seconds = UInt64(value)
+ let nanoseconds = UInt32((value - Double(seconds)) * 1.0e9)
+ writeInt(&buf, seconds)
+ writeInt(&buf, nanoseconds)
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/EnumTemplate.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/EnumTemplate.swift
new file mode 100644
index 0000000000..99f45290cc
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/EnumTemplate.swift
@@ -0,0 +1,59 @@
+// Note that we don't yet support `indirect` for enums.
+// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion.
+public enum {{ type_name }} {
+ {% for variant in e.variants() %}
+ case {{ variant.name()|enum_variant_swift_quoted }}{% if variant.fields().len() > 0 %}({% call swift::field_list_decl(variant) %}){% endif -%}
+ {% endfor %}
+}
+
+public struct {{ ffi_converter_name }}: FfiConverterRustBuffer {
+ typealias SwiftType = {{ type_name }}
+
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> {{ type_name }} {
+ let variant: Int32 = try readInt(&buf)
+ switch variant {
+ {% for variant in e.variants() %}
+ case {{ loop.index }}: return .{{ variant.name()|enum_variant_swift_quoted }}{% if variant.has_fields() %}(
+ {%- for field in variant.fields() %}
+ {{ field.name()|arg_name }}: try {{ field|read_fn }}(from: &buf)
+ {%- if !loop.last %}, {% endif %}
+ {%- endfor %}
+ ){%- endif %}
+ {% endfor %}
+ default: throw UniffiInternalError.unexpectedEnumCase
+ }
+ }
+
+ public static func write(_ value: {{ type_name }}, into buf: inout [UInt8]) {
+ switch value {
+ {% for variant in e.variants() %}
+ {% if variant.has_fields() %}
+ case let .{{ variant.name()|enum_variant_swift_quoted }}({% for field in variant.fields() %}{{ field.name()|var_name }}{%- if loop.last -%}{%- else -%},{%- endif -%}{% endfor %}):
+ writeInt(&buf, Int32({{ loop.index }}))
+ {% for field in variant.fields() -%}
+ {{ field|write_fn }}({{ field.name()|var_name }}, into: &buf)
+ {% endfor -%}
+ {% else %}
+ case .{{ variant.name()|enum_variant_swift_quoted }}:
+ writeInt(&buf, Int32({{ loop.index }}))
+ {% endif %}
+ {%- endfor %}
+ }
+ }
+}
+
+{#
+We always write these public functions just in case the enum is used as
+an external type by another crate.
+#}
+public func {{ ffi_converter_name }}_lift(_ buf: RustBuffer) throws -> {{ type_name }} {
+ return try {{ ffi_converter_name }}.lift(buf)
+}
+
+public func {{ ffi_converter_name }}_lower(_ value: {{ type_name }}) -> RustBuffer {
+ return {{ ffi_converter_name }}.lower(value)
+}
+
+{% if !contains_object_references %}
+extension {{ type_name }}: Equatable, Hashable {}
+{% endif %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ErrorTemplate.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ErrorTemplate.swift
new file mode 100644
index 0000000000..786091395b
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ErrorTemplate.swift
@@ -0,0 +1,86 @@
+public enum {{ type_name }} {
+
+ {% if e.is_flat() %}
+ {% for variant in e.variants() %}
+ // Simple error enums only carry a message
+ case {{ variant.name()|class_name }}(message: String)
+ {% endfor %}
+
+ {%- else %}
+ {% for variant in e.variants() %}
+ case {{ variant.name()|class_name }}{% if variant.fields().len() > 0 %}({% call swift::field_list_decl(variant) %}){% endif -%}
+ {% endfor %}
+
+ {%- endif %}
+
+ fileprivate static func uniffiErrorHandler(_ error: RustBuffer) throws -> Error {
+ return try {{ ffi_converter_name }}.lift(error)
+ }
+}
+
+
+public struct {{ ffi_converter_name }}: FfiConverterRustBuffer {
+ typealias SwiftType = {{ type_name }}
+
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> {{ type_name }} {
+ let variant: Int32 = try readInt(&buf)
+ switch variant {
+
+ {% if e.is_flat() %}
+
+ {% for variant in e.variants() %}
+ case {{ loop.index }}: return .{{ variant.name()|class_name }}(
+ message: try {{ Type::String.borrow()|read_fn }}(from: &buf)
+ )
+ {% endfor %}
+
+ {% else %}
+
+ {% for variant in e.variants() %}
+ case {{ loop.index }}: return .{{ variant.name()|class_name }}{% if variant.has_fields() -%}(
+ {% for field in variant.fields() -%}
+ {{ field.name()|var_name }}: try {{ field|read_fn }}(from: &buf)
+ {%- if !loop.last %}, {% endif %}
+ {% endfor -%}
+ ){% endif -%}
+ {% endfor %}
+
+ {% endif -%}
+ default: throw UniffiInternalError.unexpectedEnumCase
+ }
+ }
+
+ public static func write(_ value: {{ type_name }}, into buf: inout [UInt8]) {
+ switch value {
+
+ {% if e.is_flat() %}
+
+ {% for variant in e.variants() %}
+ case .{{ variant.name()|class_name }}(_ /* message is ignored*/):
+ writeInt(&buf, Int32({{ loop.index }}))
+ {%- endfor %}
+
+ {% else %}
+
+ {% for variant in e.variants() %}
+ {% if variant.has_fields() %}
+ case let .{{ variant.name()|class_name }}({% for field in variant.fields() %}{{ field.name()|var_name }}{%- if loop.last -%}{%- else -%},{%- endif -%}{% endfor %}):
+ writeInt(&buf, Int32({{ loop.index }}))
+ {% for field in variant.fields() -%}
+ {{ field|write_fn }}({{ field.name()|var_name }}, into: &buf)
+ {% endfor -%}
+ {% else %}
+ case .{{ variant.name()|class_name }}:
+ writeInt(&buf, Int32({{ loop.index }}))
+ {% endif %}
+ {%- endfor %}
+
+ {%- endif %}
+ }
+ }
+}
+
+{% if !contains_object_references %}
+extension {{ type_name }}: Equatable, Hashable {}
+{% endif %}
+extension {{ type_name }}: Error { }
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Float32Helper.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Float32Helper.swift
new file mode 100644
index 0000000000..fb986beab6
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Float32Helper.swift
@@ -0,0 +1,12 @@
+fileprivate struct FfiConverterFloat: FfiConverterPrimitive {
+ typealias FfiType = Float
+ typealias SwiftType = Float
+
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Float {
+ return try lift(readFloat(&buf))
+ }
+
+ public static func write(_ value: Float, into buf: inout [UInt8]) {
+ writeFloat(&buf, lower(value))
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Float64Helper.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Float64Helper.swift
new file mode 100644
index 0000000000..74421c045c
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Float64Helper.swift
@@ -0,0 +1,12 @@
+fileprivate struct FfiConverterDouble: FfiConverterPrimitive {
+ typealias FfiType = Double
+ typealias SwiftType = Double
+
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Double {
+ return try lift(readDouble(&buf))
+ }
+
+ public static func write(_ value: Double, into buf: inout [UInt8]) {
+ writeDouble(&buf, lower(value))
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ForeignExecutorTemplate.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ForeignExecutorTemplate.swift
new file mode 100644
index 0000000000..167e4c7546
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ForeignExecutorTemplate.swift
@@ -0,0 +1,69 @@
+private let UNIFFI_RUST_TASK_CALLBACK_SUCCESS: Int8 = 0
+private let UNIFFI_RUST_TASK_CALLBACK_CANCELLED: Int8 = 1
+private let UNIFFI_FOREIGN_EXECUTOR_CALLBACK_SUCCESS: Int8 = 0
+private let UNIFFI_FOREIGN_EXECUTOR_CALLBACK_CANCELED: Int8 = 1
+private let UNIFFI_FOREIGN_EXECUTOR_CALLBACK_ERROR: Int8 = 2
+
+// Encapsulates an executor that can run Rust tasks
+//
+// On Swift, `Task.detached` can handle this we just need to know what priority to send it.
+public struct UniFfiForeignExecutor {
+ var priority: TaskPriority
+
+ public init(priority: TaskPriority) {
+ self.priority = priority
+ }
+
+ public init() {
+ self.priority = Task.currentPriority
+ }
+}
+
+fileprivate struct FfiConverterForeignExecutor: FfiConverter {
+ typealias SwiftType = UniFfiForeignExecutor
+ // Rust uses a pointer to represent the FfiConverterForeignExecutor, but we only need a u8.
+ // let's use `Int`, which is equivalent to `size_t`
+ typealias FfiType = Int
+
+ public static func lift(_ value: FfiType) throws -> SwiftType {
+ UniFfiForeignExecutor(priority: TaskPriority(rawValue: numericCast(value)))
+ }
+ public static func lower(_ value: SwiftType) -> FfiType {
+ numericCast(value.priority.rawValue)
+ }
+
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType {
+ fatalError("FfiConverterForeignExecutor.read not implemented yet")
+ }
+ public static func write(_ value: SwiftType, into buf: inout [UInt8]) {
+ fatalError("FfiConverterForeignExecutor.read not implemented yet")
+ }
+}
+
+
+fileprivate func uniffiForeignExecutorCallback(executorHandle: Int, delayMs: UInt32, rustTask: UniFfiRustTaskCallback?, taskData: UnsafeRawPointer?) -> Int8 {
+ if let rustTask = rustTask {
+ let executor = try! FfiConverterForeignExecutor.lift(executorHandle)
+ Task.detached(priority: executor.priority) {
+ if delayMs != 0 {
+ let nanoseconds: UInt64 = numericCast(delayMs * 1000000)
+ try! await Task.sleep(nanoseconds: nanoseconds)
+ }
+ rustTask(taskData, UNIFFI_RUST_TASK_CALLBACK_SUCCESS)
+ }
+ return UNIFFI_FOREIGN_EXECUTOR_CALLBACK_SUCCESS
+ } else {
+ // When rustTask is null, we should drop the foreign executor.
+ // However, since its just a value type, we don't need to do anything here.
+ return UNIFFI_FOREIGN_EXECUTOR_CALLBACK_SUCCESS
+ }
+}
+
+fileprivate func uniffiInitForeignExecutor() {
+ {%- match ci.ffi_foreign_executor_callback_set() %}
+ {%- when Some with (fn) %}
+ {{ fn.name() }}(uniffiForeignExecutorCallback)
+ {%- when None %}
+ {#- No foreign executor, we don't set anything #}
+ {% endmatch %}
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Helpers.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Helpers.swift
new file mode 100644
index 0000000000..a34b128e23
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Helpers.swift
@@ -0,0 +1,101 @@
+// An error type for FFI errors. These errors occur at the UniFFI level, not
+// the library level.
+fileprivate enum UniffiInternalError: LocalizedError {
+ case bufferOverflow
+ case incompleteData
+ case unexpectedOptionalTag
+ case unexpectedEnumCase
+ case unexpectedNullPointer
+ case unexpectedRustCallStatusCode
+ case unexpectedRustCallError
+ case unexpectedStaleHandle
+ case rustPanic(_ message: String)
+
+ public var errorDescription: String? {
+ switch self {
+ case .bufferOverflow: return "Reading the requested value would read past the end of the buffer"
+ case .incompleteData: return "The buffer still has data after lifting its containing value"
+ case .unexpectedOptionalTag: return "Unexpected optional tag; should be 0 or 1"
+ case .unexpectedEnumCase: return "Raw enum value doesn't match any cases"
+ case .unexpectedNullPointer: return "Raw pointer value was null"
+ case .unexpectedRustCallStatusCode: return "Unexpected RustCallStatus code"
+ case .unexpectedRustCallError: return "CALL_ERROR but no errorClass specified"
+ case .unexpectedStaleHandle: return "The object in the handle map has been dropped already"
+ case let .rustPanic(message): return message
+ }
+ }
+}
+
+fileprivate let CALL_SUCCESS: Int8 = 0
+fileprivate let CALL_ERROR: Int8 = 1
+fileprivate let CALL_PANIC: Int8 = 2
+fileprivate let CALL_CANCELLED: Int8 = 3
+
+fileprivate extension RustCallStatus {
+ init() {
+ self.init(
+ code: CALL_SUCCESS,
+ errorBuf: RustBuffer.init(
+ capacity: 0,
+ len: 0,
+ data: nil
+ )
+ )
+ }
+}
+
+private func rustCall<T>(_ callback: (UnsafeMutablePointer<RustCallStatus>) -> T) throws -> T {
+ try makeRustCall(callback, errorHandler: nil)
+}
+
+private func rustCallWithError<T>(
+ _ errorHandler: @escaping (RustBuffer) throws -> Error,
+ _ callback: (UnsafeMutablePointer<RustCallStatus>) -> T) throws -> T {
+ try makeRustCall(callback, errorHandler: errorHandler)
+}
+
+private func makeRustCall<T>(
+ _ callback: (UnsafeMutablePointer<RustCallStatus>) -> T,
+ errorHandler: ((RustBuffer) throws -> Error)?
+) throws -> T {
+ uniffiEnsureInitialized()
+ var callStatus = RustCallStatus.init()
+ let returnedVal = callback(&callStatus)
+ try uniffiCheckCallStatus(callStatus: callStatus, errorHandler: errorHandler)
+ return returnedVal
+}
+
+private func uniffiCheckCallStatus(
+ callStatus: RustCallStatus,
+ errorHandler: ((RustBuffer) throws -> Error)?
+) throws {
+ switch callStatus.code {
+ case CALL_SUCCESS:
+ return
+
+ case CALL_ERROR:
+ if let errorHandler = errorHandler {
+ throw try errorHandler(callStatus.errorBuf)
+ } else {
+ callStatus.errorBuf.deallocate()
+ throw UniffiInternalError.unexpectedRustCallError
+ }
+
+ case CALL_PANIC:
+ // When the rust code sees a panic, it tries to construct a RustBuffer
+ // with the message. But if that code panics, then it just sends back
+ // an empty buffer.
+ if callStatus.errorBuf.len > 0 {
+ throw UniffiInternalError.rustPanic(try {{ Type::String.borrow()|lift_fn }}(callStatus.errorBuf))
+ } else {
+ callStatus.errorBuf.deallocate()
+ throw UniffiInternalError.rustPanic("Rust panic")
+ }
+
+ case CALL_CANCELLED:
+ throw CancellationError()
+
+ default:
+ throw UniffiInternalError.unexpectedRustCallStatusCode
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Int16Helper.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Int16Helper.swift
new file mode 100644
index 0000000000..ac57fc5e58
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Int16Helper.swift
@@ -0,0 +1,12 @@
+fileprivate struct FfiConverterInt16: FfiConverterPrimitive {
+ typealias FfiType = Int16
+ typealias SwiftType = Int16
+
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Int16 {
+ return try lift(readInt(&buf))
+ }
+
+ public static func write(_ value: Int16, into buf: inout [UInt8]) {
+ writeInt(&buf, lower(value))
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Int32Helper.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Int32Helper.swift
new file mode 100644
index 0000000000..0ccfc13e4e
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Int32Helper.swift
@@ -0,0 +1,12 @@
+fileprivate struct FfiConverterInt32: FfiConverterPrimitive {
+ typealias FfiType = Int32
+ typealias SwiftType = Int32
+
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Int32 {
+ return try lift(readInt(&buf))
+ }
+
+ public static func write(_ value: Int32, into buf: inout [UInt8]) {
+ writeInt(&buf, lower(value))
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Int64Helper.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Int64Helper.swift
new file mode 100644
index 0000000000..d7d4082933
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Int64Helper.swift
@@ -0,0 +1,12 @@
+fileprivate struct FfiConverterInt64: FfiConverterPrimitive {
+ typealias FfiType = Int64
+ typealias SwiftType = Int64
+
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Int64 {
+ return try lift(readInt(&buf))
+ }
+
+ public static func write(_ value: Int64, into buf: inout [UInt8]) {
+ writeInt(&buf, lower(value))
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Int8Helper.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Int8Helper.swift
new file mode 100644
index 0000000000..f2387e4340
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Int8Helper.swift
@@ -0,0 +1,12 @@
+fileprivate struct FfiConverterInt8: FfiConverterPrimitive {
+ typealias FfiType = Int8
+ typealias SwiftType = Int8
+
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Int8 {
+ return try lift(readInt(&buf))
+ }
+
+ public static func write(_ value: Int8, into buf: inout [UInt8]) {
+ writeInt(&buf, lower(value))
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/MapTemplate.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/MapTemplate.swift
new file mode 100644
index 0000000000..05713aca26
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/MapTemplate.swift
@@ -0,0 +1,22 @@
+fileprivate struct {{ ffi_converter_name }}: FfiConverterRustBuffer {
+ public static func write(_ value: {{ type_name }}, into buf: inout [UInt8]) {
+ let len = Int32(value.count)
+ writeInt(&buf, len)
+ for (key, value) in value {
+ {{ key_type|write_fn }}(key, into: &buf)
+ {{ value_type|write_fn }}(value, into: &buf)
+ }
+ }
+
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> {{ type_name }} {
+ let len: Int32 = try readInt(&buf)
+ var dict = {{ type_name }}()
+ dict.reserveCapacity(Int(len))
+ for _ in 0..<len {
+ let key = try {{ key_type|read_fn }}(from: &buf)
+ let value = try {{ value_type|read_fn }}(from: &buf)
+ dict[key] = value
+ }
+ return dict
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ModuleMapTemplate.modulemap b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ModuleMapTemplate.modulemap
new file mode 100644
index 0000000000..f5f73ceb1b
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ModuleMapTemplate.modulemap
@@ -0,0 +1,6 @@
+// This file was autogenerated by some hot garbage in the `uniffi` crate.
+// Trust me, you don't want to mess with it!
+module {{ config.ffi_module_name() }} {
+ header "{{ config.header_filename() }}"
+ export *
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift
new file mode 100644
index 0000000000..57a77ca6df
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift
@@ -0,0 +1,138 @@
+{%- let obj = ci|get_object_definition(name) %}
+public protocol {{ obj.name() }}Protocol {
+ {% for meth in obj.methods() -%}
+ func {{ meth.name()|fn_name }}({% call swift::arg_list_protocol(meth) %}) {% call swift::async(meth) %} {% call swift::throws(meth) -%}
+ {%- match meth.return_type() -%}
+ {%- when Some with (return_type) %} -> {{ return_type|type_name -}}
+ {%- else -%}
+ {%- endmatch %}
+ {% endfor %}
+}
+
+public class {{ type_name }}: {{ obj.name() }}Protocol {
+ fileprivate let pointer: UnsafeMutableRawPointer
+
+ // TODO: We'd like this to be `private` but for Swifty reasons,
+ // we can't implement `FfiConverter` without making this `required` and we can't
+ // make it `required` without making it `public`.
+ required init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) {
+ self.pointer = pointer
+ }
+
+ {%- match obj.primary_constructor() %}
+ {%- when Some with (cons) %}
+ public convenience init({% call swift::arg_list_decl(cons) -%}) {% call swift::throws(cons) %} {
+ self.init(unsafeFromRawPointer: {% call swift::to_ffi_call(cons) %})
+ }
+ {%- when None %}
+ {%- endmatch %}
+
+ deinit {
+ try! rustCall { {{ obj.ffi_object_free().name() }}(pointer, $0) }
+ }
+
+ {% for cons in obj.alternate_constructors() %}
+
+ public static func {{ cons.name()|fn_name }}({% call swift::arg_list_decl(cons) %}) {% call swift::throws(cons) %} -> {{ type_name }} {
+ return {{ type_name }}(unsafeFromRawPointer: {% call swift::to_ffi_call(cons) %})
+ }
+
+ {% endfor %}
+
+ {# // TODO: Maybe merge the two templates (i.e the one with a return type and the one without) #}
+ {% for meth in obj.methods() -%}
+ {%- if meth.is_async() %}
+
+ public func {{ meth.name()|fn_name }}({%- call swift::arg_list_decl(meth) -%}) async {% call swift::throws(meth) %}{% match meth.return_type() %}{% when Some with (return_type) %} -> {{ return_type|type_name }}{% when None %}{% endmatch %} {
+ return {% call swift::try(meth) %} await uniffiRustCallAsync(
+ rustFutureFunc: {
+ {{ meth.ffi_func().name() }}(
+ self.pointer
+ {%- for arg in meth.arguments() -%}
+ ,
+ {{ arg|lower_fn }}({{ arg.name()|var_name }})
+ {%- endfor %}
+ )
+ },
+ pollFunc: {{ meth.ffi_rust_future_poll(ci) }},
+ completeFunc: {{ meth.ffi_rust_future_complete(ci) }},
+ freeFunc: {{ meth.ffi_rust_future_free(ci) }},
+ {%- match meth.return_type() %}
+ {%- when Some(return_type) %}
+ liftFunc: {{ return_type|lift_fn }},
+ {%- when None %}
+ liftFunc: { $0 },
+ {%- endmatch %}
+ {%- match meth.throws_type() %}
+ {%- when Some with (e) %}
+ errorHandler: {{ e|ffi_converter_name }}.lift
+ {%- else %}
+ errorHandler: nil
+ {% endmatch %}
+ )
+ }
+
+ {% else -%}
+
+ {%- match meth.return_type() -%}
+
+ {%- when Some with (return_type) %}
+
+ public func {{ meth.name()|fn_name }}({% call swift::arg_list_decl(meth) %}) {% call swift::throws(meth) %} -> {{ return_type|type_name }} {
+ return {% call swift::try(meth) %} {{ return_type|lift_fn }}(
+ {% call swift::to_ffi_call_with_prefix("self.pointer", meth) %}
+ )
+ }
+
+ {%- when None %}
+
+ public func {{ meth.name()|fn_name }}({% call swift::arg_list_decl(meth) %}) {% call swift::throws(meth) %} {
+ {% call swift::to_ffi_call_with_prefix("self.pointer", meth) %}
+ }
+
+ {%- endmatch -%}
+ {%- endif -%}
+ {% endfor %}
+}
+
+public struct {{ ffi_converter_name }}: FfiConverter {
+ typealias FfiType = UnsafeMutableRawPointer
+ typealias SwiftType = {{ type_name }}
+
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> {{ type_name }} {
+ let v: UInt64 = try readInt(&buf)
+ // The Rust code won't compile if a pointer won't fit in a UInt64.
+ // We have to go via `UInt` because that's the thing that's the size of a pointer.
+ let ptr = UnsafeMutableRawPointer(bitPattern: UInt(truncatingIfNeeded: v))
+ if (ptr == nil) {
+ throw UniffiInternalError.unexpectedNullPointer
+ }
+ return try lift(ptr!)
+ }
+
+ public static func write(_ value: {{ type_name }}, into buf: inout [UInt8]) {
+ // This fiddling is because `Int` is the thing that's the same size as a pointer.
+ // The Rust code won't compile if a pointer won't fit in a `UInt64`.
+ writeInt(&buf, UInt64(bitPattern: Int64(Int(bitPattern: lower(value)))))
+ }
+
+ public static func lift(_ pointer: UnsafeMutableRawPointer) throws -> {{ type_name }} {
+ return {{ type_name}}(unsafeFromRawPointer: pointer)
+ }
+
+ public static func lower(_ value: {{ type_name }}) -> UnsafeMutableRawPointer {
+ return value.pointer
+ }
+}
+
+{#
+We always write these public functions just in case the enum is used as
+an external type by another crate.
+#}
+public func {{ ffi_converter_name }}_lift(_ pointer: UnsafeMutableRawPointer) throws -> {{ type_name }} {
+ return try {{ ffi_converter_name }}.lift(pointer)
+}
+
+public func {{ ffi_converter_name }}_lower(_ value: {{ type_name }}) -> UnsafeMutableRawPointer {
+ return {{ ffi_converter_name }}.lower(value)
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/OptionalTemplate.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/OptionalTemplate.swift
new file mode 100644
index 0000000000..1dac65be63
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/OptionalTemplate.swift
@@ -0,0 +1,20 @@
+fileprivate struct {{ ffi_converter_name }}: FfiConverterRustBuffer {
+ typealias SwiftType = {{ type_name }}
+
+ public static func write(_ value: SwiftType, into buf: inout [UInt8]) {
+ guard let value = value else {
+ writeInt(&buf, Int8(0))
+ return
+ }
+ writeInt(&buf, Int8(1))
+ {{ inner_type|write_fn }}(value, into: &buf)
+ }
+
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType {
+ switch try readInt(&buf) as Int8 {
+ case 0: return nil
+ case 1: return try {{ inner_type|read_fn }}(from: &buf)
+ default: throw UniffiInternalError.unexpectedOptionalTag
+ }
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift
new file mode 100644
index 0000000000..44de9dd358
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift
@@ -0,0 +1,62 @@
+{%- let rec = ci|get_record_definition(name) %}
+public struct {{ type_name }} {
+ {%- for field in rec.fields() %}
+ public var {{ field.name()|var_name }}: {{ field|type_name }}
+ {%- endfor %}
+
+ // Default memberwise initializers are never public by default, so we
+ // declare one manually.
+ public init({% call swift::field_list_decl(rec) %}) {
+ {%- for field in rec.fields() %}
+ self.{{ field.name()|var_name }} = {{ field.name()|var_name }}
+ {%- endfor %}
+ }
+}
+
+{% if !contains_object_references %}
+extension {{ type_name }}: Equatable, Hashable {
+ public static func ==(lhs: {{ type_name }}, rhs: {{ type_name }}) -> Bool {
+ {%- for field in rec.fields() %}
+ if lhs.{{ field.name()|var_name }} != rhs.{{ field.name()|var_name }} {
+ return false
+ }
+ {%- endfor %}
+ return true
+ }
+
+ public func hash(into hasher: inout Hasher) {
+ {%- for field in rec.fields() %}
+ hasher.combine({{ field.name()|var_name }})
+ {%- endfor %}
+ }
+}
+{% endif %}
+
+public struct {{ ffi_converter_name }}: FfiConverterRustBuffer {
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> {{ type_name }} {
+ return try {{ type_name }}(
+ {%- for field in rec.fields() %}
+ {{ field.name()|arg_name }}: {{ field|read_fn }}(from: &buf)
+ {%- if !loop.last %}, {% endif %}
+ {%- endfor %}
+ )
+ }
+
+ public static func write(_ value: {{ type_name }}, into buf: inout [UInt8]) {
+ {%- for field in rec.fields() %}
+ {{ field|write_fn }}(value.{{ field.name()|var_name }}, into: &buf)
+ {%- endfor %}
+ }
+}
+
+{#
+We always write these public functions just in case the struct is used as
+an external type by another crate.
+#}
+public func {{ ffi_converter_name }}_lift(_ buf: RustBuffer) throws -> {{ type_name }} {
+ return try {{ ffi_converter_name }}.lift(buf)
+}
+
+public func {{ ffi_converter_name }}_lower(_ value: {{ type_name }}) -> RustBuffer {
+ return {{ ffi_converter_name }}.lower(value)
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/RustBufferTemplate.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/RustBufferTemplate.swift
new file mode 100644
index 0000000000..2f737b6635
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/RustBufferTemplate.swift
@@ -0,0 +1,183 @@
+fileprivate extension RustBuffer {
+ // Allocate a new buffer, copying the contents of a `UInt8` array.
+ init(bytes: [UInt8]) {
+ let rbuf = bytes.withUnsafeBufferPointer { ptr in
+ RustBuffer.from(ptr)
+ }
+ self.init(capacity: rbuf.capacity, len: rbuf.len, data: rbuf.data)
+ }
+
+ static func from(_ ptr: UnsafeBufferPointer<UInt8>) -> RustBuffer {
+ try! rustCall { {{ ci.ffi_rustbuffer_from_bytes().name() }}(ForeignBytes(bufferPointer: ptr), $0) }
+ }
+
+ // Frees the buffer in place.
+ // The buffer must not be used after this is called.
+ func deallocate() {
+ try! rustCall { {{ ci.ffi_rustbuffer_free().name() }}(self, $0) }
+ }
+}
+
+fileprivate extension ForeignBytes {
+ init(bufferPointer: UnsafeBufferPointer<UInt8>) {
+ self.init(len: Int32(bufferPointer.count), data: bufferPointer.baseAddress)
+ }
+}
+
+// For every type used in the interface, we provide helper methods for conveniently
+// lifting and lowering that type from C-compatible data, and for reading and writing
+// values of that type in a buffer.
+
+// Helper classes/extensions that don't change.
+// Someday, this will be in a library of its own.
+
+fileprivate extension Data {
+ init(rustBuffer: RustBuffer) {
+ // TODO: This copies the buffer. Can we read directly from a
+ // Rust buffer?
+ self.init(bytes: rustBuffer.data!, count: Int(rustBuffer.len))
+ }
+}
+
+// Define reader functionality. Normally this would be defined in a class or
+// struct, but we use standalone functions instead in order to make external
+// types work.
+//
+// With external types, one swift source file needs to be able to call the read
+// method on another source file's FfiConverter, but then what visibility
+// should Reader have?
+// - If Reader is fileprivate, then this means the read() must also
+// be fileprivate, which doesn't work with external types.
+// - If Reader is internal/public, we'll get compile errors since both source
+// files will try define the same type.
+//
+// Instead, the read() method and these helper functions input a tuple of data
+
+fileprivate func createReader(data: Data) -> (data: Data, offset: Data.Index) {
+ (data: data, offset: 0)
+}
+
+// Reads an integer at the current offset, in big-endian order, and advances
+// the offset on success. Throws if reading the integer would move the
+// offset past the end of the buffer.
+fileprivate func readInt<T: FixedWidthInteger>(_ reader: inout (data: Data, offset: Data.Index)) throws -> T {
+ let range = reader.offset..<reader.offset + MemoryLayout<T>.size
+ guard reader.data.count >= range.upperBound else {
+ throw UniffiInternalError.bufferOverflow
+ }
+ if T.self == UInt8.self {
+ let value = reader.data[reader.offset]
+ reader.offset += 1
+ return value as! T
+ }
+ var value: T = 0
+ let _ = withUnsafeMutableBytes(of: &value, { reader.data.copyBytes(to: $0, from: range)})
+ reader.offset = range.upperBound
+ return value.bigEndian
+}
+
+// Reads an arbitrary number of bytes, to be used to read
+// raw bytes, this is useful when lifting strings
+fileprivate func readBytes(_ reader: inout (data: Data, offset: Data.Index), count: Int) throws -> Array<UInt8> {
+ let range = reader.offset..<(reader.offset+count)
+ guard reader.data.count >= range.upperBound else {
+ throw UniffiInternalError.bufferOverflow
+ }
+ var value = [UInt8](repeating: 0, count: count)
+ value.withUnsafeMutableBufferPointer({ buffer in
+ reader.data.copyBytes(to: buffer, from: range)
+ })
+ reader.offset = range.upperBound
+ return value
+}
+
+// Reads a float at the current offset.
+fileprivate func readFloat(_ reader: inout (data: Data, offset: Data.Index)) throws -> Float {
+ return Float(bitPattern: try readInt(&reader))
+}
+
+// Reads a float at the current offset.
+fileprivate func readDouble(_ reader: inout (data: Data, offset: Data.Index)) throws -> Double {
+ return Double(bitPattern: try readInt(&reader))
+}
+
+// Indicates if the offset has reached the end of the buffer.
+fileprivate func hasRemaining(_ reader: (data: Data, offset: Data.Index)) -> Bool {
+ return reader.offset < reader.data.count
+}
+
+// Define writer functionality. Normally this would be defined in a class or
+// struct, but we use standalone functions instead in order to make external
+// types work. See the above discussion on Readers for details.
+
+fileprivate func createWriter() -> [UInt8] {
+ return []
+}
+
+fileprivate func writeBytes<S>(_ writer: inout [UInt8], _ byteArr: S) where S: Sequence, S.Element == UInt8 {
+ writer.append(contentsOf: byteArr)
+}
+
+// Writes an integer in big-endian order.
+//
+// Warning: make sure what you are trying to write
+// is in the correct type!
+fileprivate func writeInt<T: FixedWidthInteger>(_ writer: inout [UInt8], _ value: T) {
+ var value = value.bigEndian
+ withUnsafeBytes(of: &value) { writer.append(contentsOf: $0) }
+}
+
+fileprivate func writeFloat(_ writer: inout [UInt8], _ value: Float) {
+ writeInt(&writer, value.bitPattern)
+}
+
+fileprivate func writeDouble(_ writer: inout [UInt8], _ value: Double) {
+ writeInt(&writer, value.bitPattern)
+}
+
+// Protocol for types that transfer other types across the FFI. This is
+// analogous go the Rust trait of the same name.
+fileprivate protocol FfiConverter {
+ associatedtype FfiType
+ associatedtype SwiftType
+
+ static func lift(_ value: FfiType) throws -> SwiftType
+ static func lower(_ value: SwiftType) -> FfiType
+ static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType
+ static func write(_ value: SwiftType, into buf: inout [UInt8])
+}
+
+// Types conforming to `Primitive` pass themselves directly over the FFI.
+fileprivate protocol FfiConverterPrimitive: FfiConverter where FfiType == SwiftType { }
+
+extension FfiConverterPrimitive {
+ public static func lift(_ value: FfiType) throws -> SwiftType {
+ return value
+ }
+
+ public static func lower(_ value: SwiftType) -> FfiType {
+ return value
+ }
+}
+
+// Types conforming to `FfiConverterRustBuffer` lift and lower into a `RustBuffer`.
+// Used for complex types where it's hard to write a custom lift/lower.
+fileprivate protocol FfiConverterRustBuffer: FfiConverter where FfiType == RustBuffer {}
+
+extension FfiConverterRustBuffer {
+ public static func lift(_ buf: RustBuffer) throws -> SwiftType {
+ var reader = createReader(data: Data(rustBuffer: buf))
+ let value = try read(from: &reader)
+ if hasRemaining(reader) {
+ throw UniffiInternalError.incompleteData
+ }
+ buf.deallocate()
+ return value
+ }
+
+ public static func lower(_ value: SwiftType) -> RustBuffer {
+ var writer = createWriter()
+ write(value, into: &writer)
+ return RustBuffer(bytes: writer)
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/SequenceTemplate.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/SequenceTemplate.swift
new file mode 100644
index 0000000000..bf664f6411
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/SequenceTemplate.swift
@@ -0,0 +1,21 @@
+fileprivate struct {{ ffi_converter_name }}: FfiConverterRustBuffer {
+ typealias SwiftType = {{ type_name }}
+
+ public static func write(_ value: {{ type_name }}, into buf: inout [UInt8]) {
+ let len = Int32(value.count)
+ writeInt(&buf, len)
+ for item in value {
+ {{ inner_type|write_fn }}(item, into: &buf)
+ }
+ }
+
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> {{ type_name }} {
+ let len: Int32 = try readInt(&buf)
+ var seq = {{ type_name }}()
+ seq.reserveCapacity(Int(len))
+ for _ in 0 ..< len {
+ seq.append(try {{ inner_type|read_fn }}(from: &buf))
+ }
+ return seq
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/StringHelper.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/StringHelper.swift
new file mode 100644
index 0000000000..b7d3466bdd
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/StringHelper.swift
@@ -0,0 +1,37 @@
+fileprivate struct FfiConverterString: FfiConverter {
+ typealias SwiftType = String
+ typealias FfiType = RustBuffer
+
+ public static func lift(_ value: RustBuffer) throws -> String {
+ defer {
+ value.deallocate()
+ }
+ if value.data == nil {
+ return String()
+ }
+ let bytes = UnsafeBufferPointer<UInt8>(start: value.data!, count: Int(value.len))
+ return String(bytes: bytes, encoding: String.Encoding.utf8)!
+ }
+
+ public static func lower(_ value: String) -> RustBuffer {
+ return value.utf8CString.withUnsafeBufferPointer { ptr in
+ // The swift string gives us int8_t, we want uint8_t.
+ ptr.withMemoryRebound(to: UInt8.self) { ptr in
+ // The swift string gives us a trailing null byte, we don't want it.
+ let buf = UnsafeBufferPointer(rebasing: ptr.prefix(upTo: ptr.count - 1))
+ return RustBuffer.from(buf)
+ }
+ }
+ }
+
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> String {
+ let len: Int32 = try readInt(&buf)
+ return String(bytes: try readBytes(&buf, count: Int(len)), encoding: String.Encoding.utf8)!
+ }
+
+ public static func write(_ value: String, into buf: inout [UInt8]) {
+ let len = Int32(value.utf8.count)
+ writeInt(&buf, len)
+ writeBytes(&buf, value.utf8)
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/TimestampHelper.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/TimestampHelper.swift
new file mode 100644
index 0000000000..3cd472fa0e
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/TimestampHelper.swift
@@ -0,0 +1,34 @@
+fileprivate struct FfiConverterTimestamp: FfiConverterRustBuffer {
+ typealias SwiftType = Date
+
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Date {
+ let seconds: Int64 = try readInt(&buf)
+ let nanoseconds: UInt32 = try readInt(&buf)
+ if seconds >= 0 {
+ let delta = Double(seconds) + (Double(nanoseconds) / 1.0e9)
+ return Date.init(timeIntervalSince1970: delta)
+ } else {
+ let delta = Double(seconds) - (Double(nanoseconds) / 1.0e9)
+ return Date.init(timeIntervalSince1970: delta)
+ }
+ }
+
+ public static func write(_ value: Date, into buf: inout [UInt8]) {
+ var delta = value.timeIntervalSince1970
+ var sign: Int64 = 1
+ if delta < 0 {
+ // The nanoseconds portion of the epoch offset must always be
+ // positive, to simplify the calculation we will use the absolute
+ // value of the offset.
+ sign = -1
+ delta = -delta
+ }
+ if delta.rounded(.down) > Double(Int64.max) {
+ fatalError("Timestamp overflow, exceeds max bounds supported by Uniffi")
+ }
+ let seconds = Int64(delta)
+ let nanoseconds = UInt32((delta - Double(seconds)) * 1.0e9)
+ writeInt(&buf, sign * seconds)
+ writeInt(&buf, nanoseconds)
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/TopLevelFunctionTemplate.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/TopLevelFunctionTemplate.swift
new file mode 100644
index 0000000000..a2c6311931
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/TopLevelFunctionTemplate.swift
@@ -0,0 +1,48 @@
+{%- if func.is_async() %}
+
+public func {{ func.name()|fn_name }}({%- call swift::arg_list_decl(func) -%}) async {% call swift::throws(func) %}{% match func.return_type() %}{% when Some with (return_type) %} -> {{ return_type|type_name }}{% when None %}{% endmatch %} {
+ return {% call swift::try(func) %} await uniffiRustCallAsync(
+ rustFutureFunc: {
+ {{ func.ffi_func().name() }}(
+ {%- for arg in func.arguments() %}
+ {{ arg|lower_fn }}({{ arg.name()|var_name }}){% if !loop.last %},{% endif %}
+ {%- endfor %}
+ )
+ },
+ pollFunc: {{ func.ffi_rust_future_poll(ci) }},
+ completeFunc: {{ func.ffi_rust_future_complete(ci) }},
+ freeFunc: {{ func.ffi_rust_future_free(ci) }},
+ {%- match func.return_type() %}
+ {%- when Some(return_type) %}
+ liftFunc: {{ return_type|lift_fn }},
+ {%- when None %}
+ liftFunc: { $0 },
+ {%- endmatch %}
+ {%- match func.throws_type() %}
+ {%- when Some with (e) %}
+ errorHandler: {{ e|ffi_converter_name }}.lift
+ {%- else %}
+ errorHandler: nil
+ {% endmatch %}
+ )
+}
+
+{% else %}
+
+{%- match func.return_type() -%}
+{%- when Some with (return_type) %}
+
+public func {{ func.name()|fn_name }}({%- call swift::arg_list_decl(func) -%}) {% call swift::throws(func) %} -> {{ return_type|type_name }} {
+ return {% call swift::try(func) %} {{ return_type|lift_fn }}(
+ {% call swift::to_ffi_call(func) %}
+ )
+}
+
+{%- when None %}
+
+public func {{ func.name()|fn_name }}({% call swift::arg_list_decl(func) %}) {% call swift::throws(func) %} {
+ {% call swift::to_ffi_call(func) %}
+}
+
+{% endmatch %}
+{%- endif %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Types.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Types.swift
new file mode 100644
index 0000000000..aba34f4b0b
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Types.swift
@@ -0,0 +1,98 @@
+{%- import "macros.swift" as swift %}
+{%- for type_ in ci.iter_types() %}
+{%- let type_name = type_|type_name %}
+{%- let ffi_converter_name = type_|ffi_converter_name %}
+{%- let canonical_type_name = type_|canonical_name %}
+{%- let contains_object_references = ci.item_contains_object_references(type_) %}
+
+{#
+ # Map `Type` instances to an include statement for that type.
+ #
+ # There is a companion match in `KotlinCodeOracle::create_code_type()` which performs a similar function for the
+ # Rust code.
+ #
+ # - When adding additional types here, make sure to also add a match arm to that function.
+ # - To keep things manageable, let's try to limit ourselves to these 2 mega-matches
+ #}
+{%- match type_ %}
+
+{%- when Type::Boolean %}
+{%- include "BooleanHelper.swift" %}
+
+{%- when Type::String %}
+{%- include "StringHelper.swift" %}
+
+{%- when Type::Bytes %}
+{%- include "DataHelper.swift" %}
+
+{%- when Type::Int8 %}
+{%- include "Int8Helper.swift" %}
+
+{%- when Type::Int16 %}
+{%- include "Int16Helper.swift" %}
+
+{%- when Type::Int32 %}
+{%- include "Int32Helper.swift" %}
+
+{%- when Type::Int64 %}
+{%- include "Int64Helper.swift" %}
+
+{%- when Type::UInt8 %}
+{%- include "UInt8Helper.swift" %}
+
+{%- when Type::UInt16 %}
+{%- include "UInt16Helper.swift" %}
+
+{%- when Type::UInt32 %}
+{%- include "UInt32Helper.swift" %}
+
+{%- when Type::UInt64 %}
+{%- include "UInt64Helper.swift" %}
+
+{%- when Type::Float32 %}
+{%- include "Float32Helper.swift" %}
+
+{%- when Type::Float64 %}
+{%- include "Float64Helper.swift" %}
+
+{%- when Type::Timestamp %}
+{%- include "TimestampHelper.swift" %}
+
+{%- when Type::Duration %}
+{%- include "DurationHelper.swift" %}
+
+{%- when Type::CallbackInterface { name, module_path } %}
+{%- include "CallbackInterfaceTemplate.swift" %}
+
+{%- when Type::ForeignExecutor %}
+{%- include "ForeignExecutorTemplate.swift" %}
+
+{%- when Type::Custom { name, module_path, builtin } %}
+{%- include "CustomType.swift" %}
+
+{%- when Type::Enum { name, module_path } %}
+{%- let e = ci.get_enum_definition(name).unwrap() %}
+{%- if ci.is_name_used_as_error(name) %}
+{%- include "ErrorTemplate.swift" %}
+{%- else %}
+{%- include "EnumTemplate.swift" %}
+{% endif %}
+
+{%- when Type::Object{ name, module_path, imp } %}
+{%- include "ObjectTemplate.swift" %}
+
+{%- when Type::Record { name, module_path } %}
+{%- include "RecordTemplate.swift" %}
+
+{%- when Type::Optional { inner_type } %}
+{%- include "OptionalTemplate.swift" %}
+
+{%- when Type::Sequence { inner_type } %}
+{%- include "SequenceTemplate.swift" %}
+
+{%- when Type::Map { key_type, value_type } %}
+{%- include "MapTemplate.swift" %}
+
+{%- else %}
+{%- endmatch %}
+{%- endfor %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/UInt16Helper.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/UInt16Helper.swift
new file mode 100644
index 0000000000..b7fc0942a5
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/UInt16Helper.swift
@@ -0,0 +1,12 @@
+fileprivate struct FfiConverterUInt16: FfiConverterPrimitive {
+ typealias FfiType = UInt16
+ typealias SwiftType = UInt16
+
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UInt16 {
+ return try lift(readInt(&buf))
+ }
+
+ public static func write(_ value: SwiftType, into buf: inout [UInt8]) {
+ writeInt(&buf, lower(value))
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/UInt32Helper.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/UInt32Helper.swift
new file mode 100644
index 0000000000..e7a64aab93
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/UInt32Helper.swift
@@ -0,0 +1,12 @@
+fileprivate struct FfiConverterUInt32: FfiConverterPrimitive {
+ typealias FfiType = UInt32
+ typealias SwiftType = UInt32
+
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UInt32 {
+ return try lift(readInt(&buf))
+ }
+
+ public static func write(_ value: SwiftType, into buf: inout [UInt8]) {
+ writeInt(&buf, lower(value))
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/UInt64Helper.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/UInt64Helper.swift
new file mode 100644
index 0000000000..eb674a2c53
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/UInt64Helper.swift
@@ -0,0 +1,12 @@
+fileprivate struct FfiConverterUInt64: FfiConverterPrimitive {
+ typealias FfiType = UInt64
+ typealias SwiftType = UInt64
+
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UInt64 {
+ return try lift(readInt(&buf))
+ }
+
+ public static func write(_ value: SwiftType, into buf: inout [UInt8]) {
+ writeInt(&buf, lower(value))
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/UInt8Helper.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/UInt8Helper.swift
new file mode 100644
index 0000000000..4baf613494
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/UInt8Helper.swift
@@ -0,0 +1,12 @@
+fileprivate struct FfiConverterUInt8: FfiConverterPrimitive {
+ typealias FfiType = UInt8
+ typealias SwiftType = UInt8
+
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UInt8 {
+ return try lift(readInt(&buf))
+ }
+
+ public static func write(_ value: UInt8, into buf: inout [UInt8]) {
+ writeInt(&buf, lower(value))
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/macros.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/macros.swift
new file mode 100644
index 0000000000..0a125e6f61
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/macros.swift
@@ -0,0 +1,89 @@
+{#
+// Template to call into rust. Used in several places.
+// Variable names in `arg_list_decl` should match up with arg lists
+// passed to rust via `arg_list_lowered`
+#}
+
+{%- macro to_ffi_call(func) -%}
+ {%- call try(func) -%}
+ {%- match func.throws_type() -%}
+ {%- when Some with (e) -%}
+ rustCallWithError({{ e|ffi_converter_name }}.lift) {
+ {%- else -%}
+ rustCall() {
+ {%- endmatch %}
+ {{ func.ffi_func().name() }}({% call arg_list_lowered(func) -%} $0)
+}
+{%- endmacro -%}
+
+{%- macro to_ffi_call_with_prefix(prefix, func) -%}
+{% call try(func) %}
+ {%- match func.throws_type() %}
+ {%- when Some with (e) %}
+ rustCallWithError({{ e|ffi_converter_name }}.lift) {
+ {%- else %}
+ rustCall() {
+ {% endmatch %}
+ {{ func.ffi_func().name() }}(
+ {{- prefix }}, {% call arg_list_lowered(func) -%} $0
+ )
+}
+{%- endmacro %}
+
+{%- macro arg_list_lowered(func) %}
+ {%- for arg in func.arguments() %}
+ {{ arg|lower_fn }}({{ arg.name()|var_name }}),
+ {%- endfor %}
+{%- endmacro -%}
+
+{#-
+// Arglist as used in Swift declarations of methods, functions and constructors.
+// Note the var_name and type_name filters.
+-#}
+
+{% macro arg_list_decl(func) %}
+ {%- for arg in func.arguments() -%}
+ {% if config.omit_argument_labels() %}_ {% endif %}{{ arg.name()|var_name }}: {{ arg|type_name -}}
+ {%- match arg.default_value() %}
+ {%- when Some with(literal) %} = {{ literal|literal_swift(arg) }}
+ {%- else %}
+ {%- endmatch %}
+ {%- if !loop.last %}, {% endif -%}
+ {%- endfor %}
+{%- endmacro %}
+
+{#-
+// Field lists as used in Swift declarations of Records and Enums.
+// Note the var_name and type_name filters.
+-#}
+{% macro field_list_decl(item) %}
+ {%- for field in item.fields() -%}
+ {{ field.name()|var_name }}: {{ field|type_name -}}
+ {%- match field.default_value() %}
+ {%- when Some with(literal) %} = {{ literal|literal_swift(field) }}
+ {%- else %}
+ {%- endmatch -%}
+ {% if !loop.last %}, {% endif %}
+ {%- endfor %}
+{%- endmacro %}
+
+
+{% macro arg_list_protocol(func) %}
+ {%- for arg in func.arguments() -%}
+ {% if config.omit_argument_labels() %}_ {% endif %}{{ arg.name()|var_name }}: {{ arg|type_name -}}
+ {%- if !loop.last %}, {% endif -%}
+ {%- endfor %}
+{%- endmacro %}
+
+
+{%- macro async(func) %}
+{%- if func.is_async() %}async{% endif %}
+{%- endmacro -%}
+
+{%- macro throws(func) %}
+{%- if func.throws() %}throws{% endif %}
+{%- endmacro -%}
+
+{%- macro try(func) %}
+{%- if func.throws() %}try {% else %}try! {% endif %}
+{%- endmacro -%}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/wrapper.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/wrapper.swift
new file mode 100644
index 0000000000..c34d348efb
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/wrapper.swift
@@ -0,0 +1,68 @@
+// This file was autogenerated by some hot garbage in the `uniffi` crate.
+// Trust me, you don't want to mess with it!
+{%- import "macros.swift" as swift %}
+import Foundation
+{%- for imported_class in self.imports() %}
+import {{ imported_class }}
+{%- endfor %}
+
+// Depending on the consumer's build setup, the low-level FFI code
+// might be in a separate module, or it might be compiled inline into
+// this module. This is a bit of light hackery to work with both.
+#if canImport({{ config.ffi_module_name() }})
+import {{ config.ffi_module_name() }}
+#endif
+
+{% include "RustBufferTemplate.swift" %}
+{% include "Helpers.swift" %}
+
+// Public interface members begin here.
+{{ type_helper_code }}
+
+{%- if ci.has_async_fns() %}
+{% include "Async.swift" %}
+{%- endif %}
+
+{%- for func in ci.function_definitions() %}
+{%- include "TopLevelFunctionTemplate.swift" %}
+{%- endfor %}
+
+private enum InitializationResult {
+ case ok
+ case contractVersionMismatch
+ case apiChecksumMismatch
+}
+// Use a global variables to perform the versioning checks. Swift ensures that
+// the code inside is only computed once.
+private var initializationResult: InitializationResult {
+ // Get the bindings contract version from our ComponentInterface
+ let bindings_contract_version = {{ ci.uniffi_contract_version() }}
+ // Get the scaffolding contract version by calling the into the dylib
+ let scaffolding_contract_version = {{ ci.ffi_uniffi_contract_version().name() }}()
+ if bindings_contract_version != scaffolding_contract_version {
+ return InitializationResult.contractVersionMismatch
+ }
+
+ {%- for (name, expected_checksum) in ci.iter_checksums() %}
+ if ({{ name }}() != {{ expected_checksum }}) {
+ return InitializationResult.apiChecksumMismatch
+ }
+ {%- endfor %}
+
+ {% for fn in self.initialization_fns() -%}
+ {{ fn }}()
+ {% endfor -%}
+
+ return InitializationResult.ok
+}
+
+private func uniffiEnsureInitialized() {
+ switch initializationResult {
+ case .ok:
+ break
+ case .contractVersionMismatch:
+ fatalError("UniFFI contract version mismatch: try cleaning and rebuilding your project")
+ case .apiChecksumMismatch:
+ fatalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/test.rs b/third_party/rust/uniffi_bindgen/src/bindings/swift/test.rs
new file mode 100644
index 0000000000..c3b2f15277
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/test.rs
@@ -0,0 +1,202 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use crate::{
+ bindings::{RunScriptOptions, TargetLanguage},
+ library_mode::generate_bindings,
+};
+use anyhow::{bail, Context, Result};
+use camino::{Utf8Path, Utf8PathBuf};
+use std::env::consts::{DLL_PREFIX, DLL_SUFFIX};
+use std::ffi::OsStr;
+use std::fs::{read_to_string, File};
+use std::io::Write;
+use std::process::{Command, Stdio};
+use uniffi_testing::UniFFITestHelper;
+
+/// Run Swift tests for a UniFFI test fixture
+pub fn run_test(tmp_dir: &str, fixture_name: &str, script_file: &str) -> Result<()> {
+ run_script(
+ tmp_dir,
+ fixture_name,
+ script_file,
+ vec![],
+ &RunScriptOptions::default(),
+ )
+}
+
+/// Run a Swift script
+///
+/// This function will set things up so that the script can import the UniFFI bindings for a crate
+pub fn run_script(
+ tmp_dir: &str,
+ crate_name: &str,
+ script_file: &str,
+ args: Vec<String>,
+ options: &RunScriptOptions,
+) -> Result<()> {
+ let script_path = Utf8Path::new(".").join(script_file).canonicalize_utf8()?;
+ let test_helper = UniFFITestHelper::new(crate_name)?;
+ let out_dir = test_helper.create_out_dir(tmp_dir, &script_path)?;
+ let cdylib_path = test_helper.copy_cdylib_to_out_dir(&out_dir)?;
+ let generated_sources = GeneratedSources::new(crate_name, &cdylib_path, &out_dir)?;
+
+ // Compile the generated sources together to create a single swift module
+ compile_swift_module(
+ &out_dir,
+ &generated_sources.main_module,
+ &generated_sources.generated_swift_files,
+ &generated_sources.module_map,
+ options,
+ )?;
+
+ // Run the test script against compiled bindings
+ let mut command = create_command("swift", options);
+ command
+ .current_dir(&out_dir)
+ .arg("-I")
+ .arg(&out_dir)
+ .arg("-L")
+ .arg(&out_dir)
+ .args(calc_library_args(&out_dir)?)
+ .arg("-Xcc")
+ .arg(format!(
+ "-fmodule-map-file={}",
+ generated_sources.module_map
+ ))
+ .arg(&script_path)
+ .args(args);
+ let status = command
+ .spawn()
+ .context("Failed to spawn `swiftc` when running test script")?
+ .wait()
+ .context("Failed to wait for `swiftc` when running test script")?;
+ if !status.success() {
+ bail!("running `swift` to run test script failed ({:?})", command)
+ }
+ Ok(())
+}
+
+fn compile_swift_module<T: AsRef<OsStr>>(
+ out_dir: &Utf8Path,
+ module_name: &str,
+ sources: impl IntoIterator<Item = T>,
+ module_map: &Utf8Path,
+ options: &RunScriptOptions,
+) -> Result<()> {
+ let output_filename = format!("{DLL_PREFIX}testmod_{module_name}{DLL_SUFFIX}");
+ let mut command = create_command("swiftc", options);
+ command
+ .current_dir(out_dir)
+ .arg("-emit-module")
+ .arg("-module-name")
+ .arg(module_name)
+ .arg("-o")
+ .arg(output_filename)
+ .arg("-emit-library")
+ .arg("-Xcc")
+ .arg(format!("-fmodule-map-file={module_map}"))
+ .arg("-I")
+ .arg(out_dir)
+ .arg("-L")
+ .arg(out_dir)
+ .args(calc_library_args(out_dir)?)
+ .args(sources);
+ let status = command
+ .spawn()
+ .context("Failed to spawn `swiftc` when compiling bindings")?
+ .wait()
+ .context("Failed to wait for `swiftc` when compiling bindings")?;
+ if !status.success() {
+ bail!(
+ "running `swiftc` to compile bindings failed ({:?})",
+ command
+ )
+ };
+ Ok(())
+}
+
+// Stores sources generated by `uniffi-bindgen-swift`
+struct GeneratedSources {
+ main_module: String,
+ generated_swift_files: Vec<Utf8PathBuf>,
+ module_map: Utf8PathBuf,
+}
+
+impl GeneratedSources {
+ fn new(crate_name: &str, cdylib_path: &Utf8Path, out_dir: &Utf8Path) -> Result<Self> {
+ let sources =
+ generate_bindings(cdylib_path, None, &[TargetLanguage::Swift], out_dir, false)?;
+ let main_source = sources
+ .iter()
+ .find(|s| s.package.name == crate_name)
+ .unwrap();
+ let main_module = main_source.config.bindings.swift.module_name();
+ let modulemap_glob = glob(&out_dir.join("*.modulemap"))?;
+ let module_map = match modulemap_glob.len() {
+ 0 => bail!("No modulemap files found in {out_dir}"),
+ // Normally we only generate 1 module map and can return it directly
+ 1 => modulemap_glob.into_iter().next().unwrap(),
+ // When we use multiple UDL files in a test, for example the ext-types fixture,
+ // then we get multiple module maps and need to combine them
+ _ => {
+ let path = out_dir.join("combined.modulemap");
+ let mut f = File::create(&path)?;
+ write!(
+ f,
+ "{}",
+ modulemap_glob
+ .into_iter()
+ .map(|path| Ok(read_to_string(path)?))
+ .collect::<Result<Vec<String>>>()?
+ .join("\n")
+ )?;
+ path
+ }
+ };
+
+ Ok(GeneratedSources {
+ main_module,
+ generated_swift_files: glob(&out_dir.join("*.swift"))?,
+ module_map,
+ })
+ }
+}
+
+fn create_command(program: &str, options: &RunScriptOptions) -> Command {
+ let mut command = Command::new(program);
+ if !options.show_compiler_messages {
+ // This prevents most compiler messages, but not remarks
+ command.arg("-suppress-warnings");
+ // This gets the remarks. Note: swift will eventually get a `-supress-remarks` argument,
+ // maybe we can eventually move to that
+ command.stderr(Stdio::null());
+ }
+ command
+}
+
+// Wraps glob to use Utf8Paths and flattens errors
+fn glob(globspec: &Utf8Path) -> Result<Vec<Utf8PathBuf>> {
+ glob::glob(globspec.as_str())?
+ .map(|globresult| Ok(Utf8PathBuf::try_from(globresult?)?))
+ .collect()
+}
+
+fn calc_library_args(out_dir: &Utf8Path) -> Result<Vec<String>> {
+ let results = glob::glob(out_dir.join(format!("{DLL_PREFIX}*{DLL_SUFFIX}")).as_str())?;
+ results
+ .map(|globresult| {
+ let path = Utf8PathBuf::try_from(globresult.unwrap())?;
+ Ok(format!(
+ "-l{}",
+ path.file_name()
+ .unwrap()
+ .strip_prefix(DLL_PREFIX)
+ .unwrap()
+ .strip_suffix(DLL_SUFFIX)
+ .unwrap()
+ ))
+ })
+ .collect()
+}
diff --git a/third_party/rust/uniffi_bindgen/src/interface/callbacks.rs b/third_party/rust/uniffi_bindgen/src/interface/callbacks.rs
new file mode 100644
index 0000000000..e3bca4f966
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/interface/callbacks.rs
@@ -0,0 +1,149 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! # Callback Interface definitions for a `ComponentInterface`.
+//!
+//! This module converts callback interface definitions from UDL into structures that
+//! can be added to a `ComponentInterface`. A declaration in the UDL like this:
+//!
+//! ```
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! # namespace example {};
+//! callback interface Example {
+//! string hello();
+//! };
+//! # "##, "crate_name")?;
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+//!
+//! Will result in a [`CallbackInterface`] member being added to the resulting
+//! [`crate::ComponentInterface`]:
+//!
+//! ```
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! # namespace example {};
+//! # callback interface Example {
+//! # string hello();
+//! # };
+//! # "##, "crate_name")?;
+//! let callback = ci.get_callback_interface_definition("Example").unwrap();
+//! assert_eq!(callback.name(), "Example");
+//! assert_eq!(callback.methods()[0].name(), "hello");
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+
+use uniffi_meta::Checksum;
+
+use super::ffi::{FfiArgument, FfiFunction, FfiType};
+use super::object::Method;
+use super::{AsType, Type, TypeIterator};
+
+#[derive(Debug, Clone, Checksum)]
+pub struct CallbackInterface {
+ pub(super) name: String,
+ pub(super) module_path: String,
+ pub(super) methods: Vec<Method>,
+ // We don't include the FFIFunc in the hash calculation, because:
+ // - it is entirely determined by the other fields,
+ // so excluding it is safe.
+ // - its `name` property includes a checksum derived from the very
+ // hash value we're trying to calculate here, so excluding it
+ // avoids a weird circular dependency in the calculation.
+ #[checksum_ignore]
+ pub(super) ffi_init_callback: FfiFunction,
+}
+
+impl CallbackInterface {
+ pub fn new(name: String, module_path: String) -> CallbackInterface {
+ CallbackInterface {
+ name,
+ module_path,
+ methods: Default::default(),
+ ffi_init_callback: Default::default(),
+ }
+ }
+
+ pub fn name(&self) -> &str {
+ &self.name
+ }
+
+ pub fn methods(&self) -> Vec<&Method> {
+ self.methods.iter().collect()
+ }
+
+ pub fn ffi_init_callback(&self) -> &FfiFunction {
+ &self.ffi_init_callback
+ }
+
+ pub(super) fn derive_ffi_funcs(&mut self) {
+ self.ffi_init_callback.name =
+ uniffi_meta::init_callback_fn_symbol_name(&self.module_path, &self.name);
+ self.ffi_init_callback.arguments = vec![FfiArgument {
+ name: "callback_stub".to_string(),
+ type_: FfiType::ForeignCallback,
+ }];
+ self.ffi_init_callback.return_type = None;
+ }
+
+ pub fn iter_types(&self) -> TypeIterator<'_> {
+ Box::new(self.methods.iter().flat_map(Method::iter_types))
+ }
+}
+
+impl AsType for CallbackInterface {
+ fn as_type(&self) -> Type {
+ Type::CallbackInterface {
+ name: self.name.clone(),
+ module_path: self.module_path.clone(),
+ }
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::super::ComponentInterface;
+
+ #[test]
+ fn test_empty_interface() {
+ const UDL: &str = r#"
+ namespace test{};
+ // Weird, but allowed.
+ callback interface Testing {};
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
+ assert_eq!(ci.callback_interface_definitions().len(), 1);
+ assert_eq!(
+ ci.get_callback_interface_definition("Testing")
+ .unwrap()
+ .methods()
+ .len(),
+ 0
+ );
+ }
+
+ #[test]
+ fn test_multiple_interfaces() {
+ const UDL: &str = r#"
+ namespace test{};
+ callback interface One {
+ void one();
+ };
+ callback interface Two {
+ u32 two();
+ u64 too();
+ };
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
+ assert_eq!(ci.callback_interface_definitions().len(), 2);
+
+ let callbacks_one = ci.get_callback_interface_definition("One").unwrap();
+ assert_eq!(callbacks_one.methods().len(), 1);
+ assert_eq!(callbacks_one.methods()[0].name(), "one");
+
+ let callbacks_two = ci.get_callback_interface_definition("Two").unwrap();
+ assert_eq!(callbacks_two.methods().len(), 2);
+ assert_eq!(callbacks_two.methods()[0].name(), "two");
+ assert_eq!(callbacks_two.methods()[1].name(), "too");
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/interface/enum_.rs b/third_party/rust/uniffi_bindgen/src/interface/enum_.rs
new file mode 100644
index 0000000000..82baf1dd50
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/interface/enum_.rs
@@ -0,0 +1,567 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! # Enum definitions for a `ComponentInterface`.
+//!
+//! This module converts enum definition from UDL into structures that can be
+//! added to a `ComponentInterface`. A declaration in the UDL like this:
+//!
+//! ```
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! # namespace example {};
+//! enum Example {
+//! "one",
+//! "two"
+//! };
+//! # "##, "crate_name")?;
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+//!
+//! Will result in a [`Enum`] member being added to the resulting [`crate::ComponentInterface`]:
+//!
+//! ```
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! # namespace example {};
+//! # enum Example {
+//! # "one",
+//! # "two"
+//! # };
+//! # "##, "crate_name")?;
+//! let e = ci.get_enum_definition("Example").unwrap();
+//! assert_eq!(e.name(), "Example");
+//! assert_eq!(e.variants().len(), 2);
+//! assert_eq!(e.variants()[0].name(), "one");
+//! assert_eq!(e.variants()[1].name(), "two");
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+//!
+//! Like in Rust, UniFFI enums can contain associated data, but this needs to be
+//! declared with a different syntax in order to work within the restrictions of
+//! WebIDL. A declaration like this:
+//!
+//! ```
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! # namespace example {};
+//! [Enum]
+//! interface Example {
+//! Zero();
+//! One(u32 first);
+//! Two(u32 first, string second);
+//! };
+//! # "##, "crate_name")?;
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+//!
+//! Will result in an [`Enum`] member whose variants have associated fields:
+//!
+//! ```
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! # namespace example {};
+//! # [Enum]
+//! # interface ExampleWithData {
+//! # Zero();
+//! # One(u32 first);
+//! # Two(u32 first, string second);
+//! # };
+//! # "##, "crate_name")?;
+//! let e = ci.get_enum_definition("ExampleWithData").unwrap();
+//! assert_eq!(e.name(), "ExampleWithData");
+//! assert_eq!(e.variants().len(), 3);
+//! assert_eq!(e.variants()[0].name(), "Zero");
+//! assert_eq!(e.variants()[0].fields().len(), 0);
+//! assert_eq!(e.variants()[1].name(), "One");
+//! assert_eq!(e.variants()[1].fields().len(), 1);
+//! assert_eq!(e.variants()[1].fields()[0].name(), "first");
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+//!
+//! # Enums are also used to represent error definitions for a `ComponentInterface`.
+//!
+//! ```
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! # namespace example {};
+//! [Error]
+//! enum Example {
+//! "one",
+//! "two"
+//! };
+//! # "##, "crate_name")?;
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+//!
+//! Will result in an [`Enum`] member with fieldless variants being added to the resulting [`crate::ComponentInterface`]:
+//!
+//! ```
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! # namespace example {};
+//! # [Error]
+//! # enum Example {
+//! # "one",
+//! # "two"
+//! # };
+//! # "##, "crate_name")?;
+//! let err = ci.get_enum_definition("Example").unwrap();
+//! assert_eq!(err.name(), "Example");
+//! assert_eq!(err.variants().len(), 2);
+//! assert_eq!(err.variants()[0].name(), "one");
+//! assert_eq!(err.variants()[1].name(), "two");
+//! assert_eq!(err.is_flat(), true);
+//! assert!(ci.is_name_used_as_error(&err.name()));
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+//!
+//! A declaration in the UDL like this:
+//!
+//! ```
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! # namespace example {};
+//! [Error]
+//! interface Example {
+//! one(i16 code);
+//! two(string reason);
+//! three(i32 x, i32 y);
+//! };
+//! # "##, "crate_name")?;
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+//!
+//! Will result in an [`Enum`] member with variants that have fields being added to the resulting [`crate::ComponentInterface`]:
+//!
+//! ```
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! # namespace example {};
+//! # [Error]
+//! # interface Example {
+//! # one();
+//! # two(string reason);
+//! # three(i32 x, i32 y);
+//! # };
+//! # "##, "crate_name")?;
+//! let err = ci.get_enum_definition("Example").unwrap();
+//! assert_eq!(err.name(), "Example");
+//! assert_eq!(err.variants().len(), 3);
+//! assert_eq!(err.variants()[0].name(), "one");
+//! assert_eq!(err.variants()[1].name(), "two");
+//! assert_eq!(err.variants()[2].name(), "three");
+//! assert_eq!(err.variants()[0].fields().len(), 0);
+//! assert_eq!(err.variants()[1].fields().len(), 1);
+//! assert_eq!(err.variants()[1].fields()[0].name(), "reason");
+//! assert_eq!(err.variants()[2].fields().len(), 2);
+//! assert_eq!(err.variants()[2].fields()[0].name(), "x");
+//! assert_eq!(err.variants()[2].fields()[1].name(), "y");
+//! assert_eq!(err.is_flat(), false);
+//! assert!(ci.is_name_used_as_error(err.name()));
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+
+use anyhow::Result;
+use uniffi_meta::Checksum;
+
+use super::record::Field;
+use super::{AsType, Type, TypeIterator};
+
+/// Represents an enum with named variants, each of which may have named
+/// and typed fields.
+///
+/// Enums are passed across the FFI by serializing to a bytebuffer, with a
+/// i32 indicating the variant followed by the serialization of each field.
+#[derive(Debug, Clone, PartialEq, Eq, Checksum)]
+pub struct Enum {
+ pub(super) name: String,
+ pub(super) module_path: String,
+ pub(super) variants: Vec<Variant>,
+ // NOTE: `flat` is a misleading name and to make matters worse, has 2 different
+ // meanings depending on the context :(
+ // * When used as part of Rust scaffolding generation, it means "is this enum
+ // used with an Error, and that error should we lowered to foreign bindings
+ // by converting each variant to a string and lowering the variant with that
+ // string?". In that context, it should probably be called `lowered_as_string` or
+ // similar.
+ // * When used as part of bindings generation, it means "does this enum have only
+ // variants with no associated data"? The foreign binding generators are likely
+ // to generate significantly different versions of the enum based on that value.
+ //
+ // The reason it is described as "has 2 different meanings" by way of example:
+ // * For an Enum described as being a flat error, but the enum itself has variants with data,
+ // `flat` will be `true` for the Enum when generating scaffolding and `false` when
+ // generating bindings.
+ // * For an Enum not used as an error but which has no variants with data, `flat` will be
+ // false when generating the scaffolding but `true` when generating bindings.
+ pub(super) flat: bool,
+}
+
+impl Enum {
+ pub fn name(&self) -> &str {
+ &self.name
+ }
+
+ pub fn variants(&self) -> &[Variant] {
+ &self.variants
+ }
+
+ pub fn is_flat(&self) -> bool {
+ self.flat
+ }
+
+ pub fn iter_types(&self) -> TypeIterator<'_> {
+ Box::new(self.variants.iter().flat_map(Variant::iter_types))
+ }
+
+ // Sadly can't use TryFrom due to the 'is_flat' complication.
+ pub fn try_from_meta(meta: uniffi_meta::EnumMetadata, flat: bool) -> Result<Self> {
+ // This is messy - error enums are considered "flat" if the user
+ // opted in via a special attribute, regardless of whether the enum
+ // is actually flat.
+ // Real enums are considered flat iff they are actually flat.
+ // We don't have that context here, so this is handled by our caller.
+ Ok(Self {
+ name: meta.name,
+ module_path: meta.module_path,
+ variants: meta
+ .variants
+ .into_iter()
+ .map(TryInto::try_into)
+ .collect::<Result<_>>()?,
+ flat,
+ })
+ }
+}
+
+impl AsType for Enum {
+ fn as_type(&self) -> Type {
+ Type::Enum {
+ name: self.name.clone(),
+ module_path: self.module_path.clone(),
+ }
+ }
+}
+
+/// Represents an individual variant in an Enum.
+///
+/// Each variant has a name and zero or more fields.
+#[derive(Debug, Clone, Default, PartialEq, Eq, Checksum)]
+pub struct Variant {
+ pub(super) name: String,
+ pub(super) fields: Vec<Field>,
+}
+
+impl Variant {
+ pub fn name(&self) -> &str {
+ &self.name
+ }
+
+ pub fn fields(&self) -> &[Field] {
+ &self.fields
+ }
+
+ pub fn has_fields(&self) -> bool {
+ !self.fields.is_empty()
+ }
+
+ pub fn iter_types(&self) -> TypeIterator<'_> {
+ Box::new(self.fields.iter().flat_map(Field::iter_types))
+ }
+}
+
+impl TryFrom<uniffi_meta::VariantMetadata> for Variant {
+ type Error = anyhow::Error;
+
+ fn try_from(meta: uniffi_meta::VariantMetadata) -> Result<Self> {
+ Ok(Self {
+ name: meta.name,
+ fields: meta
+ .fields
+ .into_iter()
+ .map(TryInto::try_into)
+ .collect::<Result<_>>()?,
+ })
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::super::{ComponentInterface, FfiType};
+ use super::*;
+
+ #[test]
+ fn test_duplicate_variants() {
+ const UDL: &str = r#"
+ namespace test{};
+ // Weird, but currently allowed!
+ // We should probably disallow this...
+ enum Testing { "one", "two", "one" };
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
+ assert_eq!(ci.enum_definitions().count(), 1);
+ assert_eq!(
+ ci.get_enum_definition("Testing").unwrap().variants().len(),
+ 3
+ );
+ }
+
+ #[test]
+ fn test_associated_data() {
+ const UDL: &str = r#"
+ namespace test {
+ void takes_an_enum(TestEnum e);
+ void takes_an_enum_with_data(TestEnumWithData ed);
+ TestEnum returns_an_enum();
+ TestEnumWithData returns_an_enum_with_data();
+ };
+
+ enum TestEnum { "one", "two" };
+
+ [Enum]
+ interface TestEnumWithData {
+ Zero();
+ One(u32 first);
+ Two(u32 first, string second);
+ };
+
+ [Enum]
+ interface TestEnumWithoutData {
+ One();
+ Two();
+ };
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
+ assert_eq!(ci.enum_definitions().count(), 3);
+ assert_eq!(ci.function_definitions().len(), 4);
+
+ // The "flat" enum with no associated data.
+ let e = ci.get_enum_definition("TestEnum").unwrap();
+ assert!(e.is_flat());
+ assert_eq!(e.variants().len(), 2);
+ assert_eq!(
+ e.variants().iter().map(|v| v.name()).collect::<Vec<_>>(),
+ vec!["one", "two"]
+ );
+ assert_eq!(e.variants()[0].fields().len(), 0);
+ assert_eq!(e.variants()[1].fields().len(), 0);
+
+ // The enum with associated data.
+ let ed = ci.get_enum_definition("TestEnumWithData").unwrap();
+ assert!(!ed.is_flat());
+ assert_eq!(ed.variants().len(), 3);
+ assert_eq!(
+ ed.variants().iter().map(|v| v.name()).collect::<Vec<_>>(),
+ vec!["Zero", "One", "Two"]
+ );
+ assert_eq!(ed.variants()[0].fields().len(), 0);
+ assert_eq!(
+ ed.variants()[1]
+ .fields()
+ .iter()
+ .map(|f| f.name())
+ .collect::<Vec<_>>(),
+ vec!["first"]
+ );
+ assert_eq!(
+ ed.variants()[1]
+ .fields()
+ .iter()
+ .map(|f| f.as_type())
+ .collect::<Vec<_>>(),
+ vec![Type::UInt32]
+ );
+ assert_eq!(
+ ed.variants()[2]
+ .fields()
+ .iter()
+ .map(|f| f.name())
+ .collect::<Vec<_>>(),
+ vec!["first", "second"]
+ );
+ assert_eq!(
+ ed.variants()[2]
+ .fields()
+ .iter()
+ .map(|f| f.as_type())
+ .collect::<Vec<_>>(),
+ vec![Type::UInt32, Type::String]
+ );
+
+ // The enum declared via interface, but with no associated data.
+ let ewd = ci.get_enum_definition("TestEnumWithoutData").unwrap();
+ assert_eq!(ewd.variants().len(), 2);
+ assert_eq!(
+ ewd.variants().iter().map(|v| v.name()).collect::<Vec<_>>(),
+ vec!["One", "Two"]
+ );
+ assert_eq!(ewd.variants()[0].fields().len(), 0);
+ assert_eq!(ewd.variants()[1].fields().len(), 0);
+
+ // Flat enums pass over the FFI as bytebuffers.
+ // (It might be nice to optimize these to pass as plain integers, but that's
+ // difficult atop the current factoring of `ComponentInterface` and friends).
+ let farg = ci.get_function_definition("takes_an_enum").unwrap();
+ assert_eq!(
+ farg.arguments()[0].as_type(),
+ Type::Enum {
+ name: "TestEnum".into(),
+ module_path: "crate_name".into()
+ }
+ );
+ assert_eq!(
+ farg.ffi_func().arguments()[0].type_(),
+ FfiType::RustBuffer(None)
+ );
+ let fret = ci.get_function_definition("returns_an_enum").unwrap();
+ assert!(
+ matches!(fret.return_type(), Some(Type::Enum { name, .. }) if name == "TestEnum" && !ci.is_name_used_as_error(name))
+ );
+ assert!(matches!(
+ fret.ffi_func().return_type(),
+ Some(FfiType::RustBuffer(None))
+ ));
+
+ // Enums with associated data pass over the FFI as bytebuffers.
+ let farg = ci
+ .get_function_definition("takes_an_enum_with_data")
+ .unwrap();
+ assert_eq!(
+ farg.arguments()[0].as_type(),
+ Type::Enum {
+ name: "TestEnumWithData".into(),
+ module_path: "crate_name".into()
+ }
+ );
+ assert_eq!(
+ farg.ffi_func().arguments()[0].type_(),
+ FfiType::RustBuffer(None)
+ );
+ let fret = ci
+ .get_function_definition("returns_an_enum_with_data")
+ .unwrap();
+ assert!(
+ matches!(fret.return_type(), Some(Type::Enum { name, .. }) if name == "TestEnumWithData" && !ci.is_name_used_as_error(name))
+ );
+ assert!(matches!(
+ fret.ffi_func().return_type(),
+ Some(FfiType::RustBuffer(None))
+ ));
+ }
+
+ // Tests for [Error], which are represented as `Enum`
+ #[test]
+ fn test_variants() {
+ const UDL: &str = r#"
+ namespace test{};
+ [Error]
+ enum Testing { "one", "two", "three" };
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
+ assert_eq!(ci.enum_definitions().count(), 1);
+ let error = ci.get_enum_definition("Testing").unwrap();
+ assert_eq!(
+ error
+ .variants()
+ .iter()
+ .map(|v| v.name())
+ .collect::<Vec<&str>>(),
+ vec!("one", "two", "three")
+ );
+ assert!(error.is_flat());
+ assert!(ci.is_name_used_as_error(&error.name));
+ }
+
+ #[test]
+ fn test_duplicate_error_variants() {
+ const UDL: &str = r#"
+ namespace test{};
+ // Weird, but currently allowed!
+ // We should probably disallow this...
+ [Error]
+ enum Testing { "one", "two", "one" };
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
+ assert_eq!(ci.enum_definitions().count(), 1);
+ assert_eq!(
+ ci.get_enum_definition("Testing").unwrap().variants().len(),
+ 3
+ );
+ }
+
+ #[test]
+ fn test_variant_data() {
+ const UDL: &str = r#"
+ namespace test{};
+
+ [Error]
+ interface Testing {
+ One(string reason);
+ Two(u8 code);
+ };
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
+ assert_eq!(ci.enum_definitions().count(), 1);
+ let error: &Enum = ci.get_enum_definition("Testing").unwrap();
+ assert_eq!(
+ error
+ .variants()
+ .iter()
+ .map(|v| v.name())
+ .collect::<Vec<&str>>(),
+ vec!("One", "Two")
+ );
+ assert!(!error.is_flat());
+ assert!(ci.is_name_used_as_error(&error.name));
+ }
+
+ #[test]
+ fn test_enum_variant_named_error() {
+ const UDL: &str = r#"
+ namespace test{};
+
+ [Enum]
+ interface Testing {
+ Normal(string first);
+ Error(string first);
+ };
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
+ assert_eq!(ci.enum_definitions().count(), 1);
+ let testing: &Enum = ci.get_enum_definition("Testing").unwrap();
+ assert_eq!(
+ testing.variants()[0]
+ .fields()
+ .iter()
+ .map(|f| f.name())
+ .collect::<Vec<_>>(),
+ vec!["first"]
+ );
+ assert_eq!(
+ testing.variants()[0]
+ .fields()
+ .iter()
+ .map(|f| f.as_type())
+ .collect::<Vec<_>>(),
+ vec![Type::String]
+ );
+ assert_eq!(
+ testing.variants()[1]
+ .fields()
+ .iter()
+ .map(|f| f.name())
+ .collect::<Vec<_>>(),
+ vec!["first"]
+ );
+ assert_eq!(
+ testing.variants()[1]
+ .fields()
+ .iter()
+ .map(|f| f.as_type())
+ .collect::<Vec<_>>(),
+ vec![Type::String]
+ );
+ assert_eq!(
+ testing
+ .variants()
+ .iter()
+ .map(|v| v.name())
+ .collect::<Vec<_>>(),
+ vec!["Normal", "Error"]
+ );
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/interface/ffi.rs b/third_party/rust/uniffi_bindgen/src/interface/ffi.rs
new file mode 100644
index 0000000000..d18aaf8262
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/interface/ffi.rs
@@ -0,0 +1,227 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! # Low-level typesystem for the FFI layer of a component interface.
+//!
+//! This module provides the "FFI-level" typesystem of a UniFFI Rust Component, that is,
+//! the C-style functions and structs and primitive datatypes that are used to interface
+//! between the Rust component code and the foreign-language bindings.
+//!
+//! These types are purely an implementation detail of UniFFI, so consumers shouldn't
+//! need to know about them. But as a developer working on UniFFI itself, you're likely
+//! to spend a lot of time thinking about how these low-level types are used to represent
+//! the higher-level "interface types" from the [`Type`] enum.
+/// Represents the restricted set of low-level types that can be used to construct
+/// the C-style FFI layer between a rust component and its foreign language bindings.
+///
+/// For the types that involve memory allocation, we make a distinction between
+/// "owned" types (the recipient must free it, or pass it to someone else) and
+/// "borrowed" types (the sender must keep it alive for the duration of the call).
+use uniffi_meta::{ExternalKind, Type};
+
+#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
+pub enum FfiType {
+ // N.B. there are no booleans at this layer, since they cause problems for JNA.
+ UInt8,
+ Int8,
+ UInt16,
+ Int16,
+ UInt32,
+ Int32,
+ UInt64,
+ Int64,
+ Float32,
+ Float64,
+ /// A `*const c_void` pointer to a rust-owned `Arc<T>`.
+ /// If you've got one of these, you must call the appropriate rust function to free it.
+ /// The templates will generate a unique `free` function for each T.
+ /// The inner string references the name of the `T` type.
+ RustArcPtr(String),
+ /// A byte buffer allocated by rust, and owned by whoever currently holds it.
+ /// If you've got one of these, you must either call the appropriate rust function to free it
+ /// or pass it to someone that will.
+ /// If the inner option is Some, it is the name of the external type. The bindings may need
+ /// to use this name to import the correct RustBuffer for that type.
+ RustBuffer(Option<String>),
+ /// A borrowed reference to some raw bytes owned by foreign language code.
+ /// The provider of this reference must keep it alive for the duration of the receiving call.
+ ForeignBytes,
+ /// Pointer to a callback function that handles all callbacks on the foreign language side.
+ ForeignCallback,
+ /// Pointer-sized opaque handle that represents a foreign executor. Foreign bindings can
+ /// either use an actual pointer or a usized integer.
+ ForeignExecutorHandle,
+ /// Pointer to the callback function that's invoked to schedule calls with a ForeignExecutor
+ ForeignExecutorCallback,
+ /// Pointer to a Rust future
+ RustFutureHandle,
+ /// Continuation function for a Rust future
+ RustFutureContinuationCallback,
+ RustFutureContinuationData,
+ // TODO: you can imagine a richer structural typesystem here, e.g. `Ref<String>` or something.
+ // We don't need that yet and it's possible we never will, so it isn't here for now.
+}
+
+/// When passing data across the FFI, each `Type` value will be lowered into a corresponding
+/// `FfiType` value. This conversion tells you which one.
+///
+/// Note that the conversion is one-way - given an FfiType, it is not in general possible to
+/// tell what the corresponding Type is that it's being used to represent.
+impl From<&Type> for FfiType {
+ fn from(t: &Type) -> FfiType {
+ match t {
+ // Types that are the same map to themselves, naturally.
+ Type::UInt8 => FfiType::UInt8,
+ Type::Int8 => FfiType::Int8,
+ Type::UInt16 => FfiType::UInt16,
+ Type::Int16 => FfiType::Int16,
+ Type::UInt32 => FfiType::UInt32,
+ Type::Int32 => FfiType::Int32,
+ Type::UInt64 => FfiType::UInt64,
+ Type::Int64 => FfiType::Int64,
+ Type::Float32 => FfiType::Float32,
+ Type::Float64 => FfiType::Float64,
+ // Booleans lower into an Int8, to work around a bug in JNA.
+ Type::Boolean => FfiType::Int8,
+ // Strings are always owned rust values.
+ // We might add a separate type for borrowed strings in future.
+ Type::String => FfiType::RustBuffer(None),
+ // Byte strings are also always owned rust values.
+ // We might add a separate type for borrowed byte strings in future as well.
+ Type::Bytes => FfiType::RustBuffer(None),
+ // Objects are pointers to an Arc<>
+ Type::Object { name, .. } => FfiType::RustArcPtr(name.to_owned()),
+ // Callback interfaces are passed as opaque integer handles.
+ Type::CallbackInterface { .. } => FfiType::UInt64,
+ Type::ForeignExecutor => FfiType::ForeignExecutorHandle,
+ // Other types are serialized into a bytebuffer and deserialized on the other side.
+ Type::Enum { .. }
+ | Type::Record { .. }
+ | Type::Optional { .. }
+ | Type::Sequence { .. }
+ | Type::Map { .. }
+ | Type::Timestamp
+ | Type::Duration => FfiType::RustBuffer(None),
+ Type::External {
+ name,
+ kind: ExternalKind::Interface,
+ ..
+ } => FfiType::RustArcPtr(name.clone()),
+ Type::External {
+ name,
+ kind: ExternalKind::DataClass,
+ ..
+ } => FfiType::RustBuffer(Some(name.clone())),
+ Type::Custom { builtin, .. } => FfiType::from(builtin.as_ref()),
+ }
+ }
+}
+
+// Needed for rust scaffolding askama template
+impl From<Type> for FfiType {
+ fn from(ty: Type) -> Self {
+ (&ty).into()
+ }
+}
+
+impl From<&&Type> for FfiType {
+ fn from(ty: &&Type) -> Self {
+ (*ty).into()
+ }
+}
+
+/// Represents an "extern C"-style function that will be part of the FFI.
+///
+/// These can't be declared explicitly in the UDL, but rather, are derived automatically
+/// from the high-level interface. Each callable thing in the component API will have a
+/// corresponding `FfiFunction` through which it can be invoked, and UniFFI also provides
+/// some built-in `FfiFunction` helpers for use in the foreign language bindings.
+#[derive(Debug, Clone)]
+pub struct FfiFunction {
+ pub(super) name: String,
+ pub(super) is_async: bool,
+ pub(super) arguments: Vec<FfiArgument>,
+ pub(super) return_type: Option<FfiType>,
+ pub(super) has_rust_call_status_arg: bool,
+ /// Used by C# generator to differentiate the free function and call it with void*
+ /// instead of C# `SafeHandle` type. See <https://github.com/mozilla/uniffi-rs/pull/1488>.
+ pub(super) is_object_free_function: bool,
+}
+
+impl FfiFunction {
+ pub fn name(&self) -> &str {
+ &self.name
+ }
+
+ pub fn is_async(&self) -> bool {
+ self.is_async
+ }
+
+ pub fn arguments(&self) -> Vec<&FfiArgument> {
+ self.arguments.iter().collect()
+ }
+
+ pub fn return_type(&self) -> Option<&FfiType> {
+ self.return_type.as_ref()
+ }
+
+ pub fn has_rust_call_status_arg(&self) -> bool {
+ self.has_rust_call_status_arg
+ }
+
+ pub fn is_object_free_function(&self) -> bool {
+ self.is_object_free_function
+ }
+
+ pub fn init(
+ &mut self,
+ return_type: Option<FfiType>,
+ args: impl IntoIterator<Item = FfiArgument>,
+ ) {
+ self.arguments = args.into_iter().collect();
+ if self.is_async() {
+ self.return_type = Some(FfiType::RustFutureHandle);
+ self.has_rust_call_status_arg = false;
+ } else {
+ self.return_type = return_type;
+ }
+ }
+}
+
+impl Default for FfiFunction {
+ fn default() -> Self {
+ Self {
+ name: "".into(),
+ is_async: false,
+ arguments: Vec::new(),
+ return_type: None,
+ has_rust_call_status_arg: true,
+ is_object_free_function: false,
+ }
+ }
+}
+
+/// Represents an argument to an FFI function.
+///
+/// Each argument has a name and a type.
+#[derive(Debug, Clone)]
+pub struct FfiArgument {
+ pub(super) name: String,
+ pub(super) type_: FfiType,
+}
+
+impl FfiArgument {
+ pub fn name(&self) -> &str {
+ &self.name
+ }
+ pub fn type_(&self) -> FfiType {
+ self.type_.clone()
+ }
+}
+
+#[cfg(test)]
+mod test {
+ // There's not really much to test here to be honest,
+ // it's mostly type declarations.
+}
diff --git a/third_party/rust/uniffi_bindgen/src/interface/function.rs b/third_party/rust/uniffi_bindgen/src/interface/function.rs
new file mode 100644
index 0000000000..2d18288c1c
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/interface/function.rs
@@ -0,0 +1,367 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! # Function definitions for a `ComponentInterface`.
+//!
+//! This module converts function definitions from UDL into structures that
+//! can be added to a `ComponentInterface`. A declaration in the UDL like this:
+//!
+//! ```
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! namespace example {
+//! string hello();
+//! };
+//! # "##, "crate_name")?;
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+//!
+//! Will result in a [`Function`] member being added to the resulting [`crate::ComponentInterface`]:
+//!
+//! ```
+//! # use uniffi_bindgen::interface::Type;
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! # namespace example {
+//! # string hello();
+//! # };
+//! # "##, "crate_name")?;
+//! let func = ci.get_function_definition("hello").unwrap();
+//! assert_eq!(func.name(), "hello");
+//! assert!(matches!(func.return_type(), Some(Type::String)));
+//! assert_eq!(func.arguments().len(), 0);
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+
+use anyhow::Result;
+
+use super::ffi::{FfiArgument, FfiFunction, FfiType};
+use super::{AsType, ComponentInterface, Literal, ObjectImpl, Type, TypeIterator};
+use uniffi_meta::Checksum;
+
+/// Represents a standalone function.
+///
+/// Each `Function` corresponds to a standalone function in the rust module,
+/// and has a corresponding standalone function in the foreign language bindings.
+///
+/// In the FFI, this will be a standalone function with appropriately lowered types.
+#[derive(Debug, Clone, Checksum)]
+pub struct Function {
+ pub(super) name: String,
+ pub(super) module_path: String,
+ pub(super) is_async: bool,
+ pub(super) arguments: Vec<Argument>,
+ pub(super) return_type: Option<Type>,
+ // We don't include the FFIFunc in the hash calculation, because:
+ // - it is entirely determined by the other fields,
+ // so excluding it is safe.
+ // - its `name` property includes a checksum derived from the very
+ // hash value we're trying to calculate here, so excluding it
+ // avoids a weird circular dependency in the calculation.
+ #[checksum_ignore]
+ pub(super) ffi_func: FfiFunction,
+ pub(super) throws: Option<Type>,
+ pub(super) checksum_fn_name: String,
+ // Force a checksum value, or we'll fallback to the trait.
+ #[checksum_ignore]
+ pub(super) checksum: Option<u16>,
+}
+
+impl Function {
+ pub fn name(&self) -> &str {
+ &self.name
+ }
+
+ pub fn is_async(&self) -> bool {
+ self.is_async
+ }
+
+ pub fn arguments(&self) -> Vec<&Argument> {
+ self.arguments.iter().collect()
+ }
+
+ pub fn full_arguments(&self) -> Vec<Argument> {
+ self.arguments.to_vec()
+ }
+
+ pub fn return_type(&self) -> Option<&Type> {
+ self.return_type.as_ref()
+ }
+
+ pub fn ffi_func(&self) -> &FfiFunction {
+ &self.ffi_func
+ }
+
+ pub fn checksum_fn_name(&self) -> &str {
+ &self.checksum_fn_name
+ }
+
+ pub fn checksum(&self) -> u16 {
+ self.checksum.unwrap_or_else(|| uniffi_meta::checksum(self))
+ }
+
+ pub fn throws(&self) -> bool {
+ self.throws.is_some()
+ }
+
+ pub fn throws_name(&self) -> Option<&str> {
+ super::throws_name(&self.throws)
+ }
+
+ pub fn throws_type(&self) -> Option<&Type> {
+ self.throws.as_ref()
+ }
+
+ pub fn derive_ffi_func(&mut self) -> Result<()> {
+ assert!(!self.ffi_func.name.is_empty());
+ self.ffi_func.init(
+ self.return_type.as_ref().map(Into::into),
+ self.arguments.iter().map(Into::into),
+ );
+ Ok(())
+ }
+
+ pub fn iter_types(&self) -> TypeIterator<'_> {
+ Box::new(
+ self.arguments
+ .iter()
+ .flat_map(Argument::iter_types)
+ .chain(self.return_type.iter().flat_map(Type::iter_types)),
+ )
+ }
+}
+
+impl From<uniffi_meta::FnParamMetadata> for Argument {
+ fn from(meta: uniffi_meta::FnParamMetadata) -> Self {
+ Argument {
+ name: meta.name,
+ type_: meta.ty,
+ by_ref: meta.by_ref,
+ optional: meta.optional,
+ default: meta.default,
+ }
+ }
+}
+
+impl From<uniffi_meta::FnMetadata> for Function {
+ fn from(meta: uniffi_meta::FnMetadata) -> Self {
+ let ffi_name = meta.ffi_symbol_name();
+ let checksum_fn_name = meta.checksum_symbol_name();
+ let is_async = meta.is_async;
+ let return_type = meta.return_type.map(Into::into);
+ let arguments = meta.inputs.into_iter().map(Into::into).collect();
+
+ let ffi_func = FfiFunction {
+ name: ffi_name,
+ is_async,
+ ..FfiFunction::default()
+ };
+
+ Self {
+ name: meta.name,
+ module_path: meta.module_path,
+ is_async,
+ arguments,
+ return_type,
+ ffi_func,
+ throws: meta.throws,
+ checksum_fn_name,
+ checksum: meta.checksum,
+ }
+ }
+}
+
+/// Represents an argument to a function/constructor/method call.
+///
+/// Each argument has a name and a type, along with some optional metadata.
+#[derive(Debug, Clone, Checksum)]
+pub struct Argument {
+ pub(super) name: String,
+ pub(super) type_: Type,
+ pub(super) by_ref: bool,
+ pub(super) optional: bool,
+ pub(super) default: Option<Literal>,
+}
+
+impl Argument {
+ pub fn name(&self) -> &str {
+ &self.name
+ }
+
+ pub fn by_ref(&self) -> bool {
+ self.by_ref
+ }
+
+ pub fn is_trait_ref(&self) -> bool {
+ matches!(&self.type_, Type::Object { imp, .. } if *imp == ObjectImpl::Trait)
+ }
+
+ pub fn default_value(&self) -> Option<&Literal> {
+ self.default.as_ref()
+ }
+
+ pub fn iter_types(&self) -> TypeIterator<'_> {
+ self.type_.iter_types()
+ }
+}
+
+impl AsType for Argument {
+ fn as_type(&self) -> Type {
+ self.type_.clone()
+ }
+}
+
+impl From<&Argument> for FfiArgument {
+ fn from(a: &Argument) -> FfiArgument {
+ FfiArgument {
+ name: a.name.clone(),
+ type_: (&a.type_).into(),
+ }
+ }
+}
+
+/// Combines the return and throws type of a function/method
+#[derive(Debug, PartialOrd, Ord, PartialEq, Eq)]
+pub struct ResultType {
+ pub return_type: Option<Type>,
+ pub throws_type: Option<Type>,
+}
+
+impl ResultType {
+ /// Get the `T` parameters for the `FutureCallback<T>` for this ResultType
+ pub fn future_callback_param(&self) -> FfiType {
+ match &self.return_type {
+ Some(t) => t.into(),
+ None => FfiType::UInt8,
+ }
+ }
+}
+
+/// Implemented by function-like types (Function, Method, Constructor)
+pub trait Callable {
+ fn arguments(&self) -> Vec<&Argument>;
+ fn return_type(&self) -> Option<Type>;
+ fn throws_type(&self) -> Option<Type>;
+ fn is_async(&self) -> bool;
+ fn result_type(&self) -> ResultType {
+ ResultType {
+ return_type: self.return_type(),
+ throws_type: self.throws_type(),
+ }
+ }
+
+ // Quick way to get the rust future scaffolding function that corresponds to our return type.
+
+ fn ffi_rust_future_poll(&self, ci: &ComponentInterface) -> String {
+ ci.ffi_rust_future_poll(self.return_type().map(Into::into))
+ .name()
+ .to_owned()
+ }
+
+ fn ffi_rust_future_cancel(&self, ci: &ComponentInterface) -> String {
+ ci.ffi_rust_future_cancel(self.return_type().map(Into::into))
+ .name()
+ .to_owned()
+ }
+
+ fn ffi_rust_future_complete(&self, ci: &ComponentInterface) -> String {
+ ci.ffi_rust_future_complete(self.return_type().map(Into::into))
+ .name()
+ .to_owned()
+ }
+
+ fn ffi_rust_future_free(&self, ci: &ComponentInterface) -> String {
+ ci.ffi_rust_future_free(self.return_type().map(Into::into))
+ .name()
+ .to_owned()
+ }
+}
+
+impl Callable for Function {
+ fn arguments(&self) -> Vec<&Argument> {
+ self.arguments()
+ }
+
+ fn return_type(&self) -> Option<Type> {
+ self.return_type().cloned()
+ }
+
+ fn throws_type(&self) -> Option<Type> {
+ self.throws_type().cloned()
+ }
+
+ fn is_async(&self) -> bool {
+ self.is_async
+ }
+}
+
+// Needed because Askama likes to add extra refs to variables
+impl<T: Callable> Callable for &T {
+ fn arguments(&self) -> Vec<&Argument> {
+ (*self).arguments()
+ }
+
+ fn return_type(&self) -> Option<Type> {
+ (*self).return_type()
+ }
+
+ fn throws_type(&self) -> Option<Type> {
+ (*self).throws_type()
+ }
+
+ fn is_async(&self) -> bool {
+ (*self).is_async()
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::super::ComponentInterface;
+ use super::*;
+
+ #[test]
+ fn test_minimal_and_rich_function() -> Result<()> {
+ let ci = ComponentInterface::from_webidl(
+ r#"
+ namespace test {
+ void minimal();
+ [Throws=TestError]
+ sequence<string?> rich(u32 arg1, TestDict arg2);
+ };
+ [Error]
+ enum TestError { "err" };
+ dictionary TestDict {
+ u32 field;
+ };
+ "#,
+ "crate_name",
+ )?;
+
+ let func1 = ci.get_function_definition("minimal").unwrap();
+ assert_eq!(func1.name(), "minimal");
+ assert!(func1.return_type().is_none());
+ assert!(func1.throws_type().is_none());
+ assert_eq!(func1.arguments().len(), 0);
+
+ let func2 = ci.get_function_definition("rich").unwrap();
+ assert_eq!(func2.name(), "rich");
+ assert_eq!(
+ func2.return_type().unwrap(),
+ &Type::Sequence {
+ inner_type: Box::new(Type::Optional {
+ inner_type: Box::new(Type::String)
+ })
+ }
+ );
+ assert!(
+ matches!(func2.throws_type(), Some(Type::Enum { name, .. }) if name == "TestError" && ci.is_name_used_as_error(name))
+ );
+ assert_eq!(func2.arguments().len(), 2);
+ assert_eq!(func2.arguments()[0].name(), "arg1");
+ assert_eq!(func2.arguments()[0].as_type(), Type::UInt32);
+ assert_eq!(func2.arguments()[1].name(), "arg2");
+ assert!(
+ matches!(func2.arguments()[1].as_type(), Type::Record { name, .. } if name == "TestDict")
+ );
+ Ok(())
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/interface/mod.rs b/third_party/rust/uniffi_bindgen/src/interface/mod.rs
new file mode 100644
index 0000000000..8e4df2149b
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/interface/mod.rs
@@ -0,0 +1,1234 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! # Component Interface Definition.
+//!
+//! This module provides an abstract representation of the interface provided by a UniFFI Rust Component,
+//! in high-level terms suitable for translation into target consumer languages such as Kotlin
+//! and Swift. It also provides facilities for parsing a WebIDL interface definition file into such a
+//! representation.
+//!
+//! The entrypoint to this crate is the `ComponentInterface` struct, which holds a complete definition
+//! of the interface provided by a component, in two parts:
+//!
+//! * The high-level consumer API, in terms of objects and records and methods and so-on
+//! * The low-level FFI contract through which the foreign language code can call into Rust.
+//!
+//! That's really the key concept of this crate so it's worth repeating: a `ComponentInterface` completely
+//! defines the shape and semantics of an interface between the Rust-based implementation of a component
+//! and its foreign language consumers, including details like:
+//!
+//! * The names of all symbols in the compiled object file
+//! * The type and arity of all exported functions
+//! * The layout and conventions used for all arguments and return types
+//!
+//! If you have a dynamic library compiled from a Rust Component using this crate, and a foreign
+//! language binding generated from the same `ComponentInterface` using the same version of this
+//! module, then there should be no opportunities for them to disagree on how the two sides should
+//! interact.
+//!
+//! General and incomplete TODO list for this thing:
+//!
+//! * It should prevent user error and the possibility of generating bad code by doing (at least)
+//! the following checks:
+//! * No duplicate names (types, methods, args, etc)
+//! * No shadowing of builtin names, or names we use in code generation
+//! We expect that if the user actually does one of these things, then they *should* get a compile
+//! error when trying to build the component, because the codegen will be invalid. But we can't
+//! guarantee that there's not some edge-case where it produces valid-but-incorrect code.
+//!
+//! * There is a *lot* of cloning going on, in the spirit of "first make it work". There's probably
+//! a good opportunity here for e.g. interned strings, but we're nowhere near the point were we need
+//! that kind of optimization just yet.
+//!
+//! * Error messages and general developer experience leave a lot to be desired.
+
+use std::{
+ collections::{btree_map::Entry, BTreeMap, BTreeSet, HashSet},
+ iter,
+};
+
+use anyhow::{anyhow, bail, ensure, Result};
+
+pub mod universe;
+pub use uniffi_meta::{AsType, ExternalKind, ObjectImpl, Type};
+use universe::{TypeIterator, TypeUniverse};
+
+mod callbacks;
+pub use callbacks::CallbackInterface;
+mod enum_;
+pub use enum_::{Enum, Variant};
+mod function;
+pub use function::{Argument, Callable, Function, ResultType};
+mod object;
+pub use object::{Constructor, Method, Object, UniffiTrait};
+mod record;
+pub use record::{Field, Record};
+
+pub mod ffi;
+pub use ffi::{FfiArgument, FfiFunction, FfiType};
+pub use uniffi_meta::Radix;
+use uniffi_meta::{
+ ConstructorMetadata, LiteralMetadata, NamespaceMetadata, ObjectMetadata, TraitMethodMetadata,
+ UniffiTraitMetadata, UNIFFI_CONTRACT_VERSION,
+};
+pub type Literal = LiteralMetadata;
+
+/// The main public interface for this module, representing the complete details of an interface exposed
+/// by a rust component and the details of consuming it via an extern-C FFI layer.
+#[derive(Debug, Default)]
+pub struct ComponentInterface {
+ /// All of the types used in the interface.
+ // We can't checksum `self.types`, but its contents are implied by the other fields
+ // anyway, so it's safe to ignore it.
+ pub(super) types: TypeUniverse,
+ /// The high-level API provided by the component.
+ enums: BTreeMap<String, Enum>,
+ records: BTreeMap<String, Record>,
+ functions: Vec<Function>,
+ objects: Vec<Object>,
+ callback_interfaces: Vec<CallbackInterface>,
+ // Type names which were seen used as an error.
+ errors: HashSet<String>,
+ // Types which were seen used as callback interface error.
+ callback_interface_throws_types: BTreeSet<Type>,
+}
+
+impl ComponentInterface {
+ pub fn new(crate_name: &str) -> Self {
+ assert!(!crate_name.is_empty());
+ Self {
+ types: TypeUniverse::new(NamespaceMetadata {
+ crate_name: crate_name.to_string(),
+ ..Default::default()
+ }),
+ ..Default::default()
+ }
+ }
+
+ /// Parse a `ComponentInterface` from a string containing a WebIDL definition.
+ pub fn from_webidl(idl: &str, module_path: &str) -> Result<Self> {
+ ensure!(
+ !module_path.is_empty(),
+ "you must specify a valid crate name"
+ );
+ let group = uniffi_udl::parse_udl(idl, module_path)?;
+ Self::from_metadata(group)
+ }
+
+ /// Create a `ComponentInterface` from a `MetadataGroup`
+ /// Public so that external binding generators can use it.
+ pub fn from_metadata(group: uniffi_meta::MetadataGroup) -> Result<Self> {
+ let mut ci = Self {
+ types: TypeUniverse::new(group.namespace.clone()),
+ ..Default::default()
+ };
+ ci.add_metadata(group)?;
+ Ok(ci)
+ }
+
+ /// Add a metadata group to a `ComponentInterface`.
+ pub fn add_metadata(&mut self, group: uniffi_meta::MetadataGroup) -> Result<()> {
+ if self.types.namespace.name.is_empty() {
+ self.types.namespace = group.namespace.clone();
+ } else if self.types.namespace != group.namespace {
+ bail!(
+ "Namespace mismatch: {:?} - {:?}",
+ group.namespace,
+ self.types.namespace
+ );
+ }
+ // Unconditionally add the String type, which is used by the panic handling
+ self.types.add_known_type(&uniffi_meta::Type::String)?;
+ crate::macro_metadata::add_group_to_ci(self, group)?;
+ Ok(())
+ }
+
+ /// The string namespace within which this API should be presented to the caller.
+ ///
+ /// This string would typically be used to prefix function names in the FFI, to build
+ /// a package or module name for the foreign language, etc.
+ pub fn namespace(&self) -> &str {
+ &self.types.namespace.name
+ }
+
+ pub fn uniffi_contract_version(&self) -> u32 {
+ // This is set by the scripts in the version-mismatch fixture
+ let force_version = std::env::var("UNIFFI_FORCE_CONTRACT_VERSION");
+ match force_version {
+ Ok(v) if !v.is_empty() => v.parse().unwrap(),
+ _ => UNIFFI_CONTRACT_VERSION,
+ }
+ }
+
+ /// Get the definitions for every Enum type in the interface.
+ pub fn enum_definitions(&self) -> impl Iterator<Item = &Enum> {
+ self.enums.values()
+ }
+
+ /// Get an Enum definition by name, or None if no such Enum is defined.
+ pub fn get_enum_definition(&self, name: &str) -> Option<&Enum> {
+ self.enums.get(name)
+ }
+
+ /// Get the definitions for every Record type in the interface.
+ pub fn record_definitions(&self) -> impl Iterator<Item = &Record> {
+ self.records.values()
+ }
+
+ /// Get a Record definition by name, or None if no such Record is defined.
+ pub fn get_record_definition(&self, name: &str) -> Option<&Record> {
+ self.records.get(name)
+ }
+
+ /// Get the definitions for every Function in the interface.
+ pub fn function_definitions(&self) -> &[Function] {
+ &self.functions
+ }
+
+ /// Get a Function definition by name, or None if no such Function is defined.
+ pub fn get_function_definition(&self, name: &str) -> Option<&Function> {
+ // TODO: probably we could store these internally in a HashMap to make this easier?
+ self.functions.iter().find(|f| f.name == name)
+ }
+
+ /// Get the definitions for every Object type in the interface.
+ pub fn object_definitions(&self) -> &[Object] {
+ &self.objects
+ }
+
+ /// Get an Object definition by name, or None if no such Object is defined.
+ pub fn get_object_definition(&self, name: &str) -> Option<&Object> {
+ // TODO: probably we could store these internally in a HashMap to make this easier?
+ self.objects.iter().find(|o| o.name == name)
+ }
+
+ /// Get the definitions for every Callback Interface type in the interface.
+ pub fn callback_interface_definitions(&self) -> &[CallbackInterface] {
+ &self.callback_interfaces
+ }
+
+ /// Get a Callback interface definition by name, or None if no such interface is defined.
+ pub fn get_callback_interface_definition(&self, name: &str) -> Option<&CallbackInterface> {
+ // TODO: probably we could store these internally in a HashMap to make this easier?
+ self.callback_interfaces.iter().find(|o| o.name == name)
+ }
+
+ /// Get the definitions for every Method type in the interface.
+ pub fn iter_callables(&self) -> impl Iterator<Item = &dyn Callable> {
+ // Each of the `as &dyn Callable` casts is a trivial cast, but it seems like the clearest
+ // way to express the logic in the current Rust
+ #[allow(trivial_casts)]
+ self.function_definitions()
+ .iter()
+ .map(|f| f as &dyn Callable)
+ .chain(self.objects.iter().flat_map(|o| {
+ o.constructors()
+ .into_iter()
+ .map(|c| c as &dyn Callable)
+ .chain(o.methods().into_iter().map(|m| m as &dyn Callable))
+ }))
+ }
+
+ /// Should we generate read (and lift) functions for errors?
+ ///
+ /// This is a workaround for the fact that lower/write can't be generated for some errors,
+ /// specifically errors that are defined as flat in the UDL, but actually have fields in the
+ /// Rust source.
+ pub fn should_generate_error_read(&self, e: &Enum) -> bool {
+ // We can and should always generate read() methods for fielded errors
+ let fielded = !e.is_flat();
+ // For flat errors, we should only generate read() methods if we need them to support
+ // callback interface errors
+ let used_in_callback_interface = self
+ .callback_interface_definitions()
+ .iter()
+ .flat_map(|cb| cb.methods())
+ .any(|m| m.throws_type() == Some(&e.as_type()));
+
+ self.is_name_used_as_error(&e.name) && (fielded || used_in_callback_interface)
+ }
+
+ /// Get details about all `Type::External` types.
+ /// Returns an iterator of (name, crate_name, kind)
+ pub fn iter_external_types(
+ &self,
+ ) -> impl Iterator<Item = (&String, String, ExternalKind, bool)> {
+ self.types.iter_known_types().filter_map(|t| match t {
+ Type::External {
+ name,
+ module_path,
+ kind,
+ tagged,
+ ..
+ } => Some((
+ name,
+ module_path.split("::").next().unwrap().to_string(),
+ *kind,
+ *tagged,
+ )),
+ _ => None,
+ })
+ }
+
+ /// Get details about all `Type::Custom` types
+ pub fn iter_custom_types(&self) -> impl Iterator<Item = (&String, &Type)> {
+ self.types.iter_known_types().filter_map(|t| match t {
+ Type::Custom { name, builtin, .. } => Some((name, &**builtin)),
+ _ => None,
+ })
+ }
+
+ /// Iterate over all known types in the interface.
+ pub fn iter_types(&self) -> impl Iterator<Item = &Type> {
+ self.types.iter_known_types()
+ }
+
+ /// Get a specific type
+ pub fn get_type(&self, name: &str) -> Option<Type> {
+ self.types.get_type_definition(name)
+ }
+
+ /// Iterate over all types contained in the given item.
+ ///
+ /// This method uses `iter_types` to iterate over the types contained within the given type,
+ /// but additionally recurses into the definition of user-defined types like records and enums
+ /// to yield the types that *they* contain.
+ fn iter_types_in_item<'a>(&'a self, item: &'a Type) -> impl Iterator<Item = &'a Type> + 'a {
+ RecursiveTypeIterator::new(self, item)
+ }
+
+ /// Check whether the given item contains any (possibly nested) Type::Object references.
+ ///
+ /// This is important to know in language bindings that cannot integrate object types
+ /// tightly with the host GC, and hence need to perform manual destruction of objects.
+ pub fn item_contains_object_references(&self, item: &Type) -> bool {
+ self.iter_types_in_item(item)
+ .any(|t| matches!(t, Type::Object { .. }))
+ }
+
+ /// Check whether the given item contains any (possibly nested) unsigned types
+ pub fn item_contains_unsigned_types(&self, item: &Type) -> bool {
+ self.iter_types_in_item(item)
+ .any(|t| matches!(t, Type::UInt8 | Type::UInt16 | Type::UInt32 | Type::UInt64))
+ }
+
+ /// Check whether the interface contains any optional types
+ pub fn contains_optional_types(&self) -> bool {
+ self.types
+ .iter_known_types()
+ .any(|t| matches!(t, Type::Optional { .. }))
+ }
+
+ /// Check whether the interface contains any sequence types
+ pub fn contains_sequence_types(&self) -> bool {
+ self.types
+ .iter_known_types()
+ .any(|t| matches!(t, Type::Sequence { .. }))
+ }
+
+ /// Check whether the interface contains any map types
+ pub fn contains_map_types(&self) -> bool {
+ self.types
+ .iter_known_types()
+ .any(|t| matches!(t, Type::Map { .. }))
+ }
+
+ // The namespace to use in crate-level FFI function definitions. Not used as the ffi
+ // namespace for types - each type has its own `module_path` which is used for them.
+ fn ffi_namespace(&self) -> &str {
+ &self.types.namespace.crate_name
+ }
+
+ /// Builtin FFI function to get the current contract version
+ /// This is needed so that the foreign language bindings can check that they are using the same
+ /// ABI as the scaffolding
+ pub fn ffi_uniffi_contract_version(&self) -> FfiFunction {
+ FfiFunction {
+ name: format!("ffi_{}_uniffi_contract_version", self.ffi_namespace()),
+ is_async: false,
+ arguments: vec![],
+ return_type: Some(FfiType::UInt32),
+ has_rust_call_status_arg: false,
+ is_object_free_function: false,
+ }
+ }
+
+ /// Builtin FFI function for allocating a new `RustBuffer`.
+ /// This is needed so that the foreign language bindings can create buffers in which to pass
+ /// complex data types across the FFI.
+ pub fn ffi_rustbuffer_alloc(&self) -> FfiFunction {
+ FfiFunction {
+ name: format!("ffi_{}_rustbuffer_alloc", self.ffi_namespace()),
+ is_async: false,
+ arguments: vec![FfiArgument {
+ name: "size".to_string(),
+ type_: FfiType::Int32,
+ }],
+ return_type: Some(FfiType::RustBuffer(None)),
+ has_rust_call_status_arg: true,
+ is_object_free_function: false,
+ }
+ }
+
+ /// Builtin FFI function for copying foreign-owned bytes
+ /// This is needed so that the foreign language bindings can create buffers in which to pass
+ /// complex data types across the FFI.
+ pub fn ffi_rustbuffer_from_bytes(&self) -> FfiFunction {
+ FfiFunction {
+ name: format!("ffi_{}_rustbuffer_from_bytes", self.ffi_namespace()),
+ is_async: false,
+ arguments: vec![FfiArgument {
+ name: "bytes".to_string(),
+ type_: FfiType::ForeignBytes,
+ }],
+ return_type: Some(FfiType::RustBuffer(None)),
+ has_rust_call_status_arg: true,
+ is_object_free_function: false,
+ }
+ }
+
+ /// Builtin FFI function for freeing a `RustBuffer`.
+ /// This is needed so that the foreign language bindings can free buffers in which they received
+ /// complex data types returned across the FFI.
+ pub fn ffi_rustbuffer_free(&self) -> FfiFunction {
+ FfiFunction {
+ name: format!("ffi_{}_rustbuffer_free", self.ffi_namespace()),
+ is_async: false,
+ arguments: vec![FfiArgument {
+ name: "buf".to_string(),
+ type_: FfiType::RustBuffer(None),
+ }],
+ return_type: None,
+ has_rust_call_status_arg: true,
+ is_object_free_function: false,
+ }
+ }
+
+ /// Builtin FFI function for reserving extra space in a `RustBuffer`.
+ /// This is needed so that the foreign language bindings can grow buffers used for passing
+ /// complex data types across the FFI.
+ pub fn ffi_rustbuffer_reserve(&self) -> FfiFunction {
+ FfiFunction {
+ name: format!("ffi_{}_rustbuffer_reserve", self.ffi_namespace()),
+ is_async: false,
+ arguments: vec![
+ FfiArgument {
+ name: "buf".to_string(),
+ type_: FfiType::RustBuffer(None),
+ },
+ FfiArgument {
+ name: "additional".to_string(),
+ type_: FfiType::Int32,
+ },
+ ],
+ return_type: Some(FfiType::RustBuffer(None)),
+ has_rust_call_status_arg: true,
+ is_object_free_function: false,
+ }
+ }
+
+ /// Builtin FFI function to set the Rust Future continuation callback
+ pub fn ffi_rust_future_continuation_callback_set(&self) -> FfiFunction {
+ FfiFunction {
+ name: format!(
+ "ffi_{}_rust_future_continuation_callback_set",
+ self.ffi_namespace()
+ ),
+ arguments: vec![FfiArgument {
+ name: "callback".to_owned(),
+ type_: FfiType::RustFutureContinuationCallback,
+ }],
+ return_type: None,
+ is_async: false,
+ has_rust_call_status_arg: false,
+ is_object_free_function: false,
+ }
+ }
+
+ /// Builtin FFI function to poll a Rust future.
+ pub fn ffi_rust_future_poll(&self, return_ffi_type: Option<FfiType>) -> FfiFunction {
+ FfiFunction {
+ name: self.rust_future_ffi_fn_name("rust_future_poll", return_ffi_type),
+ is_async: false,
+ arguments: vec![
+ FfiArgument {
+ name: "handle".to_owned(),
+ type_: FfiType::RustFutureHandle,
+ },
+ // Data to pass to the continuation
+ FfiArgument {
+ name: "uniffi_callback".to_owned(),
+ type_: FfiType::RustFutureContinuationData,
+ },
+ ],
+ return_type: None,
+ has_rust_call_status_arg: false,
+ is_object_free_function: false,
+ }
+ }
+
+ /// Builtin FFI function to complete a Rust future and get it's result.
+ ///
+ /// We generate one of these for each FFI return type.
+ pub fn ffi_rust_future_complete(&self, return_ffi_type: Option<FfiType>) -> FfiFunction {
+ FfiFunction {
+ name: self.rust_future_ffi_fn_name("rust_future_complete", return_ffi_type.clone()),
+ is_async: false,
+ arguments: vec![FfiArgument {
+ name: "handle".to_owned(),
+ type_: FfiType::RustFutureHandle,
+ }],
+ return_type: return_ffi_type,
+ has_rust_call_status_arg: true,
+ is_object_free_function: false,
+ }
+ }
+
+ /// Builtin FFI function for cancelling a Rust Future
+ pub fn ffi_rust_future_cancel(&self, return_ffi_type: Option<FfiType>) -> FfiFunction {
+ FfiFunction {
+ name: self.rust_future_ffi_fn_name("rust_future_cancel", return_ffi_type),
+ is_async: false,
+ arguments: vec![FfiArgument {
+ name: "handle".to_owned(),
+ type_: FfiType::RustFutureHandle,
+ }],
+ return_type: None,
+ has_rust_call_status_arg: false,
+ is_object_free_function: false,
+ }
+ }
+
+ /// Builtin FFI function for freeing a Rust Future
+ pub fn ffi_rust_future_free(&self, return_ffi_type: Option<FfiType>) -> FfiFunction {
+ FfiFunction {
+ name: self.rust_future_ffi_fn_name("rust_future_free", return_ffi_type),
+ is_async: false,
+ arguments: vec![FfiArgument {
+ name: "handle".to_owned(),
+ type_: FfiType::RustFutureHandle,
+ }],
+ return_type: None,
+ has_rust_call_status_arg: false,
+ is_object_free_function: false,
+ }
+ }
+
+ fn rust_future_ffi_fn_name(&self, base_name: &str, return_ffi_type: Option<FfiType>) -> String {
+ let namespace = self.ffi_namespace();
+ match return_ffi_type {
+ Some(t) => match t {
+ FfiType::UInt8 => format!("ffi_{namespace}_{base_name}_u8"),
+ FfiType::Int8 => format!("ffi_{namespace}_{base_name}_i8"),
+ FfiType::UInt16 => format!("ffi_{namespace}_{base_name}_u16"),
+ FfiType::Int16 => format!("ffi_{namespace}_{base_name}_i16"),
+ FfiType::UInt32 => format!("ffi_{namespace}_{base_name}_u32"),
+ FfiType::Int32 => format!("ffi_{namespace}_{base_name}_i32"),
+ FfiType::UInt64 => format!("ffi_{namespace}_{base_name}_u64"),
+ FfiType::Int64 => format!("ffi_{namespace}_{base_name}_i64"),
+ FfiType::Float32 => format!("ffi_{namespace}_{base_name}_f32"),
+ FfiType::Float64 => format!("ffi_{namespace}_{base_name}_f64"),
+ FfiType::RustArcPtr(_) => format!("ffi_{namespace}_{base_name}_pointer"),
+ FfiType::RustBuffer(_) => format!("ffi_{namespace}_{base_name}_rust_buffer"),
+ _ => unimplemented!("FFI return type: {t:?}"),
+ },
+ None => format!("ffi_{namespace}_{base_name}_void"),
+ }
+ }
+
+ /// Does this interface contain async functions?
+ pub fn has_async_fns(&self) -> bool {
+ self.iter_ffi_function_definitions().any(|f| f.is_async())
+ }
+
+ /// Iterate over `T` parameters of the `FutureCallback<T>` callbacks in this interface
+ pub fn iter_future_callback_params(&self) -> impl Iterator<Item = FfiType> {
+ let unique_results = self
+ .iter_callables()
+ .map(|c| c.result_type().future_callback_param())
+ .collect::<BTreeSet<_>>();
+ unique_results.into_iter()
+ }
+
+ /// Iterate over return/throws types for async functions
+ pub fn iter_async_result_types(&self) -> impl Iterator<Item = ResultType> {
+ let unique_results = self
+ .iter_callables()
+ .map(|c| c.result_type())
+ .collect::<BTreeSet<_>>();
+ unique_results.into_iter()
+ }
+
+ /// List the definitions of all FFI functions in the interface.
+ ///
+ /// The set of FFI functions is derived automatically from the set of higher-level types
+ /// along with the builtin FFI helper functions.
+ pub fn iter_ffi_function_definitions(&self) -> impl Iterator<Item = FfiFunction> + '_ {
+ self.iter_user_ffi_function_definitions()
+ .cloned()
+ .chain(self.iter_rust_buffer_ffi_function_definitions())
+ .chain(self.iter_futures_ffi_function_definitons())
+ .chain(self.iter_checksum_ffi_functions())
+ .chain(self.ffi_foreign_executor_callback_set())
+ .chain([self.ffi_uniffi_contract_version()])
+ }
+
+ /// Alternate version of iter_ffi_function_definitions for languages that don't support async
+ pub fn iter_ffi_function_definitions_non_async(
+ &self,
+ ) -> impl Iterator<Item = FfiFunction> + '_ {
+ self.iter_user_ffi_function_definitions()
+ .cloned()
+ .chain(self.iter_rust_buffer_ffi_function_definitions())
+ .chain(self.iter_checksum_ffi_functions())
+ .chain([self.ffi_uniffi_contract_version()])
+ }
+
+ /// List all FFI functions definitions for user-defined interfaces
+ ///
+ /// This includes FFI functions for:
+ /// - Top-level functions
+ /// - Object methods
+ /// - Callback interfaces
+ pub fn iter_user_ffi_function_definitions(&self) -> impl Iterator<Item = &FfiFunction> + '_ {
+ iter::empty()
+ .chain(
+ self.objects
+ .iter()
+ .flat_map(|obj| obj.iter_ffi_function_definitions()),
+ )
+ .chain(
+ self.callback_interfaces
+ .iter()
+ .map(|cb| cb.ffi_init_callback()),
+ )
+ .chain(self.functions.iter().map(|f| &f.ffi_func))
+ }
+
+ /// List all FFI functions definitions for RustBuffer functionality.
+ pub fn iter_rust_buffer_ffi_function_definitions(&self) -> impl Iterator<Item = FfiFunction> {
+ [
+ self.ffi_rustbuffer_alloc(),
+ self.ffi_rustbuffer_from_bytes(),
+ self.ffi_rustbuffer_free(),
+ self.ffi_rustbuffer_reserve(),
+ ]
+ .into_iter()
+ }
+
+ /// List all FFI functions definitions for async functionality.
+ pub fn iter_futures_ffi_function_definitons(&self) -> impl Iterator<Item = FfiFunction> + '_ {
+ let all_possible_return_ffi_types = [
+ Some(FfiType::UInt8),
+ Some(FfiType::Int8),
+ Some(FfiType::UInt16),
+ Some(FfiType::Int16),
+ Some(FfiType::UInt32),
+ Some(FfiType::Int32),
+ Some(FfiType::UInt64),
+ Some(FfiType::Int64),
+ Some(FfiType::Float32),
+ Some(FfiType::Float64),
+ // RustBuffer and RustArcPtr have an inner field which doesn't affect the rust future
+ // complete scaffolding function, so we just use a placeholder value here.
+ Some(FfiType::RustArcPtr("".to_owned())),
+ Some(FfiType::RustBuffer(None)),
+ None,
+ ];
+
+ iter::once(self.ffi_rust_future_continuation_callback_set()).chain(
+ all_possible_return_ffi_types
+ .into_iter()
+ .flat_map(|return_type| {
+ [
+ self.ffi_rust_future_poll(return_type.clone()),
+ self.ffi_rust_future_cancel(return_type.clone()),
+ self.ffi_rust_future_free(return_type.clone()),
+ self.ffi_rust_future_complete(return_type),
+ ]
+ }),
+ )
+ }
+
+ /// The ffi_foreign_executor_callback_set FFI function
+ ///
+ /// We only include this in the FFI if the `ForeignExecutor` type is actually used
+ pub fn ffi_foreign_executor_callback_set(&self) -> Option<FfiFunction> {
+ if self.types.contains(&Type::ForeignExecutor) {
+ Some(FfiFunction {
+ name: format!("ffi_{}_foreign_executor_callback_set", self.ffi_namespace()),
+ arguments: vec![FfiArgument {
+ name: "callback".into(),
+ type_: FfiType::ForeignExecutorCallback,
+ }],
+ return_type: None,
+ is_async: false,
+ has_rust_call_status_arg: false,
+ is_object_free_function: false,
+ })
+ } else {
+ None
+ }
+ }
+
+ /// List all API checksums to check
+ ///
+ /// Returns a list of (export_symbol_name, checksum) items
+ pub fn iter_checksums(&self) -> impl Iterator<Item = (String, u16)> + '_ {
+ let func_checksums = self
+ .functions
+ .iter()
+ .map(|f| (f.checksum_fn_name(), f.checksum()));
+ let method_checksums = self.objects.iter().flat_map(|o| {
+ o.methods()
+ .into_iter()
+ .map(|m| (m.checksum_fn_name(), m.checksum()))
+ });
+ let constructor_checksums = self.objects.iter().flat_map(|o| {
+ o.constructors()
+ .into_iter()
+ .map(|c| (c.checksum_fn_name(), c.checksum()))
+ });
+ let callback_method_checksums = self.callback_interfaces.iter().flat_map(|cbi| {
+ cbi.methods().into_iter().filter_map(|m| {
+ if m.checksum_fn_name().is_empty() {
+ // UDL-based callbacks don't have checksum functions, skip these
+ None
+ } else {
+ Some((m.checksum_fn_name(), m.checksum()))
+ }
+ })
+ });
+ func_checksums
+ .chain(method_checksums)
+ .chain(constructor_checksums)
+ .chain(callback_method_checksums)
+ .map(|(fn_name, checksum)| (fn_name.to_string(), checksum))
+ }
+
+ pub fn iter_checksum_ffi_functions(&self) -> impl Iterator<Item = FfiFunction> + '_ {
+ self.iter_checksums().map(|(name, _)| FfiFunction {
+ name,
+ is_async: false,
+ arguments: vec![],
+ return_type: Some(FfiType::UInt16),
+ has_rust_call_status_arg: false,
+ is_object_free_function: false,
+ })
+ }
+
+ // Private methods for building a ComponentInterface.
+ //
+ /// Called by `APIBuilder` impls to add a newly-parsed enum definition to the `ComponentInterface`.
+ pub(super) fn add_enum_definition(&mut self, defn: Enum) -> Result<()> {
+ match self.enums.entry(defn.name().to_owned()) {
+ Entry::Vacant(v) => {
+ self.types.add_known_types(defn.iter_types())?;
+ v.insert(defn);
+ }
+ Entry::Occupied(o) => {
+ let existing_def = o.get();
+ if defn != *existing_def {
+ bail!(
+ "Mismatching definition for enum `{}`!\n\
+ existing definition: {existing_def:#?},\n\
+ new definition: {defn:#?}",
+ defn.name(),
+ );
+ }
+ }
+ }
+
+ Ok(())
+ }
+
+ /// Adds a newly-parsed record definition to the `ComponentInterface`.
+ pub(super) fn add_record_definition(&mut self, defn: Record) -> Result<()> {
+ match self.records.entry(defn.name().to_owned()) {
+ Entry::Vacant(v) => {
+ self.types.add_known_types(defn.iter_types())?;
+ v.insert(defn);
+ }
+ Entry::Occupied(o) => {
+ let existing_def = o.get();
+ if defn != *existing_def {
+ bail!(
+ "Mismatching definition for record `{}`!\n\
+ existing definition: {existing_def:#?},\n\
+ new definition: {defn:#?}",
+ defn.name(),
+ );
+ }
+ }
+ }
+
+ Ok(())
+ }
+
+ /// Called by `APIBuilder` impls to add a newly-parsed function definition to the `ComponentInterface`.
+ pub(super) fn add_function_definition(&mut self, defn: Function) -> Result<()> {
+ // Since functions are not a first-class type, we have to check for duplicates here
+ // rather than relying on the type-finding pass to catch them.
+ if self.functions.iter().any(|f| f.name == defn.name) {
+ bail!("duplicate function definition: \"{}\"", defn.name);
+ }
+ if self.types.get_type_definition(defn.name()).is_some() {
+ bail!("Conflicting type definition for \"{}\"", defn.name());
+ }
+ self.types.add_known_types(defn.iter_types())?;
+ self.functions.push(defn);
+
+ Ok(())
+ }
+
+ pub(super) fn add_constructor_meta(&mut self, meta: ConstructorMetadata) -> Result<()> {
+ let object = get_object(&mut self.objects, &meta.self_name)
+ .ok_or_else(|| anyhow!("add_constructor_meta: object {} not found", &meta.self_name))?;
+ let defn: Constructor = meta.into();
+
+ self.types.add_known_types(defn.iter_types())?;
+ object.constructors.push(defn);
+
+ Ok(())
+ }
+
+ pub(super) fn add_method_meta(&mut self, meta: impl Into<Method>) -> Result<()> {
+ let mut method: Method = meta.into();
+ let object = get_object(&mut self.objects, &method.object_name)
+ .ok_or_else(|| anyhow!("add_method_meta: object {} not found", &method.object_name))?;
+
+ self.types.add_known_types(method.iter_types())?;
+ method.object_impl = object.imp;
+ object.methods.push(method);
+ Ok(())
+ }
+
+ pub(super) fn add_uniffitrait_meta(&mut self, meta: UniffiTraitMetadata) -> Result<()> {
+ let object = get_object(&mut self.objects, meta.self_name())
+ .ok_or_else(|| anyhow!("add_uniffitrait_meta: object not found"))?;
+ let ut: UniffiTrait = meta.into();
+ self.types.add_known_types(ut.iter_types())?;
+ object.uniffi_traits.push(ut);
+ Ok(())
+ }
+
+ pub(super) fn add_object_meta(&mut self, meta: ObjectMetadata) -> Result<()> {
+ self.add_object_definition(meta.into())
+ }
+
+ /// Called by `APIBuilder` impls to add a newly-parsed object definition to the `ComponentInterface`.
+ fn add_object_definition(&mut self, defn: Object) -> Result<()> {
+ self.types.add_known_types(defn.iter_types())?;
+ self.objects.push(defn);
+ Ok(())
+ }
+
+ pub(super) fn note_name_used_as_error(&mut self, name: &str) {
+ self.errors.insert(name.to_string());
+ }
+
+ pub fn is_name_used_as_error(&self, name: &str) -> bool {
+ self.errors.contains(name)
+ }
+
+ /// Called by `APIBuilder` impls to add a newly-parsed callback interface definition to the `ComponentInterface`.
+ pub(super) fn add_callback_interface_definition(&mut self, defn: CallbackInterface) {
+ self.callback_interfaces.push(defn);
+ }
+
+ pub(super) fn add_trait_method_meta(&mut self, meta: TraitMethodMetadata) -> Result<()> {
+ if let Some(cbi) = get_callback_interface(&mut self.callback_interfaces, &meta.trait_name) {
+ // uniffi_meta should ensure that we process callback interface methods in order, double
+ // check that here
+ if cbi.methods.len() != meta.index as usize {
+ bail!(
+ "UniFFI internal error: callback interface method index mismatch for {}::{} (expected {}, saw {})",
+ meta.trait_name,
+ meta.name,
+ cbi.methods.len(),
+ meta.index,
+ );
+ }
+ let method: Method = meta.into();
+ if let Some(error) = method.throws_type() {
+ self.callback_interface_throws_types.insert(error.clone());
+ }
+ self.types.add_known_types(method.iter_types())?;
+ cbi.methods.push(method);
+ } else {
+ self.add_method_meta(meta)?;
+ }
+ Ok(())
+ }
+
+ /// Perform global consistency checks on the declared interface.
+ ///
+ /// This method checks for consistency problems in the declared interface
+ /// as a whole, and which can only be detected after we've finished defining
+ /// the entire interface.
+ pub fn check_consistency(&self) -> Result<()> {
+ if self.namespace().is_empty() {
+ bail!("missing namespace definition");
+ }
+
+ // Because functions aren't first class types, we need to check here that
+ // a function name hasn't already been used as a type name.
+ for f in self.functions.iter() {
+ if self.types.get_type_definition(f.name()).is_some() {
+ bail!("Conflicting type definition for \"{}\"", f.name());
+ }
+ }
+
+ for ty in self.iter_types() {
+ match ty {
+ Type::Object { name, .. } => {
+ ensure!(
+ self.objects.iter().any(|o| o.name == *name),
+ "Object `{name}` has no definition"
+ );
+ }
+ Type::Record { name, .. } => {
+ ensure!(
+ self.records.contains_key(name),
+ "Record `{name}` has no definition",
+ );
+ }
+ Type::Enum { name, .. } => {
+ ensure!(
+ self.enums.contains_key(name),
+ "Enum `{name}` has no definition",
+ );
+ }
+ _ => {}
+ }
+ }
+
+ Ok(())
+ }
+
+ /// Automatically derive the low-level FFI functions from the high-level types in the interface.
+ ///
+ /// This should only be called after the high-level types have been completed defined, otherwise
+ /// the resulting set will be missing some entries.
+ pub fn derive_ffi_funcs(&mut self) -> Result<()> {
+ for func in self.functions.iter_mut() {
+ func.derive_ffi_func()?;
+ }
+ for obj in self.objects.iter_mut() {
+ obj.derive_ffi_funcs()?;
+ }
+ for callback in self.callback_interfaces.iter_mut() {
+ callback.derive_ffi_funcs();
+ }
+ Ok(())
+ }
+}
+
+fn get_object<'a>(objects: &'a mut [Object], name: &str) -> Option<&'a mut Object> {
+ objects.iter_mut().find(|o| o.name == name)
+}
+
+fn get_callback_interface<'a>(
+ callback_interfaces: &'a mut [CallbackInterface],
+ name: &str,
+) -> Option<&'a mut CallbackInterface> {
+ callback_interfaces.iter_mut().find(|o| o.name == name)
+}
+
+/// Stateful iterator for yielding all types contained in a given type.
+///
+/// This struct is the implementation of [`ComponentInterface::iter_types_in_item`] and should be
+/// considered an opaque implementation detail. It's a separate struct because I couldn't
+/// figure out a way to implement it using iterators and closures that would make the lifetimes
+/// work out correctly.
+///
+/// The idea here is that we want to yield all the types from `iter_types` on a given type, and
+/// additionally we want to recurse into the definition of any user-provided types like records,
+/// enums, etc so we can also yield the types contained therein.
+///
+/// To guard against infinite recursion, we maintain a list of previously-seen user-defined
+/// types, ensuring that we recurse into the definition of those types only once. To simplify
+/// the implementation, we maintain a queue of pending user-defined types that we have seen
+/// but not yet recursed into. (Ironically, the use of an explicit queue means our implementation
+/// is not actually recursive...)
+struct RecursiveTypeIterator<'a> {
+ /// The [`ComponentInterface`] from which this iterator was created.
+ ci: &'a ComponentInterface,
+ /// The currently-active iterator from which we're yielding.
+ current: TypeIterator<'a>,
+ /// A set of names of user-defined types that we have already seen.
+ seen: HashSet<&'a str>,
+ /// A queue of user-defined types that we need to recurse into.
+ pending: Vec<&'a Type>,
+}
+
+impl<'a> RecursiveTypeIterator<'a> {
+ /// Allocate a new `RecursiveTypeIterator` over the given item.
+ fn new(ci: &'a ComponentInterface, item: &'a Type) -> RecursiveTypeIterator<'a> {
+ RecursiveTypeIterator {
+ ci,
+ // We begin by iterating over the types from the item itself.
+ current: item.iter_types(),
+ seen: Default::default(),
+ pending: Default::default(),
+ }
+ }
+
+ /// Add a new type to the queue of pending types, if not previously seen.
+ fn add_pending_type(&mut self, type_: &'a Type) {
+ match type_ {
+ Type::Record { name, .. }
+ | Type::Enum { name, .. }
+ | Type::Object { name, .. }
+ | Type::CallbackInterface { name, .. } => {
+ if !self.seen.contains(name.as_str()) {
+ self.pending.push(type_);
+ self.seen.insert(name.as_str());
+ }
+ }
+ _ => (),
+ }
+ }
+
+ /// Advance the iterator to recurse into the next pending type, if any.
+ ///
+ /// This method is called when the current iterator is empty, and it will select
+ /// the next pending type from the queue and start iterating over its contained types.
+ /// The return value will be the first item from the new iterator.
+ fn advance_to_next_type(&mut self) -> Option<&'a Type> {
+ if let Some(next_type) = self.pending.pop() {
+ // This is a little awkward because the various definition lookup methods return an `Option<T>`.
+ // In the unlikely event that one of them returns `None` then, rather than trying to advance
+ // to a non-existent type, we just leave the existing iterator in place and allow the recursive
+ // call to `next()` to try again with the next pending type.
+ let next_iter = match next_type {
+ Type::Record { name, .. } => {
+ self.ci.get_record_definition(name).map(Record::iter_types)
+ }
+ Type::Enum { name, .. } => self.ci.get_enum_definition(name).map(Enum::iter_types),
+ Type::Object { name, .. } => {
+ self.ci.get_object_definition(name).map(Object::iter_types)
+ }
+ Type::CallbackInterface { name, .. } => self
+ .ci
+ .get_callback_interface_definition(name)
+ .map(CallbackInterface::iter_types),
+ _ => None,
+ };
+ if let Some(next_iter) = next_iter {
+ self.current = next_iter;
+ }
+ // Advance the new iterator to its first item. If the new iterator happens to be empty,
+ // this will recurse back in to `advance_to_next_type` until we find one that isn't.
+ self.next()
+ } else {
+ // We've completely finished the iteration over all pending types.
+ None
+ }
+ }
+}
+
+impl<'a> Iterator for RecursiveTypeIterator<'a> {
+ type Item = &'a Type;
+ fn next(&mut self) -> Option<Self::Item> {
+ if let Some(type_) = self.current.next() {
+ self.add_pending_type(type_);
+ Some(type_)
+ } else {
+ self.advance_to_next_type()
+ }
+ }
+}
+
+// Helpers for functions/methods/constructors which all have the same "throws" semantics.
+fn throws_name(throws: &Option<Type>) -> Option<&str> {
+ // Type has no `name()` method, just `canonical_name()` which isn't what we want.
+ match throws {
+ None => None,
+ Some(Type::Enum { name, .. }) => Some(name),
+ _ => panic!("unknown throw type: {throws:?}"),
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ // Note that much of the functionality of `ComponentInterface` is tested via its interactions
+ // with specific member types, in the sub-modules defining those member types.
+
+ #[test]
+ fn test_duplicate_type_names_are_an_error() {
+ const UDL: &str = r#"
+ namespace test{};
+ interface Testing {
+ constructor();
+ };
+ dictionary Testing {
+ u32 field;
+ };
+ "#;
+ let err = ComponentInterface::from_webidl(UDL, "crate_name").unwrap_err();
+ assert_eq!(
+ err.to_string(),
+ "Conflicting type definition for `Testing`! \
+ existing definition: Object { module_path: \"crate_name\", name: \"Testing\", imp: Struct }, \
+ new definition: Record { module_path: \"crate_name\", name: \"Testing\" }"
+ );
+
+ const UDL2: &str = r#"
+ namespace test{};
+ enum Testing {
+ "one", "two"
+ };
+ [Error]
+ enum Testing { "three", "four" };
+ "#;
+ let err = ComponentInterface::from_webidl(UDL2, "crate_name").unwrap_err();
+ assert_eq!(
+ err.to_string(),
+ "Mismatching definition for enum `Testing`!\nexisting definition: Enum {
+ name: \"Testing\",
+ module_path: \"crate_name\",
+ variants: [
+ Variant {
+ name: \"one\",
+ fields: [],
+ },
+ Variant {
+ name: \"two\",
+ fields: [],
+ },
+ ],
+ flat: true,
+},
+new definition: Enum {
+ name: \"Testing\",
+ module_path: \"crate_name\",
+ variants: [
+ Variant {
+ name: \"three\",
+ fields: [],
+ },
+ Variant {
+ name: \"four\",
+ fields: [],
+ },
+ ],
+ flat: true,
+}",
+ );
+
+ const UDL3: &str = r#"
+ namespace test{
+ u32 Testing();
+ };
+ enum Testing {
+ "one", "two"
+ };
+ "#;
+ let err = ComponentInterface::from_webidl(UDL3, "crate_name").unwrap_err();
+ assert!(format!("{err:#}").contains("Conflicting type definition for \"Testing\""));
+ }
+
+ #[test]
+ fn test_contains_optional_types() {
+ let mut ci = ComponentInterface {
+ ..Default::default()
+ };
+
+ // check that `contains_optional_types` returns false when there is no Optional type in the interface
+ assert!(!ci.contains_optional_types());
+
+ // check that `contains_optional_types` returns true when there is an Optional type in the interface
+ assert!(ci
+ .types
+ .add_known_type(&Type::Optional {
+ inner_type: Box::new(Type::String)
+ })
+ .is_ok());
+ assert!(ci.contains_optional_types());
+ }
+
+ #[test]
+ fn test_contains_sequence_types() {
+ let mut ci = ComponentInterface {
+ ..Default::default()
+ };
+
+ // check that `contains_sequence_types` returns false when there is no Sequence type in the interface
+ assert!(!ci.contains_sequence_types());
+
+ // check that `contains_sequence_types` returns true when there is a Sequence type in the interface
+ assert!(ci
+ .types
+ .add_known_type(&Type::Sequence {
+ inner_type: Box::new(Type::UInt64)
+ })
+ .is_ok());
+ assert!(ci.contains_sequence_types());
+ assert!(ci.types.contains(&Type::UInt64));
+ }
+
+ #[test]
+ fn test_contains_map_types() {
+ let mut ci = ComponentInterface {
+ ..Default::default()
+ };
+
+ // check that `contains_map_types` returns false when there is no Map type in the interface
+ assert!(!ci.contains_map_types());
+
+ // check that `contains_map_types` returns true when there is a Map type in the interface
+ assert!(ci
+ .types
+ .add_known_type(&Type::Map {
+ key_type: Box::new(Type::String),
+ value_type: Box::new(Type::Boolean)
+ })
+ .is_ok());
+ assert!(ci.contains_map_types());
+ assert!(ci.types.contains(&Type::String));
+ assert!(ci.types.contains(&Type::Boolean));
+ }
+
+ #[test]
+ fn test_no_infinite_recursion_when_walking_types() {
+ const UDL: &str = r#"
+ namespace test{};
+ interface Testing {
+ void tester(Testing foo);
+ };
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
+ assert!(!ci.item_contains_unsigned_types(&Type::Object {
+ name: "Testing".into(),
+ module_path: "".into(),
+ imp: ObjectImpl::Struct,
+ }));
+ }
+
+ #[test]
+ fn test_correct_recursion_when_walking_types() {
+ const UDL: &str = r#"
+ namespace test{};
+ interface TestObj {
+ void tester(TestRecord foo);
+ };
+ dictionary TestRecord {
+ NestedRecord bar;
+ };
+ dictionary NestedRecord {
+ u64 baz;
+ };
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
+ assert!(ci.item_contains_unsigned_types(&Type::Object {
+ name: "TestObj".into(),
+ module_path: "".into(),
+ imp: ObjectImpl::Struct,
+ }));
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/interface/object.rs b/third_party/rust/uniffi_bindgen/src/interface/object.rs
new file mode 100644
index 0000000000..942032b3c6
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/interface/object.rs
@@ -0,0 +1,773 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! # Object definitions for a `ComponentInterface`.
+//!
+//! This module converts "interface" definitions from UDL into [`Object`] structures
+//! that can be added to a `ComponentInterface`, which are the main way we define stateful
+//! objects with behaviour for a UniFFI Rust Component. An [`Object`] is an opaque handle
+//! to some state on which methods can be invoked.
+//!
+//! (The terminology mismatch between "interface" and "object" is a historical artifact of
+//! this tool prior to committing to WebIDL syntax).
+//!
+//! A declaration in the UDL like this:
+//!
+//! ```
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! # namespace example {};
+//! interface Example {
+//! constructor(string? name);
+//! string my_name();
+//! };
+//! # "##, "crate_name")?;
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+//!
+//! Will result in an [`Object`] member with one [`Constructor`] and one [`Method`] being added
+//! to the resulting [`crate::ComponentInterface`]:
+//!
+//! ```
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! # namespace example {};
+//! # interface Example {
+//! # constructor(string? name);
+//! # string my_name();
+//! # };
+//! # "##, "crate_name")?;
+//! let obj = ci.get_object_definition("Example").unwrap();
+//! assert_eq!(obj.name(), "Example");
+//! assert_eq!(obj.constructors().len(), 1);
+//! assert_eq!(obj.constructors()[0].arguments()[0].name(), "name");
+//! assert_eq!(obj.methods().len(),1 );
+//! assert_eq!(obj.methods()[0].name(), "my_name");
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+//!
+//! It's not necessary for all interfaces to have constructors.
+//! ```
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! # namespace example {};
+//! # interface Example {};
+//! # "##, "crate_name")?;
+//! let obj = ci.get_object_definition("Example").unwrap();
+//! assert_eq!(obj.name(), "Example");
+//! assert_eq!(obj.constructors().len(), 0);
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+
+use std::iter;
+
+use anyhow::Result;
+use uniffi_meta::Checksum;
+
+use super::ffi::{FfiArgument, FfiFunction, FfiType};
+use super::function::{Argument, Callable};
+use super::{AsType, ObjectImpl, Type, TypeIterator};
+
+/// An "object" is an opaque type that is passed around by reference, can
+/// have methods called on it, and so on - basically your classic Object Oriented Programming
+/// type of deal, except without elaborate inheritance hierarchies. Some can be instantiated.
+///
+/// In UDL these correspond to the `interface` keyword.
+///
+/// At the FFI layer, objects are represented by an opaque integer handle and a set of functions
+/// a common prefix. The object's constructors are functions that return new objects by handle,
+/// and its methods are functions that take a handle as first argument. The foreign language
+/// binding code is expected to stitch these functions back together into an appropriate class
+/// definition (or that language's equivalent thereof).
+///
+/// TODO:
+/// - maybe "Class" would be a better name than "Object" here?
+#[derive(Debug, Clone, Checksum)]
+pub struct Object {
+ pub(super) name: String,
+ /// How this object is implemented in Rust
+ pub(super) imp: ObjectImpl,
+ pub(super) module_path: String,
+ pub(super) constructors: Vec<Constructor>,
+ pub(super) methods: Vec<Method>,
+ // The "trait" methods - they have a (presumably "well known") name, and
+ // a regular method (albeit with a generated name)
+ // XXX - this should really be a HashSet, but not enough transient types support hash to make it worthwhile now.
+ pub(super) uniffi_traits: Vec<UniffiTrait>,
+ // We don't include the FfiFunc in the hash calculation, because:
+ // - it is entirely determined by the other fields,
+ // so excluding it is safe.
+ // - its `name` property includes a checksum derived from the very
+ // hash value we're trying to calculate here, so excluding it
+ // avoids a weird circular dependency in the calculation.
+ #[checksum_ignore]
+ pub(super) ffi_func_free: FfiFunction,
+}
+
+impl Object {
+ pub fn name(&self) -> &str {
+ &self.name
+ }
+
+ /// Returns the fully qualified name that should be used by Rust code for this object.
+ /// Includes `r#`, traits get a leading `dyn`. If we ever supported associated types, then
+ /// this would also include them.
+ pub fn rust_name(&self) -> String {
+ self.imp.rust_name_for(&self.name)
+ }
+
+ pub fn imp(&self) -> &ObjectImpl {
+ &self.imp
+ }
+
+ pub fn constructors(&self) -> Vec<&Constructor> {
+ self.constructors.iter().collect()
+ }
+
+ pub fn primary_constructor(&self) -> Option<&Constructor> {
+ self.constructors
+ .iter()
+ .find(|cons| cons.is_primary_constructor())
+ }
+
+ pub fn alternate_constructors(&self) -> Vec<&Constructor> {
+ self.constructors
+ .iter()
+ .filter(|cons| !cons.is_primary_constructor())
+ .collect()
+ }
+
+ pub fn methods(&self) -> Vec<&Method> {
+ self.methods.iter().collect()
+ }
+
+ pub fn get_method(&self, name: &str) -> Method {
+ let matches: Vec<_> = self.methods.iter().filter(|m| m.name() == name).collect();
+ match matches.len() {
+ 1 => matches[0].clone(),
+ n => panic!("{n} methods named {name}"),
+ }
+ }
+
+ pub fn uniffi_traits(&self) -> Vec<&UniffiTrait> {
+ self.uniffi_traits.iter().collect()
+ }
+
+ pub fn ffi_object_free(&self) -> &FfiFunction {
+ &self.ffi_func_free
+ }
+
+ pub fn iter_ffi_function_definitions(&self) -> impl Iterator<Item = &FfiFunction> {
+ iter::once(&self.ffi_func_free)
+ .chain(self.constructors.iter().map(|f| &f.ffi_func))
+ .chain(self.methods.iter().map(|f| &f.ffi_func))
+ .chain(
+ self.uniffi_traits
+ .iter()
+ .flat_map(|ut| match ut {
+ UniffiTrait::Display { fmt: m }
+ | UniffiTrait::Debug { fmt: m }
+ | UniffiTrait::Hash { hash: m } => vec![m],
+ UniffiTrait::Eq { eq, ne } => vec![eq, ne],
+ })
+ .map(|m| &m.ffi_func),
+ )
+ }
+
+ pub fn derive_ffi_funcs(&mut self) -> Result<()> {
+ assert!(!self.ffi_func_free.name().is_empty());
+ self.ffi_func_free.arguments = vec![FfiArgument {
+ name: "ptr".to_string(),
+ type_: FfiType::RustArcPtr(self.name.to_string()),
+ }];
+ self.ffi_func_free.return_type = None;
+ self.ffi_func_free.is_object_free_function = true;
+
+ for cons in self.constructors.iter_mut() {
+ cons.derive_ffi_func();
+ }
+ for meth in self.methods.iter_mut() {
+ meth.derive_ffi_func()?;
+ }
+ for ut in self.uniffi_traits.iter_mut() {
+ ut.derive_ffi_func()?;
+ }
+
+ Ok(())
+ }
+
+ pub fn iter_types(&self) -> TypeIterator<'_> {
+ Box::new(
+ self.methods
+ .iter()
+ .map(Method::iter_types)
+ .chain(self.uniffi_traits.iter().map(UniffiTrait::iter_types))
+ .chain(self.constructors.iter().map(Constructor::iter_types))
+ .flatten(),
+ )
+ }
+}
+
+impl AsType for Object {
+ fn as_type(&self) -> Type {
+ Type::Object {
+ name: self.name.clone(),
+ module_path: self.module_path.clone(),
+ imp: self.imp,
+ }
+ }
+}
+
+impl From<uniffi_meta::ObjectMetadata> for Object {
+ fn from(meta: uniffi_meta::ObjectMetadata) -> Self {
+ let ffi_free_name = meta.free_ffi_symbol_name();
+ Object {
+ module_path: meta.module_path,
+ name: meta.name,
+ imp: meta.imp,
+ constructors: Default::default(),
+ methods: Default::default(),
+ uniffi_traits: Default::default(),
+ ffi_func_free: FfiFunction {
+ name: ffi_free_name,
+ ..Default::default()
+ },
+ }
+ }
+}
+
+impl From<uniffi_meta::UniffiTraitMetadata> for UniffiTrait {
+ fn from(meta: uniffi_meta::UniffiTraitMetadata) -> Self {
+ match meta {
+ uniffi_meta::UniffiTraitMetadata::Debug { fmt } => {
+ UniffiTrait::Debug { fmt: fmt.into() }
+ }
+ uniffi_meta::UniffiTraitMetadata::Display { fmt } => {
+ UniffiTrait::Display { fmt: fmt.into() }
+ }
+ uniffi_meta::UniffiTraitMetadata::Eq { eq, ne } => UniffiTrait::Eq {
+ eq: eq.into(),
+ ne: ne.into(),
+ },
+ uniffi_meta::UniffiTraitMetadata::Hash { hash } => {
+ UniffiTrait::Hash { hash: hash.into() }
+ }
+ }
+ }
+}
+
+// Represents a constructor for an object type.
+//
+// In the FFI, this will be a function that returns a pointer to an instance
+// of the corresponding object type.
+#[derive(Debug, Clone, Checksum)]
+pub struct Constructor {
+ pub(super) name: String,
+ pub(super) object_name: String,
+ pub(super) object_module_path: String,
+ pub(super) arguments: Vec<Argument>,
+ // We don't include the FFIFunc in the hash calculation, because:
+ // - it is entirely determined by the other fields,
+ // so excluding it is safe.
+ // - its `name` property includes a checksum derived from the very
+ // hash value we're trying to calculate here, so excluding it
+ // avoids a weird circular dependency in the calculation.
+ #[checksum_ignore]
+ pub(super) ffi_func: FfiFunction,
+ pub(super) throws: Option<Type>,
+ pub(super) checksum_fn_name: String,
+ // Force a checksum value, or we'll fallback to the trait.
+ #[checksum_ignore]
+ pub(super) checksum: Option<u16>,
+}
+
+impl Constructor {
+ pub fn name(&self) -> &str {
+ &self.name
+ }
+
+ pub fn arguments(&self) -> Vec<&Argument> {
+ self.arguments.iter().collect()
+ }
+
+ pub fn full_arguments(&self) -> Vec<Argument> {
+ self.arguments.to_vec()
+ }
+
+ pub fn ffi_func(&self) -> &FfiFunction {
+ &self.ffi_func
+ }
+
+ pub fn checksum_fn_name(&self) -> &str {
+ &self.checksum_fn_name
+ }
+
+ pub fn checksum(&self) -> u16 {
+ self.checksum.unwrap_or_else(|| uniffi_meta::checksum(self))
+ }
+
+ pub fn throws(&self) -> bool {
+ self.throws.is_some()
+ }
+
+ pub fn throws_name(&self) -> Option<&str> {
+ super::throws_name(&self.throws)
+ }
+
+ pub fn throws_type(&self) -> Option<&Type> {
+ self.throws.as_ref()
+ }
+
+ pub fn is_primary_constructor(&self) -> bool {
+ self.name == "new"
+ }
+
+ fn derive_ffi_func(&mut self) {
+ assert!(!self.ffi_func.name().is_empty());
+ self.ffi_func.arguments = self.arguments.iter().map(Into::into).collect();
+ self.ffi_func.return_type = Some(FfiType::RustArcPtr(self.object_name.clone()));
+ }
+
+ pub fn iter_types(&self) -> TypeIterator<'_> {
+ Box::new(self.arguments.iter().flat_map(Argument::iter_types))
+ }
+}
+
+impl From<uniffi_meta::ConstructorMetadata> for Constructor {
+ fn from(meta: uniffi_meta::ConstructorMetadata) -> Self {
+ let ffi_name = meta.ffi_symbol_name();
+ let checksum_fn_name = meta.checksum_symbol_name();
+ let arguments = meta.inputs.into_iter().map(Into::into).collect();
+
+ let ffi_func = FfiFunction {
+ name: ffi_name,
+ ..FfiFunction::default()
+ };
+ Self {
+ name: meta.name,
+ object_name: meta.self_name,
+ object_module_path: meta.module_path,
+ arguments,
+ ffi_func,
+ throws: meta.throws.map(Into::into),
+ checksum_fn_name,
+ checksum: meta.checksum,
+ }
+ }
+}
+
+// Represents an instance method for an object type.
+//
+// The FFI will represent this as a function whose first/self argument is a
+// `FfiType::RustArcPtr` to the instance.
+#[derive(Debug, Clone, Checksum)]
+pub struct Method {
+ pub(super) name: String,
+ pub(super) object_name: String,
+ pub(super) object_module_path: String,
+ pub(super) is_async: bool,
+ pub(super) object_impl: ObjectImpl,
+ pub(super) arguments: Vec<Argument>,
+ pub(super) return_type: Option<Type>,
+ // We don't include the FFIFunc in the hash calculation, because:
+ // - it is entirely determined by the other fields,
+ // so excluding it is safe.
+ // - its `name` property includes a checksum derived from the very
+ // hash value we're trying to calculate here, so excluding it
+ // avoids a weird circular dependency in the calculation.
+ #[checksum_ignore]
+ pub(super) ffi_func: FfiFunction,
+ pub(super) throws: Option<Type>,
+ pub(super) takes_self_by_arc: bool,
+ pub(super) checksum_fn_name: String,
+ // Force a checksum value, or we'll fallback to the trait.
+ #[checksum_ignore]
+ pub(super) checksum: Option<u16>,
+}
+
+impl Method {
+ pub fn name(&self) -> &str {
+ &self.name
+ }
+
+ pub fn is_async(&self) -> bool {
+ self.is_async
+ }
+
+ pub fn arguments(&self) -> Vec<&Argument> {
+ self.arguments.iter().collect()
+ }
+
+ // Methods have a special implicit first argument for the object instance,
+ // hence `arguments` and `full_arguments` are different.
+ pub fn full_arguments(&self) -> Vec<Argument> {
+ vec![Argument {
+ name: "ptr".to_string(),
+ // TODO: ideally we'd get this via `ci.resolve_type_expression` so that it
+ // is contained in the proper `TypeUniverse`, but this works for now.
+ type_: Type::Object {
+ name: self.object_name.clone(),
+ module_path: self.object_module_path.clone(),
+ imp: self.object_impl,
+ },
+ by_ref: !self.takes_self_by_arc,
+ optional: false,
+ default: None,
+ }]
+ .into_iter()
+ .chain(self.arguments.iter().cloned())
+ .collect()
+ }
+
+ pub fn return_type(&self) -> Option<&Type> {
+ self.return_type.as_ref()
+ }
+
+ pub fn ffi_func(&self) -> &FfiFunction {
+ &self.ffi_func
+ }
+
+ pub fn checksum_fn_name(&self) -> &str {
+ &self.checksum_fn_name
+ }
+
+ pub fn checksum(&self) -> u16 {
+ self.checksum.unwrap_or_else(|| uniffi_meta::checksum(self))
+ }
+
+ pub fn throws(&self) -> bool {
+ self.throws.is_some()
+ }
+
+ pub fn throws_name(&self) -> Option<&str> {
+ super::throws_name(&self.throws)
+ }
+
+ pub fn throws_type(&self) -> Option<&Type> {
+ self.throws.as_ref()
+ }
+
+ pub fn takes_self_by_arc(&self) -> bool {
+ self.takes_self_by_arc
+ }
+
+ pub fn derive_ffi_func(&mut self) -> Result<()> {
+ assert!(!self.ffi_func.name().is_empty());
+ self.ffi_func.init(
+ self.return_type.as_ref().map(Into::into),
+ self.full_arguments().iter().map(Into::into),
+ );
+ Ok(())
+ }
+
+ pub fn iter_types(&self) -> TypeIterator<'_> {
+ Box::new(
+ self.arguments
+ .iter()
+ .flat_map(Argument::iter_types)
+ .chain(self.return_type.iter().flat_map(Type::iter_types)),
+ )
+ }
+}
+
+impl From<uniffi_meta::MethodMetadata> for Method {
+ fn from(meta: uniffi_meta::MethodMetadata) -> Self {
+ let ffi_name = meta.ffi_symbol_name();
+ let checksum_fn_name = meta.checksum_symbol_name();
+ let is_async = meta.is_async;
+ let return_type = meta.return_type.map(Into::into);
+ let arguments = meta.inputs.into_iter().map(Into::into).collect();
+
+ let ffi_func = FfiFunction {
+ name: ffi_name,
+ is_async,
+ ..FfiFunction::default()
+ };
+
+ Self {
+ name: meta.name,
+ object_name: meta.self_name,
+ object_module_path: meta.module_path,
+ is_async,
+ object_impl: ObjectImpl::Struct, // will be filled in later
+ arguments,
+ return_type,
+ ffi_func,
+ throws: meta.throws.map(Into::into),
+ takes_self_by_arc: meta.takes_self_by_arc,
+ checksum_fn_name,
+ checksum: meta.checksum,
+ }
+ }
+}
+
+impl From<uniffi_meta::TraitMethodMetadata> for Method {
+ fn from(meta: uniffi_meta::TraitMethodMetadata) -> Self {
+ let ffi_name = meta.ffi_symbol_name();
+ let checksum_fn_name = meta.checksum_symbol_name();
+ let return_type = meta.return_type.map(Into::into);
+ let arguments = meta.inputs.into_iter().map(Into::into).collect();
+ let ffi_func = FfiFunction {
+ name: ffi_name,
+ ..FfiFunction::default()
+ };
+ Self {
+ name: meta.name,
+ object_name: meta.trait_name,
+ object_module_path: meta.module_path,
+ is_async: false,
+ arguments,
+ return_type,
+ throws: meta.throws.map(Into::into),
+ takes_self_by_arc: meta.takes_self_by_arc,
+ checksum_fn_name,
+ checksum: meta.checksum,
+ ffi_func,
+ object_impl: ObjectImpl::Struct,
+ }
+ }
+}
+
+/// The list of traits we support generating helper methods for.
+#[derive(Clone, Debug, Checksum)]
+pub enum UniffiTrait {
+ Debug { fmt: Method },
+ Display { fmt: Method },
+ Eq { eq: Method, ne: Method },
+ Hash { hash: Method },
+}
+
+impl UniffiTrait {
+ pub fn iter_types(&self) -> TypeIterator<'_> {
+ Box::new(
+ match self {
+ UniffiTrait::Display { fmt: m }
+ | UniffiTrait::Debug { fmt: m }
+ | UniffiTrait::Hash { hash: m } => vec![m.iter_types()],
+ UniffiTrait::Eq { eq, ne } => vec![eq.iter_types(), ne.iter_types()],
+ }
+ .into_iter()
+ .flatten(),
+ )
+ }
+
+ pub fn derive_ffi_func(&mut self) -> Result<()> {
+ match self {
+ UniffiTrait::Display { fmt: m }
+ | UniffiTrait::Debug { fmt: m }
+ | UniffiTrait::Hash { hash: m } => {
+ m.derive_ffi_func()?;
+ }
+ UniffiTrait::Eq { eq, ne } => {
+ eq.derive_ffi_func()?;
+ ne.derive_ffi_func()?;
+ }
+ }
+ Ok(())
+ }
+}
+
+impl Callable for Constructor {
+ fn arguments(&self) -> Vec<&Argument> {
+ self.arguments()
+ }
+
+ fn return_type(&self) -> Option<Type> {
+ Some(Type::Object {
+ name: self.object_name.clone(),
+ module_path: self.object_module_path.clone(),
+ imp: ObjectImpl::Struct,
+ })
+ }
+
+ fn throws_type(&self) -> Option<Type> {
+ self.throws_type().cloned()
+ }
+
+ fn is_async(&self) -> bool {
+ false
+ }
+}
+
+impl Callable for Method {
+ fn arguments(&self) -> Vec<&Argument> {
+ self.arguments()
+ }
+
+ fn return_type(&self) -> Option<Type> {
+ self.return_type().cloned()
+ }
+
+ fn throws_type(&self) -> Option<Type> {
+ self.throws_type().cloned()
+ }
+
+ fn is_async(&self) -> bool {
+ self.is_async
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::super::ComponentInterface;
+ use super::*;
+
+ #[test]
+ fn test_that_all_argument_and_return_types_become_known() {
+ const UDL: &str = r#"
+ namespace test{};
+ interface Testing {
+ constructor(string? name, u16 age);
+ sequence<u32> code_points_of_name();
+ };
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
+ assert_eq!(ci.object_definitions().len(), 1);
+ ci.get_object_definition("Testing").unwrap();
+
+ assert_eq!(ci.iter_types().count(), 6);
+ assert!(ci.iter_types().any(|t| t == &Type::UInt16));
+ assert!(ci.iter_types().any(|t| t == &Type::UInt32));
+ assert!(ci.iter_types().any(|t| t
+ == &Type::Sequence {
+ inner_type: Box::new(Type::UInt32)
+ }));
+ assert!(ci.iter_types().any(|t| t == &Type::String));
+ assert!(ci.iter_types().any(|t| t
+ == &Type::Optional {
+ inner_type: Box::new(Type::String)
+ }));
+ assert!(ci
+ .iter_types()
+ .any(|t| matches!(t, Type::Object { name, ..} if name == "Testing")));
+ }
+
+ #[test]
+ fn test_alternate_constructors() {
+ const UDL: &str = r#"
+ namespace test{};
+ interface Testing {
+ constructor();
+ [Name=new_with_u32]
+ constructor(u32 v);
+ };
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
+ assert_eq!(ci.object_definitions().len(), 1);
+
+ let obj = ci.get_object_definition("Testing").unwrap();
+ assert!(obj.primary_constructor().is_some());
+ assert_eq!(obj.alternate_constructors().len(), 1);
+ assert_eq!(obj.methods().len(), 0);
+
+ let cons = obj.primary_constructor().unwrap();
+ assert_eq!(cons.name(), "new");
+ assert_eq!(cons.arguments.len(), 0);
+ assert_eq!(cons.ffi_func.arguments.len(), 0);
+
+ let cons = obj.alternate_constructors()[0];
+ assert_eq!(cons.name(), "new_with_u32");
+ assert_eq!(cons.arguments.len(), 1);
+ assert_eq!(cons.ffi_func.arguments.len(), 1);
+ }
+
+ #[test]
+ fn test_the_name_new_identifies_the_primary_constructor() {
+ const UDL: &str = r#"
+ namespace test{};
+ interface Testing {
+ [Name=newish]
+ constructor();
+ [Name=new]
+ constructor(u32 v);
+ };
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
+ assert_eq!(ci.object_definitions().len(), 1);
+
+ let obj = ci.get_object_definition("Testing").unwrap();
+ assert!(obj.primary_constructor().is_some());
+ assert_eq!(obj.alternate_constructors().len(), 1);
+ assert_eq!(obj.methods().len(), 0);
+
+ let cons = obj.primary_constructor().unwrap();
+ assert_eq!(cons.name(), "new");
+ assert_eq!(cons.arguments.len(), 1);
+
+ let cons = obj.alternate_constructors()[0];
+ assert_eq!(cons.name(), "newish");
+ assert_eq!(cons.arguments.len(), 0);
+ assert_eq!(cons.ffi_func.arguments.len(), 0);
+ }
+
+ #[test]
+ fn test_the_name_new_is_reserved_for_constructors() {
+ const UDL: &str = r#"
+ namespace test{};
+ interface Testing {
+ constructor();
+ void new(u32 v);
+ };
+ "#;
+ let err = ComponentInterface::from_webidl(UDL, "crate_name").unwrap_err();
+ assert_eq!(
+ err.to_string(),
+ "the method name \"new\" is reserved for the default constructor"
+ );
+ }
+
+ #[test]
+ fn test_duplicate_primary_constructors_not_allowed() {
+ const UDL: &str = r#"
+ namespace test{};
+ interface Testing {
+ constructor();
+ constructor(u32 v);
+ };
+ "#;
+ let err = ComponentInterface::from_webidl(UDL, "crate_name").unwrap_err();
+ assert_eq!(err.to_string(), "Duplicate interface member name: \"new\"");
+
+ const UDL2: &str = r#"
+ namespace test{};
+ interface Testing {
+ constructor();
+ [Name=new]
+ constructor(u32 v);
+ };
+ "#;
+ let err = ComponentInterface::from_webidl(UDL2, "crate_name").unwrap_err();
+ assert_eq!(err.to_string(), "Duplicate interface member name: \"new\"");
+ }
+
+ #[test]
+ fn test_trait_attribute() {
+ const UDL: &str = r#"
+ namespace test{};
+ interface NotATrait {
+ };
+ [Trait]
+ interface ATrait {
+ };
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
+ let obj = ci.get_object_definition("NotATrait").unwrap();
+ assert_eq!(obj.imp.rust_name_for(&obj.name), "r#NotATrait");
+ let obj = ci.get_object_definition("ATrait").unwrap();
+ assert_eq!(obj.imp.rust_name_for(&obj.name), "dyn r#ATrait");
+ }
+
+ #[test]
+ fn test_trait_constructors_not_allowed() {
+ const UDL: &str = r#"
+ namespace test{};
+ [Trait]
+ interface Testing {
+ constructor();
+ };
+ "#;
+ let err = ComponentInterface::from_webidl(UDL, "crate_name").unwrap_err();
+ assert_eq!(
+ err.to_string(),
+ "Trait interfaces can not have constructors: \"new\""
+ );
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/interface/record.rs b/third_party/rust/uniffi_bindgen/src/interface/record.rs
new file mode 100644
index 0000000000..17d3774a49
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/interface/record.rs
@@ -0,0 +1,230 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! # Record definitions for a `ComponentInterface`.
+//!
+//! This module converts "dictionary" definitions from UDL into [`Record`] structures
+//! that can be added to a `ComponentInterface`, which are the main way we define structured
+//! data types for a UniFFI Rust Component. A [`Record`] has a fixed set of named fields,
+//! each of a specific type.
+//!
+//! (The terminology mismatch between "dictionary" and "record" is a historical artifact
+//! due to this tool being loosely inspired by WebAssembly Interface Types, which used
+//! the term "record" for this sort of data).
+//!
+//! A declaration in the UDL like this:
+//!
+//! ```
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! # namespace example {};
+//! dictionary Example {
+//! string name;
+//! u32 value;
+//! };
+//! # "##, "crate_name")?;
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+//!
+//! Will result in a [`Record`] member with two [`Field`]s being added to the resulting
+//! [`crate::ComponentInterface`]:
+//!
+//! ```
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! # namespace example {};
+//! # dictionary Example {
+//! # string name;
+//! # u32 value;
+//! # };
+//! # "##, "crate_name")?;
+//! let record = ci.get_record_definition("Example").unwrap();
+//! assert_eq!(record.name(), "Example");
+//! assert_eq!(record.fields()[0].name(), "name");
+//! assert_eq!(record.fields()[1].name(), "value");
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+
+use anyhow::Result;
+use uniffi_meta::Checksum;
+
+use super::Literal;
+use super::{AsType, Type, TypeIterator};
+
+/// Represents a "data class" style object, for passing around complex values.
+///
+/// In the FFI these are represented as a byte buffer, which one side explicitly
+/// serializes the data into and the other serializes it out of. So I guess they're
+/// kind of like "pass by clone" values.
+#[derive(Debug, Clone, PartialEq, Eq, Checksum)]
+pub struct Record {
+ pub(super) name: String,
+ pub(super) module_path: String,
+ pub(super) fields: Vec<Field>,
+}
+
+impl Record {
+ pub fn name(&self) -> &str {
+ &self.name
+ }
+
+ pub fn fields(&self) -> &[Field] {
+ &self.fields
+ }
+
+ pub fn iter_types(&self) -> TypeIterator<'_> {
+ Box::new(self.fields.iter().flat_map(Field::iter_types))
+ }
+}
+
+impl AsType for Record {
+ fn as_type(&self) -> Type {
+ Type::Record {
+ name: self.name.clone(),
+ module_path: self.module_path.clone(),
+ }
+ }
+}
+
+impl TryFrom<uniffi_meta::RecordMetadata> for Record {
+ type Error = anyhow::Error;
+
+ fn try_from(meta: uniffi_meta::RecordMetadata) -> Result<Self> {
+ Ok(Self {
+ name: meta.name,
+ module_path: meta.module_path,
+ fields: meta
+ .fields
+ .into_iter()
+ .map(TryInto::try_into)
+ .collect::<Result<_>>()?,
+ })
+ }
+}
+
+// Represents an individual field on a Record.
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Checksum)]
+pub struct Field {
+ pub(super) name: String,
+ pub(super) type_: Type,
+ pub(super) default: Option<Literal>,
+}
+
+impl Field {
+ pub fn name(&self) -> &str {
+ &self.name
+ }
+
+ pub fn default_value(&self) -> Option<&Literal> {
+ self.default.as_ref()
+ }
+
+ pub fn iter_types(&self) -> TypeIterator<'_> {
+ self.type_.iter_types()
+ }
+}
+
+impl AsType for Field {
+ fn as_type(&self) -> Type {
+ self.type_.clone()
+ }
+}
+
+impl TryFrom<uniffi_meta::FieldMetadata> for Field {
+ type Error = anyhow::Error;
+
+ fn try_from(meta: uniffi_meta::FieldMetadata) -> Result<Self> {
+ let name = meta.name;
+ let type_ = meta.ty;
+ let default = meta.default;
+ Ok(Self {
+ name,
+ type_,
+ default,
+ })
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::super::ComponentInterface;
+ use super::*;
+ use uniffi_meta::Radix;
+
+ #[test]
+ fn test_multiple_record_types() {
+ const UDL: &str = r#"
+ namespace test{};
+ dictionary Empty {};
+ dictionary Simple {
+ u32 field;
+ };
+ dictionary Complex {
+ string? key;
+ u32 value = 0;
+ required boolean spin;
+ };
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
+ assert_eq!(ci.record_definitions().count(), 3);
+
+ let record = ci.get_record_definition("Empty").unwrap();
+ assert_eq!(record.name(), "Empty");
+ assert_eq!(record.fields().len(), 0);
+
+ let record = ci.get_record_definition("Simple").unwrap();
+ assert_eq!(record.name(), "Simple");
+ assert_eq!(record.fields().len(), 1);
+ assert_eq!(record.fields()[0].name(), "field");
+ assert_eq!(record.fields()[0].as_type(), Type::UInt32);
+ assert!(record.fields()[0].default_value().is_none());
+
+ let record = ci.get_record_definition("Complex").unwrap();
+ assert_eq!(record.name(), "Complex");
+ assert_eq!(record.fields().len(), 3);
+ assert_eq!(record.fields()[0].name(), "key");
+ assert_eq!(
+ record.fields()[0].as_type(),
+ Type::Optional {
+ inner_type: Box::new(Type::String)
+ },
+ );
+ assert!(record.fields()[0].default_value().is_none());
+ assert_eq!(record.fields()[1].name(), "value");
+ assert_eq!(record.fields()[1].as_type(), Type::UInt32);
+ assert!(matches!(
+ record.fields()[1].default_value(),
+ Some(Literal::UInt(0, Radix::Decimal, Type::UInt32))
+ ));
+ assert_eq!(record.fields()[2].name(), "spin");
+ assert_eq!(record.fields()[2].as_type(), Type::Boolean);
+ assert!(record.fields()[2].default_value().is_none());
+ }
+
+ #[test]
+ fn test_that_all_field_types_become_known() {
+ const UDL: &str = r#"
+ namespace test{};
+ dictionary Testing {
+ string? maybe_name;
+ u32 value;
+ };
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
+ assert_eq!(ci.record_definitions().count(), 1);
+ let record = ci.get_record_definition("Testing").unwrap();
+ assert_eq!(record.fields().len(), 2);
+ assert_eq!(record.fields()[0].name(), "maybe_name");
+ assert_eq!(record.fields()[1].name(), "value");
+
+ assert_eq!(ci.iter_types().count(), 4);
+ assert!(ci.iter_types().any(|t| t == &Type::UInt32));
+ assert!(ci.iter_types().any(|t| t == &Type::String));
+ assert!(ci.iter_types().any(|t| t
+ == &Type::Optional {
+ inner_type: Box::new(Type::String)
+ }));
+ assert!(ci
+ .iter_types()
+ .any(|t| matches!(t, Type::Record { name, .. } if name == "Testing")));
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/interface/universe.rs b/third_party/rust/uniffi_bindgen/src/interface/universe.rs
new file mode 100644
index 0000000000..e69d86e44f
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/interface/universe.rs
@@ -0,0 +1,136 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! The set of all [`Type`]s used in a component interface is represented by a `TypeUniverse`,
+//! which can be used by the bindings generator code to determine what type-related helper
+//! functions to emit for a given component.
+//!
+use anyhow::Result;
+use std::{collections::hash_map::Entry, collections::BTreeSet, collections::HashMap};
+
+pub use uniffi_meta::{AsType, ExternalKind, NamespaceMetadata, ObjectImpl, Type, TypeIterator};
+
+/// The set of all possible types used in a particular component interface.
+///
+/// Every component API uses a finite number of types, including primitive types, API-defined
+/// types like records and enums, and recursive types such as sequences of the above. Our
+/// component API doesn't support fancy generics so this is a finitely-enumerable set, which
+/// is useful to be able to operate on explicitly.
+///
+/// You could imagine this struct doing some clever interning of names and so-on in future,
+/// to reduce the overhead of passing around [Type] instances. For now we just do a whole
+/// lot of cloning.
+#[derive(Debug, Default)]
+pub(crate) struct TypeUniverse {
+ /// The unique prefixes that we'll use for namespacing when exposing this component's API.
+ pub namespace: NamespaceMetadata,
+
+ // Named type definitions (including aliases).
+ type_definitions: HashMap<String, Type>,
+ // All the types in the universe, by canonical type name, in a well-defined order.
+ all_known_types: BTreeSet<Type>,
+}
+
+impl TypeUniverse {
+ pub fn new(namespace: NamespaceMetadata) -> Self {
+ Self {
+ namespace,
+ ..Default::default()
+ }
+ }
+
+ /// Add the definition of a named [Type].
+ fn add_type_definition(&mut self, name: &str, type_: &Type) -> Result<()> {
+ match self.type_definitions.entry(name.to_string()) {
+ Entry::Occupied(o) => {
+ // all conflicts have been resolved by now in udl.
+ // I doubt procmacros could cause this?
+ assert_eq!(type_, o.get());
+ Ok(())
+ }
+ Entry::Vacant(e) => {
+ e.insert(type_.clone());
+ Ok(())
+ }
+ }
+ }
+
+ /// Get the [Type] corresponding to a given name, if any.
+ pub(super) fn get_type_definition(&self, name: &str) -> Option<Type> {
+ self.type_definitions.get(name).cloned()
+ }
+
+ /// Add a [Type] to the set of all types seen in the component interface.
+ pub fn add_known_type(&mut self, type_: &Type) -> Result<()> {
+ // Types are more likely to already be known than not, so avoid unnecessary cloning.
+ if !self.all_known_types.contains(type_) {
+ self.all_known_types.insert(type_.to_owned());
+ }
+ match type_ {
+ Type::UInt8 => self.add_type_definition("u8", type_)?,
+ Type::Int8 => self.add_type_definition("i8", type_)?,
+ Type::UInt16 => self.add_type_definition("u16", type_)?,
+ Type::Int16 => self.add_type_definition("i16", type_)?,
+ Type::UInt32 => self.add_type_definition("i32", type_)?,
+ Type::Int32 => self.add_type_definition("u32", type_)?,
+ Type::UInt64 => self.add_type_definition("u64", type_)?,
+ Type::Int64 => self.add_type_definition("i64", type_)?,
+ Type::Float32 => self.add_type_definition("f32", type_)?,
+ Type::Float64 => self.add_type_definition("f64", type_)?,
+ Type::Boolean => self.add_type_definition("bool", type_)?,
+ Type::String => self.add_type_definition("string", type_)?,
+ Type::Bytes => self.add_type_definition("bytes", type_)?,
+ Type::Timestamp => self.add_type_definition("timestamp", type_)?,
+ Type::Duration => self.add_type_definition("duration", type_)?,
+ Type::ForeignExecutor => {
+ self.add_type_definition("ForeignExecutor", type_)?;
+ }
+ Type::Object { name, .. }
+ | Type::Record { name, .. }
+ | Type::Enum { name, .. }
+ | Type::CallbackInterface { name, .. }
+ | Type::External { name, .. } => self.add_type_definition(name, type_)?,
+ Type::Custom { name, builtin, .. } => {
+ self.add_type_definition(name, type_)?;
+ self.add_known_type(builtin)?;
+ }
+ // Structurally recursive types.
+ Type::Optional { inner_type, .. } | Type::Sequence { inner_type, .. } => {
+ self.add_known_type(inner_type)?;
+ }
+ Type::Map {
+ key_type,
+ value_type,
+ } => {
+ self.add_known_type(key_type)?;
+ self.add_known_type(value_type)?;
+ }
+ }
+ Ok(())
+ }
+
+ /// Add many [`Type`]s...
+ pub fn add_known_types(&mut self, types: TypeIterator<'_>) -> Result<()> {
+ for t in types {
+ self.add_known_type(t)?
+ }
+ Ok(())
+ }
+
+ /// Check if a [Type] is present
+ pub fn contains(&self, type_: &Type) -> bool {
+ self.all_known_types.contains(type_)
+ }
+
+ /// Iterator over all the known types in this universe.
+ pub fn iter_known_types(&self) -> impl Iterator<Item = &Type> {
+ self.all_known_types.iter()
+ }
+}
+
+#[cfg(test)]
+mod test_type_universe {
+ // All the useful functionality of the `TypeUniverse` struct
+ // is tested as part of the `TypeFinder` and `TypeResolver` test suites.
+}
diff --git a/third_party/rust/uniffi_bindgen/src/lib.rs b/third_party/rust/uniffi_bindgen/src/lib.rs
new file mode 100644
index 0000000000..019b24022f
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/lib.rs
@@ -0,0 +1,519 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! # Uniffi: easily build cross-platform software components in Rust
+//!
+//! This is a highly-experimental crate for building cross-language software components
+//! in Rust, based on things we've learned and patterns we've developed in the
+//! [mozilla/application-services](https://github.com/mozilla/application-services) project.
+//!
+//! The idea is to let you write your code once, in Rust, and then re-use it from many
+//! other programming languages via Rust's C-compatible FFI layer and some automagically
+//! generated binding code. If you think of it as a kind of [wasm-bindgen](https://github.com/rustwasm/wasm-bindgen)
+//! wannabe, with a clunkier developer experience but support for more target languages,
+//! you'll be pretty close to the mark.
+//!
+//! Currently supported target languages include Kotlin, Swift and Python.
+//!
+//! ## Usage
+//
+//! To build a cross-language component using `uniffi`, follow these steps.
+//!
+//! ### 1) Specify your Component Interface
+//!
+//! Start by thinking about the interface you want to expose for use
+//! from other languages. Use the Interface Definition Language to specify your interface
+//! in a `.udl` file, where it can be processed by the tools from this crate.
+//! For example you might define an interface like this:
+//!
+//! ```text
+//! namespace example {
+//! u32 foo(u32 bar);
+//! }
+//!
+//! dictionary MyData {
+//! u32 num_foos;
+//! bool has_a_bar;
+//! }
+//! ```
+//!
+//! ### 2) Implement the Component Interface as a Rust crate
+//!
+//! With the interface, defined, provide a corresponding implementation of that interface
+//! as a standard-looking Rust crate, using functions and structs and so-on. For example
+//! an implementation of the above Component Interface might look like this:
+//!
+//! ```text
+//! fn foo(bar: u32) -> u32 {
+//! // TODO: a better example!
+//! bar + 42
+//! }
+//!
+//! struct MyData {
+//! num_foos: u32,
+//! has_a_bar: bool
+//! }
+//! ```
+//!
+//! ### 3) Generate and include component scaffolding from the UDL file
+//!
+//! First you will need to install `uniffi-bindgen` on your system using `cargo install uniffi_bindgen`.
+//! Then add to your crate `uniffi_build` under `[build-dependencies]`.
+//! Finally, add a `build.rs` script to your crate and have it call `uniffi_build::generate_scaffolding`
+//! to process your `.udl` file. This will generate some Rust code to be included in the top-level source
+//! code of your crate. If your UDL file is named `example.udl`, then your build script would call:
+//!
+//! ```text
+//! uniffi_build::generate_scaffolding("src/example.udl")
+//! ```
+//!
+//! This would output a rust file named `example.uniffi.rs`, ready to be
+//! included into the code of your rust crate like this:
+//!
+//! ```text
+//! include_scaffolding!("example");
+//! ```
+//!
+//! ### 4) Generate foreign language bindings for the library
+//!
+//! The `uniffi-bindgen` utility provides a command-line tool that can produce code to
+//! consume the Rust library in any of several supported languages.
+//! It is done by calling (in kotlin for example):
+//!
+//! ```text
+//! uniffi-bindgen --language kotlin ./src/example.udl
+//! ```
+//!
+//! This will produce a file `example.kt` in the same directory as the .udl file, containing kotlin bindings
+//! to load and use the compiled rust code via its C-compatible FFI.
+//!
+
+#![warn(rust_2018_idioms, unused_qualifications)]
+#![allow(unknown_lints)]
+
+use anyhow::{anyhow, bail, Context, Result};
+use camino::{Utf8Path, Utf8PathBuf};
+use fs_err::{self as fs, File};
+use serde::{de::DeserializeOwned, Deserialize, Serialize};
+use std::io::prelude::*;
+use std::io::ErrorKind;
+use std::{collections::HashMap, process::Command};
+
+pub mod backend;
+pub mod bindings;
+pub mod interface;
+pub mod library_mode;
+pub mod macro_metadata;
+pub mod scaffolding;
+
+use bindings::TargetLanguage;
+pub use interface::ComponentInterface;
+use scaffolding::RustScaffolding;
+
+/// Trait for bindings configuration. Each bindings language defines one of these.
+///
+/// BindingsConfigs are initially loaded from `uniffi.toml` file. Then the trait methods are used
+/// to fill in missing values.
+pub trait BindingsConfig: DeserializeOwned {
+ /// Update missing values using the `ComponentInterface`
+ fn update_from_ci(&mut self, ci: &ComponentInterface);
+
+ /// Update missing values using the dylib file for the main crate, when in library mode.
+ ///
+ /// cdylib_name will be the library filename without the leading `lib` and trailing extension
+ fn update_from_cdylib_name(&mut self, cdylib_name: &str);
+
+ /// Update missing values from config instances from dependent crates
+ ///
+ /// config_map maps crate names to config instances. This is mostly used to set up external
+ /// types.
+ fn update_from_dependency_configs(&mut self, config_map: HashMap<&str, &Self>);
+}
+
+/// Binding generator config with no members
+#[derive(Clone, Debug, Deserialize, Hash, PartialEq, PartialOrd, Ord, Eq)]
+pub struct EmptyBindingsConfig;
+
+impl BindingsConfig for EmptyBindingsConfig {
+ fn update_from_ci(&mut self, _ci: &ComponentInterface) {}
+ fn update_from_cdylib_name(&mut self, _cdylib_name: &str) {}
+ fn update_from_dependency_configs(&mut self, _config_map: HashMap<&str, &Self>) {}
+}
+
+/// A trait representing a UniFFI Binding Generator
+///
+/// External crates that implement binding generators, should implement this type
+/// and call the [`generate_external_bindings`] using a type that implements this trait.
+pub trait BindingGenerator: Sized {
+ /// Handles configuring the bindings
+ type Config: BindingsConfig;
+
+ /// Writes the bindings to the output directory
+ ///
+ /// # Arguments
+ /// - `ci`: A [`ComponentInterface`] representing the interface
+ /// - `config`: An instance of the [`BindingsConfig`] associated with this type
+ /// - `out_dir`: The path to where the binding generator should write the output bindings
+ fn write_bindings(
+ &self,
+ ci: &ComponentInterface,
+ config: &Self::Config,
+ out_dir: &Utf8Path,
+ ) -> Result<()>;
+
+ /// Check if `library_path` used by library mode is valid for this generator
+ fn check_library_path(&self, library_path: &Utf8Path, cdylib_name: Option<&str>) -> Result<()>;
+}
+
+struct BindingGeneratorDefault {
+ target_languages: Vec<TargetLanguage>,
+ try_format_code: bool,
+}
+
+impl BindingGenerator for BindingGeneratorDefault {
+ type Config = Config;
+
+ fn write_bindings(
+ &self,
+ ci: &ComponentInterface,
+ config: &Self::Config,
+ out_dir: &Utf8Path,
+ ) -> Result<()> {
+ for &language in &self.target_languages {
+ bindings::write_bindings(
+ &config.bindings,
+ ci,
+ out_dir,
+ language,
+ self.try_format_code,
+ )?;
+ }
+ Ok(())
+ }
+
+ fn check_library_path(&self, library_path: &Utf8Path, cdylib_name: Option<&str>) -> Result<()> {
+ for &language in &self.target_languages {
+ if cdylib_name.is_none() && language != TargetLanguage::Swift {
+ bail!("Generate bindings for {language} requires a cdylib, but {library_path} was given");
+ }
+ }
+ Ok(())
+ }
+}
+
+/// Generate bindings for an external binding generator
+/// Ideally, this should replace the [`generate_bindings`] function below
+///
+/// Implements an entry point for external binding generators.
+/// The function does the following:
+/// - It parses the `udl` in a [`ComponentInterface`]
+/// - Parses the `uniffi.toml` and loads it into the type that implements [`BindingsConfig`]
+/// - Creates an instance of [`BindingGenerator`], based on type argument `B`, and run [`BindingGenerator::write_bindings`] on it
+///
+/// # Arguments
+/// - `binding_generator`: Type that implements BindingGenerator
+/// - `udl_file`: The path to the UDL file
+/// - `config_file_override`: The path to the configuration toml file, most likely called `uniffi.toml`. If [`None`], the function will try to guess based on the crate's root.
+/// - `out_dir_override`: The path to write the bindings to. If [`None`], it will be the path to the parent directory of the `udl_file`
+/// - `library_file`: The path to a dynamic library to attempt to extract the definitions from and extend the component interface with. No extensions to component interface occur if it's [`None`]
+/// - `crate_name`: Override the default crate name that is guessed from UDL file path.
+pub fn generate_external_bindings<T: BindingGenerator>(
+ binding_generator: T,
+ udl_file: impl AsRef<Utf8Path>,
+ config_file_override: Option<impl AsRef<Utf8Path>>,
+ out_dir_override: Option<impl AsRef<Utf8Path>>,
+ library_file: Option<impl AsRef<Utf8Path>>,
+ crate_name: Option<&str>,
+) -> Result<()> {
+ let crate_name = crate_name
+ .map(|c| Ok(c.to_string()))
+ .unwrap_or_else(|| crate_name_from_cargo_toml(udl_file.as_ref()))?;
+ let mut component = parse_udl(udl_file.as_ref(), &crate_name)?;
+ if let Some(ref library_file) = library_file {
+ macro_metadata::add_to_ci_from_library(&mut component, library_file.as_ref())?;
+ }
+ let crate_root = &guess_crate_root(udl_file.as_ref()).context("Failed to guess crate root")?;
+
+ let config_file_override = config_file_override.as_ref().map(|p| p.as_ref());
+
+ let config = {
+ let mut config = load_initial_config::<T::Config>(crate_root, config_file_override)?;
+ config.update_from_ci(&component);
+ if let Some(ref library_file) = library_file {
+ if let Some(cdylib_name) = crate::library_mode::calc_cdylib_name(library_file.as_ref())
+ {
+ config.update_from_cdylib_name(cdylib_name)
+ }
+ };
+ config
+ };
+
+ let out_dir = get_out_dir(
+ udl_file.as_ref(),
+ out_dir_override.as_ref().map(|p| p.as_ref()),
+ )?;
+ binding_generator.write_bindings(&component, &config, &out_dir)
+}
+
+// Generate the infrastructural Rust code for implementing the UDL interface,
+// such as the `extern "C"` function definitions and record data types.
+// Locates and parses Cargo.toml to determine the name of the crate.
+pub fn generate_component_scaffolding(
+ udl_file: &Utf8Path,
+ out_dir_override: Option<&Utf8Path>,
+ format_code: bool,
+) -> Result<()> {
+ let component = parse_udl(udl_file, &crate_name_from_cargo_toml(udl_file)?)?;
+ generate_component_scaffolding_inner(component, udl_file, out_dir_override, format_code)
+}
+
+// Generate the infrastructural Rust code for implementing the UDL interface,
+// such as the `extern "C"` function definitions and record data types, using
+// the specified crate name.
+pub fn generate_component_scaffolding_for_crate(
+ udl_file: &Utf8Path,
+ crate_name: &str,
+ out_dir_override: Option<&Utf8Path>,
+ format_code: bool,
+) -> Result<()> {
+ let component = parse_udl(udl_file, crate_name)?;
+ generate_component_scaffolding_inner(component, udl_file, out_dir_override, format_code)
+}
+
+fn generate_component_scaffolding_inner(
+ component: ComponentInterface,
+ udl_file: &Utf8Path,
+ out_dir_override: Option<&Utf8Path>,
+ format_code: bool,
+) -> Result<()> {
+ let file_stem = udl_file.file_stem().context("not a file")?;
+ let filename = format!("{file_stem}.uniffi.rs");
+ let out_path = get_out_dir(udl_file, out_dir_override)?.join(filename);
+ let mut f = File::create(&out_path)?;
+ write!(f, "{}", RustScaffolding::new(&component, file_stem))
+ .context("Failed to write output file")?;
+ if format_code {
+ format_code_with_rustfmt(&out_path)?;
+ }
+ Ok(())
+}
+
+// Generate the bindings in the target languages that call the scaffolding
+// Rust code.
+pub fn generate_bindings(
+ udl_file: &Utf8Path,
+ config_file_override: Option<&Utf8Path>,
+ target_languages: Vec<TargetLanguage>,
+ out_dir_override: Option<&Utf8Path>,
+ library_file: Option<&Utf8Path>,
+ crate_name: Option<&str>,
+ try_format_code: bool,
+) -> Result<()> {
+ generate_external_bindings(
+ BindingGeneratorDefault {
+ target_languages,
+ try_format_code,
+ },
+ udl_file,
+ config_file_override,
+ out_dir_override,
+ library_file,
+ crate_name,
+ )
+}
+
+pub fn print_repr(library_path: &Utf8Path) -> Result<()> {
+ let metadata = macro_metadata::extract_from_library(library_path)?;
+ println!("{metadata:#?}");
+ Ok(())
+}
+
+// Given the path to a UDL file, locate and parse the corresponding Cargo.toml to determine
+// the library crate name.
+// Note that this is largely a copy of code in uniffi_macros/src/util.rs, but sharing it
+// isn't trivial and it's not particularly complicated so we've just copied it.
+fn crate_name_from_cargo_toml(udl_file: &Utf8Path) -> Result<String> {
+ #[derive(Deserialize)]
+ struct CargoToml {
+ package: Package,
+ #[serde(default)]
+ lib: Lib,
+ }
+
+ #[derive(Deserialize)]
+ struct Package {
+ name: String,
+ }
+
+ #[derive(Default, Deserialize)]
+ struct Lib {
+ name: Option<String>,
+ }
+
+ let file = guess_crate_root(udl_file)?.join("Cargo.toml");
+ let cargo_toml_bytes =
+ fs::read(file).context("Can't find Cargo.toml to determine the crate name")?;
+
+ let cargo_toml = toml::from_slice::<CargoToml>(&cargo_toml_bytes)?;
+
+ let lib_crate_name = cargo_toml
+ .lib
+ .name
+ .unwrap_or_else(|| cargo_toml.package.name.replace('-', "_"));
+
+ Ok(lib_crate_name)
+}
+
+/// Guess the root directory of the crate from the path of its UDL file.
+///
+/// For now, we assume that the UDL file is in `./src/something.udl` relative
+/// to the crate root. We might consider something more sophisticated in
+/// future.
+pub fn guess_crate_root(udl_file: &Utf8Path) -> Result<&Utf8Path> {
+ let path_guess = udl_file
+ .parent()
+ .context("UDL file has no parent folder!")?
+ .parent()
+ .context("UDL file has no grand-parent folder!")?;
+ if !path_guess.join("Cargo.toml").is_file() {
+ bail!("UDL file does not appear to be inside a crate")
+ }
+ Ok(path_guess)
+}
+
+fn get_out_dir(udl_file: &Utf8Path, out_dir_override: Option<&Utf8Path>) -> Result<Utf8PathBuf> {
+ Ok(match out_dir_override {
+ Some(s) => {
+ // Create the directory if it doesn't exist yet.
+ fs::create_dir_all(s)?;
+ s.canonicalize_utf8().context("Unable to find out-dir")?
+ }
+ None => udl_file
+ .parent()
+ .context("File has no parent directory")?
+ .to_owned(),
+ })
+}
+
+fn parse_udl(udl_file: &Utf8Path, crate_name: &str) -> Result<ComponentInterface> {
+ let udl = fs::read_to_string(udl_file)
+ .with_context(|| format!("Failed to read UDL from {udl_file}"))?;
+ let group = uniffi_udl::parse_udl(&udl, crate_name)?;
+ ComponentInterface::from_metadata(group)
+}
+
+fn format_code_with_rustfmt(path: &Utf8Path) -> Result<()> {
+ let status = Command::new("rustfmt").arg(path).status().map_err(|e| {
+ let ctx = match e.kind() {
+ ErrorKind::NotFound => "formatting was requested, but rustfmt was not found",
+ _ => "unknown error when calling rustfmt",
+ };
+ anyhow!(e).context(ctx)
+ })?;
+ if !status.success() {
+ bail!("rustmt failed when formatting scaffolding. Note: --no-format can be used to skip formatting");
+ }
+ Ok(())
+}
+
+fn load_initial_config<Config: DeserializeOwned>(
+ crate_root: &Utf8Path,
+ config_file_override: Option<&Utf8Path>,
+) -> Result<Config> {
+ let path = match config_file_override {
+ Some(cfg) => Some(cfg.to_owned()),
+ None => crate_root.join("uniffi.toml").canonicalize_utf8().ok(),
+ };
+ let toml_config = match path {
+ Some(path) => {
+ let contents = fs::read_to_string(path).context("Failed to read config file")?;
+ toml::de::from_str(&contents)?
+ }
+ None => toml::Value::from(toml::value::Table::default()),
+ };
+ Ok(toml_config.try_into()?)
+}
+
+#[derive(Debug, Clone, Default, Serialize, Deserialize)]
+pub struct Config {
+ #[serde(default)]
+ bindings: bindings::Config,
+}
+
+impl BindingsConfig for Config {
+ fn update_from_ci(&mut self, ci: &ComponentInterface) {
+ self.bindings.kotlin.update_from_ci(ci);
+ self.bindings.swift.update_from_ci(ci);
+ self.bindings.python.update_from_ci(ci);
+ self.bindings.ruby.update_from_ci(ci);
+ }
+
+ fn update_from_cdylib_name(&mut self, cdylib_name: &str) {
+ self.bindings.kotlin.update_from_cdylib_name(cdylib_name);
+ self.bindings.swift.update_from_cdylib_name(cdylib_name);
+ self.bindings.python.update_from_cdylib_name(cdylib_name);
+ self.bindings.ruby.update_from_cdylib_name(cdylib_name);
+ }
+
+ fn update_from_dependency_configs(&mut self, config_map: HashMap<&str, &Self>) {
+ self.bindings.kotlin.update_from_dependency_configs(
+ config_map
+ .iter()
+ .map(|(key, config)| (*key, &config.bindings.kotlin))
+ .collect(),
+ );
+ self.bindings.swift.update_from_dependency_configs(
+ config_map
+ .iter()
+ .map(|(key, config)| (*key, &config.bindings.swift))
+ .collect(),
+ );
+ self.bindings.python.update_from_dependency_configs(
+ config_map
+ .iter()
+ .map(|(key, config)| (*key, &config.bindings.python))
+ .collect(),
+ );
+ self.bindings.ruby.update_from_dependency_configs(
+ config_map
+ .iter()
+ .map(|(key, config)| (*key, &config.bindings.ruby))
+ .collect(),
+ );
+ }
+}
+
+// FIXME(HACK):
+// Include the askama config file into the build.
+// That way cargo tracks the file and other tools relying on file tracking see it as well.
+// See https://bugzilla.mozilla.org/show_bug.cgi?id=1774585
+// In the future askama should handle that itself by using the `track_path::path` API,
+// see https://github.com/rust-lang/rust/pull/84029
+#[allow(dead_code)]
+mod __unused {
+ const _: &[u8] = include_bytes!("../askama.toml");
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn test_guessing_of_crate_root_directory_from_udl_file() {
+ // When running this test, this will be the ./uniffi_bindgen directory.
+ let this_crate_root = Utf8PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap());
+
+ let example_crate_root = this_crate_root
+ .parent()
+ .expect("should have a parent directory")
+ .join("examples/arithmetic");
+ assert_eq!(
+ guess_crate_root(&example_crate_root.join("src/arthmetic.udl")).unwrap(),
+ example_crate_root
+ );
+
+ let not_a_crate_root = &this_crate_root.join("src/templates");
+ assert!(guess_crate_root(&not_a_crate_root.join("src/example.udl")).is_err());
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/library_mode.rs b/third_party/rust/uniffi_bindgen/src/library_mode.rs
new file mode 100644
index 0000000000..f170ea5e91
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/library_mode.rs
@@ -0,0 +1,287 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/// Alternative implementation for the `generate` command, that we plan to eventually replace the current default with.
+///
+/// Traditionally, users would invoke `uniffi-bindgen generate` to generate bindings for a single crate, passing it the UDL file, config file, etc.
+///
+/// library_mode is a new way to generate bindings for multiple crates at once.
+/// Users pass the path to the build cdylib file and UniFFI figures everything out, leveraging `cargo_metadata`, the metadata UniFFI stores inside exported symbols in the dylib, etc.
+///
+/// This brings several advantages.:
+/// - No more need to specify the dylib in the `uniffi.toml` file(s)
+/// - UniFFI can figure out the dependencies based on the dylib exports and generate the sources for
+/// all of them at once.
+/// - UniFFI can figure out the package/module names for each crate, eliminating the external
+/// package maps.
+use crate::{
+ bindings::TargetLanguage, load_initial_config, macro_metadata, BindingGenerator,
+ BindingGeneratorDefault, BindingsConfig, ComponentInterface, Result,
+};
+use anyhow::{bail, Context};
+use camino::Utf8Path;
+use cargo_metadata::{MetadataCommand, Package};
+use std::{
+ collections::{HashMap, HashSet},
+ fs,
+};
+use uniffi_meta::{
+ create_metadata_groups, fixup_external_type, group_metadata, Metadata, MetadataGroup,
+};
+
+/// Generate foreign bindings
+///
+/// Returns the list of sources used to generate the bindings, in no particular order.
+pub fn generate_bindings(
+ library_path: &Utf8Path,
+ crate_name: Option<String>,
+ target_languages: &[TargetLanguage],
+ out_dir: &Utf8Path,
+ try_format_code: bool,
+) -> Result<Vec<Source<crate::Config>>> {
+ generate_external_bindings(
+ BindingGeneratorDefault {
+ target_languages: target_languages.into(),
+ try_format_code,
+ },
+ library_path,
+ crate_name,
+ out_dir,
+ )
+}
+
+/// Generate foreign bindings
+///
+/// Returns the list of sources used to generate the bindings, in no particular order.
+pub fn generate_external_bindings<T: BindingGenerator>(
+ binding_generator: T,
+ library_path: &Utf8Path,
+ crate_name: Option<String>,
+ out_dir: &Utf8Path,
+) -> Result<Vec<Source<T::Config>>> {
+ let cargo_metadata = MetadataCommand::new()
+ .exec()
+ .context("error running cargo metadata")?;
+ let cdylib_name = calc_cdylib_name(library_path);
+ binding_generator.check_library_path(library_path, cdylib_name)?;
+
+ let mut sources = find_sources(&cargo_metadata, library_path, cdylib_name)?;
+ for i in 0..sources.len() {
+ // Partition up the sources list because we're eventually going to call
+ // `update_from_dependency_configs()` which requires an exclusive reference to one source and
+ // shared references to all other sources.
+ let (sources_before, rest) = sources.split_at_mut(i);
+ let (source, sources_after) = rest.split_first_mut().unwrap();
+ let other_sources = sources_before.iter().chain(sources_after.iter());
+ // Calculate which configs come from dependent crates
+ let dependencies =
+ HashSet::<&str>::from_iter(source.package.dependencies.iter().map(|d| d.name.as_str()));
+ let config_map: HashMap<&str, &T::Config> = other_sources
+ .filter_map(|s| {
+ dependencies
+ .contains(s.package.name.as_str())
+ .then_some((s.crate_name.as_str(), &s.config))
+ })
+ .collect();
+ // We can finally call update_from_dependency_configs
+ source.config.update_from_dependency_configs(config_map);
+ }
+ fs::create_dir_all(out_dir)?;
+ if let Some(crate_name) = &crate_name {
+ let old_elements = sources.drain(..);
+ let mut matches: Vec<_> = old_elements
+ .filter(|s| &s.crate_name == crate_name)
+ .collect();
+ match matches.len() {
+ 0 => bail!("Crate {crate_name} not found in {library_path}"),
+ 1 => sources.push(matches.pop().unwrap()),
+ n => bail!("{n} crates named {crate_name} found in {library_path}"),
+ }
+ }
+
+ for source in sources.iter() {
+ binding_generator.write_bindings(&source.ci, &source.config, out_dir)?;
+ }
+
+ Ok(sources)
+}
+
+// A single source that we generate bindings for
+#[derive(Debug)]
+pub struct Source<Config: BindingsConfig> {
+ pub package: Package,
+ pub crate_name: String,
+ pub ci: ComponentInterface,
+ pub config: Config,
+}
+
+// If `library_path` is a C dynamic library, return its name
+pub fn calc_cdylib_name(library_path: &Utf8Path) -> Option<&str> {
+ let cdylib_extentions = [".so", ".dll", ".dylib"];
+ let filename = library_path.file_name()?;
+ let filename = filename.strip_prefix("lib").unwrap_or(filename);
+ for ext in cdylib_extentions {
+ if let Some(f) = filename.strip_suffix(ext) {
+ return Some(f);
+ }
+ }
+ None
+}
+
+fn find_sources<Config: BindingsConfig>(
+ cargo_metadata: &cargo_metadata::Metadata,
+ library_path: &Utf8Path,
+ cdylib_name: Option<&str>,
+) -> Result<Vec<Source<Config>>> {
+ let items = macro_metadata::extract_from_library(library_path)?;
+ let mut metadata_groups = create_metadata_groups(&items);
+ group_metadata(&mut metadata_groups, items)?;
+
+ // Collect and process all UDL from all groups at the start - the fixups
+ // of external types makes this tricky to do as we finalize the group.
+ let mut udl_items: HashMap<String, MetadataGroup> = HashMap::new();
+
+ for group in metadata_groups.values() {
+ let package = find_package_by_crate_name(cargo_metadata, &group.namespace.crate_name)?;
+ let crate_root = package
+ .manifest_path
+ .parent()
+ .context("manifest path has no parent")?;
+ let crate_name = group.namespace.crate_name.clone();
+ if let Some(mut metadata_group) = load_udl_metadata(group, crate_root, &crate_name)? {
+ // fixup the items.
+ metadata_group.items = metadata_group
+ .items
+ .into_iter()
+ .map(|item| fixup_external_type(item, &metadata_groups))
+ // some items are both in UDL and library metadata. For many that's fine but
+ // uniffi-traits aren't trivial to compare meaning we end up with dupes.
+ // We filter out such problematic items here.
+ .filter(|item| !matches!(item, Metadata::UniffiTrait { .. }))
+ .collect();
+ udl_items.insert(crate_name, metadata_group);
+ };
+ }
+
+ metadata_groups
+ .into_values()
+ .map(|group| {
+ let package = find_package_by_crate_name(cargo_metadata, &group.namespace.crate_name)?;
+ let crate_root = package
+ .manifest_path
+ .parent()
+ .context("manifest path has no parent")?;
+ let crate_name = group.namespace.crate_name.clone();
+ let mut ci = ComponentInterface::new(&crate_name);
+ if let Some(metadata) = udl_items.remove(&crate_name) {
+ ci.add_metadata(metadata)?;
+ };
+ ci.add_metadata(group)?;
+ let mut config = load_initial_config::<Config>(crate_root, None)?;
+ if let Some(cdylib_name) = cdylib_name {
+ config.update_from_cdylib_name(cdylib_name);
+ }
+ config.update_from_ci(&ci);
+ Ok(Source {
+ config,
+ crate_name,
+ ci,
+ package,
+ })
+ })
+ .collect()
+}
+
+fn find_package_by_crate_name(
+ metadata: &cargo_metadata::Metadata,
+ crate_name: &str,
+) -> Result<Package> {
+ let matching: Vec<&Package> = metadata
+ .packages
+ .iter()
+ .filter(|p| {
+ p.targets
+ .iter()
+ .any(|t| t.name.replace('-', "_") == crate_name)
+ })
+ .collect();
+ match matching.len() {
+ 1 => Ok(matching[0].clone()),
+ n => bail!("cargo metadata returned {n} packages for crate name {crate_name}"),
+ }
+}
+
+fn load_udl_metadata(
+ group: &MetadataGroup,
+ crate_root: &Utf8Path,
+ crate_name: &str,
+) -> Result<Option<MetadataGroup>> {
+ let udl_items = group
+ .items
+ .iter()
+ .filter_map(|i| match i {
+ uniffi_meta::Metadata::UdlFile(meta) => Some(meta),
+ _ => None,
+ })
+ .collect::<Vec<_>>();
+ match udl_items.len() {
+ // No UDL files, load directly from the group
+ 0 => Ok(None),
+ // Found a UDL file, use it to load the CI, then add the MetadataGroup
+ 1 => {
+ if udl_items[0].module_path != crate_name {
+ bail!(
+ "UDL is for crate '{}' but this crate name is '{}'",
+ udl_items[0].module_path,
+ crate_name
+ );
+ }
+ let ci_name = &udl_items[0].file_stub;
+ let ci_path = crate_root.join("src").join(format!("{ci_name}.udl"));
+ if ci_path.exists() {
+ let udl = fs::read_to_string(ci_path)?;
+ let udl_group = uniffi_udl::parse_udl(&udl, crate_name)?;
+ Ok(Some(udl_group))
+ } else {
+ bail!("{ci_path} not found");
+ }
+ }
+ n => bail!("{n} UDL files found for {crate_root}"),
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn calc_cdylib_name_is_correct() {
+ assert_eq!(
+ "uniffi",
+ calc_cdylib_name("/path/to/libuniffi.so".into()).unwrap()
+ );
+ assert_eq!(
+ "uniffi",
+ calc_cdylib_name("/path/to/libuniffi.dylib".into()).unwrap()
+ );
+ assert_eq!(
+ "uniffi",
+ calc_cdylib_name("/path/to/uniffi.dll".into()).unwrap()
+ );
+ }
+
+ /// Right now we unconditionally strip the `lib` prefix.
+ ///
+ /// Technically Windows DLLs do not start with a `lib` prefix,
+ /// but a library name could start with a `lib` prefix.
+ /// On Linux/macOS this would result in a `liblibuniffi.{so,dylib}` file.
+ #[test]
+ #[ignore] // Currently fails.
+ fn calc_cdylib_name_is_correct_on_windows() {
+ assert_eq!(
+ "libuniffi",
+ calc_cdylib_name("/path/to/libuniffi.dll".into()).unwrap()
+ );
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/macro_metadata/ci.rs b/third_party/rust/uniffi_bindgen/src/macro_metadata/ci.rs
new file mode 100644
index 0000000000..7ce6c3a70b
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/macro_metadata/ci.rs
@@ -0,0 +1,145 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use crate::interface::{CallbackInterface, ComponentInterface, Enum, Record, Type};
+use anyhow::{bail, Context};
+use uniffi_meta::{
+ create_metadata_groups, group_metadata, EnumMetadata, ErrorMetadata, Metadata, MetadataGroup,
+};
+
+/// Add Metadata items to the ComponentInterface
+///
+/// This function exists to support the transition period where the `uniffi::export` macro can only
+/// handle some components. This means that crates need to continue using UDL files to define the
+/// parts of the components that aren't supported yet.
+///
+/// To make things work, we generate a `ComponentInterface` from the UDL file, then combine it with
+/// the `Metadata` items that the macro creates.
+pub fn add_to_ci(
+ iface: &mut ComponentInterface,
+ metadata_items: Vec<Metadata>,
+) -> anyhow::Result<()> {
+ let mut group_map = create_metadata_groups(&metadata_items);
+ group_metadata(&mut group_map, metadata_items)?;
+ for group in group_map.into_values() {
+ if group.items.is_empty() {
+ continue;
+ }
+ if group.namespace.name != iface.namespace() {
+ let crate_name = group.namespace.crate_name;
+ bail!("Found metadata items from crate `{crate_name}`. Use the `--library` to generate bindings for multiple crates")
+ }
+ add_group_to_ci(iface, group)?;
+ }
+
+ Ok(())
+}
+
+/// Add items from a MetadataGroup to a component interface
+pub fn add_group_to_ci(iface: &mut ComponentInterface, group: MetadataGroup) -> anyhow::Result<()> {
+ if group.namespace.name != iface.namespace() {
+ bail!(
+ "Namespace mismatch: {} - {}",
+ group.namespace.name,
+ iface.namespace()
+ );
+ }
+
+ for item in group.items {
+ add_item_to_ci(iface, item)?
+ }
+
+ iface
+ .derive_ffi_funcs()
+ .context("Failed to derive FFI functions")?;
+ iface
+ .check_consistency()
+ .context("ComponentInterface consistency error")?;
+ Ok(())
+}
+
+fn add_enum_to_ci(
+ iface: &mut ComponentInterface,
+ meta: EnumMetadata,
+ is_flat: bool,
+) -> anyhow::Result<()> {
+ let ty = Type::Enum {
+ name: meta.name.clone(),
+ module_path: meta.module_path.clone(),
+ };
+ iface.types.add_known_type(&ty)?;
+
+ let enum_ = Enum::try_from_meta(meta, is_flat)?;
+ iface.add_enum_definition(enum_)?;
+ Ok(())
+}
+
+fn add_item_to_ci(iface: &mut ComponentInterface, item: Metadata) -> anyhow::Result<()> {
+ match item {
+ Metadata::Namespace(_) => unreachable!(),
+ Metadata::UdlFile(_) => (),
+ Metadata::Func(meta) => {
+ iface.add_function_definition(meta.into())?;
+ }
+ Metadata::Constructor(meta) => {
+ iface.add_constructor_meta(meta)?;
+ }
+ Metadata::Method(meta) => {
+ iface.add_method_meta(meta)?;
+ }
+ Metadata::Record(meta) => {
+ let ty = Type::Record {
+ name: meta.name.clone(),
+ module_path: meta.module_path.clone(),
+ };
+ iface.types.add_known_type(&ty)?;
+ let record: Record = meta.try_into()?;
+ iface.add_record_definition(record)?;
+ }
+ Metadata::Enum(meta) => {
+ let flat = meta.variants.iter().all(|v| v.fields.is_empty());
+ add_enum_to_ci(iface, meta, flat)?;
+ }
+ Metadata::Object(meta) => {
+ iface.types.add_known_type(&Type::Object {
+ module_path: meta.module_path.clone(),
+ name: meta.name.clone(),
+ imp: meta.imp,
+ })?;
+ iface.add_object_meta(meta)?;
+ }
+ Metadata::UniffiTrait(meta) => {
+ iface.add_uniffitrait_meta(meta)?;
+ }
+ Metadata::CallbackInterface(meta) => {
+ iface.types.add_known_type(&Type::CallbackInterface {
+ module_path: meta.module_path.clone(),
+ name: meta.name.clone(),
+ })?;
+ iface.add_callback_interface_definition(CallbackInterface::new(
+ meta.name,
+ meta.module_path,
+ ));
+ }
+ Metadata::TraitMethod(meta) => {
+ iface.add_trait_method_meta(meta)?;
+ }
+ Metadata::Error(meta) => {
+ iface.note_name_used_as_error(meta.name());
+ match meta {
+ ErrorMetadata::Enum { enum_, is_flat } => {
+ add_enum_to_ci(iface, enum_, is_flat)?;
+ }
+ };
+ }
+ Metadata::CustomType(meta) => {
+ iface.types.add_known_type(&Type::Custom {
+ module_path: meta.module_path.clone(),
+ name: meta.name,
+ builtin: Box::new(meta.builtin),
+ })?;
+ }
+ }
+ Ok(())
+}
diff --git a/third_party/rust/uniffi_bindgen/src/macro_metadata/extract.rs b/third_party/rust/uniffi_bindgen/src/macro_metadata/extract.rs
new file mode 100644
index 0000000000..25b5ef17ba
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/macro_metadata/extract.rs
@@ -0,0 +1,192 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use anyhow::{bail, Context};
+use camino::Utf8Path;
+use fs_err as fs;
+use goblin::{
+ archive::Archive,
+ elf::Elf,
+ mach::{segment::Section, symbols, Mach, MachO, SingleArch},
+ pe::PE,
+ Object,
+};
+use std::collections::HashSet;
+use uniffi_meta::Metadata;
+
+/// Extract metadata written by the `uniffi::export` macro from a library file
+///
+/// In addition to generating the scaffolding, that macro and also encodes the
+/// `uniffi_meta::Metadata` for the components which can be used to generate the bindings side of
+/// the interface.
+pub fn extract_from_library(path: &Utf8Path) -> anyhow::Result<Vec<Metadata>> {
+ extract_from_bytes(&fs::read(path)?)
+}
+
+fn extract_from_bytes(file_data: &[u8]) -> anyhow::Result<Vec<Metadata>> {
+ match Object::parse(file_data)? {
+ Object::Elf(elf) => extract_from_elf(elf, file_data),
+ Object::PE(pe) => extract_from_pe(pe, file_data),
+ Object::Mach(mach) => extract_from_mach(mach, file_data),
+ Object::Archive(archive) => extract_from_archive(archive, file_data),
+ Object::Unknown(_) => bail!("Unknown library format"),
+ }
+}
+
+pub fn extract_from_elf(elf: Elf<'_>, file_data: &[u8]) -> anyhow::Result<Vec<Metadata>> {
+ let mut extracted = ExtractedItems::new();
+ let iter = elf
+ .syms
+ .iter()
+ .filter_map(|sym| elf.section_headers.get(sym.st_shndx).map(|sh| (sym, sh)));
+
+ for (sym, sh) in iter {
+ let name = elf
+ .strtab
+ .get_at(sym.st_name)
+ .context("Error getting symbol name")?;
+ if is_metadata_symbol(name) {
+ // Offset relative to the start of the section.
+ let section_offset = sym.st_value - sh.sh_addr;
+ // Offset relative to the start of the file contents
+ extracted.extract_item(name, file_data, (sh.sh_offset + section_offset) as usize)?;
+ }
+ }
+ Ok(extracted.into_metadata())
+}
+
+pub fn extract_from_pe(pe: PE<'_>, file_data: &[u8]) -> anyhow::Result<Vec<Metadata>> {
+ let mut extracted = ExtractedItems::new();
+ for export in pe.exports {
+ if let Some(name) = export.name {
+ if is_metadata_symbol(name) {
+ extracted.extract_item(
+ name,
+ file_data,
+ export.offset.context("Error getting symbol offset")?,
+ )?;
+ }
+ }
+ }
+ Ok(extracted.into_metadata())
+}
+
+pub fn extract_from_mach(mach: Mach<'_>, file_data: &[u8]) -> anyhow::Result<Vec<Metadata>> {
+ match mach {
+ Mach::Binary(macho) => extract_from_macho(macho, file_data),
+ // Multi-binary library, just extract the first one
+ Mach::Fat(multi_arch) => match multi_arch.get(0)? {
+ SingleArch::MachO(macho) => extract_from_macho(macho, file_data),
+ SingleArch::Archive(archive) => extract_from_archive(archive, file_data),
+ },
+ }
+}
+
+pub fn extract_from_macho(macho: MachO<'_>, file_data: &[u8]) -> anyhow::Result<Vec<Metadata>> {
+ let mut sections: Vec<Section> = Vec::new();
+ for sects in macho.segments.sections() {
+ sections.extend(sects.map(|r| r.expect("section").0));
+ }
+ let mut extracted = ExtractedItems::new();
+ sections.sort_by_key(|s| s.addr);
+
+ // Iterate through the symbols. This picks up symbols from the .o files embedded in a Darwin
+ // archive.
+ for (name, nlist) in macho.symbols().flatten() {
+ // Check that the symbol:
+ // - Is global (exported)
+ // - Has type=N_SECT (it's regular data as opposed to something like
+ // "undefined" or "indirect")
+ // - Has a metadata symbol name
+ if nlist.is_global() && nlist.get_type() == symbols::N_SECT && is_metadata_symbol(name) {
+ let section = &sections[nlist.n_sect];
+ // `nlist.n_value` is an address, so we can calculating the offset inside the section
+ // using the difference between that and `section.addr`
+ let offset = section.offset as usize + nlist.n_value as usize - section.addr as usize;
+ extracted.extract_item(name, file_data, offset)?;
+ }
+ }
+
+ // Iterate through the exports. This picks up symbols from .dylib files.
+ for export in macho.exports()? {
+ let name = &export.name;
+ if is_metadata_symbol(name) {
+ extracted.extract_item(name, file_data, export.offset as usize)?;
+ }
+ }
+ Ok(extracted.into_metadata())
+}
+
+pub fn extract_from_archive(
+ archive: Archive<'_>,
+ file_data: &[u8],
+) -> anyhow::Result<Vec<Metadata>> {
+ // Store the names of archive members that have metadata symbols in them
+ let mut members_to_check: HashSet<&str> = HashSet::new();
+ for (member_name, _, symbols) in archive.summarize() {
+ for name in symbols {
+ if is_metadata_symbol(name) {
+ members_to_check.insert(member_name);
+ }
+ }
+ }
+
+ let mut items = vec![];
+ for member_name in members_to_check {
+ items.append(
+ &mut extract_from_bytes(
+ archive
+ .extract(member_name, file_data)
+ .with_context(|| format!("Failed to extract archive member `{member_name}`"))?,
+ )
+ .with_context(|| {
+ format!("Failed to extract data from archive member `{member_name}`")
+ })?,
+ );
+ }
+ Ok(items)
+}
+
+/// Container for extracted metadata items
+#[derive(Default)]
+struct ExtractedItems {
+ items: Vec<Metadata>,
+ /// symbol names for the extracted items, we use this to ensure that we don't extract the same
+ /// symbol twice
+ names: HashSet<String>,
+}
+
+impl ExtractedItems {
+ fn new() -> Self {
+ Self::default()
+ }
+
+ fn extract_item(&mut self, name: &str, file_data: &[u8], offset: usize) -> anyhow::Result<()> {
+ if self.names.contains(name) {
+ // Already extracted this item
+ return Ok(());
+ }
+
+ // Use the file data starting from offset, without specifying the end position. We don't
+ // always know the end position, because goblin reports the symbol size as 0 for PE and
+ // MachO files.
+ //
+ // This works fine, because `MetadataReader` knows when the serialized data is terminated
+ // and will just ignore the trailing data.
+ let data = &file_data[offset..];
+ self.items.push(Metadata::read(data)?);
+ self.names.insert(name.to_string());
+ Ok(())
+ }
+
+ fn into_metadata(self) -> Vec<Metadata> {
+ self.items
+ }
+}
+
+fn is_metadata_symbol(name: &str) -> bool {
+ // Skip the "_" char that Darwin prepends, if present
+ let name = name.strip_prefix('_').unwrap_or(name);
+ name.starts_with("UNIFFI_META")
+}
diff --git a/third_party/rust/uniffi_bindgen/src/macro_metadata/mod.rs b/third_party/rust/uniffi_bindgen/src/macro_metadata/mod.rs
new file mode 100644
index 0000000000..bc5a0a790f
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/macro_metadata/mod.rs
@@ -0,0 +1,24 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use super::ComponentInterface;
+use anyhow::Context;
+use camino::Utf8Path;
+
+mod ci;
+mod extract;
+
+pub use ci::{add_group_to_ci, add_to_ci};
+pub use extract::extract_from_library;
+
+pub fn add_to_ci_from_library(
+ iface: &mut ComponentInterface,
+ library_path: &Utf8Path,
+) -> anyhow::Result<()> {
+ add_to_ci(
+ iface,
+ extract_from_library(library_path).context("Failed to extract proc-macro metadata")?,
+ )
+ .context("Failed to add proc-macro metadata to ComponentInterface")
+}
diff --git a/third_party/rust/uniffi_bindgen/src/scaffolding/mod.rs b/third_party/rust/uniffi_bindgen/src/scaffolding/mod.rs
new file mode 100644
index 0000000000..f3759cf6fa
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/scaffolding/mod.rs
@@ -0,0 +1,109 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use anyhow::Result;
+use askama::Template;
+use std::borrow::Borrow;
+
+use super::interface::*;
+use heck::{ToShoutySnakeCase, ToSnakeCase};
+
+#[derive(Template)]
+#[template(syntax = "rs", escape = "none", path = "scaffolding_template.rs")]
+pub struct RustScaffolding<'a> {
+ ci: &'a ComponentInterface,
+ udl_base_name: &'a str,
+}
+impl<'a> RustScaffolding<'a> {
+ pub fn new(ci: &'a ComponentInterface, udl_base_name: &'a str) -> Self {
+ Self { ci, udl_base_name }
+ }
+}
+mod filters {
+ use super::*;
+
+ pub fn type_rs(type_: &Type) -> Result<String, askama::Error> {
+ Ok(match type_ {
+ Type::Int8 => "i8".into(),
+ Type::UInt8 => "u8".into(),
+ Type::Int16 => "i16".into(),
+ Type::UInt16 => "u16".into(),
+ Type::Int32 => "i32".into(),
+ Type::UInt32 => "u32".into(),
+ Type::Int64 => "i64".into(),
+ Type::UInt64 => "u64".into(),
+ Type::Float32 => "f32".into(),
+ Type::Float64 => "f64".into(),
+ Type::Boolean => "bool".into(),
+ Type::String => "String".into(),
+ Type::Bytes => "Vec<u8>".into(),
+ Type::Timestamp => "std::time::SystemTime".into(),
+ Type::Duration => "std::time::Duration".into(),
+ Type::Enum { name, .. } | Type::Record { name, .. } => format!("r#{name}"),
+ Type::Object { name, imp, .. } => {
+ format!("std::sync::Arc<{}>", imp.rust_name_for(name))
+ }
+ Type::CallbackInterface { name, .. } => format!("Box<dyn r#{name}>"),
+ Type::ForeignExecutor => "::uniffi::ForeignExecutor".into(),
+ Type::Optional { inner_type } => {
+ format!("std::option::Option<{}>", type_rs(inner_type)?)
+ }
+ Type::Sequence { inner_type } => format!("std::vec::Vec<{}>", type_rs(inner_type)?),
+ Type::Map {
+ key_type,
+ value_type,
+ } => format!(
+ "std::collections::HashMap<{}, {}>",
+ type_rs(key_type)?,
+ type_rs(value_type)?
+ ),
+ Type::Custom { name, .. } => format!("r#{name}"),
+ Type::External {
+ name,
+ kind: ExternalKind::Interface,
+ ..
+ } => format!("::std::sync::Arc<r#{name}>"),
+ Type::External { name, .. } => format!("r#{name}"),
+ })
+ }
+
+ // Map a type to Rust code that specifies the FfiConverter implementation.
+ //
+ // This outputs something like `<MyStruct as Lift<crate::UniFfiTag>>`
+ pub fn ffi_trait(type_: &Type, trait_name: &str) -> Result<String, askama::Error> {
+ Ok(match type_ {
+ Type::External {
+ name,
+ kind: ExternalKind::Interface,
+ ..
+ } => {
+ format!("<::std::sync::Arc<r#{name}> as ::uniffi::{trait_name}<crate::UniFfiTag>>")
+ }
+ _ => format!(
+ "<{} as ::uniffi::{trait_name}<crate::UniFfiTag>>",
+ type_rs(type_)?
+ ),
+ })
+ }
+
+ pub fn return_type<T: Callable>(callable: &T) -> Result<String, askama::Error> {
+ let return_type = match callable.return_type() {
+ Some(t) => type_rs(&t)?,
+ None => "()".to_string(),
+ };
+ match callable.throws_type() {
+ Some(t) => type_rs(&t)?,
+ None => "()".to_string(),
+ };
+ Ok(match callable.throws_type() {
+ Some(e) => format!("::std::result::Result<{return_type}, {}>", type_rs(&e)?),
+ None => return_type,
+ })
+ }
+
+ // Turns a `crate-name` into the `crate_name` the .rs code needs to specify.
+ pub fn crate_name_rs(nm: &str) -> Result<String, askama::Error> {
+ Ok(format!("r#{}", nm.to_string().to_snake_case()))
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/scaffolding/templates/CallbackInterfaceTemplate.rs b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/CallbackInterfaceTemplate.rs
new file mode 100644
index 0000000000..64c69e4d8e
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/CallbackInterfaceTemplate.rs
@@ -0,0 +1,82 @@
+{#
+// For each Callback Interface definition, we assume that there is a corresponding trait defined in Rust client code.
+// If the UDL callback interface and Rust trait's methods don't match, the Rust compiler will complain.
+// We generate:
+// * an init function to accept that `ForeignCallback` from the foreign language, and stores it.
+// * a holder for a `ForeignCallback`, of type `uniffi::ForeignCallbackInternals`.
+// * a proxy `struct` which implements the `trait` that the Callback Interface corresponds to. This
+// is the object that client code interacts with.
+// - for each method, arguments will be packed into a `RustBuffer` and sent over the `ForeignCallback` to be
+// unpacked and called. The return value is packed into another `RustBuffer` and sent back to Rust.
+// - a `Drop` `impl`, which tells the foreign language to forget about the real callback object.
+#}
+{% let trait_name = cbi.name() -%}
+{% let trait_impl = format!("UniFFICallbackHandler{}", trait_name) %}
+{% let foreign_callback_internals = format!("foreign_callback_{}_internals", trait_name)|upper -%}
+
+// Register a foreign callback for getting across the FFI.
+#[doc(hidden)]
+static {{ foreign_callback_internals }}: uniffi::ForeignCallbackInternals = uniffi::ForeignCallbackInternals::new();
+
+#[doc(hidden)]
+#[no_mangle]
+pub extern "C" fn {{ cbi.ffi_init_callback().name() }}(callback: uniffi::ForeignCallback, _: &mut uniffi::RustCallStatus) {
+ {{ foreign_callback_internals }}.set_callback(callback);
+ // The call status should be initialized to CALL_SUCCESS, so no need to modify it.
+}
+
+// Make an implementation which will shell out to the foreign language.
+#[doc(hidden)]
+#[derive(Debug)]
+struct {{ trait_impl }} {
+ handle: u64
+}
+
+impl {{ trait_impl }} {
+ fn new(handle: u64) -> Self {
+ Self { handle }
+ }
+}
+
+impl Drop for {{ trait_impl }} {
+ fn drop(&mut self) {
+ {{ foreign_callback_internals }}.invoke_callback::<(), crate::UniFfiTag>(
+ self.handle, uniffi::IDX_CALLBACK_FREE, Default::default()
+ )
+ }
+}
+
+uniffi::deps::static_assertions::assert_impl_all!({{ trait_impl }}: Send);
+
+impl r#{{ trait_name }} for {{ trait_impl }} {
+ {%- for meth in cbi.methods() %}
+
+ {#- Method declaration #}
+ fn r#{{ meth.name() -}}
+ ({% call rs::arg_list_decl_with_prefix("&self", meth) %})
+ {%- match (meth.return_type(), meth.throws_type()) %}
+ {%- when (Some(return_type), None) %} -> {{ return_type.borrow()|type_rs }}
+ {%- when (Some(return_type), Some(err)) %} -> ::std::result::Result<{{ return_type.borrow()|type_rs }}, {{ err|type_rs }}>
+ {%- when (None, Some(err)) %} -> ::std::result::Result<(), {{ err|type_rs }}>
+ {% else -%}
+ {%- endmatch -%} {
+ {#- Method body #}
+
+ {#- Packing args into a RustBuffer #}
+ {% if meth.arguments().len() == 0 -%}
+ let args_buf = Vec::new();
+ {% else -%}
+ let mut args_buf = Vec::new();
+ {% endif -%}
+ {%- for arg in meth.arguments() %}
+ {{ arg.as_type().borrow()|ffi_trait("Lower") }}::write(r#{{ arg.name() }}, &mut args_buf);
+ {%- endfor -%}
+ let args_rbuf = uniffi::RustBuffer::from_vec(args_buf);
+
+ {#- Calling into foreign code. #}
+ {{ foreign_callback_internals }}.invoke_callback::<{{ meth|return_type }}, crate::UniFfiTag>(self.handle, {{ loop.index }}, args_rbuf)
+ }
+ {%- endfor %}
+}
+
+::uniffi::scaffolding_ffi_converter_callback_interface!(r#{{ trait_name }}, {{ trait_impl }});
diff --git a/third_party/rust/uniffi_bindgen/src/scaffolding/templates/Checksums.rs b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/Checksums.rs
new file mode 100644
index 0000000000..a59009d169
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/Checksums.rs
@@ -0,0 +1,7 @@
+{%- for (name, checksum) in ci.iter_checksums() %}
+#[no_mangle]
+#[doc(hidden)]
+pub extern "C" fn r#{{ name }}() -> u16 {
+ {{ checksum }}
+}
+{%- endfor %}
diff --git a/third_party/rust/uniffi_bindgen/src/scaffolding/templates/EnumTemplate.rs b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/EnumTemplate.rs
new file mode 100644
index 0000000000..6b9f96f224
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/EnumTemplate.rs
@@ -0,0 +1,19 @@
+{#
+// For each enum declared in the UDL, we assume the caller has provided a corresponding
+// rust `enum`. We provide the traits for sending it across the FFI, which will fail to
+// compile if the provided struct has a different shape to the one declared in the UDL.
+//
+// We define a unit-struct to implement the trait to sidestep Rust's orphan rule (ADR-0006). It's
+// public so other crates can refer to it via an `[External='crate'] typedef`
+#}
+
+#[::uniffi::derive_enum_for_udl]
+enum r#{{ e.name() }} {
+ {%- for variant in e.variants() %}
+ r#{{ variant.name() }} {
+ {%- for field in variant.fields() %}
+ r#{{ field.name() }}: {{ field.as_type().borrow()|type_rs }},
+ {%- endfor %}
+ },
+ {%- endfor %}
+}
diff --git a/third_party/rust/uniffi_bindgen/src/scaffolding/templates/ErrorTemplate.rs b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/ErrorTemplate.rs
new file mode 100644
index 0000000000..94538ecaa8
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/ErrorTemplate.rs
@@ -0,0 +1,26 @@
+{#
+// For each error declared in the UDL, we assume the caller has provided a corresponding
+// rust `enum`. We provide the traits for sending it across the FFI, which will fail to
+// compile if the provided struct has a different shape to the one declared in the UDL.
+//
+// We define a unit-struct to implement the trait to sidestep Rust's orphan rule (ADR-0006). It's
+// public so other crates can refer to it via an `[External='crate'] typedef`
+#}
+
+#[::uniffi::derive_error_for_udl(
+ {% if e.is_flat() -%}
+ flat_error,
+ {% if ci.should_generate_error_read(e) -%}
+ with_try_read,
+ {%- endif %}
+ {%- endif %}
+)]
+enum r#{{ e.name() }} {
+ {%- for variant in e.variants() %}
+ r#{{ variant.name() }} {
+ {%- for field in variant.fields() %}
+ r#{{ field.name() }}: {{ field.as_type().borrow()|type_rs }},
+ {%- endfor %}
+ },
+ {%- endfor %}
+}
diff --git a/third_party/rust/uniffi_bindgen/src/scaffolding/templates/ExternalTypesTemplate.rs b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/ExternalTypesTemplate.rs
new file mode 100644
index 0000000000..ade1578897
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/ExternalTypesTemplate.rs
@@ -0,0 +1,20 @@
+// Support for external types.
+
+// Types with an external `FfiConverter`...
+{% for (name, crate_name, kind, tagged) in ci.iter_external_types() %}
+// The FfiConverter for `{{ name }}` is defined in `{{ crate_name }}`
+// If it has its existing FfiConverter defined with a UniFFITag, it needs forwarding.
+{% if tagged %}
+{%- match kind %}
+{%- when ExternalKind::DataClass %}
+::uniffi::ffi_converter_forward!(r#{{ name }}, ::{{ crate_name|crate_name_rs }}::UniFfiTag, crate::UniFfiTag);
+{%- when ExternalKind::Interface %}
+::uniffi::ffi_converter_arc_forward!(r#{{ name }}, ::{{ crate_name|crate_name_rs }}::UniFfiTag, crate::UniFfiTag);
+{%- endmatch %}
+{% endif %}
+{%- endfor %}
+
+// We generate support for each Custom Type and the builtin type it uses.
+{%- for (name, builtin) in ci.iter_custom_types() %}
+::uniffi::custom_type!(r#{{ name }}, {{builtin|type_rs}});
+{%- endfor -%}
diff --git a/third_party/rust/uniffi_bindgen/src/scaffolding/templates/ObjectTemplate.rs b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/ObjectTemplate.rs
new file mode 100644
index 0000000000..e2445c670d
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/ObjectTemplate.rs
@@ -0,0 +1,89 @@
+// For each Object definition, we assume the caller has provided an appropriately-shaped `struct T`
+// with an `impl` for each method on the object. We create an `Arc<T>` for "safely" handing out
+// references to these structs to foreign language code, and we provide a `pub extern "C"` function
+// corresponding to each method.
+//
+// (Note that "safely" is in "scare quotes" - that's because we use functions on an `Arc` that
+// that are inherently unsafe, but the code we generate is safe in practice.)
+//
+// If the caller's implementation of the struct does not match with the methods or types specified
+// in the UDL, then the rust compiler will complain with a (hopefully at least somewhat helpful!)
+// error message when processing this generated code.
+
+{%- match obj.imp() -%}
+{%- when ObjectImpl::Trait %}
+#[::uniffi::export_for_udl]
+pub trait r#{{ obj.name() }} {
+ {%- for meth in obj.methods() %}
+ fn {{ meth.name() }}(
+ {% if meth.takes_self_by_arc()%}self: Arc<Self>{% else %}&self{% endif %},
+ {%- for arg in meth.arguments() %}
+ {{ arg.name() }}: {% if arg.by_ref() %}&{% endif %}{{ arg.as_type().borrow()|type_rs }},
+ {%- endfor %}
+ )
+ {%- match (meth.return_type(), meth.throws_type()) %}
+ {%- when (Some(return_type), None) %} -> {{ return_type|type_rs }};
+ {%- when (Some(return_type), Some(error_type)) %} -> ::std::result::Result::<{{ return_type|type_rs }}, {{ error_type|type_rs }}>;
+ {%- when (None, Some(error_type)) %} -> ::std::result::Result::<(), {{ error_type|type_rs }}>;
+ {%- when (None, None) %};
+ {%- endmatch %}
+ {% endfor %}
+}
+{% when ObjectImpl::Struct %}
+{%- for tm in obj.uniffi_traits() %}
+{% match tm %}
+{% when UniffiTrait::Debug { fmt }%}
+#[uniffi::export(Debug)]
+{% when UniffiTrait::Display { fmt }%}
+#[uniffi::export(Display)]
+{% when UniffiTrait::Hash { hash }%}
+#[uniffi::export(Hash)]
+{% when UniffiTrait::Eq { eq, ne }%}
+#[uniffi::export(Eq)]
+{% endmatch %}
+{% endfor %}
+#[::uniffi::derive_object_for_udl]
+struct {{ obj.rust_name() }} { }
+
+{%- for cons in obj.constructors() %}
+#[::uniffi::export_for_udl(constructor)]
+impl {{ obj.rust_name() }} {
+ pub fn r#{{ cons.name() }}(
+ {%- for arg in cons.arguments() %}
+ r#{{ arg.name() }}: {% if arg.by_ref() %}&{% endif %}{{ arg.as_type().borrow()|type_rs }},
+ {%- endfor %}
+ )
+ {%- match (cons.return_type(), cons.throws_type()) %}
+ {%- when (Some(return_type), None) %} -> {{ return_type|type_rs }}
+ {%- when (Some(return_type), Some(error_type)) %} -> ::std::result::Result::<{{ return_type|type_rs }}, {{ error_type|type_rs }}>
+ {%- when (None, Some(error_type)) %} -> ::std::result::Result::<(), {{ error_type|type_rs }}>
+ {%- when (None, None) %}
+ {%- endmatch %}
+ {
+ unreachable!()
+ }
+}
+{%- endfor %}
+
+{%- for meth in obj.methods() %}
+#[::uniffi::export_for_udl]
+impl {{ obj.rust_name() }} {
+ pub fn r#{{ meth.name() }}(
+ {% if meth.takes_self_by_arc()%}self: Arc<Self>{% else %}&self{% endif %},
+ {%- for arg in meth.arguments() %}
+ r#{{ arg.name() }}: {% if arg.by_ref() %}&{% endif %}{{ arg.as_type().borrow()|type_rs }},
+ {%- endfor %}
+ )
+ {%- match (meth.return_type(), meth.throws_type()) %}
+ {%- when (Some(return_type), None) %} -> {{ return_type|type_rs }}
+ {%- when (Some(return_type), Some(error_type)) %} -> ::std::result::Result::<{{ return_type|type_rs }}, {{ error_type|type_rs }}>
+ {%- when (None, Some(error_type)) %} -> ::std::result::Result::<(), {{ error_type|type_rs }}>
+ {%- when (None, None) %}
+ {%- endmatch %}
+ {
+ unreachable!()
+ }
+}
+{%- endfor %}
+
+{% endmatch %}
diff --git a/third_party/rust/uniffi_bindgen/src/scaffolding/templates/RecordTemplate.rs b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/RecordTemplate.rs
new file mode 100644
index 0000000000..85e131dd8c
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/RecordTemplate.rs
@@ -0,0 +1,16 @@
+{#
+// For each record declared in the UDL, we assume the caller has provided a corresponding
+// rust `struct` with the declared fields. We provide the traits for sending it across the FFI.
+// If the caller's struct does not match the shape and types declared in the UDL then the rust
+// compiler will complain with a type error.
+//
+// We define a unit-struct to implement the trait to sidestep Rust's orphan rule (ADR-0006). It's
+// public so other crates can refer to it via an `[External='crate'] typedef`
+#}
+
+#[::uniffi::derive_record_for_udl]
+struct r#{{ rec.name() }} {
+ {%- for field in rec.fields() %}
+ r#{{ field.name() }}: {{ field.as_type().borrow()|type_rs }},
+ {%- endfor %}
+}
diff --git a/third_party/rust/uniffi_bindgen/src/scaffolding/templates/ReexportUniFFIScaffolding.rs b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/ReexportUniFFIScaffolding.rs
new file mode 100644
index 0000000000..0707feef27
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/ReexportUniFFIScaffolding.rs
@@ -0,0 +1,28 @@
+// Code to re-export the UniFFI scaffolding functions.
+//
+// Rust won't always re-export the functions from dependencies
+// ([rust-lang#50007](https://github.com/rust-lang/rust/issues/50007))
+//
+// A workaround for this is to have the dependent crate reference a function from its dependency in
+// an extern "C" function. This is clearly hacky and brittle, but at least we have some unittests
+// that check if this works (fixtures/reexport-scaffolding-macro).
+//
+// The main way we use this macro is for that contain multiple UniFFI components (libxul,
+// megazord). The combined library has a cargo dependency for each component and calls
+// uniffi_reexport_scaffolding!() for each one.
+
+#[allow(missing_docs)]
+#[doc(hidden)]
+pub const fn uniffi_reexport_hack() {}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! uniffi_reexport_scaffolding {
+ () => {
+ #[doc(hidden)]
+ #[no_mangle]
+ pub extern "C" fn {{ ci.namespace() }}_uniffi_reexport_hack() {
+ $crate::uniffi_reexport_hack()
+ }
+ };
+}
diff --git a/third_party/rust/uniffi_bindgen/src/scaffolding/templates/TopLevelFunctionTemplate.rs b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/TopLevelFunctionTemplate.rs
new file mode 100644
index 0000000000..eeee0f5ee2
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/TopLevelFunctionTemplate.rs
@@ -0,0 +1,15 @@
+#[::uniffi::export_for_udl]
+pub fn r#{{ func.name() }}(
+ {%- for arg in func.arguments() %}
+ r#{{ arg.name() }}: {% if arg.by_ref() %}&{% endif %}{{ arg.as_type().borrow()|type_rs }},
+ {%- endfor %}
+)
+{%- match (func.return_type(), func.throws_type()) %}
+{%- when (Some(return_type), None) %} -> {{ return_type|type_rs }}
+{%- when (Some(return_type), Some(error_type)) %} -> ::std::result::Result::<{{ return_type|type_rs }}, {{ error_type|type_rs }}>
+{%- when (None, Some(error_type)) %} -> ::std::result::Result::<(), {{ error_type|type_rs }}>
+{%- when (None, None) %}
+{%- endmatch %}
+{
+ unreachable!()
+}
diff --git a/third_party/rust/uniffi_bindgen/src/scaffolding/templates/UdlMetadata.rs b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/UdlMetadata.rs
new file mode 100644
index 0000000000..0b800343d6
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/UdlMetadata.rs
@@ -0,0 +1,16 @@
+
+/// Export info about the UDL while used to create us
+/// See `uniffi_bindgen::macro_metadata` for how this is used.
+
+// ditto for info about the UDL which spawned us.
+{%- let const_udl_var = "UNIFFI_META_CONST_UDL_{}"|format(ci.namespace().to_shouty_snake_case()) %}
+{%- let static_udl_var = "UNIFFI_META_UDL_{}"|format(ci.namespace().to_shouty_snake_case()) %}
+
+const {{ const_udl_var }}: ::uniffi::MetadataBuffer = ::uniffi::MetadataBuffer::from_code(::uniffi::metadata::codes::UDL_FILE)
+ .concat_str("{{ ci.types.namespace.crate_name }}")
+ .concat_str("{{ ci.namespace() }}")
+ .concat_str("{{ udl_base_name }}");
+
+#[doc(hidden)]
+#[no_mangle]
+pub static {{ static_udl_var }}: [u8; {{ const_udl_var }}.size] = {{ const_udl_var }}.into_array();
diff --git a/third_party/rust/uniffi_bindgen/src/scaffolding/templates/macros.rs b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/macros.rs
new file mode 100644
index 0000000000..8b1f94cd1c
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/macros.rs
@@ -0,0 +1,28 @@
+{#
+// Template to receive calls into rust.
+
+// Arglist as used in the _UniFFILib function declarations.
+// Note unfiltered name but type_ffi filters.
+-#}
+{%- macro arg_list_ffi_decl(func) %}
+ {%- for arg in func.arguments() %}
+ r#{{- arg.name() }}: {{ arg.type_().borrow()|type_ffi -}},
+ {%- endfor %}
+ call_status: &mut uniffi::RustCallStatus
+{%- endmacro -%}
+
+{%- macro arg_list_decl_with_prefix(prefix, meth) %}
+ {{- prefix -}}
+ {%- if meth.arguments().len() > 0 %}, {# whitespace #}
+ {%- for arg in meth.arguments() %}
+ r#{{- arg.name() }}: {{ arg.as_type().borrow()|type_rs -}}{% if loop.last %}{% else %},{% endif %}
+ {%- endfor %}
+ {%- endif %}
+{%- endmacro -%}
+
+{% macro return_signature(func) %}
+{%- match func.return_type() %}
+{%- when Some with (return_type) %} -> {{ return_type|ffi_trait("LowerReturn") }}::ReturnType
+{%- else -%}
+{%- endmatch -%}
+{%- endmacro -%}
diff --git a/third_party/rust/uniffi_bindgen/src/scaffolding/templates/scaffolding_template.rs b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/scaffolding_template.rs
new file mode 100644
index 0000000000..c88e204e97
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/scaffolding_template.rs
@@ -0,0 +1,52 @@
+// This file was autogenerated by some hot garbage in the `uniffi` crate.
+// Trust me, you don't want to mess with it!
+{% import "macros.rs" as rs %}
+
+::uniffi::setup_scaffolding!("{{ ci.namespace() }}");
+
+{% include "UdlMetadata.rs" %}
+
+{% for ty in ci.iter_types() %}
+{%- match ty %}
+{%- when Type::Map { key_type: k, value_type: v } -%}
+{# Next comment MUST be after the line to be in the compiler output #}
+uniffi::deps::static_assertions::assert_impl_all!({{ k|type_rs }}: ::std::cmp::Eq, ::std::hash::Hash); // record<{{ k|type_rs }}, {{ v|type_rs }}>
+{%- else %}
+{%- endmatch %}
+{% endfor %}
+
+{% for e in ci.enum_definitions() %}
+{% if ci.is_name_used_as_error(e.name()) %}
+// Error definitions, corresponding to `error` in the UDL.
+{% include "ErrorTemplate.rs" %}
+{% else %}
+// Enum definitions, corresponding to `enum` in UDL.
+{% include "EnumTemplate.rs" %}
+{% endif %}
+{% endfor %}
+
+// Record definitions, implemented as method-less structs, corresponding to `dictionary` objects.
+{% for rec in ci.record_definitions() %}
+{% include "RecordTemplate.rs" %}
+{% endfor %}
+
+// Top level functions, corresponding to UDL `namespace` functions.
+{%- for func in ci.function_definitions() %}
+{% include "TopLevelFunctionTemplate.rs" %}
+{% endfor -%}
+
+// Object definitions, corresponding to UDL `interface` definitions.
+{% for obj in ci.object_definitions() %}
+{% include "ObjectTemplate.rs" %}
+{% endfor %}
+
+// Callback Interface definitions, corresponding to UDL `callback interface` definitions.
+{% for cbi in ci.callback_interface_definitions() %}
+{% include "CallbackInterfaceTemplate.rs" %}
+{% endfor %}
+
+// External and Wrapped types
+{% include "ExternalTypesTemplate.rs" %}
+
+// Export scaffolding checksums for UDL items
+{% include "Checksums.rs" %}