Skip to content

Commit

Permalink
Support RustString in extern Swift functions (#225)
Browse files Browse the repository at this point in the history
Related to #201. This commit adds support `RustString`  in extern Swift functions.

Here's an example of using this.

```Swift
//Swift
func reflect_swift_string(arg: RustString) -> RustString {
    arg
}

```

```Rust
//Rust
#[swift_bridge::bridge]
mod ffi {
    extern "Swift" {
        fn reflect_swift_string(arg: String) -> String;
    }
}

let foo = "foo";
let string = ffi::reflect_swift_string(foo.to_string());
assert_eq!(string.len(), 3);
assert_eq!(&string, foo);
```
  • Loading branch information
NiwakaDev committed Jun 23, 2023
1 parent 36565f2 commit 2724644
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,7 @@ import Foundation
func create_swift_string() -> String {
"hello"
}

func reflect_rust_string(arg: RustString) -> RustString {
arg
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class StringTests: XCTestCase {

func testRustStringToString() throws {
let string = "hi"

XCTAssertEqual(
create_string(string).toString(),
"hi"
Expand Down
8 changes: 7 additions & 1 deletion crates/swift-bridge-ir/src/bridged_type/bridgeable_string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,13 @@ impl BridgeableType for BridgedString {

fn to_swift_type(&self, type_pos: TypePosition, _types: &TypeDeclarations) -> String {
match type_pos {
TypePosition::FnArg(_func_host_lang, _) => "GenericIntoRustString".to_string(),
TypePosition::FnArg(func_host_lang, _) => {
if func_host_lang.is_rust() {
"GenericIntoRustString".to_string()
} else {
"UnsafeMutableRawPointer".to_string()
}
}
TypePosition::FnReturn(func_host_lang) => {
if func_host_lang.is_rust() {
"RustString".to_string()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,3 +262,50 @@ func __swift_bridge__some_function () -> UnsafeMutableRawPointer {
.test();
}
}

/// Test code generation for Swift function that takes and returns an owned String argument.
mod extern_swift_func_takes_and_returns_string {
use super::*;

fn bridge_module_tokens() -> TokenStream {
quote! {
mod foo {
extern "Swift" {
fn some_function (value: String) -> String;
}
}
}
}

fn expected_rust_tokens() -> ExpectedRustTokens {
ExpectedRustTokens::Contains(quote! {
pub fn some_function (value: String) -> String {
unsafe {
Box :: from_raw (unsafe { __swift_bridge__some_function (swift_bridge :: string :: RustString (value) . box_into_raw ()) }) . 0
}
}
})
}

const EXPECTED_SWIFT_CODE: ExpectedSwiftCode = ExpectedSwiftCode::ContainsAfterTrim(
r#"
@_cdecl("__swift_bridge__$some_function")
func __swift_bridge__some_function (_ value: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer {
{ let rustString = some_function(value: RustString(ptr: value)).intoRustString(); rustString.isOwned = false; return rustString.ptr }()
}
"#,
);

const EXPECTED_C_HEADER: ExpectedCHeader = ExpectedCHeader::ExactAfterTrim(r#""#);

#[test]
fn extern_rust_fn_takes_and_returns_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();
}
}
6 changes: 6 additions & 0 deletions crates/swift-integration-tests/src/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,19 @@ mod ffi {

extern "Swift" {
fn create_swift_string() -> String;
fn reflect_rust_string(arg: String) -> String;
}
}

fn run_string_tests() {
let string = ffi::create_swift_string();
assert_eq!(string.len(), 5);
assert_eq!(&string, "hello");

let foo = "foo";
let string = ffi::reflect_rust_string(foo.to_string());
assert_eq!(string.len(), 3);
assert_eq!(&string, foo);
}

fn create_string(str: &str) -> String {
Expand Down

0 comments on commit 2724644

Please sign in to comment.