Skip to content

Commit

Permalink
Get external errors working.
Browse files Browse the repository at this point in the history
Almost - Swift+Python work, can't work out Kotlin
  • Loading branch information
mhammond committed Jan 7, 2025
1 parent e6cb4a3 commit 9642a98
Show file tree
Hide file tree
Showing 8 changed files with 94 additions and 12 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 15 additions & 2 deletions fixtures/ext-types/lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use ext_types_external_crate::{
};
use std::sync::Arc;
use uniffi_one::{
UniffiOneEnum, UniffiOneInterface, UniffiOneProcMacroType, UniffiOneTrait, UniffiOneType,
UniffiOneUDLTrait,
UniffiOneEnum, UniffiOneError, UniffiOneErrorInterface, UniffiOneInterface,
UniffiOneProcMacroType, UniffiOneTrait, UniffiOneType, UniffiOneUDLTrait,
};
use uniffi_sublib::SubLibType;
use url::Url;
Expand Down Expand Up @@ -195,6 +195,19 @@ fn get_uniffi_one_proc_macro_type(t: UniffiOneProcMacroType) -> UniffiOneProcMac
t
}

#[uniffi::export]
fn throw_uniffi_one_error() -> Result<(), UniffiOneError> {
Err(UniffiOneError::Oops("oh no".to_string()))
}

// external interface errors don't quite work.
// #[uniffi::export]
fn _throw_uniffi_one_error_interface() -> Result<(), UniffiOneErrorInterface> {
Err(UniffiOneErrorInterface {
e: "interface oops".to_string(),
})
}

fn get_external_crate_interface(val: String) -> Arc<ExternalCrateInterface> {
Arc::new(ExternalCrateInterface::new(val))
}
Expand Down
7 changes: 7 additions & 0 deletions fixtures/ext-types/lib/tests/bindings/test_imported_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,13 @@ def test_misc_external_types(self):
self.assertEqual(get_nested_external_guid(None), "nested-external")
self.assertEqual(get_nested_external_ouid(None), "nested-external-ouid")

def test_external_errors(self):
with self.assertRaises(UniffiOneError) as cm:
throw_uniffi_one_error()

# with self.assertRaises(UniffiOneErrorInterface) as cm:
# throw_uniffi_one_error_interface()

if __name__=='__main__':
test_external_callback_interface_impl()
unittest.main()
11 changes: 11 additions & 0 deletions fixtures/ext-types/lib/tests/bindings/test_imported_types.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,5 +61,16 @@ assert(getMaybeUniffiOneEnum(e: nil) == nil)
assert(getUniffiOneEnums(es: [UniffiOneEnum.one]) == [UniffiOneEnum.one])
assert(getMaybeUniffiOneEnums(es: [UniffiOneEnum.one, nil]) == [UniffiOneEnum.one, nil])

do {
try throwUniffiOneError()
fatalError("Should have thrown")
} catch let e as UniffiOneError {
if case let .Oops(reason) = e {
assert(reason == "oh no")
} else {
fatalError("wrong error variant: \(e)")
}
}

assert(ct.ecd.sval == "ecd")
assert(getExternalCrateInterface(val: "foo").value() == "foo")
1 change: 1 addition & 0 deletions fixtures/ext-types/uniffi-one/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ name = "uniffi_one"
[dependencies]
anyhow = "1"
bytes = "1.3"
thiserror = "1"
uniffi = { workspace = true }

[build-dependencies]
Expand Down
33 changes: 33 additions & 0 deletions fixtures/ext-types/uniffi-one/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,39 @@ pub trait UniffiOneTrait: Send + Sync {
fn hello(&self) -> String;
}

// A couple of errors used as external types.
#[derive(thiserror::Error, uniffi::Error, Debug)]
pub enum UniffiOneError {
#[error("{0}")]
Oops(String),
}

#[derive(Debug, uniffi::Object, thiserror::Error)]
#[uniffi::export(Debug, Display)]
pub struct UniffiOneErrorInterface {
pub e: String,
}

#[uniffi::export]
impl UniffiOneErrorInterface {
fn message(&self) -> String {
self.e.clone()
}
}

impl std::fmt::Display for UniffiOneErrorInterface {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "UniffiOneErrorInterface({})", self.e)
}
}

// xxx - once we get interfaces working, we still *must* have this so error
// support is generated. We should fix that and remove this.
// #[uniffi::export]
fn _just_to_get_error_support() -> Result<(), UniffiOneErrorInterface> {
Ok(())
}

// Note `UDL` vs `Udl` is important here to test foreign binding name fixups.
pub trait UniffiOneUDLTrait: Send + Sync {
fn hello(&self) -> String;
Expand Down
34 changes: 25 additions & 9 deletions uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,24 @@ public struct {{ ffi_converter_name }}: FfiConverter {
}
}

{#
We always write these public functions just in case the object is used as
an external type by another crate.
#}
#if swift(>=5.8)
@_documentation(visibility: private)
#endif
public func {{ ffi_converter_name }}_lift(_ pointer: UnsafeMutableRawPointer) throws -> {{ type_name }} {
return try {{ ffi_converter_name }}.lift(pointer)
}

#if swift(>=5.8)
@_documentation(visibility: private)
#endif
public func {{ ffi_converter_name }}_lower(_ value: {{ type_name }}) -> UnsafeMutableRawPointer {
return {{ ffi_converter_name }}.lower(value)
}

{# Objects as error #}
{%- if is_error %}

Expand Down Expand Up @@ -208,22 +226,20 @@ public struct {{ ffi_converter_name }}__as_error: FfiConverterRustBuffer {
fatalError("not implemented")
}
}
{%- endif %}

{#
We always write these public functions just in case the object is used as
an external type by another crate.
#}
{# Error FFI converters also need these public functions. #}
#if swift(>=5.8)
@_documentation(visibility: private)
#endif
public func {{ ffi_converter_name }}_lift(_ pointer: UnsafeMutableRawPointer) throws -> {{ type_name }} {
return try {{ ffi_converter_name }}.lift(pointer)
public func {{ ffi_converter_name }}__as_error_lift(_ buf: RustBuffer) throws -> {{ type_name }} {
return try {{ ffi_converter_name }}__as_error.lift(buf)
}

#if swift(>=5.8)
@_documentation(visibility: private)
#endif
public func {{ ffi_converter_name }}_lower(_ value: {{ type_name }}) -> UnsafeMutableRawPointer {
return {{ ffi_converter_name }}.lower(value)
public func {{ ffi_converter_name }}__as_error_lower(_ value: {{ type_name }}) -> RustBuffer {
return {{ ffi_converter_name }}__as_error.lower(value)
}

{%- endif %}
2 changes: 1 addition & 1 deletion uniffi_bindgen/src/bindings/swift/templates/macros.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
{%- macro to_ffi_call(func) -%}
{%- call is_try(func) -%}
{%- if let(Some(e)) = func.throws_type() -%}
rustCallWithError({{ e|ffi_error_converter_name }}.lift) {
rustCallWithError({{ e|ffi_error_converter_name }}_lift) {
{%- else -%}
rustCall() {
{%- endif %}
Expand Down

0 comments on commit 9642a98

Please sign in to comment.