Skip to content

Commit 6b151f1

Browse files
authored
Adding derive() macro support to SharedStructs (#198)
This commit adds support for `#[derive(Copy, Clone)]` to `SharedStruct`s. ## Example ```rust #[swift_bridge::bridge] mod ffi { #[swift_bridge(swift_repr = "struct")] #[derive(Copy, Clone)] struct Foo { field: u8, } } ```
1 parent 2d60d9e commit 6b151f1

File tree

7 files changed

+250
-25
lines changed

7 files changed

+250
-25
lines changed

crates/swift-bridge-ir/src/bridged_type/shared_struct.rs

+7
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@ pub(crate) struct SharedStruct {
1919
pub fields: StructFields,
2020
pub swift_name: Option<LitStr>,
2121
pub already_declared: bool,
22+
pub derives: StructDerives,
23+
}
24+
25+
#[derive(Clone)]
26+
pub(crate) struct StructDerives {
27+
pub copy: bool,
28+
pub clone: bool,
2229
}
2330

2431
impl SharedStruct {

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

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ mod argument_label_codegen_tests;
3232
mod async_function_codegen_tests;
3333
mod boxed_fnonce_codegen_tests;
3434
mod conditional_compilation_codegen_tests;
35+
mod derive_struct_attribute_codegen_tests;
3536
mod extern_rust_function_opaque_rust_type_argument_codegen_tests;
3637
mod extern_rust_function_opaque_rust_type_return_codegen_tests;
3738
mod extern_rust_method_swift_class_placement_codegen_tests;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
use super::{CodegenTest, ExpectedCHeader, ExpectedRustTokens, ExpectedSwiftCode};
2+
use proc_macro2::TokenStream;
3+
use quote::quote;
4+
5+
/// Verify that we can derive the `Copy` trait on a transparent struct.
6+
mod derive_copy_struct {
7+
use super::*;
8+
9+
fn bridge_module_tokens() -> TokenStream {
10+
quote! {
11+
#[swift_bridge::bridge]
12+
mod ffi {
13+
#[swift_bridge(swift_repr = "struct")]
14+
#[derive(Copy)]
15+
struct SomeStruct {
16+
field: u8,
17+
}
18+
}
19+
}
20+
}
21+
22+
fn expected_rust_tokens() -> ExpectedRustTokens {
23+
ExpectedRustTokens::ContainsMany(vec![quote! {
24+
#[derive(Copy)]
25+
pub struct SomeStruct {
26+
pub field: u8
27+
}
28+
}])
29+
}
30+
31+
fn expected_swift_code() -> ExpectedSwiftCode {
32+
ExpectedSwiftCode::SkipTest
33+
}
34+
35+
fn expected_c_header() -> ExpectedCHeader {
36+
ExpectedCHeader::SkipTest
37+
}
38+
39+
#[test]
40+
fn generates_struct() {
41+
CodegenTest {
42+
bridge_module: bridge_module_tokens().into(),
43+
expected_rust_tokens: expected_rust_tokens(),
44+
expected_swift_code: expected_swift_code(),
45+
expected_c_header: expected_c_header(),
46+
}
47+
.test();
48+
}
49+
}
50+
51+
/// Verify that we can derive the `Clone` trait on a transparent struct.
52+
mod derive_clone_struct {
53+
use super::*;
54+
55+
fn bridge_module_tokens() -> TokenStream {
56+
quote! {
57+
#[swift_bridge::bridge]
58+
mod ffi {
59+
#[swift_bridge(swift_repr = "struct")]
60+
#[derive(Clone)]
61+
struct SomeStruct {
62+
field: u8,
63+
}
64+
}
65+
}
66+
}
67+
68+
fn expected_rust_tokens() -> ExpectedRustTokens {
69+
ExpectedRustTokens::ContainsMany(vec![quote! {
70+
#[derive(Clone)]
71+
pub struct SomeStruct {
72+
pub field: u8
73+
}
74+
}])
75+
}
76+
77+
fn expected_swift_code() -> ExpectedSwiftCode {
78+
ExpectedSwiftCode::SkipTest
79+
}
80+
81+
fn expected_c_header() -> ExpectedCHeader {
82+
ExpectedCHeader::SkipTest
83+
}
84+
85+
#[test]
86+
fn generates_struct() {
87+
CodegenTest {
88+
bridge_module: bridge_module_tokens().into(),
89+
expected_rust_tokens: expected_rust_tokens(),
90+
expected_swift_code: expected_swift_code(),
91+
expected_c_header: expected_c_header(),
92+
}
93+
.test();
94+
}
95+
}

crates/swift-bridge-ir/src/codegen/generate_rust_tokens/shared_struct.rs

+9
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,16 @@ impl SwiftBridgeModule {
9090
}
9191
};
9292

93+
let mut derives: Vec<TokenStream> = vec![];
94+
if shared_struct.derives.copy {
95+
derives.push(quote! {Copy});
96+
}
97+
if shared_struct.derives.clone {
98+
derives.push(quote! {Clone});
99+
}
100+
93101
let definition = quote! {
102+
#[derive(#(#derives),*)]
94103
pub struct #struct_name #struct_fields
95104

96105
#struct_ffi_repr

crates/swift-bridge-ir/src/parse/parse_struct.rs

+109-25
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
use crate::bridged_type::{SharedStruct, StructFields, StructSwiftRepr};
1+
use crate::bridged_type::{
2+
shared_struct::StructDerives, SharedStruct, StructFields, StructSwiftRepr,
3+
};
24
use crate::errors::{ParseError, ParseErrors};
35
use crate::parse::move_input_cursor_to_next_comma;
46
use proc_macro2::Ident;
7+
use quote::ToTokens;
58
use syn::parse::{Parse, ParseStream};
6-
use syn::{ItemStruct, LitStr, Token};
9+
use syn::{ItemStruct, LitStr, Meta, Token};
710

811
pub(crate) struct SharedStructDeclarationParser<'a> {
912
pub item_struct: ItemStruct,
@@ -27,6 +30,16 @@ struct StructAttribs {
2730
swift_repr: Option<(StructSwiftRepr, LitStr)>,
2831
swift_name: Option<LitStr>,
2932
already_declared: bool,
33+
derives: StructDerives,
34+
}
35+
36+
impl Default for StructDerives {
37+
fn default() -> Self {
38+
StructDerives {
39+
copy: false,
40+
clone: false,
41+
}
42+
}
3043
}
3144

3245
struct ParsedAttribs(Vec<StructAttr>);
@@ -81,32 +94,52 @@ impl<'a> SharedStructDeclarationParser<'a> {
8194
let mut attribs = StructAttribs::default();
8295

8396
for attr in item_struct.attrs {
84-
let sections: ParsedAttribs = attr.parse_args()?;
85-
86-
for attr in sections.0 {
87-
match attr {
88-
StructAttr::SwiftRepr((repr, lit_str)) => {
89-
attribs.swift_repr = Some((repr, lit_str));
90-
}
91-
StructAttr::SwiftName(name) => {
92-
attribs.swift_name = Some(name);
97+
let attribute_name = attr.path.to_token_stream().to_string();
98+
99+
match attribute_name.as_str() {
100+
"swift_bridge" => {
101+
let sections: ParsedAttribs = attr.parse_args()?;
102+
103+
for attr in sections.0 {
104+
match attr {
105+
StructAttr::SwiftRepr((repr, lit_str)) => {
106+
attribs.swift_repr = Some((repr, lit_str));
107+
}
108+
StructAttr::SwiftName(name) => {
109+
attribs.swift_name = Some(name);
110+
}
111+
StructAttr::Error(err) => match err {
112+
StructAttrParseError::InvalidSwiftRepr(val) => {
113+
self.errors.push(ParseError::StructInvalidSwiftRepr {
114+
swift_repr_attr_value: val.clone(),
115+
});
116+
attribs.swift_repr = Some((StructSwiftRepr::Structure, val));
117+
}
118+
StructAttrParseError::UnrecognizedAttribute(attribute) => {
119+
self.errors.push(ParseError::StructUnrecognizedAttribute {
120+
attribute,
121+
});
122+
}
123+
},
124+
StructAttr::AlreadyDeclared => {
125+
attribs.already_declared = true;
126+
}
127+
};
93128
}
94-
StructAttr::Error(err) => match err {
95-
StructAttrParseError::InvalidSwiftRepr(val) => {
96-
self.errors.push(ParseError::StructInvalidSwiftRepr {
97-
swift_repr_attr_value: val.clone(),
98-
});
99-
attribs.swift_repr = Some((StructSwiftRepr::Structure, val));
100-
}
101-
StructAttrParseError::UnrecognizedAttribute(attribute) => {
102-
self.errors
103-
.push(ParseError::StructUnrecognizedAttribute { attribute });
129+
}
130+
"derive" => match attr.parse_meta()? {
131+
Meta::List(meta_list) => {
132+
for derive in meta_list.nested {
133+
match derive.to_token_stream().to_string().as_str() {
134+
"Copy" => attribs.derives.copy = true,
135+
"Clone" => attribs.derives.clone = true,
136+
_ => {}
137+
}
104138
}
105-
},
106-
StructAttr::AlreadyDeclared => {
107-
attribs.already_declared = true;
108139
}
109-
};
140+
_ => todo!("Push parse error that derive attribute is in incorrect format"),
141+
},
142+
_ => todo!("Push unsupported attribute error."),
110143
}
111144
}
112145

@@ -137,6 +170,7 @@ impl<'a> SharedStructDeclarationParser<'a> {
137170
fields: StructFields::from_syn_fields(item_struct.fields),
138171
swift_name: attribs.swift_name,
139172
already_declared: attribs.already_declared,
173+
derives: attribs.derives,
140174
};
141175

142176
Ok(shared_struct)
@@ -302,6 +336,33 @@ mod tests {
302336
assert_eq!(ty.swift_name.as_ref().unwrap().value(), "FfiFoo");
303337
}
304338

339+
/// Verify that we parse the derive(...)
340+
#[test]
341+
fn parse_derive_attribute() {
342+
let tokens = quote! {
343+
#[swift_bridge::bridge]
344+
mod ffi {
345+
#[derive(Copy, Clone)]
346+
struct Foo;
347+
348+
#[derive(Clone)]
349+
struct Bar;
350+
}
351+
};
352+
353+
let module = parse_ok(tokens);
354+
355+
let ty = module.types.types()[0].unwrap_shared_struct();
356+
357+
assert_eq!(ty.derives.copy, true);
358+
assert_eq!(ty.derives.clone, true);
359+
360+
let ty2 = module.types.types()[1].unwrap_shared_struct();
361+
362+
assert_eq!(ty2.derives.copy, false);
363+
assert_eq!(ty2.derives.clone, true);
364+
}
365+
305366
/// Verify that we properly parse multiple comma separated struct attributes.
306367
#[test]
307368
fn parses_multiple_struct_attributes() {
@@ -322,6 +383,29 @@ mod tests {
322383
assert_eq!(ty.swift_repr, StructSwiftRepr::Class);
323384
}
324385

386+
/// Verify that we properly parse multiple comma separated struct attributes and derive attributes.
387+
#[test]
388+
fn parses_multiple_struct_attributes_and_derive() {
389+
let tokens = quote! {
390+
#[swift_bridge::bridge]
391+
mod ffi {
392+
#[swift_bridge(swift_name = "FfiFoo", swift_repr = "class")]
393+
#[derive(Copy, Clone)]
394+
struct Foo {
395+
fied: u8
396+
}
397+
}
398+
};
399+
400+
let module = parse_ok(tokens);
401+
402+
let ty = module.types.types()[0].unwrap_shared_struct();
403+
assert_eq!(ty.swift_name.as_ref().unwrap().value(), "FfiFoo");
404+
assert_eq!(ty.swift_repr, StructSwiftRepr::Class);
405+
assert_eq!(ty.derives.copy, true);
406+
assert_eq!(ty.derives.clone, true);
407+
}
408+
325409
/// Verify that we can parse an `already_defined = "struct"` attribute.
326410
#[test]
327411
fn parses_struct_already_declared_attribute() {
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
mod already_declared;
2+
mod derive;
23
mod swift_name;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#[swift_bridge::bridge]
2+
mod ffi {
3+
#[swift_bridge(swift_repr = "struct")]
4+
#[derive(Copy, Clone)]
5+
struct StructDeriveCopy1;
6+
7+
#[swift_bridge(swift_repr = "struct")]
8+
#[derive(Copy, Clone)]
9+
struct StructDeriveCopy2 {
10+
field: u8,
11+
}
12+
13+
#[swift_bridge(swift_repr = "struct")]
14+
#[derive(Clone)]
15+
struct StructDeriveClone1;
16+
17+
#[swift_bridge(swift_repr = "struct")]
18+
#[derive(Clone)]
19+
struct StructDeriveClone2 {
20+
field: u8,
21+
}
22+
23+
#[swift_bridge(swift_repr = "struct")]
24+
#[derive(Clone)]
25+
struct StructDeriveClone3 {
26+
field: String,
27+
}
28+
}

0 commit comments

Comments
 (0)