Skip to content

Commit

Permalink
Support returning Result<tuple, Any Type> from Rust (#213)
Browse files Browse the repository at this point in the history
This commit adds support for returning `Result<tuple, Any Type>` from Rust functions. Note that this doesn't support returning `Result<Any Type, tuple>` from Rust functions.

Here's an example of using this.

```Rust
#[swift_bridge::bridge]
mod ffi {
    extern "Rust" {
        fn rust_func_return_result_tuple_transparent_enum(
            succeed: bool,
        ) -> Result<(i32, ResultTestOpaqueRustType, String), ResultTransparentEnum>;
    }
}
```

```Swift
//...
do {
    //...
    let tuple: (Int32, ResultTestOpaqueRustType, RustString) = try rust_func_return_result_tuple_transparent_enum(true)
    //...
} catch {
    //...
}
//...
```
  • Loading branch information
NiwakaDev authored May 4, 2023
1 parent 7e140ca commit 852ece7
Show file tree
Hide file tree
Showing 13 changed files with 274 additions and 75 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -158,4 +158,38 @@ class ResultTests: XCTestCase {
}
}
}

/// Verify that we can receive a Result<(primitive type, OpaqueRustType, String), TransparentEnum> from Rust
func testResultTupleTransparentEnum() throws {
XCTContext.runActivity(named: "Should return a tuple type") {
_ in
do {
let tuple: (Int32, ResultTestOpaqueRustType, RustString) = try rust_func_return_result_tuple_transparent_enum(true)
XCTAssertEqual(tuple.0, 123)
XCTAssertEqual(tuple.1.val(), ResultTestOpaqueRustType(123).val())
XCTAssertEqual(tuple.2.toString(), "hello")
} catch {
XCTFail()
}
}

XCTContext.runActivity(named: "Should throw an error") {
_ in
do {
let _: (Int32, ResultTestOpaqueRustType, RustString) = try rust_func_return_result_tuple_transparent_enum(false)
XCTFail("The function should have returned an error.")
} catch let error as ResultTransparentEnum {
switch error {
case .NamedField(let data):
XCTAssertEqual(data, -123)
case .UnnamedFields(_, _):
XCTFail()
case .NoFields:
XCTFail()
}
} catch {
XCTFail()
}
}
}
}
44 changes: 25 additions & 19 deletions crates/swift-bridge-ir/src/bridged_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,23 +86,23 @@ pub(crate) trait BridgeableType: Debug {
/// # Examples
/// String -> None
/// Result<String, OpaqueRust> -> None
/// Result<(), TransparentEnum> -> pub enum ResultVoidAndTransparentEnum { //... }
fn generate_custom_rust_ffi_type(
/// Result<(), TransparentEnum> -> Some(vec![pub enum ResultVoidAndTransparentEnum { //... }])
fn generate_custom_rust_ffi_types(
&self,
swift_bridge_path: &Path,
types: &TypeDeclarations,
) -> Option<TokenStream>;
) -> Option<Vec<TokenStream>>;

/// Generate the type's c declaration if needed.
///
/// # Examples
/// String -> None
/// Result<String, OpaqueRust> -> None
/// Result<(), TransparentEnum> ->
/// typedef enum __swift_bridge__$ResultVoidAndTransparentEnum$Tag { //... };
/// Some(vec![typedef enum __swift_bridge__$ResultVoidAndTransparentEnum$Tag { //... };])
/// // ...
/// typedef struct __swift_bridge__$ResultVoidAndTransparentEnum { //... };
fn generate_custom_c_ffi_type(&self, types: &TypeDeclarations) -> Option<String>;
/// Some(vec![typedef struct __swift_bridge__$ResultVoidAndTransparentEnum { //... };])
fn generate_custom_c_ffi_types(&self, types: &TypeDeclarations) -> Option<Vec<String>>;

/// Get the Rust representation of this type.
/// For a string this might be `std::string::String`.
Expand Down Expand Up @@ -307,7 +307,7 @@ pub(crate) trait BridgeableType: Debug {
/// type Foo would return Foo
/// Option<T> would return Option_{T.to_alpha_numeric_underscore_name()}
/// () would return "Void"
fn to_alpha_numeric_underscore_name(&self) -> String;
fn to_alpha_numeric_underscore_name(&self, types: &TypeDeclarations) -> String;
}

/// Parse a BridgeableType from a stringified token stream.
Expand Down Expand Up @@ -487,31 +487,33 @@ impl BridgeableType for BridgedType {
}
}

fn generate_custom_rust_ffi_type(
fn generate_custom_rust_ffi_types(
&self,
swift_bridge_path: &Path,
types: &TypeDeclarations,
) -> Option<TokenStream> {
) -> Option<Vec<TokenStream>> {
match self {
BridgedType::StdLib(ty) => match ty {
StdLibType::Result(ty) => {
ty.generate_custom_rust_ffi_type(swift_bridge_path, types)
ty.generate_custom_rust_ffi_types(swift_bridge_path, types)
}
StdLibType::Tuple(ty) => {
ty.generate_custom_rust_ffi_types(swift_bridge_path, types)
}
StdLibType::Tuple(ty) => ty.generate_custom_rust_ffi_type(swift_bridge_path, types),
_ => None,
},
BridgedType::Foreign(_) => None,
BridgedType::Bridgeable(ty) => {
ty.generate_custom_rust_ffi_type(swift_bridge_path, types)
ty.generate_custom_rust_ffi_types(swift_bridge_path, types)
}
}
}

fn generate_custom_c_ffi_type(&self, types: &TypeDeclarations) -> Option<String> {
fn generate_custom_c_ffi_types(&self, types: &TypeDeclarations) -> Option<Vec<String>> {
match self {
BridgedType::StdLib(ty) => match ty {
StdLibType::Result(ty) => ty.generate_custom_c_ffi_type(types),
StdLibType::Tuple(ty) => ty.generate_custom_c_ffi_type(types),
StdLibType::Result(ty) => ty.generate_custom_c_ffi_types(types),
StdLibType::Tuple(ty) => ty.generate_custom_c_ffi_types(types),
_ => None,
},
BridgedType::Foreign(_) => None,
Expand Down Expand Up @@ -694,8 +696,8 @@ impl BridgeableType for BridgedType {
}
}

fn to_alpha_numeric_underscore_name(&self) -> String {
self.to_alpha_numeric_underscore_name()
fn to_alpha_numeric_underscore_name(&self, types: &TypeDeclarations) -> String {
self.to_alpha_numeric_underscore_name(types)
}
}

Expand Down Expand Up @@ -822,6 +824,9 @@ impl BridgedType {
return Some(BridgedType::StdLib(StdLibType::BoxedFnOnce(
BridgeableBoxedFnOnce::from_str_tokens(&tokens, types)?,
)));
} else if tokens.starts_with("(") {
let tuple: Type = syn::parse2(TokenStream::from_str(&tokens).unwrap()).unwrap();
return BridgedType::new_with_type(&tuple, types);
}

let ty = match tokens {
Expand Down Expand Up @@ -1844,7 +1849,7 @@ impl BridgedType {
}
}

pub fn to_alpha_numeric_underscore_name(&self) -> String {
pub fn to_alpha_numeric_underscore_name(&self, types: &TypeDeclarations) -> String {
match self {
BridgedType::StdLib(ty) => match ty {
StdLibType::Result(_ty) => todo!(),
Expand All @@ -1860,6 +1865,7 @@ impl BridgedType {
StdLibType::Bool => "Bool".to_string(),
StdLibType::F32 => "F32".to_string(),
StdLibType::F64 => "F64".to_string(),
StdLibType::Tuple(ty) => ty.to_alpha_numeric_underscore_name(types),
_ => todo!(),
},
BridgedType::Foreign(ty) => match ty {
Expand All @@ -1868,7 +1874,7 @@ impl BridgedType {
SharedType::Enum(ty) => ty.name.to_string(),
},
},
BridgedType::Bridgeable(b) => b.to_alpha_numeric_underscore_name(),
BridgedType::Bridgeable(b) => b.to_alpha_numeric_underscore_name(types),
}
}

Expand Down
8 changes: 4 additions & 4 deletions crates/swift-bridge-ir/src/bridged_type/bridgeable_pointer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,15 @@ impl BridgeableType for BuiltInPointer {
todo!()
}

fn generate_custom_rust_ffi_type(
fn generate_custom_rust_ffi_types(
&self,
_swift_bridge_path: &Path,
_types: &TypeDeclarations,
) -> Option<TokenStream> {
) -> Option<Vec<TokenStream>> {
None
}

fn generate_custom_c_ffi_type(&self, _types: &TypeDeclarations) -> Option<String> {
fn generate_custom_c_ffi_types(&self, _types: &TypeDeclarations) -> Option<Vec<String>> {
None
}

Expand Down Expand Up @@ -251,7 +251,7 @@ impl BridgeableType for BuiltInPointer {
todo!()
}

fn to_alpha_numeric_underscore_name(&self) -> String {
fn to_alpha_numeric_underscore_name(&self, _types: &TypeDeclarations) -> String {
todo!()
}
}
Expand Down
90 changes: 63 additions & 27 deletions crates/swift-bridge-ir/src/bridged_type/bridgeable_result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ impl BuiltInResult {
types: &TypeDeclarations,
) -> TokenStream {
if self.is_custom_result_type() {
let ty = format_ident!("{}", self.custom_c_struct_name());
let ty = format_ident!("{}", self.custom_c_struct_name(types));
return quote! {
#ty
};
Expand Down Expand Up @@ -188,7 +188,11 @@ impl BuiltInResult {
todo!()
}
if self.is_custom_result_type() {
return format!("{}${}", SWIFT_BRIDGE_PREFIX, self.custom_c_struct_name());
return format!(
"{}${}",
SWIFT_BRIDGE_PREFIX,
self.custom_c_struct_name(types)
);
}
"__private__ResultPtrAndPtr".to_string()
}
Expand All @@ -205,8 +209,8 @@ impl BuiltInResult {
if self.err_ty.can_be_encoded_with_zero_bytes() {
todo!();
}
let c_ok_name = self.c_ok_tag_name();
let c_err_name = self.c_err_tag_name();
let c_ok_name = self.c_ok_tag_name(types);
let c_err_name = self.c_err_tag_name(types);
let ok_swift_type = if self.ok_ty.can_be_encoded_with_zero_bytes() {
"".to_string()
} else {
Expand Down Expand Up @@ -310,7 +314,7 @@ impl BuiltInResult {
return format!(
"struct {}${}",
SWIFT_BRIDGE_PREFIX,
self.custom_c_struct_name()
self.custom_c_struct_name(types)
);
}
// TODO: Choose the kind of Result representation based on whether or not the ok and error
Expand All @@ -323,15 +327,14 @@ impl BuiltInResult {
}
}

pub fn generate_custom_rust_ffi_type(
pub fn generate_custom_rust_ffi_types(
&self,
swift_bridge_path: &Path,
types: &TypeDeclarations,
) -> Option<TokenStream> {
) -> Option<Vec<TokenStream>> {
if !self.is_custom_result_type() {
return None;
}

if self.err_ty.can_be_encoded_with_zero_bytes() {
todo!()
}
Expand All @@ -348,23 +351,45 @@ impl BuiltInResult {
let err = self
.err_ty
.to_ffi_compatible_rust_type(swift_bridge_path, types);
return Some(quote! {
let mut custom_rust_ffi_types = vec![];
custom_rust_ffi_types.push(quote! {
#[repr(C)]
pub enum #ty {
Ok #ok,
Err(#err),
}
});
let ok_custom_rust_ffi_types = self
.ok_ty
.generate_custom_rust_ffi_types(swift_bridge_path, types);
let err_custom_rust_ffi_types = self
.err_ty
.generate_custom_rust_ffi_types(swift_bridge_path, types);
if let Some(ok_custom_rust_ffi_types) = ok_custom_rust_ffi_types {
for custom_rust_ffi_type in ok_custom_rust_ffi_types {
custom_rust_ffi_types.push(custom_rust_ffi_type);
}
}
if let Some(err_custom_rust_ffi_types) = err_custom_rust_ffi_types {
for custom_rust_ffi_type in err_custom_rust_ffi_types {
custom_rust_ffi_types.push(custom_rust_ffi_type);
}
}
return Some(custom_rust_ffi_types);
}

pub fn generate_custom_c_ffi_type(&self, types: &TypeDeclarations) -> Option<String> {
pub fn generate_custom_c_ffi_types(&self, types: &TypeDeclarations) -> Option<Vec<String>> {
if !self.is_custom_result_type() {
return None;
}
if self.err_ty.can_be_encoded_with_zero_bytes() {
todo!();
}
let c_type = format!("{}${}", SWIFT_BRIDGE_PREFIX, self.custom_c_struct_name());
let c_type = format!(
"{}${}",
SWIFT_BRIDGE_PREFIX,
self.custom_c_struct_name(types)
);
let c_enum_name = c_type.clone();
let c_tag_name = format!("{}$Tag", c_type.clone());
let c_fields_name = format!("{}$Fields", c_type);
Expand All @@ -375,10 +400,10 @@ impl BuiltInResult {
format!("{} ok; ", self.ok_ty.to_c_type(types))
};
let err_c_field_name = self.err_ty.to_c_type(types);
let ok_c_tag_name = self.c_ok_tag_name();
let err_c_tag_name = self.c_err_tag_name();

return Some(format!(
let ok_c_tag_name = self.c_ok_tag_name(types);
let err_c_tag_name = self.c_err_tag_name(types);
let mut custom_c_ffi_types = vec![];
custom_c_ffi_types.push(format!(
"typedef enum {c_tag_name} {{{ok_c_tag_name}, {err_c_tag_name}}} {c_tag_name};
union {c_fields_name} {{{ok_c_field_name}{err_c_field_name} err;}};
typedef struct {c_enum_name}{{{c_tag_name} tag; union {c_fields_name} payload;}} {c_enum_name};",
Expand All @@ -390,6 +415,17 @@ typedef struct {c_enum_name}{{{c_tag_name} tag; union {c_fields_name} payload;}}
ok_c_tag_name = ok_c_tag_name,
err_c_tag_name = err_c_tag_name,
));
if let Some(ok_custom_c_ffi_types) = self.ok_ty.generate_custom_c_ffi_types(types) {
for ok_custom_c_ffi_type in ok_custom_c_ffi_types {
custom_c_ffi_types.push(ok_custom_c_ffi_type);
}
}
if let Some(err_custom_c_ffi_types) = self.err_ty.generate_custom_c_ffi_types(types) {
for err_custom_c_ffi_type in err_custom_c_ffi_types {
custom_c_ffi_types.push(err_custom_c_ffi_type);
}
}
return Some(custom_c_ffi_types);
}

fn is_custom_result_type(&self) -> bool {
Expand Down Expand Up @@ -434,8 +470,8 @@ typedef struct {c_enum_name}{{{c_tag_name} tag; union {c_fields_name} payload;}}
expression = expression,
ok = ok,
err = err,
c_ok_tag_name = self.c_ok_tag_name(),
c_err_tag_name = self.c_err_tag_name()
c_ok_tag_name = self.c_ok_tag_name(types),
c_err_tag_name = self.c_err_tag_name(types)
);
}
let ok = self.ok_ty.to_swift_type(type_pos, types);
Expand All @@ -460,9 +496,9 @@ impl BuiltInResult {
let trimmed = trimmed.trim_end_matches(" >");

// [A, B]
let mut ok_and_err = trimmed.split(",");
let ok = ok_and_err.next()?.trim();
let err = ok_and_err.next()?.trim();
let ok_and_err = trimmed.rsplit_once(",")?;
let ok = ok_and_err.0.trim();
let err = ok_and_err.1.trim();

let ok = BridgedType::new_with_str(ok, types)?;
let err = BridgedType::new_with_str(err, types)?;
Expand All @@ -475,28 +511,28 @@ impl BuiltInResult {
}

impl BuiltInResult {
fn custom_c_struct_name(&self) -> String {
fn custom_c_struct_name(&self, types: &TypeDeclarations) -> String {
let ok = &self.ok_ty;
let err = &self.err_ty;

let ok = ok.to_alpha_numeric_underscore_name();
let err = err.to_alpha_numeric_underscore_name();
let ok = ok.to_alpha_numeric_underscore_name(types);
let err = err.to_alpha_numeric_underscore_name(types);

format!("Result{ok}And{err}")
}
fn c_ok_tag_name(&self) -> String {
fn c_ok_tag_name(&self, types: &TypeDeclarations) -> String {
format!(
"{}${}$ResultOk",
SWIFT_BRIDGE_PREFIX,
self.custom_c_struct_name()
self.custom_c_struct_name(types)
)
}

fn c_err_tag_name(&self) -> String {
fn c_err_tag_name(&self, types: &TypeDeclarations) -> String {
format!(
"{}${}$ResultErr",
SWIFT_BRIDGE_PREFIX,
self.custom_c_struct_name()
self.custom_c_struct_name(types)
)
}
}
Expand Down
Loading

0 comments on commit 852ece7

Please sign in to comment.