-
Notifications
You must be signed in to change notification settings - Fork 64
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add example of using swift-ui from Rust
- Loading branch information
Showing
37 changed files
with
830 additions
and
144 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
12 changes: 12 additions & 0 deletions
12
...tRustIntegrationTestRunner/SwiftRustIntegrationTestRunner/SwiftFnUsesOpaqueRustType.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
// | ||
// SwiftFnUsesOpaqueRustType.swift | ||
// SwiftRustIntegrationTestRunner | ||
// | ||
// Created by Frankie Nwafili on 11/28/21. | ||
// | ||
|
||
import Foundation | ||
|
||
func increment_some_owned_opaque_rust_type(arg: SomeRustType, amount: UInt32) { | ||
arg.increment_counter(amount) | ||
} |
File renamed without changes.
25 changes: 25 additions & 0 deletions
25
...rationTestRunner/SwiftRustIntegrationTestRunnerTests/SwiftFnUsesOpaqueRustTypeTests.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
// | ||
// SwiftFnUsesOpaqueRustTypeTests.swift | ||
// SwiftRustIntegrationTestRunnerTests | ||
// | ||
// Created by Frankie Nwafili on 11/28/21. | ||
// | ||
|
||
import XCTest | ||
@testable import SwiftRustIntegrationTestRunner | ||
|
||
|
||
class SwiftFnUsesOpaqueRustTypeTests: XCTestCase { | ||
|
||
override func setUpWithError() throws { | ||
// Put setup code here. This method is called before the invocation of each test method in the class. | ||
} | ||
|
||
override func tearDownWithError() throws { | ||
// Put teardown code here. This method is called after the invocation of each test method in the class. | ||
} | ||
|
||
func testRustFnCallsWithFnWithOwnedOpaqueArg() throws { | ||
test_call_swift_fn_with_owned_opaque_rust_arg() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,25 @@ | ||
# swift-bridge | ||
|
||
`swift-bridge` generates bindings for calling Rust from Swift and vice versa. | ||
|
||
# Work In Progress | ||
|
||
The `swift-bridge` book is a work-in-progress with many chapter either sparse or empty. | ||
|
||
We've love your help in improving it! | ||
|
||
Here's how you can contribute to the book even if you don't know much about `swift-bridge`. | ||
|
||
1. You came to the book in order to figure out how to do something or get an answer to a question. | ||
|
||
2. The book did not have a section that answered your question or pointed you in the right direction. | ||
|
||
3. [Open a pull request][pulls] where you fill out a new or existing section with questions that you have. | ||
|
||
4. A maintainer will answer your questions in the pull request comments. | ||
|
||
5. Using your newfound understanding, replace your questions with text that answers them. | ||
|
||
6. Pull request merged! | ||
|
||
[pulls]: https://github.com/chinedufn/swift-bridge/pulls |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# Design of `swift-bridge` | ||
|
||
This chapter explores how `swift-bridge` works internally. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# Code Generation | ||
|
||
- Talk about Rust token stream generation | ||
|
||
- Talk about Swift codegen | ||
|
||
- Talk about C header codegen | ||
|
||
- Talk about how we test codegen |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,6 @@ | ||
mod generate_c_header; | ||
mod generate_swift; | ||
mod generate_rust_tokens; | ||
|
||
#[cfg(test)] | ||
mod codegen_tests; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
//! Tests for codegen for different scenarios. | ||
//! | ||
//! Tests are grouped into modules, each of which takes a set of tokens and then tests that the | ||
//! generated Rust, Swift and C code matches what we expect. | ||
//! | ||
//! This entire module is conditionally compiled with `#[cfg(test)]`, so there is no need to | ||
//! conditionally compile it's submodules. | ||
//! | ||
//! We previously kept out Rust, Swift and C codegen tests in separate files, but then moved to | ||
//! this approach to make it easier to reason about our codegen. | ||
//! | ||
//! There are a bunch of tests in generate_rust_tokens.rs generate_swift.rs and | ||
//! generate_c_header.rs that were written before this module was created. They should be | ||
//! re-organized into this module over time. | ||
#![cfg(test)] | ||
|
||
use crate::test_utils::{ | ||
assert_generated_contains_expected, assert_generated_equals_expected, assert_tokens_contain, | ||
assert_tokens_eq, parse_ok, | ||
}; | ||
use proc_macro2::TokenStream; | ||
use quote::quote; | ||
use quote::ToTokens; | ||
|
||
/// Test code generation for freestanding Swift function that takes an opaque Rust type argument. | ||
mod extern_swift_freestanding_fn_with_owned_opaque_rust_type_arg { | ||
use super::*; | ||
|
||
fn bridge_module_tokens() -> TokenStream { | ||
quote! { | ||
mod foo { | ||
extern "Rust" { | ||
type MyType; | ||
} | ||
|
||
extern "Swift" { | ||
fn some_function (arg: MyType); | ||
} | ||
} | ||
} | ||
} | ||
|
||
fn expected_rust_tokens() -> TokenStream { | ||
quote! { | ||
pub fn some_function (arg: super::MyType) { | ||
unsafe { __swift_bridge__some_function( Box::into_raw(Box::new(arg)) ) } | ||
} | ||
|
||
extern "C" { | ||
#[link_name = "__swift_bridge__$some_function"] | ||
fn __swift_bridge__some_function (arg: *mut super::MyType); | ||
} | ||
} | ||
} | ||
|
||
const EXPECTED_SWIFT: &'static str = r#" | ||
@_cdecl("__swift_bridge__$some_function") | ||
func __swift_bridge__some_function (_ arg: UnsafeMutableRawPointer) { | ||
some_function(arg: MyType(ptr: arg, isOwned: true)) | ||
} | ||
"#; | ||
|
||
const EXPECTED_C_HEADER: &'static str = r#" | ||
typedef struct MyType MyType; | ||
void __swift_bridge__$MyType$_free(void* self); | ||
"#; | ||
|
||
#[test] | ||
fn extern_swift_freestanding_fn_with_owned_opaque_rust_type_arg() { | ||
CodegenTest { | ||
bridge_module_tokens: bridge_module_tokens(), | ||
expected_rust_tokens: ExpectedRustTokens::Contains(expected_rust_tokens()), | ||
expected_swift_code: ExpectedSwiftCode::Contains(EXPECTED_SWIFT), | ||
expected_c_header: EXPECTED_C_HEADER, | ||
} | ||
.test(); | ||
} | ||
} | ||
|
||
/// Test code generation for freestanding Swift function that takes an opaque Swift type argument. | ||
mod extern_swift_freestanding_fn_with_owned_opaque_swift_type_arg { | ||
use super::*; | ||
|
||
fn bridge_module_tokens() -> TokenStream { | ||
quote! { | ||
mod foo { | ||
extern "Swift" { | ||
type MyType; | ||
fn some_function (arg: MyType); | ||
} | ||
} | ||
} | ||
} | ||
|
||
fn expected_rust_tokens() -> ExpectedRustTokens { | ||
ExpectedRustTokens::Contains(quote! { | ||
pub fn some_function (arg: MyType) { | ||
unsafe { __swift_bridge__some_function (arg) } | ||
} | ||
|
||
#[repr(C)] | ||
pub struct MyType(*mut std::ffi::c_void); | ||
|
||
impl Drop for MyType { | ||
fn drop (&mut self) { | ||
unsafe { __swift_bridge__MyType__free(self.0) } | ||
} | ||
} | ||
|
||
extern "C" { | ||
#[link_name = "__swift_bridge__$some_function"] | ||
fn __swift_bridge__some_function (arg: MyType); | ||
|
||
#[link_name = "__swift_bridge__$MyType$_free"] | ||
fn __swift_bridge__MyType__free (this: *mut std::ffi::c_void); | ||
} | ||
}) | ||
} | ||
|
||
const EXPECTED_SWIFT_CODE: ExpectedSwiftCode = ExpectedSwiftCode::Contains( | ||
r#" | ||
@_cdecl("__swift_bridge__$some_function") | ||
func __swift_bridge__some_function (_ arg: __private__PointerToSwiftType) { | ||
some_function(arg: Unmanaged<MyType>.fromOpaque(arg.ptr).takeRetainedValue()) | ||
} | ||
"#, | ||
); | ||
|
||
const EXPECTED_C_HEADER: &'static str = r#""#; | ||
|
||
#[test] | ||
fn extern_swift_freestanding_fn_with_owned_opaque_swift_type_arg() { | ||
CodegenTest { | ||
bridge_module_tokens: bridge_module_tokens(), | ||
expected_rust_tokens: expected_rust_tokens(), | ||
expected_swift_code: EXPECTED_SWIFT_CODE, | ||
expected_c_header: EXPECTED_C_HEADER, | ||
} | ||
.test(); | ||
} | ||
} | ||
|
||
struct CodegenTest { | ||
bridge_module_tokens: TokenStream, | ||
// Gets turned into a Vec<String> and compared to a Vec<String> of the generated Rust tokens. | ||
expected_rust_tokens: ExpectedRustTokens, | ||
// Gets trimmed and compared to the generated Swift code. | ||
expected_swift_code: ExpectedSwiftCode, | ||
// Gets trimmed and compared to the generated C header. | ||
expected_c_header: &'static str, | ||
} | ||
|
||
enum ExpectedRustTokens { | ||
/// The generated Rust token stream matches the provided stream. | ||
Exact(TokenStream), | ||
/// The generated Rust tokens stream contains the provided stream. | ||
Contains(TokenStream), | ||
} | ||
|
||
enum ExpectedSwiftCode { | ||
/// Assert that after we trim our | ||
ExactAfterTrim(&'static str), | ||
Contains(&'static str), | ||
} | ||
|
||
impl CodegenTest { | ||
fn test(self) { | ||
let module = parse_ok(self.bridge_module_tokens); | ||
let generated_tokens = module.to_token_stream(); | ||
|
||
match self.expected_rust_tokens { | ||
ExpectedRustTokens::Exact(expected_tokens) => { | ||
assert_tokens_eq(&generated_tokens, &expected_tokens); | ||
} | ||
ExpectedRustTokens::Contains(expected_contained_tokens) => { | ||
assert_tokens_contain(&generated_tokens, &expected_contained_tokens); | ||
} | ||
}; | ||
|
||
match self.expected_swift_code { | ||
ExpectedSwiftCode::ExactAfterTrim(expected_swift) => { | ||
assert_generated_equals_expected(&module.generate_swift(), expected_swift); | ||
} | ||
ExpectedSwiftCode::Contains(expected_contained_swift) => { | ||
assert_generated_contains_expected( | ||
&module.generate_swift(), | ||
expected_contained_swift, | ||
); | ||
} | ||
}; | ||
|
||
assert_generated_equals_expected(&module.generate_c_header_inner(), self.expected_c_header); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.