summaryrefslogtreecommitdiffstats
path: root/third_party/rust/uniffi_bindgen/src/bindings/swift
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/uniffi_bindgen/src/bindings/swift')
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/callback_interface.rs25
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/compounds.rs98
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/custom.rs25
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/enum_.rs33
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/error.rs25
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/external.rs35
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/miscellany.rs29
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/mod.rs476
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/object.rs25
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/primitives.rs87
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/record.rs25
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/mod.rs99
-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.h55
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/CallbackInterfaceRuntime.swift60
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/CallbackInterfaceTemplate.swift154
-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/DurationHelper.swift24
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/EnumTemplate.swift60
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ErrorTemplate.swift83
-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/Helpers.swift84
-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.swift88
-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.swift15
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Types.swift90
-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.swift97
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/wrapper.swift40
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/test.rs225
45 files changed, 2668 insertions, 0 deletions
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..828a8823ee
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/callback_interface.rs
@@ -0,0 +1,25 @@
+/* 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::{CodeOracle, CodeType};
+
+pub struct CallbackInterfaceCodeType {
+ id: String,
+}
+
+impl CallbackInterfaceCodeType {
+ pub fn new(id: String) -> Self {
+ Self { id }
+ }
+}
+
+impl CodeType for CallbackInterfaceCodeType {
+ fn type_label(&self, oracle: &dyn CodeOracle) -> String {
+ oracle.class_name(&self.id)
+ }
+
+ fn canonical_name(&self, oracle: &dyn CodeOracle) -> String {
+ format!("CallbackInterface{}", self.type_label(oracle))
+ }
+}
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..f91be9ce54
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/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 crate::backend::{CodeOracle, CodeType, Literal, TypeIdentifier};
+
+pub struct OptionalCodeType {
+ inner: TypeIdentifier,
+}
+
+impl OptionalCodeType {
+ pub fn new(inner: TypeIdentifier) -> Self {
+ Self { inner }
+ }
+}
+
+impl CodeType for OptionalCodeType {
+ fn type_label(&self, oracle: &dyn CodeOracle) -> String {
+ format!("{}?", oracle.find(&self.inner).type_label(oracle))
+ }
+
+ fn canonical_name(&self, oracle: &dyn CodeOracle) -> String {
+ format!("Option{}", oracle.find(&self.inner).canonical_name(oracle))
+ }
+
+ fn literal(&self, oracle: &dyn CodeOracle, literal: &Literal) -> String {
+ match literal {
+ Literal::Null => "nil".into(),
+ _ => oracle.find(&self.inner).literal(oracle, literal),
+ }
+ }
+}
+
+pub struct SequenceCodeType {
+ inner: TypeIdentifier,
+}
+
+impl SequenceCodeType {
+ pub fn new(inner: TypeIdentifier) -> Self {
+ Self { inner }
+ }
+}
+
+impl CodeType for SequenceCodeType {
+ fn type_label(&self, oracle: &dyn CodeOracle) -> String {
+ format!("[{}]", oracle.find(&self.inner).type_label(oracle))
+ }
+
+ fn canonical_name(&self, oracle: &dyn CodeOracle) -> String {
+ format!(
+ "Sequence{}",
+ oracle.find(&self.inner).canonical_name(oracle)
+ )
+ }
+
+ fn literal(&self, _oracle: &dyn CodeOracle, literal: &Literal) -> String {
+ match literal {
+ Literal::EmptySequence => "[]".into(),
+ _ => unreachable!(),
+ }
+ }
+}
+
+pub struct MapCodeType {
+ key: TypeIdentifier,
+ value: TypeIdentifier,
+}
+
+impl MapCodeType {
+ pub fn new(key: TypeIdentifier, value: TypeIdentifier) -> Self {
+ Self { key, value }
+ }
+}
+
+impl CodeType for MapCodeType {
+ fn type_label(&self, oracle: &dyn CodeOracle) -> String {
+ format!(
+ "[{}: {}]",
+ oracle.find(&self.key).type_label(oracle),
+ oracle.find(&self.value).type_label(oracle)
+ )
+ }
+
+ fn canonical_name(&self, oracle: &dyn CodeOracle) -> String {
+ format!(
+ "Dictionary{}{}",
+ oracle.find(&self.key).canonical_name(oracle),
+ oracle.find(&self.value).canonical_name(oracle)
+ )
+ }
+
+ fn literal(&self, _oracle: &dyn CodeOracle, 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..bf1806e5e2
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/custom.rs
@@ -0,0 +1,25 @@
+/* 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::{CodeOracle, CodeType};
+
+pub struct CustomCodeType {
+ name: String,
+}
+
+impl CustomCodeType {
+ pub fn new(name: String) -> Self {
+ CustomCodeType { name }
+ }
+}
+
+impl CodeType for CustomCodeType {
+ fn type_label(&self, _oracle: &dyn CodeOracle) -> String {
+ self.name.clone()
+ }
+
+ fn canonical_name(&self, _oracle: &dyn CodeOracle) -> 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..04d8422f6a
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/enum_.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 crate::backend::{CodeOracle, CodeType, Literal};
+
+pub struct EnumCodeType {
+ id: String,
+}
+
+impl EnumCodeType {
+ pub fn new(id: String) -> Self {
+ Self { id }
+ }
+}
+
+impl CodeType for EnumCodeType {
+ fn type_label(&self, oracle: &dyn CodeOracle) -> String {
+ oracle.class_name(&self.id)
+ }
+
+ fn canonical_name(&self, _oracle: &dyn CodeOracle) -> String {
+ format!("Type{}", self.id)
+ }
+
+ fn literal(&self, oracle: &dyn CodeOracle, literal: &Literal) -> String {
+ if let Literal::Enum(v, _) = literal {
+ format!(".{}", oracle.enum_variant_name(v))
+ } else {
+ unreachable!();
+ }
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/error.rs b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/error.rs
new file mode 100644
index 0000000000..4219f24c39
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/error.rs
@@ -0,0 +1,25 @@
+/* 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::{CodeOracle, CodeType};
+
+pub struct ErrorCodeType {
+ id: String,
+}
+
+impl ErrorCodeType {
+ pub fn new(id: String) -> Self {
+ Self { id }
+ }
+}
+
+impl CodeType for ErrorCodeType {
+ fn type_label(&self, oracle: &dyn CodeOracle) -> String {
+ oracle.class_name(&self.id)
+ }
+
+ fn canonical_name(&self, _oracle: &dyn CodeOracle) -> String {
+ format!("Type{}", self.id)
+ }
+}
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..c9a925cdaa
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/external.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 crate::backend::{CodeOracle, CodeType};
+
+pub struct ExternalCodeType {
+ name: String,
+}
+
+impl ExternalCodeType {
+ pub fn new(name: String) -> Self {
+ ExternalCodeType { name }
+ }
+}
+
+impl CodeType for ExternalCodeType {
+ fn type_label(&self, _oracle: &dyn CodeOracle) -> String {
+ self.name.clone()
+ }
+
+ fn canonical_name(&self, _oracle: &dyn CodeOracle) -> String {
+ format!("Type{}", self.name)
+ }
+
+ // lower and lift need to call public function which were generated for
+ // the original types.
+ fn lower(&self, oracle: &dyn CodeOracle) -> String {
+ format!("{}_lower", self.ffi_converter_name(oracle))
+ }
+
+ fn lift(&self, oracle: &dyn CodeOracle) -> String {
+ format!("{}_lift", self.ffi_converter_name(oracle))
+ }
+}
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..4f57734934
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/miscellany.rs
@@ -0,0 +1,29 @@
+/* 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::{CodeOracle, CodeType};
+
+pub struct TimestampCodeType;
+
+impl CodeType for TimestampCodeType {
+ fn type_label(&self, _oracle: &dyn CodeOracle) -> String {
+ "Date".into()
+ }
+
+ fn canonical_name(&self, _oracle: &dyn CodeOracle) -> String {
+ "Timestamp".into()
+ }
+}
+
+pub struct DurationCodeType;
+
+impl CodeType for DurationCodeType {
+ fn type_label(&self, _oracle: &dyn CodeOracle) -> String {
+ "TimeInterval".into()
+ }
+
+ fn canonical_name(&self, _oracle: &dyn CodeOracle) -> 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..abbe0b3539
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/mod.rs
@@ -0,0 +1,476 @@
+/* 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 anyhow::{Context, Result};
+use askama::Template;
+use heck::{ToLowerCamelCase, ToUpperCamelCase};
+use serde::{Deserialize, Serialize};
+
+use super::Bindings;
+use crate::backend::{CodeOracle, CodeType, TemplateExpression, TypeIdentifier};
+use crate::interface::*;
+use crate::MergeWith;
+
+mod callback_interface;
+mod compounds;
+mod custom;
+mod enum_;
+mod error;
+mod external;
+mod miscellany;
+mod object;
+mod primitives;
+mod record;
+
+/// 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 From<&ComponentInterface> for Config {
+ fn from(ci: &ComponentInterface) -> Self {
+ Config {
+ module_name: Some(ci.namespace().into()),
+ cdylib_name: Some(format!("uniffi_{}", ci.namespace())),
+ ..Default::default()
+ }
+ }
+}
+
+impl MergeWith for Config {
+ fn merge_with(&self, other: &Self) -> Self {
+ Config {
+ module_name: self.module_name.merge_with(&other.module_name),
+ ffi_module_name: self.ffi_module_name.merge_with(&other.ffi_module_name),
+ cdylib_name: self.cdylib_name.merge_with(&other.cdylib_name),
+ ffi_module_filename: self
+ .ffi_module_filename
+ .merge_with(&other.ffi_module_filename),
+ generate_module_map: self
+ .generate_module_map
+ .merge_with(&other.generate_module_map),
+ omit_argument_labels: self
+ .omit_argument_labels
+ .merge_with(&other.omit_argument_labels),
+ custom_types: self.custom_types.merge_with(&other.custom_types),
+ }
+ }
+}
+
+/// 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 = "kt", 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.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(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>,
+}
+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,
+ }
+ }
+
+ pub fn imports(&self) -> Vec<String> {
+ self.type_imports.iter().cloned().collect()
+ }
+
+ pub fn initialization_fns(&self) -> Vec<String> {
+ self.ci
+ .iter_types()
+ .into_iter()
+ .filter_map(|t| t.initialization_fn(&SwiftCodeOracle))
+ .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_: TypeIdentifier) -> 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::Timestamp => Box::new(miscellany::TimestampCodeType),
+ Type::Duration => Box::new(miscellany::DurationCodeType),
+
+ Type::Enum(id) => Box::new(enum_::EnumCodeType::new(id)),
+ Type::Object(id) => Box::new(object::ObjectCodeType::new(id)),
+ Type::Record(id) => Box::new(record::RecordCodeType::new(id)),
+ Type::Error(id) => Box::new(error::ErrorCodeType::new(id)),
+ Type::CallbackInterface(id) => {
+ Box::new(callback_interface::CallbackInterfaceCodeType::new(id))
+ }
+
+ Type::Optional(inner) => Box::new(compounds::OptionalCodeType::new(*inner)),
+ Type::Sequence(inner) => Box::new(compounds::SequenceCodeType::new(*inner)),
+ Type::Map(key, value) => Box::new(compounds::MapCodeType::new(*key, *value)),
+ Type::External { name, .. } => Box::new(external::ExternalCodeType::new(name)),
+ Type::Custom { name, .. } => Box::new(custom::CustomCodeType::new(name)),
+ Type::Unresolved { name } => {
+ unreachable!("Type `{name}` must be resolved before calling create_code_type")
+ }
+ }
+ }
+}
+
+impl CodeOracle for SwiftCodeOracle {
+ fn find(&self, type_: &TypeIdentifier) -> 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 {
+ format!("`{}`", nm.to_string().to_lower_camel_case())
+ }
+
+ /// Get the idiomatic Swift rendering of a variable name.
+ fn var_name(&self, nm: &str) -> String {
+ format!("`{}`", 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 {
+ format!("`{}`", nm.to_string().to_lower_camel_case())
+ }
+
+ /// Get the idiomatic Swift rendering of an exception name.
+ fn error_name(&self, nm: &str) -> String {
+ format!("`{}`", self.class_name(nm))
+ }
+
+ fn ffi_type_label(&self, ffi_type: &FfiType) -> String {
+ 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".to_string(),
+ }
+ }
+}
+
+pub mod filters {
+ use super::*;
+
+ fn oracle() -> &'static SwiftCodeOracle {
+ &SwiftCodeOracle
+ }
+
+ pub fn type_name(codetype: &impl CodeType) -> Result<String, askama::Error> {
+ let oracle = oracle();
+ Ok(codetype.type_label(oracle))
+ }
+
+ pub fn canonical_name(codetype: &impl CodeType) -> Result<String, askama::Error> {
+ let oracle = oracle();
+ Ok(codetype.canonical_name(oracle))
+ }
+
+ pub fn ffi_converter_name(codetype: &impl CodeType) -> Result<String, askama::Error> {
+ Ok(codetype.ffi_converter_name(oracle()))
+ }
+
+ pub fn lower_fn(codetype: &impl CodeType) -> Result<String, askama::Error> {
+ Ok(codetype.lower(oracle()))
+ }
+
+ pub fn write_fn(codetype: &impl CodeType) -> Result<String, askama::Error> {
+ Ok(codetype.write(oracle()))
+ }
+
+ pub fn lift_fn(codetype: &impl CodeType) -> Result<String, askama::Error> {
+ Ok(codetype.lift(oracle()))
+ }
+
+ pub fn read_fn(codetype: &impl CodeType) -> Result<String, askama::Error> {
+ Ok(codetype.read(oracle()))
+ }
+
+ pub fn literal_swift(
+ literal: &Literal,
+ codetype: &impl CodeType,
+ ) -> Result<String, askama::Error> {
+ let oracle = oracle();
+ Ok(codetype.literal(oracle, literal))
+ }
+
+ /// Get the Swift syntax for representing a given low-level `FfiType`.
+ pub fn ffi_type_name(type_: &FfiType) -> Result<String, askama::Error> {
+ Ok(oracle().ffi_type_label(type_))
+ }
+
+ /// Get the type that a type is lowered into. This is subtly different than `type_ffi`, see
+ /// #1106 for details
+ pub fn type_ffi_lowered(ffi_type: &FfiType) -> Result<String, askama::Error> {
+ Ok(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(_) => "void*_Nonnull".into(),
+ FfiType::RustBuffer(_) => "RustBuffer".into(),
+ FfiType::ForeignBytes => "ForeignBytes".into(),
+ FfiType::ForeignCallback => "ForeignCallback _Nonnull".to_string(),
+ })
+ }
+
+ /// 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(oracle().fn_name(nm))
+ }
+
+ /// Get the idiomatic Swift rendering of a variable name.
+ pub fn var_name(nm: &str) -> Result<String, askama::Error> {
+ Ok(oracle().var_name(nm))
+ }
+
+ /// Get the idiomatic Swift rendering of an individual enum variant.
+ pub fn enum_variant_swift(nm: &str) -> Result<String, askama::Error> {
+ Ok(oracle().enum_variant_name(nm))
+ }
+}
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..d227f8e7cd
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/object.rs
@@ -0,0 +1,25 @@
+/* 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::{CodeOracle, CodeType};
+
+pub struct ObjectCodeType {
+ id: String,
+}
+
+impl ObjectCodeType {
+ pub fn new(id: String) -> Self {
+ Self { id }
+ }
+}
+
+impl CodeType for ObjectCodeType {
+ fn type_label(&self, oracle: &dyn CodeOracle) -> String {
+ oracle.class_name(&self.id)
+ }
+
+ fn canonical_name(&self, _oracle: &dyn CodeOracle) -> 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..9989e0a421
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/primitives.rs
@@ -0,0 +1,87 @@
+/* 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::{CodeOracle, CodeType, Literal};
+use crate::interface::{types::Type, Radix};
+use paste::paste;
+
+fn render_literal(oracle: &dyn CodeOracle, literal: &Literal) -> String {
+ fn typed_number(oracle: &dyn CodeOracle, 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})", oracle.find(type_).type_label(oracle))
+ }
+ _ => 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(
+ oracle,
+ 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(
+ oracle,
+ type_,
+ match radix {
+ Radix::Octal => format!("0o{i:o}"),
+ Radix::Decimal => format!("{i}"),
+ Radix::Hexadecimal => format!("{i:#x}"),
+ },
+ ),
+ Literal::Float(string, type_) => typed_number(oracle, type_, string.clone()),
+ _ => unreachable!("Literal"),
+ }
+}
+
+macro_rules! impl_code_type_for_primitive {
+ ($T:ty, $class_name:literal) => {
+ paste! {
+ pub struct $T;
+
+ impl CodeType for $T {
+ fn type_label(&self, _oracle: &dyn CodeOracle) -> String {
+ $class_name.into()
+ }
+
+ fn literal(&self, oracle: &dyn CodeOracle, literal: &Literal) -> String {
+ render_literal(oracle, &literal)
+ }
+ }
+ }
+ };
+}
+
+impl_code_type_for_primitive!(BooleanCodeType, "Bool");
+impl_code_type_for_primitive!(StringCodeType, "String");
+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..0943bcaada
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/record.rs
@@ -0,0 +1,25 @@
+/* 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::{CodeOracle, CodeType};
+
+pub struct RecordCodeType {
+ id: String,
+}
+
+impl RecordCodeType {
+ pub fn new(id: String) -> Self {
+ Self { id }
+ }
+}
+
+impl CodeType for RecordCodeType {
+ fn type_label(&self, oracle: &dyn CodeOracle) -> String {
+ oracle.class_name(&self.id)
+ }
+
+ fn canonical_name(&self, _oracle: &dyn CodeOracle) -> 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..5fd1c404cb
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/mod.rs
@@ -0,0 +1,99 @@
+/* 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::{io::Write, process::Command};
+
+use anyhow::Result;
+use camino::Utf8Path;
+use fs_err::File;
+
+pub mod gen_swift;
+pub use gen_swift::{generate_bindings, Config};
+mod test;
+
+use super::super::interface::ComponentInterface;
+pub use test::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()));
+ let mut l = File::create(&source_file)?;
+ write!(l, "{library}")?;
+
+ let mut h = File::create(out_dir.join(config.header_filename()))?;
+ write!(h, "{header}")?;
+
+ if let Some(modulemap) = modulemap {
+ let mut m = File::create(out_dir.join(config.modulemap_filename()))?;
+ write!(m, "{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: {:?}",
+ source_file.file_name().unwrap(),
+ e
+ )
+ }
+ }
+
+ Ok(())
+}
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..50b95d96e8
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/BridgingHeaderTemplate.h
@@ -0,0 +1,55 @@
+// 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 <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, RustBuffer, RustBuffer *_Nonnull);
+
+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
+
+{% for func in ci.iter_ffi_function_definitions() -%}
+ {%- match func.return_type() -%}{%- when Some with (type_) %}{{ type_|ffi_type_name }}{% when None %}void{% endmatch %} {{ func.name() }}(
+ {% call swift::arg_list_ffi_decl(func) %}
+ );
+{% 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..9ab9bdbe48
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/CallbackInterfaceRuntime.swift
@@ -0,0 +1,60 @@
+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
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..7842511a83
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/CallbackInterfaceTemplate.swift
@@ -0,0 +1,154 @@
+{%- let cbi = ci.get_callback_interface_definition(name).unwrap() %}
+{%- 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, args: RustBuffer, 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 }}, _ args: RustBuffer) throws -> RustBuffer {
+ defer { args.deallocate() }
+ {#- Unpacking args from the RustBuffer #}
+ {%- if meth.arguments().len() != 0 -%}
+ {#- Calling the concrete callback object #}
+
+ var reader = createReader(data: Data(rustBuffer: args))
+ {% if meth.return_type().is_some() %}let result = {% endif -%}
+ {% 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 -%}
+ )
+ {% else %}
+ {% if meth.return_type().is_some() %}let result = {% endif -%}
+ {% if meth.throws() %}try {% endif -%}
+ swiftCallbackInterface.{{ meth.name()|fn_name }}()
+ {% endif -%}
+
+ {#- Packing up the return value into a RustBuffer #}
+ {%- match meth.return_type() -%}
+ {%- when Some with (return_type) -%}
+ var writer = [UInt8]()
+ {{ return_type|write_fn }}(result, into: &writer)
+ return RustBuffer(bytes: writer)
+ {%- else -%}
+ return RustBuffer()
+ {% endmatch -%}
+ // TODO catch errors and report them back to Rust.
+ // https://github.com/mozilla/uniffi-rs/issues/351
+
+ }
+ {% endfor %}
+
+ 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 -1
+ }
+
+ switch method {
+ case IDX_CALLBACK_FREE:
+ {{ ffi_converter_name }}.drop(handle: handle)
+ // No return value.
+ // See docs of ForeignCallback in `uniffi/src/ffi/foreigncallbacks.rs`
+ return 0
+ {% for meth in cbi.methods() -%}
+ {% let method_name = format!("invoke_{}", meth.name())|fn_name -%}
+ case {{ loop.index }}:
+ do {
+ out_buf.pointee = try {{ method_name }}(cb, args)
+ // Value written to out buffer.
+ // See docs of ForeignCallback in `uniffi/src/ffi/foreigncallbacks.rs`
+ return 1
+ {%- match meth.throws_type() %}
+ {%- when Some(error_type) %}
+ } catch let error as {{ error_type|type_name }} {
+ out_buf.pointee = {{ error_type|lower_fn }}(error)
+ return -2
+ {%- else %}
+ {%- endmatch %}
+ } catch let error {
+ out_buf.pointee = {{ Type::String.borrow()|lower_fn }}(String(describing: error))
+ return -1
+ }
+ {% 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/src/ffi/foreigncallbacks.rs`
+ return -1
+ }
+ }
+
+// FfiConverter protocol for callback interfaces
+fileprivate struct {{ ffi_converter_name }} {
+ // Initialize our callback method with the scaffolding code
+ private static var callbackInitialized = false
+ private static func initCallback() {
+ try! rustCall { (err: UnsafeMutablePointer<RustCallStatus>) in
+ {{ cbi.ffi_init_callback().name() }}({{ foreign_callback }}, err)
+ }
+ }
+ private static func ensureCallbackinitialized() {
+ if !callbackInitialized {
+ initCallback()
+ callbackInitialized = true
+ }
+ }
+
+ 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..cb63f0b97b
--- /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().borrow()|type_ffi_lowered %}
+{%- 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)
+ }
+}
+
+{#
+We always write these public functions just incase the type is used as
+an external type by another crate.
+#}
+public func FfiConverterType{{ name }}_lift(_ buf: RustBuffer) throws -> {{ name }} {
+ return try FfiConverterType{{ name }}.lift(buf)
+}
+
+public func FfiConverterType{{ name }}_lower(_ value: {{ name }}) -> RustBuffer {
+ return FfiConverterType{{ name }}.lower(value)
+}
+
+{%- endmatch %}
+
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..74f0a3effe
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/EnumTemplate.swift
@@ -0,0 +1,60 @@
+// Note that we don't yet support `indirect` for enums.
+// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion.
+{%- let e = ci.get_enum_definition(name).unwrap() %}
+public enum {{ type_name }} {
+ {% for variant in e.variants() %}
+ case {{ variant.name()|enum_variant_swift }}{% 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 }}{% 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 %}
+ 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 }}({% 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 }}:
+ 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..b2c4bfe492
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ErrorTemplate.swift
@@ -0,0 +1,83 @@
+{%- let e = ci.get_error_definition(name).unwrap() %}
+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 %}
+}
+
+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 let .{{ variant.name()|class_name }}(message):
+ writeInt(&buf, Int32({{ loop.index }}))
+ {{ Type::String.borrow()|write_fn }}(message, into: &buf)
+ {%- 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/Helpers.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Helpers.swift
new file mode 100644
index 0000000000..18e2397733
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Helpers.swift
@@ -0,0 +1,84 @@
+// 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 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: {
+ $0.deallocate()
+ return UniffiInternalError.unexpectedRustCallError
+ })
+}
+
+private func rustCallWithError<T, F: FfiConverter>
+ (_ errorFfiConverter: F.Type, _ callback: (UnsafeMutablePointer<RustCallStatus>) -> T) throws -> T
+ where F.SwiftType: Error, F.FfiType == RustBuffer
+ {
+ try makeRustCall(callback, errorHandler: { return try errorFfiConverter.lift($0) })
+}
+
+private func makeRustCall<T>(_ callback: (UnsafeMutablePointer<RustCallStatus>) -> T, errorHandler: (RustBuffer) throws -> Error) throws -> T {
+ var callStatus = RustCallStatus.init()
+ let returnedVal = callback(&callStatus)
+ switch callStatus.code {
+ case CALL_SUCCESS:
+ return returnedVal
+
+ case CALL_ERROR:
+ throw try errorHandler(callStatus.errorBuf)
+
+ case CALL_PANIC:
+ // When the rust code sees a panic, it tries to construct a RustBuffer
+ // with the message. But if that code panics, then it just sends back
+ // an empty buffer.
+ if callStatus.errorBuf.len > 0 {
+ throw UniffiInternalError.rustPanic(try {{ Type::String.borrow()|lift_fn }}(callStatus.errorBuf))
+ } else {
+ callStatus.errorBuf.deallocate()
+ throw UniffiInternalError.rustPanic("Rust panic")
+ }
+
+ 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..ab0b22b1d8
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift
@@ -0,0 +1,88 @@
+{%- let obj = ci.get_object_definition(name).unwrap() %}
+public protocol {{ obj.name() }}Protocol {
+ {% for meth in obj.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 %}
+}
+
+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() -%}
+ {%- 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 %}
+ {% 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
+ }
+}
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..7f2de44052
--- /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).unwrap() %}
+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()|var_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..5ce762d2a0
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/TopLevelFunctionTemplate.swift
@@ -0,0 +1,15 @@
+{%- 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 %}
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..6e37e1b7f3
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Types.swift
@@ -0,0 +1,90 @@
+{%- 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::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) %}
+{%- include "CallbackInterfaceTemplate.swift" %}
+
+{%- when Type::Custom { name, builtin } %}
+{%- include "CustomType.swift" %}
+
+{%- when Type::Enum(name) %}
+{%- include "EnumTemplate.swift" %}
+
+{%- when Type::Error(name) %}
+{%- include "ErrorTemplate.swift" %}
+
+{%- when Type::Object(name) %}
+{%- include "ObjectTemplate.swift" %}
+
+{%- when Type::Record(name) %}
+{%- 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..dcc0a2df74
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/macros.swift
@@ -0,0 +1,97 @@
+{#
+// 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`
+#}
+
+{%- macro to_ffi_call(func) -%}
+{% call try(func) %}
+ {% match func.throws_type() %}
+ {% when Some with (e) %}
+ rustCallWithError({{ e|ffi_converter_name }}.self) {
+ {% else %}
+ rustCall() {
+ {% endmatch %}
+ {{ func.ffi_func().name() }}({% call _arg_list_ffi_call(func) -%}{% if func.arguments().len() > 0 %}, {% endif %}$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 }}.self) {
+ {%- else %}
+ rustCall() {
+ {% endmatch %}
+ {{ func.ffi_func().name() }}(
+ {{- prefix }}, {% call _arg_list_ffi_call(func) -%}{% if func.arguments().len() > 0 %}, {% endif %}$0
+ )
+}
+{%- endmacro %}
+
+{%- macro _arg_list_ffi_call(func) %}
+ {%- for arg in func.arguments() %}
+ {{ arg|lower_fn }}({{ arg.name()|var_name }})
+ {%- if !loop.last %}, {% endif -%}
+ {%- 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 %}
+
+
+{#-
+// 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 }} {{ arg.name() -}},
+ {%- endfor %}
+ RustCallStatus *_Nonnull out_status
+{%- 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..ac0557912e
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/wrapper.swift
@@ -0,0 +1,40 @@
+// 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 }}
+
+{%- for func in ci.function_definitions() %}
+{%- include "TopLevelFunctionTemplate.swift" %}
+{%- endfor %}
+
+/**
+ * Top level initializers and tear down methods.
+ *
+ * This is generated by uniffi.
+ */
+public enum {{ config.module_name().borrow()|class_name }}Lifecycle {
+ /**
+ * Initialize the FFI and Rust library. This should be only called once per application.
+ */
+ func initialize() {
+ {%- for initialization_fn in self.initialization_fns() %}
+ {{ initialization_fn }}()
+ {%- endfor %}
+ }
+}
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..e51c4a9c05
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/test.rs
@@ -0,0 +1,225 @@
+/* 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, Result};
+use camino::{Utf8Path, Utf8PathBuf};
+use heck::ToSnakeCase;
+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;
+use uniffi_testing::{CompileSource, UniFFITestHelper};
+
+/// Run Swift tests for a UniFFI test fixture
+pub fn run_test(tmp_dir: &str, fixture_name: &str, script_file: &str) -> Result<()> {
+ let script_path = Utf8Path::new(".").join(script_file).canonicalize_utf8()?;
+ let test_helper = UniFFITestHelper::new(fixture_name).context("UniFFITestHelper::new")?;
+ let out_dir = test_helper
+ .create_out_dir(tmp_dir, &script_path)
+ .context("create_out_dir()")?;
+ test_helper
+ .copy_cdylibs_to_out_dir(&out_dir)
+ .context("copy_fixture_library_to_out_dir()")?;
+ let generated_sources =
+ GeneratedSources::new(&test_helper.cdylib_path()?, &out_dir, &test_helper)
+ .context("generate_sources()")?;
+
+ // Compile the generated sources together to create a single swift module
+ compile_swift_module(
+ &out_dir,
+ &calc_module_name(&generated_sources.main_source_filename),
+ &generated_sources.generated_swift_files,
+ &generated_sources.module_map,
+ )?;
+
+ // Run the test script against compiled bindings
+
+ let mut command = Command::new("swift");
+ 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);
+ 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 calc_module_name(filename: &str) -> String {
+ filename.strip_suffix(".swift").unwrap().to_snake_case()
+}
+
+fn compile_swift_module<T: AsRef<OsStr>>(
+ out_dir: &Utf8Path,
+ module_name: &str,
+ sources: impl IntoIterator<Item = T>,
+ module_map: &Utf8Path,
+) -> Result<()> {
+ let output_filename = format!("{DLL_PREFIX}testmod_{module_name}{DLL_SUFFIX}");
+ let mut command = Command::new("swiftc");
+ 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 {
+ generated_swift_files: Vec<Utf8PathBuf>,
+ module_map: Utf8PathBuf,
+ main_source_filename: String,
+}
+
+impl GeneratedSources {
+ fn new(
+ library_path: &Utf8Path,
+ out_dir: &Utf8Path,
+ test_helper: &UniFFITestHelper,
+ ) -> Result<Self> {
+ // Generate the bindings for the main compile source, and use that for the swift module name
+ let main_compile_source = test_helper.get_main_compile_source()?;
+ Self::run_generate_bindings(&main_compile_source, library_path, out_dir)?;
+ let generated_files = glob(&out_dir.join("*.swift"))?;
+ let main_source_filename = match generated_files.len() {
+ 0 => bail!(
+ "No .swift file generated for {}",
+ main_compile_source.udl_path
+ ),
+ 1 => generated_files
+ .into_iter()
+ .next()
+ .unwrap()
+ .file_name()
+ .unwrap()
+ .to_string(),
+ n => bail!(
+ "{n} .swift files generated for {}",
+ main_compile_source.udl_path
+ ),
+ };
+
+ // Generate the bindings for other compile sources (crates used by external types)
+ for source in test_helper.get_external_compile_sources()? {
+ crate::generate_bindings(
+ &source.udl_path,
+ source.config_path.as_deref(),
+ vec!["swift"],
+ Some(out_dir),
+ Some(library_path),
+ false,
+ )?;
+ }
+
+ let generated_module_maps = glob(&out_dir.join("*.modulemap"))?;
+
+ Ok(GeneratedSources {
+ main_source_filename,
+ generated_swift_files: glob(&out_dir.join("*.swift"))?,
+ module_map: match generated_module_maps.len() {
+ 0 => bail!("No modulemap files found in {out_dir}"),
+ // Normally we only generate 1 module map and can return it directly
+ 1 => generated_module_maps.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,
+ "{}",
+ generated_module_maps
+ .into_iter()
+ .map(|path| Ok(read_to_string(path)?))
+ .collect::<Result<Vec<String>>>()?
+ .join("\n")
+ )?;
+ path
+ }
+ },
+ })
+ }
+
+ fn run_generate_bindings(
+ source: &CompileSource,
+ library_path: &Utf8Path,
+ out_dir: &Utf8Path,
+ ) -> Result<()> {
+ crate::generate_bindings(
+ &source.udl_path,
+ source.config_path.as_deref(),
+ vec!["swift"],
+ Some(out_dir),
+ Some(library_path),
+ false,
+ )
+ }
+}
+
+// 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()
+}