Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Rust returning -> Result<_, String> #296

Merged
merged 3 commits into from
Oct 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,30 @@ class ResultTests: XCTestCase {
rust_func_takes_result_string(.Ok("Success Message"))
rust_func_takes_result_string(.Err("Error Message"))
}


/// Verify that we can return a Result<String, String> from Rust -> Swift.
///
/// The Err case evidences Swift’s `Error` protocol is implemented correctly
/// for `RustStringRef`, i.e. `extension RustStringRef: Error {}`
func testSwiftCallRustReturnsResultString() throws {
let resultOk = try! rust_func_returns_result_string(true)
XCTAssertEqual(resultOk.toString(), "Success Message")

do {
let _ = try rust_func_returns_result_string(false)
XCTFail("The function should have returned an error.")
} catch let error as RustString {
XCTAssertEqual(error.toString(), "Error Message")
}
}

/// Verify that we can pass a Result<OpaqueRust, OpaqueRust> from Swift -> Rust
func testSwiftCallRustResultOpaqueRust() throws {
let reflectedOk = try! rust_func_reflect_result_opaque_rust(
.Ok(ResultTestOpaqueRustType(111))
)
XCTAssertEqual(reflectedOk.val(), 111)

do {
let _ = try rust_func_reflect_result_opaque_rust(
.Err(ResultTestOpaqueRustType(222))
Expand All @@ -30,7 +46,7 @@ class ResultTests: XCTestCase {
XCTAssertEqual(error.val(), 222)
}
}

/// Verify that we can pass a Result<OpaqueSwift, OpaqueSwift> from Swift -> Rust
func testSwiftCallRustResultOpaqueSwift() throws {
rust_func_takes_result_opaque_swift(
Expand Down Expand Up @@ -64,7 +80,7 @@ class ResultTests: XCTestCase {
XCTAssertEqual(error.val(), 222)
}
}

/// Verify that we can receive a Result<OpaqueRust, TransparentEnum> from Rust
func testResultOpaqueRustTransparentEnum() throws {
XCTContext.runActivity(named: "Should return a ResultTestOpaqueRustType") {
Expand All @@ -75,7 +91,7 @@ class ResultTests: XCTestCase {
XCTFail()
}
}

XCTContext.runActivity(named: "Should throw an error") {
_ in
do {
Expand All @@ -95,7 +111,7 @@ class ResultTests: XCTestCase {
}
}
}

/// Verify that we can receive a Result<TransparentEnum, OpaqueRust> from Rust
func testResultTransparentEnumOpaqueRust() throws {
XCTContext.runActivity(named: "Should return a ResultTestOpaqueRustType") {
Expand All @@ -114,7 +130,7 @@ class ResultTests: XCTestCase {
XCTFail()
}
}

XCTContext.runActivity(named: "Should throw an error") {
_ in
do {
Expand All @@ -127,7 +143,7 @@ class ResultTests: XCTestCase {
}
}
}

/// Verify that we can receive a Result<(), TransparentEnum> from Rust
func testResultUnitTypeTransparentEnum() throws {
XCTContext.runActivity(named: "Should return a Unit type") {
Expand All @@ -138,7 +154,7 @@ class ResultTests: XCTestCase {
XCTFail()
}
}

XCTContext.runActivity(named: "Should throw an error") {
_ in
do {
Expand All @@ -158,7 +174,7 @@ 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") {
Expand All @@ -172,7 +188,7 @@ class ResultTests: XCTestCase {
XCTFail()
}
}

XCTContext.runActivity(named: "Should throw an error") {
_ in
do {
Expand Down Expand Up @@ -249,7 +265,7 @@ class ResultTests: XCTestCase {
XCTAssertEqual(UInt32(i), value.val())
}
}

/// Verify that we can use throwing initializers defined on the Rust side.
func testThrowingInitializers() throws {
XCTContext.runActivity(named: "Should fail") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ extension RustStringRef {
__swift_bridge__$RustString$trim(ptr)
}
}
/// exercised in SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/ResultTests.swift:
/// - see `func testSwiftCallRustReturnsResultString`
extension RustStringRef: Error {}
brittlewis12 marked this conversation as resolved.
Show resolved Hide resolved
extension RustString: Vectorizable {
public static func vecOfSelfNew() -> UnsafeMutableRawPointer {
__swift_bridge__$Vec_RustString$new()
Expand Down Expand Up @@ -94,4 +97,4 @@ extension RustString: Vectorizable {
public static func vecOfSelfLen(vecPtr: UnsafeMutableRawPointer) -> UInt {
__swift_bridge__$Vec_RustString$len(vecPtr)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use super::{CodegenTest, ExpectedCHeader, ExpectedRustTokens, ExpectedSwiftCode}
use proc_macro2::TokenStream;
use quote::quote;

/// Test code generation for Rust function that accepts and returns a Result<T, E>
/// Test code generation for Rust function that accepts a Result<T, E>
/// where T and E are Strings.
mod extern_rust_fn_result_string {
use super::*;
Expand Down Expand Up @@ -64,6 +64,72 @@ void __swift_bridge__$some_function(struct __private__ResultPtrAndPtr arg);
}
}

/// Test code generation for Rust function that returns a Result<T, E>
/// where T and E are Strings.
mod extern_rust_fn_return_result_string {
use super::*;

fn bridge_module_tokens() -> TokenStream {
quote! {
mod ffi {
extern "Rust" {
fn some_function() -> Result<String, String>;
}
}
}
}

fn expected_rust_tokens() -> ExpectedRustTokens {
ExpectedRustTokens::Contains(quote! {
#[export_name = "__swift_bridge__$some_function"]
pub extern "C" fn __swift_bridge__some_function(
) -> swift_bridge::result::ResultPtrAndPtr {
match super::some_function() {
Ok(ok) => {
swift_bridge::result::ResultPtrAndPtr {
is_ok: true,
ok_or_err: swift_bridge::string::RustString(ok).box_into_raw() as *mut std::ffi::c_void
}
}
Err(err) => {
swift_bridge::result::ResultPtrAndPtr {
is_ok: false,
ok_or_err: swift_bridge::string::RustString(err).box_into_raw() as *mut std::ffi::c_void
}
}
}
}
})
}

fn expected_swift_code() -> ExpectedSwiftCode {
ExpectedSwiftCode::ContainsAfterTrim(
r#"
public func some_function() throws -> RustString {
try { let val = __swift_bridge__$some_function(); if val.is_ok { return RustString(ptr: val.ok_or_err!) } else { throw RustString(ptr: val.ok_or_err!) } }()
}
"#,
)
}

const EXPECTED_C_HEADER: ExpectedCHeader = ExpectedCHeader::ExactAfterTrim(
r#"
struct __private__ResultPtrAndPtr __swift_bridge__$some_function(void);
"#,
);

#[test]
fn extern_rust_fn_return_result_string() {
CodegenTest {
bridge_module: bridge_module_tokens().into(),
expected_rust_tokens: expected_rust_tokens(),
expected_swift_code: expected_swift_code(),
expected_c_header: EXPECTED_C_HEADER,
}
.test();
}
}

/// Test code generation for Rust function that accepts a Result<T, E> where T and E are
/// opaque Rust types.
mod extern_rust_fn_arg_result_opaque_rust {
Expand Down Expand Up @@ -449,7 +515,7 @@ public func some_function() throws -> SomeOkType {
r#"
typedef enum __swift_bridge__$ResultSomeOkTypeAndSomeErrEnum$Tag {__swift_bridge__$ResultSomeOkTypeAndSomeErrEnum$ResultOk, __swift_bridge__$ResultSomeOkTypeAndSomeErrEnum$ResultErr} __swift_bridge__$ResultSomeOkTypeAndSomeErrEnum$Tag;
union __swift_bridge__$ResultSomeOkTypeAndSomeErrEnum$Fields {void* ok; struct __swift_bridge__$SomeErrEnum err;};
typedef struct __swift_bridge__$ResultSomeOkTypeAndSomeErrEnum{__swift_bridge__$ResultSomeOkTypeAndSomeErrEnum$Tag tag; union __swift_bridge__$ResultSomeOkTypeAndSomeErrEnum$Fields payload;} __swift_bridge__$ResultSomeOkTypeAndSomeErrEnum;
typedef struct __swift_bridge__$ResultSomeOkTypeAndSomeErrEnum{__swift_bridge__$ResultSomeOkTypeAndSomeErrEnum$Tag tag; union __swift_bridge__$ResultSomeOkTypeAndSomeErrEnum$Fields payload;} __swift_bridge__$ResultSomeOkTypeAndSomeErrEnum;
"#,
r#"struct __swift_bridge__$ResultSomeOkTypeAndSomeErrEnum __swift_bridge__$some_function(void)"#,
])
Expand Down Expand Up @@ -531,7 +597,7 @@ public func some_function() throws -> SomeOkEnum {
r#"
typedef enum __swift_bridge__$ResultSomeOkEnumAndSomeErrType$Tag {__swift_bridge__$ResultSomeOkEnumAndSomeErrType$ResultOk, __swift_bridge__$ResultSomeOkEnumAndSomeErrType$ResultErr} __swift_bridge__$ResultSomeOkEnumAndSomeErrType$Tag;
union __swift_bridge__$ResultSomeOkEnumAndSomeErrType$Fields {struct __swift_bridge__$SomeOkEnum ok; void* err;};
typedef struct __swift_bridge__$ResultSomeOkEnumAndSomeErrType{__swift_bridge__$ResultSomeOkEnumAndSomeErrType$Tag tag; union __swift_bridge__$ResultSomeOkEnumAndSomeErrType$Fields payload;} __swift_bridge__$ResultSomeOkEnumAndSomeErrType;
typedef struct __swift_bridge__$ResultSomeOkEnumAndSomeErrType{__swift_bridge__$ResultSomeOkEnumAndSomeErrType$Tag tag; union __swift_bridge__$ResultSomeOkEnumAndSomeErrType$Fields payload;} __swift_bridge__$ResultSomeOkEnumAndSomeErrType;
"#,
r#"struct __swift_bridge__$ResultSomeOkEnumAndSomeErrType __swift_bridge__$some_function(void)"#,
])
Expand Down Expand Up @@ -606,7 +672,7 @@ public func some_function() throws -> () {
r#"
typedef enum __swift_bridge__$ResultVoidAndSomeErrEnum$Tag {__swift_bridge__$ResultVoidAndSomeErrEnum$ResultOk, __swift_bridge__$ResultVoidAndSomeErrEnum$ResultErr} __swift_bridge__$ResultVoidAndSomeErrEnum$Tag;
union __swift_bridge__$ResultVoidAndSomeErrEnum$Fields {struct __swift_bridge__$SomeErrEnum err;};
typedef struct __swift_bridge__$ResultVoidAndSomeErrEnum{__swift_bridge__$ResultVoidAndSomeErrEnum$Tag tag; union __swift_bridge__$ResultVoidAndSomeErrEnum$Fields payload;} __swift_bridge__$ResultVoidAndSomeErrEnum;
typedef struct __swift_bridge__$ResultVoidAndSomeErrEnum{__swift_bridge__$ResultVoidAndSomeErrEnum$Tag tag; union __swift_bridge__$ResultVoidAndSomeErrEnum$Fields payload;} __swift_bridge__$ResultVoidAndSomeErrEnum;
"#,
r#"struct __swift_bridge__$ResultVoidAndSomeErrEnum __swift_bridge__$some_function(void)"#,
])
Expand Down Expand Up @@ -689,7 +755,7 @@ public func some_function() throws -> (Int32, UInt32) {
r#"
typedef enum __swift_bridge__$ResultTupleI32U32AndSomeErrEnum$Tag {__swift_bridge__$ResultTupleI32U32AndSomeErrEnum$ResultOk, __swift_bridge__$ResultTupleI32U32AndSomeErrEnum$ResultErr} __swift_bridge__$ResultTupleI32U32AndSomeErrEnum$Tag;
union __swift_bridge__$ResultTupleI32U32AndSomeErrEnum$Fields {struct __swift_bridge__$tuple$I32U32 ok; struct __swift_bridge__$SomeErrEnum err;};
typedef struct __swift_bridge__$ResultTupleI32U32AndSomeErrEnum{__swift_bridge__$ResultTupleI32U32AndSomeErrEnum$Tag tag; union __swift_bridge__$ResultTupleI32U32AndSomeErrEnum$Fields payload;} __swift_bridge__$ResultTupleI32U32AndSomeErrEnum;
typedef struct __swift_bridge__$ResultTupleI32U32AndSomeErrEnum{__swift_bridge__$ResultTupleI32U32AndSomeErrEnum$Tag tag; union __swift_bridge__$ResultTupleI32U32AndSomeErrEnum$Fields payload;} __swift_bridge__$ResultTupleI32U32AndSomeErrEnum;
"#,
r#"struct __swift_bridge__$ResultTupleI32U32AndSomeErrEnum __swift_bridge__$some_function(void)"#,
r#"typedef struct __swift_bridge__$tuple$I32U32 { int32_t _0; uint32_t _1; } __swift_bridge__$tuple$I32U32;"#,
Expand Down
9 changes: 9 additions & 0 deletions crates/swift-integration-tests/src/result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ mod ffi {
) -> Result<ResultTestOpaqueRustType, ResultTestOpaqueRustType>;

fn rust_func_takes_result_string(arg: Result<String, String>);
fn rust_func_returns_result_string(ok: bool) -> Result<String, String>;

fn rust_func_takes_result_opaque_swift(
arg: Result<ResultTestOpaqueSwiftType, ResultTestOpaqueSwiftType>,
);
Expand Down Expand Up @@ -109,6 +111,13 @@ fn rust_func_takes_result_string(arg: Result<String, String>) {
}
}

fn rust_func_returns_result_string(ok: bool) -> Result<String, String> {
if !ok {
return Err("Error Message".to_string());
}
Ok("Success Message".to_string())
}

fn rust_func_reflect_result_opaque_rust(
arg: Result<ResultTestOpaqueRustType, ResultTestOpaqueRustType>,
) -> Result<ResultTestOpaqueRustType, ResultTestOpaqueRustType> {
Expand Down
Loading