Skip to content

Commit c45a38c

Browse files
authored
Support Rust returning -> Result<_, String> (#296)
Support Rust returning `-> Result<_, String>`, e.g.: ```rust #[swift_bridge::bridge] mod ffi { extern "Rust" { fn do_fallible_work() -> Result<(), String>; } } ``` --- Incorporate the changes from #282 to implement swift's `Error` protocol for ruststring, & extend them with the tests requested in #295 (comment)
1 parent 0698b41 commit c45a38c

File tree

4 files changed

+112
-18
lines changed

4 files changed

+112
-18
lines changed

SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/ResultTests.swift

+28-12
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,30 @@ class ResultTests: XCTestCase {
1313
rust_func_takes_result_string(.Ok("Success Message"))
1414
rust_func_takes_result_string(.Err("Error Message"))
1515
}
16-
16+
17+
/// Verify that we can return a Result<String, String> from Rust -> Swift.
18+
///
19+
/// The Err case evidences Swift’s `Error` protocol is implemented correctly
20+
/// for `RustStringRef`, i.e. `extension RustStringRef: Error {}`
21+
func testSwiftCallRustReturnsResultString() throws {
22+
let resultOk = try! rust_func_returns_result_string(true)
23+
XCTAssertEqual(resultOk.toString(), "Success Message")
24+
25+
do {
26+
let _ = try rust_func_returns_result_string(false)
27+
XCTFail("The function should have returned an error.")
28+
} catch let error as RustString {
29+
XCTAssertEqual(error.toString(), "Error Message")
30+
}
31+
}
32+
1733
/// Verify that we can pass a Result<OpaqueRust, OpaqueRust> from Swift -> Rust
1834
func testSwiftCallRustResultOpaqueRust() throws {
1935
let reflectedOk = try! rust_func_reflect_result_opaque_rust(
2036
.Ok(ResultTestOpaqueRustType(111))
2137
)
2238
XCTAssertEqual(reflectedOk.val(), 111)
23-
39+
2440
do {
2541
let _ = try rust_func_reflect_result_opaque_rust(
2642
.Err(ResultTestOpaqueRustType(222))
@@ -30,7 +46,7 @@ class ResultTests: XCTestCase {
3046
XCTAssertEqual(error.val(), 222)
3147
}
3248
}
33-
49+
3450
/// Verify that we can pass a Result<OpaqueSwift, OpaqueSwift> from Swift -> Rust
3551
func testSwiftCallRustResultOpaqueSwift() throws {
3652
rust_func_takes_result_opaque_swift(
@@ -64,7 +80,7 @@ class ResultTests: XCTestCase {
6480
XCTAssertEqual(error.val(), 222)
6581
}
6682
}
67-
83+
6884
/// Verify that we can receive a Result<OpaqueRust, TransparentEnum> from Rust
6985
func testResultOpaqueRustTransparentEnum() throws {
7086
XCTContext.runActivity(named: "Should return a ResultTestOpaqueRustType") {
@@ -75,7 +91,7 @@ class ResultTests: XCTestCase {
7591
XCTFail()
7692
}
7793
}
78-
94+
7995
XCTContext.runActivity(named: "Should throw an error") {
8096
_ in
8197
do {
@@ -95,7 +111,7 @@ class ResultTests: XCTestCase {
95111
}
96112
}
97113
}
98-
114+
99115
/// Verify that we can receive a Result<TransparentEnum, OpaqueRust> from Rust
100116
func testResultTransparentEnumOpaqueRust() throws {
101117
XCTContext.runActivity(named: "Should return a ResultTestOpaqueRustType") {
@@ -114,7 +130,7 @@ class ResultTests: XCTestCase {
114130
XCTFail()
115131
}
116132
}
117-
133+
118134
XCTContext.runActivity(named: "Should throw an error") {
119135
_ in
120136
do {
@@ -127,7 +143,7 @@ class ResultTests: XCTestCase {
127143
}
128144
}
129145
}
130-
146+
131147
/// Verify that we can receive a Result<(), TransparentEnum> from Rust
132148
func testResultUnitTypeTransparentEnum() throws {
133149
XCTContext.runActivity(named: "Should return a Unit type") {
@@ -138,7 +154,7 @@ class ResultTests: XCTestCase {
138154
XCTFail()
139155
}
140156
}
141-
157+
142158
XCTContext.runActivity(named: "Should throw an error") {
143159
_ in
144160
do {
@@ -158,7 +174,7 @@ class ResultTests: XCTestCase {
158174
}
159175
}
160176
}
161-
177+
162178
/// Verify that we can receive a Result<(primitive type, OpaqueRustType, String), TransparentEnum> from Rust
163179
func testResultTupleTransparentEnum() throws {
164180
XCTContext.runActivity(named: "Should return a tuple type") {
@@ -172,7 +188,7 @@ class ResultTests: XCTestCase {
172188
XCTFail()
173189
}
174190
}
175-
191+
176192
XCTContext.runActivity(named: "Should throw an error") {
177193
_ in
178194
do {
@@ -249,7 +265,7 @@ class ResultTests: XCTestCase {
249265
XCTAssertEqual(UInt32(i), value.val())
250266
}
251267
}
252-
268+
253269
/// Verify that we can use throwing initializers defined on the Rust side.
254270
func testThrowingInitializers() throws {
255271
XCTContext.runActivity(named: "Should fail") {

crates/swift-bridge-build/src/generate_core/rust_string.swift

+4-1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ extension RustStringRef {
4747
__swift_bridge__$RustString$trim(ptr)
4848
}
4949
}
50+
/// exercised in SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/ResultTests.swift:
51+
/// - see `func testSwiftCallRustReturnsResultString`
52+
extension RustStringRef: Error {}
5053
extension RustString: Vectorizable {
5154
public static func vecOfSelfNew() -> UnsafeMutableRawPointer {
5255
__swift_bridge__$Vec_RustString$new()
@@ -94,4 +97,4 @@ extension RustString: Vectorizable {
9497
public static func vecOfSelfLen(vecPtr: UnsafeMutableRawPointer) -> UInt {
9598
__swift_bridge__$Vec_RustString$len(vecPtr)
9699
}
97-
}
100+
}

crates/swift-bridge-ir/src/codegen/codegen_tests/result_codegen_tests.rs

+71-5
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use super::{CodegenTest, ExpectedCHeader, ExpectedRustTokens, ExpectedSwiftCode}
44
use proc_macro2::TokenStream;
55
use quote::quote;
66

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

67+
/// Test code generation for Rust function that returns a Result<T, E>
68+
/// where T and E are Strings.
69+
mod extern_rust_fn_return_result_string {
70+
use super::*;
71+
72+
fn bridge_module_tokens() -> TokenStream {
73+
quote! {
74+
mod ffi {
75+
extern "Rust" {
76+
fn some_function() -> Result<String, String>;
77+
}
78+
}
79+
}
80+
}
81+
82+
fn expected_rust_tokens() -> ExpectedRustTokens {
83+
ExpectedRustTokens::Contains(quote! {
84+
#[export_name = "__swift_bridge__$some_function"]
85+
pub extern "C" fn __swift_bridge__some_function(
86+
) -> swift_bridge::result::ResultPtrAndPtr {
87+
match super::some_function() {
88+
Ok(ok) => {
89+
swift_bridge::result::ResultPtrAndPtr {
90+
is_ok: true,
91+
ok_or_err: swift_bridge::string::RustString(ok).box_into_raw() as *mut std::ffi::c_void
92+
}
93+
}
94+
Err(err) => {
95+
swift_bridge::result::ResultPtrAndPtr {
96+
is_ok: false,
97+
ok_or_err: swift_bridge::string::RustString(err).box_into_raw() as *mut std::ffi::c_void
98+
}
99+
}
100+
}
101+
}
102+
})
103+
}
104+
105+
fn expected_swift_code() -> ExpectedSwiftCode {
106+
ExpectedSwiftCode::ContainsAfterTrim(
107+
r#"
108+
public func some_function() throws -> RustString {
109+
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!) } }()
110+
}
111+
"#,
112+
)
113+
}
114+
115+
const EXPECTED_C_HEADER: ExpectedCHeader = ExpectedCHeader::ExactAfterTrim(
116+
r#"
117+
struct __private__ResultPtrAndPtr __swift_bridge__$some_function(void);
118+
"#,
119+
);
120+
121+
#[test]
122+
fn extern_rust_fn_return_result_string() {
123+
CodegenTest {
124+
bridge_module: bridge_module_tokens().into(),
125+
expected_rust_tokens: expected_rust_tokens(),
126+
expected_swift_code: expected_swift_code(),
127+
expected_c_header: EXPECTED_C_HEADER,
128+
}
129+
.test();
130+
}
131+
}
132+
67133
/// Test code generation for Rust function that accepts a Result<T, E> where T and E are
68134
/// opaque Rust types.
69135
mod extern_rust_fn_arg_result_opaque_rust {
@@ -449,7 +515,7 @@ public func some_function() throws -> SomeOkType {
449515
r#"
450516
typedef enum __swift_bridge__$ResultSomeOkTypeAndSomeErrEnum$Tag {__swift_bridge__$ResultSomeOkTypeAndSomeErrEnum$ResultOk, __swift_bridge__$ResultSomeOkTypeAndSomeErrEnum$ResultErr} __swift_bridge__$ResultSomeOkTypeAndSomeErrEnum$Tag;
451517
union __swift_bridge__$ResultSomeOkTypeAndSomeErrEnum$Fields {void* ok; struct __swift_bridge__$SomeErrEnum err;};
452-
typedef struct __swift_bridge__$ResultSomeOkTypeAndSomeErrEnum{__swift_bridge__$ResultSomeOkTypeAndSomeErrEnum$Tag tag; union __swift_bridge__$ResultSomeOkTypeAndSomeErrEnum$Fields payload;} __swift_bridge__$ResultSomeOkTypeAndSomeErrEnum;
518+
typedef struct __swift_bridge__$ResultSomeOkTypeAndSomeErrEnum{__swift_bridge__$ResultSomeOkTypeAndSomeErrEnum$Tag tag; union __swift_bridge__$ResultSomeOkTypeAndSomeErrEnum$Fields payload;} __swift_bridge__$ResultSomeOkTypeAndSomeErrEnum;
453519
"#,
454520
r#"struct __swift_bridge__$ResultSomeOkTypeAndSomeErrEnum __swift_bridge__$some_function(void)"#,
455521
])
@@ -531,7 +597,7 @@ public func some_function() throws -> SomeOkEnum {
531597
r#"
532598
typedef enum __swift_bridge__$ResultSomeOkEnumAndSomeErrType$Tag {__swift_bridge__$ResultSomeOkEnumAndSomeErrType$ResultOk, __swift_bridge__$ResultSomeOkEnumAndSomeErrType$ResultErr} __swift_bridge__$ResultSomeOkEnumAndSomeErrType$Tag;
533599
union __swift_bridge__$ResultSomeOkEnumAndSomeErrType$Fields {struct __swift_bridge__$SomeOkEnum ok; void* err;};
534-
typedef struct __swift_bridge__$ResultSomeOkEnumAndSomeErrType{__swift_bridge__$ResultSomeOkEnumAndSomeErrType$Tag tag; union __swift_bridge__$ResultSomeOkEnumAndSomeErrType$Fields payload;} __swift_bridge__$ResultSomeOkEnumAndSomeErrType;
600+
typedef struct __swift_bridge__$ResultSomeOkEnumAndSomeErrType{__swift_bridge__$ResultSomeOkEnumAndSomeErrType$Tag tag; union __swift_bridge__$ResultSomeOkEnumAndSomeErrType$Fields payload;} __swift_bridge__$ResultSomeOkEnumAndSomeErrType;
535601
"#,
536602
r#"struct __swift_bridge__$ResultSomeOkEnumAndSomeErrType __swift_bridge__$some_function(void)"#,
537603
])
@@ -606,7 +672,7 @@ public func some_function() throws -> () {
606672
r#"
607673
typedef enum __swift_bridge__$ResultVoidAndSomeErrEnum$Tag {__swift_bridge__$ResultVoidAndSomeErrEnum$ResultOk, __swift_bridge__$ResultVoidAndSomeErrEnum$ResultErr} __swift_bridge__$ResultVoidAndSomeErrEnum$Tag;
608674
union __swift_bridge__$ResultVoidAndSomeErrEnum$Fields {struct __swift_bridge__$SomeErrEnum err;};
609-
typedef struct __swift_bridge__$ResultVoidAndSomeErrEnum{__swift_bridge__$ResultVoidAndSomeErrEnum$Tag tag; union __swift_bridge__$ResultVoidAndSomeErrEnum$Fields payload;} __swift_bridge__$ResultVoidAndSomeErrEnum;
675+
typedef struct __swift_bridge__$ResultVoidAndSomeErrEnum{__swift_bridge__$ResultVoidAndSomeErrEnum$Tag tag; union __swift_bridge__$ResultVoidAndSomeErrEnum$Fields payload;} __swift_bridge__$ResultVoidAndSomeErrEnum;
610676
"#,
611677
r#"struct __swift_bridge__$ResultVoidAndSomeErrEnum __swift_bridge__$some_function(void)"#,
612678
])
@@ -689,7 +755,7 @@ public func some_function() throws -> (Int32, UInt32) {
689755
r#"
690756
typedef enum __swift_bridge__$ResultTupleI32U32AndSomeErrEnum$Tag {__swift_bridge__$ResultTupleI32U32AndSomeErrEnum$ResultOk, __swift_bridge__$ResultTupleI32U32AndSomeErrEnum$ResultErr} __swift_bridge__$ResultTupleI32U32AndSomeErrEnum$Tag;
691757
union __swift_bridge__$ResultTupleI32U32AndSomeErrEnum$Fields {struct __swift_bridge__$tuple$I32U32 ok; struct __swift_bridge__$SomeErrEnum err;};
692-
typedef struct __swift_bridge__$ResultTupleI32U32AndSomeErrEnum{__swift_bridge__$ResultTupleI32U32AndSomeErrEnum$Tag tag; union __swift_bridge__$ResultTupleI32U32AndSomeErrEnum$Fields payload;} __swift_bridge__$ResultTupleI32U32AndSomeErrEnum;
758+
typedef struct __swift_bridge__$ResultTupleI32U32AndSomeErrEnum{__swift_bridge__$ResultTupleI32U32AndSomeErrEnum$Tag tag; union __swift_bridge__$ResultTupleI32U32AndSomeErrEnum$Fields payload;} __swift_bridge__$ResultTupleI32U32AndSomeErrEnum;
693759
"#,
694760
r#"struct __swift_bridge__$ResultTupleI32U32AndSomeErrEnum __swift_bridge__$some_function(void)"#,
695761
r#"typedef struct __swift_bridge__$tuple$I32U32 { int32_t _0; uint32_t _1; } __swift_bridge__$tuple$I32U32;"#,

crates/swift-integration-tests/src/result.rs

+9
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ mod ffi {
1010
) -> Result<ResultTestOpaqueRustType, ResultTestOpaqueRustType>;
1111

1212
fn rust_func_takes_result_string(arg: Result<String, String>);
13+
fn rust_func_returns_result_string(ok: bool) -> Result<String, String>;
14+
1315
fn rust_func_takes_result_opaque_swift(
1416
arg: Result<ResultTestOpaqueSwiftType, ResultTestOpaqueSwiftType>,
1517
);
@@ -109,6 +111,13 @@ fn rust_func_takes_result_string(arg: Result<String, String>) {
109111
}
110112
}
111113

114+
fn rust_func_returns_result_string(ok: bool) -> Result<String, String> {
115+
if !ok {
116+
return Err("Error Message".to_string());
117+
}
118+
Ok("Success Message".to_string())
119+
}
120+
112121
fn rust_func_reflect_result_opaque_rust(
113122
arg: Result<ResultTestOpaqueRustType, ResultTestOpaqueRustType>,
114123
) -> Result<ResultTestOpaqueRustType, ResultTestOpaqueRustType> {

0 commit comments

Comments
 (0)