Skip to content

Commit

Permalink
Generate public initializers for shared structs (#101)
Browse files Browse the repository at this point in the history
Thanks for submitting your first PR!
  • Loading branch information
samnm committed Jun 6, 2022
1 parent 91b4802 commit a207c24
Show file tree
Hide file tree
Showing 9 changed files with 151 additions and 44 deletions.
Expand Up @@ -3,6 +3,14 @@ mod ffi {
extern "Rust" {
fn hello_rust() -> String;
}

#[swift_bridge(swift_repr = "struct")]
struct SomeStruct {
field: u8,
}

#[swift_bridge(swift_repr = "struct")]
struct UnnamedStruct(u8);
}

fn hello_rust() -> String {
Expand Down
Expand Up @@ -6,4 +6,12 @@ final class swift_package_test_packageTests: XCTestCase {
func testPackageRun() throws {
XCTAssertEqual("Hello, From Rust!", hello_rust().toString())
}

func testInstantiateSharedStruct() throws {
XCTAssertEqual(SomeStruct(field: 1).field, 1);
}

func testInstantiateSharedStructUnnamed() throws {
XCTAssertEqual(UnnamedStruct(_0: 1)._0, 1);
}
}
2 changes: 1 addition & 1 deletion crates/swift-bridge-ir/src/bridged_type.rs
Expand Up @@ -16,7 +16,7 @@ pub(crate) use self::shared_struct::{SharedStruct, StructFields, StructSwiftRepr

mod bridged_option;
mod shared_enum;
mod shared_struct;
pub(crate) mod shared_struct;

// FIXME: Rename to BridgedType
#[derive(Debug, PartialEq, Clone)]
Expand Down
1 change: 1 addition & 0 deletions crates/swift-bridge-ir/src/bridged_type/shared_struct.rs
Expand Up @@ -7,6 +7,7 @@ use std::fmt::{Debug, Formatter};
use syn::spanned::Spanned;
use syn::{LitStr, Path};

pub(crate) use self::struct_field::StructField;
pub(crate) use self::struct_field::StructFields;

mod struct_field;
Expand Down
Expand Up @@ -106,14 +106,27 @@ pub(crate) struct UnnamedStructField {
pub idx: usize,
}

impl NamedStructField {
pub fn swift_name_string(&self) -> String {
pub(crate) trait StructField {
fn field_type(&self) -> &Type;
fn swift_name_string(&self) -> String;
}

impl StructField for NamedStructField {
fn field_type(&self) -> &Type {
&self.ty
}

fn swift_name_string(&self) -> String {
self.name.to_string()
}
}

impl UnnamedStructField {
pub fn swift_name_string(&self) -> String {
impl StructField for UnnamedStructField {
fn field_type(&self) -> &Type {
&self.ty
}

fn swift_name_string(&self) -> String {
format!("_{}", self.idx)
}
}
Expand Down
Expand Up @@ -846,7 +846,11 @@ mod shared_struct_with_option_field_ffi_repr {
ExpectedSwiftCode::ContainsAfterTrim(
r#"
public struct SomeStruct {
var field: Optional<UInt8>
public var field: Optional<UInt8>
public init(field: Optional<UInt8>) {
self.field = field
}
@inline(__always)
func intoFfiRepr() -> __swift_bridge__$SomeStruct {
Expand Down
Expand Up @@ -55,6 +55,8 @@ mod generates_struct_to_and_from_ffi_conversions_no_fields {
ExpectedSwiftCode::ContainsAfterTrim(
r#"
public struct SomeStruct {
public init() {}
@inline(__always)
func intoFfiRepr() -> __swift_bridge__$SomeStruct {
__swift_bridge__$SomeStruct(_private: 123)
Expand Down Expand Up @@ -148,7 +150,11 @@ mod generates_struct_to_and_from_ffi_conversions_with_fields {
ExpectedSwiftCode::ContainsAfterTrim(
r#"
struct SomeStruct {
var field: UInt8
public var field: UInt8
public init(field: UInt8) {
self.field = field
}
@inline(__always)
func intoFfiRepr() -> __swift_bridge__$SomeStruct {
Expand Down Expand Up @@ -231,11 +237,19 @@ mod struct_with_primitive_field {
ExpectedSwiftCode::ContainsManyAfterTrim(vec![
r#"
struct SomeStruct {
var field: UInt8
public var field: UInt8
public init(field: UInt8) {
self.field = field
}
"#,
r#"
struct AnotherStruct {
var _0: UInt8
public var _0: UInt8
public init(_0: UInt8) {
self._0 = _0
}
"#,
])
}
Expand Down
1 change: 1 addition & 0 deletions crates/swift-bridge-ir/src/codegen/generate_c_header.rs
@@ -1,5 +1,6 @@
//! Tests can be found in src/codegen/codegen_tests.rs and its submodules.

use crate::bridged_type::shared_struct::StructField;
use crate::bridged_type::{BridgedType, StdLibType, StructFields};
use crate::codegen::CodegenConfig;
use crate::parse::{SharedTypeDeclaration, TypeDeclaration, TypeDeclarations};
Expand Down
128 changes: 93 additions & 35 deletions crates/swift-bridge-ir/src/codegen/generate_swift/shared_struct.rs
@@ -1,3 +1,4 @@
use crate::bridged_type::shared_struct::StructField;
use crate::bridged_type::{BridgedType, SharedStruct, StructFields, StructSwiftRepr, TypePosition};
use crate::SwiftBridgeModule;

Expand All @@ -19,47 +20,27 @@ impl SwiftBridgeModule {
todo!()
}
StructSwiftRepr::Structure => {
let mut fields = match &shared_struct.fields {
StructFields::Named(named) => {
let mut fields = "".to_string();

for field in named.iter() {
let bridged_ty =
BridgedType::new_with_type(&field.ty, &self.types).unwrap();

fields += &format!(
" var {}: {}\n",
field.swift_name_string(),
bridged_ty
.to_swift_type(TypePosition::SharedStructField, &self.types)
);
}

fields
}
let initializer_params = match &shared_struct.fields {
StructFields::Named(named) => self.convert_fields_to_initializer_params(named),
StructFields::Unnamed(unnamed) => {
let mut fields = "".to_string();

for field in unnamed.iter() {
let bridged_ty =
BridgedType::new_with_type(&field.ty, &self.types).unwrap();

fields += &format!(
" var {}: {}\n",
field.swift_name_string(),
bridged_ty
.to_swift_type(TypePosition::SharedStructField, &self.types)
);
}
self.convert_fields_to_initializer_params(unnamed)
}
StructFields::Unit => "".to_string(),
};

fields
let initializer_body = match &shared_struct.fields {
StructFields::Named(named) => self.convert_fields_to_initializer_body(named),
StructFields::Unnamed(unnamed) => {
self.convert_fields_to_initializer_body(unnamed)
}
StructFields::Unit => "".to_string(),
};

if fields.len() > 0 {
fields = format!("\n{}", fields)
}
let fields = match &shared_struct.fields {
StructFields::Named(named) => self.declare_fields(named),
StructFields::Unnamed(unnamed) => self.declare_fields(unnamed),
StructFields::Unit => "".to_string(),
};

let convert_swift_to_ffi_repr =
shared_struct.convert_swift_to_ffi_repr("self", &self.types);
Expand All @@ -70,6 +51,8 @@ impl SwiftBridgeModule {
// struct from our C header typedef that we generate for this struct.
let swift_struct = format!(
r#"public struct {struct_name} {{{fields}
public init({initializer_params}) {{{initializer_body}}}
@inline(__always)
func intoFfiRepr() -> {ffi_repr_name} {{
{convert_swift_to_ffi_repr}
Expand Down Expand Up @@ -101,6 +84,8 @@ extension {option_ffi_name} {{
}}
}}"#,
struct_name = struct_name,
initializer_params = initializer_params,
initializer_body = initializer_body,
fields = fields,
ffi_repr_name = shared_struct.ffi_name_string(),
option_ffi_name = option_ffi_name,
Expand All @@ -112,4 +97,77 @@ extension {option_ffi_name} {{
}
}
}

fn convert_fields_to_initializer_params<'a, T>(
&self,
struct_fields: impl IntoIterator<Item = &'a T>,
) -> String
where
T: StructField + 'a,
{
let mut params = "".to_string();

for field in struct_fields.into_iter() {
let bridged_ty = BridgedType::new_with_type(field.field_type(), &self.types).unwrap();

params += &format!(
"{}: {},",
field.swift_name_string(),
bridged_ty.to_swift_type(TypePosition::SharedStructField, &self.types)
);
}

if !params.is_empty() {
params.pop();
}

params
}

fn convert_fields_to_initializer_body<'a, T>(
&self,
struct_fields: impl IntoIterator<Item = &'a T>,
) -> String
where
T: StructField + 'a,
{
let mut body = "".to_string();

for field in struct_fields.into_iter() {
body += &format!(
" self.{} = {}\n",
field.swift_name_string(),
field.swift_name_string()
);
}

if !body.is_empty() {
body = format!("\n{} ", body);
}

body
}

fn declare_fields<'a, T>(&self, struct_fields: impl IntoIterator<Item = &'a T>) -> String
where
T: StructField + 'a,
{
let mut fields = "".to_string();

for field in struct_fields.into_iter() {
let bridged_ty = BridgedType::new_with_type(field.field_type(), &self.types).unwrap();

fields += &format!(
" public var {}: {}\n",
field.swift_name_string(),
bridged_ty.to_swift_type(TypePosition::SharedStructField, &self.types)
);
}

if !fields.is_empty() {
fields = format!("\n{}", fields)
}

fields
}
}

0 comments on commit a207c24

Please sign in to comment.