Skip to content
Open
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
5 changes: 4 additions & 1 deletion crates/swift-bridge-ir/src/bridged_type/shared_struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,10 +253,13 @@ pub(crate) struct SharedStruct {
pub derives: StructDerives,
}

#[derive(Clone)]
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct StructDerives {
pub copy: bool,
pub clone: bool,
pub debug: bool,
pub serialize: bool,
pub deserialize: bool,
}

impl SharedStruct {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,141 @@ mod derive_clone_struct {
.test();
}
}

/// Verify that we can derive the `Debug` trait on a transparent struct.
mod derive_debug_struct {
use super::*;

fn bridge_module_tokens() -> TokenStream {
quote! {
#[swift_bridge::bridge]
mod ffi {
#[swift_bridge(swift_repr = "struct")]
#[derive(Debug)]
struct SomeStruct {
field: u8,
}
}
}
}

fn expected_rust_tokens() -> ExpectedRustTokens {
ExpectedRustTokens::ContainsMany(vec![quote! {
#[derive(Debug)]
pub struct SomeStruct {
pub field: u8
}
}])
}

fn expected_swift_code() -> ExpectedSwiftCode {
ExpectedSwiftCode::SkipTest
}

fn expected_c_header() -> ExpectedCHeader {
ExpectedCHeader::SkipTest
}

#[test]
fn generates_struct() {
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();
}
}

/// Verify that we can derive the `serde::Serialize` trait on a transparent struct.
mod derive_serialize_struct {
use super::*;

fn bridge_module_tokens() -> TokenStream {
quote! {
#[swift_bridge::bridge]
mod ffi {
#[swift_bridge(swift_repr = "struct")]
#[derive(serde::Serialize)]
struct SomeStruct {
field: u8,
}
}
}
}

fn expected_rust_tokens() -> ExpectedRustTokens {
ExpectedRustTokens::ContainsMany(vec![quote! {
#[derive(serde::Serialize)]
pub struct SomeStruct {
pub field: u8
}
}])
}

fn expected_swift_code() -> ExpectedSwiftCode {
ExpectedSwiftCode::SkipTest
}

fn expected_c_header() -> ExpectedCHeader {
ExpectedCHeader::SkipTest
}

#[test]
fn generates_struct() {
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();
}
}

/// Verify that we can derive the `serde::Deserialize` trait on a transparent struct.
mod derive_deserialize_struct {
use super::*;

fn bridge_module_tokens() -> TokenStream {
quote! {
#[swift_bridge::bridge]
mod ffi {
#[swift_bridge(swift_repr = "struct")]
#[derive(serde::Deserialize)]
struct SomeStruct {
field: u8,
}
}
}
}

fn expected_rust_tokens() -> ExpectedRustTokens {
ExpectedRustTokens::ContainsMany(vec![quote! {
#[derive(serde::Deserialize)]
pub struct SomeStruct {
pub field: u8
}
}])
}

fn expected_swift_code() -> ExpectedSwiftCode {
ExpectedSwiftCode::SkipTest
}

fn expected_c_header() -> ExpectedCHeader {
ExpectedCHeader::SkipTest
}

#[test]
fn generates_struct() {
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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,15 @@ impl SwiftBridgeModule {
if shared_struct.derives.clone {
derives.push(quote! {Clone});
}
if shared_struct.derives.debug {
derives.push(quote! {Debug});
}
if shared_struct.derives.serialize {
derives.push(quote! {serde::Serialize});
}
if shared_struct.derives.deserialize {
derives.push(quote! {serde::Deserialize});
}

let definition = quote! {
#[derive(#(#derives),*)]
Expand Down
71 changes: 61 additions & 10 deletions crates/swift-bridge-ir/src/parse/parse_struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ impl Default for StructDerives {
StructDerives {
copy: false,
clone: false,
debug: false,
serialize: false,
deserialize: false,
}
}
}
Expand Down Expand Up @@ -133,6 +136,9 @@ impl<'a> SharedStructDeclarationParser<'a> {
match derive.to_token_stream().to_string().as_str() {
"Copy" => attribs.derives.copy = true,
"Clone" => attribs.derives.clone = true,
"Debug" => attribs.derives.debug = true,
"serde :: Serialize" => attribs.derives.serialize = true,
"serde :: Deserialize" => attribs.derives.deserialize = true,
_ => {}
}
}
Expand Down Expand Up @@ -353,20 +359,62 @@ mod tests {

#[derive(Clone)]
struct Bar;
}
};

let module = parse_ok(tokens);
#[derive(serde::Serialize)]
struct FooSerialize;

let ty = module.types.types()[0].unwrap_shared_struct();
#[derive(serde::Deserialize)]
struct FooDeserialize;

assert_eq!(ty.derives.copy, true);
assert_eq!(ty.derives.clone, true);
#[derive(Debug)]
struct FooDebug;

let ty2 = module.types.types()[1].unwrap_shared_struct();
#[derive(Copy, Clone, Debug, serde::Serialize, serde::Deserialize)]
struct FooAll;
}
};

assert_eq!(ty2.derives.copy, false);
assert_eq!(ty2.derives.clone, true);
let module = parse_ok(tokens);

let expected = [
StructDerives {
copy: true,
clone: true,
..Default::default()
},
StructDerives {
clone: true,
..Default::default()
},
StructDerives {
serialize: true,
..Default::default()
},
StructDerives {
deserialize: true,
..Default::default()
},
StructDerives {
debug: true,
..Default::default()
},
StructDerives {
copy: true,
clone: true,
debug: true,
serialize: true,
deserialize: true,
},
];

let actual = module
.types
.types()
.iter()
.map(|val| val.unwrap_shared_struct().derives.clone())
.collect::<Vec<_>>();

assert_eq!(&actual, expected.as_slice());
}

/// Verify that we properly parse multiple comma separated struct attributes.
Expand Down Expand Up @@ -396,7 +444,7 @@ mod tests {
#[swift_bridge::bridge]
mod ffi {
#[swift_bridge(swift_name = "FfiFoo", swift_repr = "class")]
#[derive(Copy, Clone)]
#[derive(Copy, Clone, Debug, serde::Serialize, serde::Deserialize)]
struct Foo {
fied: u8
}
Expand All @@ -410,6 +458,9 @@ mod tests {
assert_eq!(ty.swift_repr, StructSwiftRepr::Class);
assert_eq!(ty.derives.copy, true);
assert_eq!(ty.derives.clone, true);
assert_eq!(ty.derives.debug, true);
assert_eq!(ty.derives.serialize, true);
assert_eq!(ty.derives.deserialize, true);
}

/// Verify that we can parse an `already_defined = "struct"` attribute.
Expand Down
2 changes: 1 addition & 1 deletion crates/swift-bridge-ir/src/parse/type_declarations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ impl TypeDeclarations {
}

pub(crate) fn get_with_type(&self, ty: &Type) -> Option<&TypeDeclaration> {
let ty = match ty.deref() {
let ty = match ty {
Type::Reference(reference) => reference.elem.to_token_stream().to_string(),
Type::Path(path) => path.to_token_stream().to_string(),
_ => todo!("Handle other cases"),
Expand Down
30 changes: 30 additions & 0 deletions crates/swift-integration-tests/src/struct_attributes/derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,34 @@ mod ffi {
struct StructDeriveClone3 {
field: String,
}

#[swift_bridge(swift_repr = "struct")]
#[derive(serde::Debug)]
struct StructDeriveDebug {
field: String,
}

#[swift_bridge(swift_repr = "struct")]
#[derive(serde::Serialize)]
struct StructDeriveSerialize {
field: String,
}

#[swift_bridge(swift_repr = "struct")]
#[derive(serde::Deserialize)]
struct StructDeriveDeserialize {
field: String,
}

#[swift_bridge(swift_repr = "struct")]
#[derive(serde::Serialize, serde::Deserialize)]
struct StructDeriveSerDe {
field: String,
}

#[swift_bridge(swift_repr = "struct")]
#[derive(Copy, Clone, Debug, serde::Serialize, serde::Deserialize)]
struct StructDeriveAll {
field: u8,
}
}