Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support nested fields in get and get_with attributes #92

Open
chinedufn opened this issue May 8, 2022 · 1 comment
Open

Support nested fields in get and get_with attributes #92

chinedufn opened this issue May 8, 2022 · 1 comment
Labels
good first issue Good for newcomers

Comments

@chinedufn
Copy link
Owner

Add support for a dot . notation that lets us expose nested fields.

For example, the following should be possible:

#[swift_bridge::bridge]
mod ffi {
   extern "Rust" {
        type SomeType;
       
        #[swift_bridge(get(field.another_field))]
        fn another_field(&self) -> u16;
   } 
}

pub struct SomeType {
    field: AnotherType
}

struct AnotherType {
    another_field: u16
}
@chinedufn
Copy link
Owner Author

chinedufn commented Jan 27, 2023

Implementation Guide

Vec of fields

Change the field_name to be a Vec<Ident>

pub(crate) field_name: Ident,

pub(crate) field_name: Ident,

Parser Test

Add a get test that has a nested field

/// Verify that we can parse the `get` attribute.
#[test]
fn parses_get_attribute() {
let tokens = quote! {
#[swift_bridge::bridge]
mod ffi {
extern "Rust" {
type Foo;
#[swift_bridge(get(field))]
fn some_function(&self);
#[swift_bridge(get(&field))]
fn some_function_ref(&self);
#[swift_bridge(get(&mut field))]
fn some_function_ref_mut(&mut self);
}
}
};
let module = parse_ok(tokens);
let tests = vec![(false, false), (true, false), (true, true)];
for (idx, (has_ref, has_mut)) in tests.into_iter().enumerate() {
let funcs = &module.functions;
let field = funcs[idx].get_field.as_ref().unwrap().unwrap_direct();
assert_eq!(field.maybe_ref.is_some(), has_ref);
assert_eq!(field.maybe_mut.is_some(), has_mut);
}
}

Add a get_with test that has a nested field

/// Verify that we can parse the `get_with` attribute.
#[test]
fn parses_get_with_attribute() {
let tokens = quote! {
#[swift_bridge::bridge]
mod ffi {
extern "Rust" {
type Foo;
#[swift_bridge(get_with(field = a::b::c))]
fn some_function(&self);
#[swift_bridge(get_with(&field = a::b::c))]
fn some_function_ref(&self);
#[swift_bridge(get_with(&mut field = a::b::c))]
fn some_function_ref_mut(&mut self);
}
}
};
let module = parse_ok(tokens);
let tests = vec![(false, false), (true, false), (true, true)];
for (idx, (has_ref, has_mut)) in tests.into_iter().enumerate() {
let funcs = &module.functions;
let field = funcs[idx].get_field.as_ref().unwrap().unwrap_with();
assert_eq!(field.maybe_ref.is_some(), has_ref);
assert_eq!(field.maybe_mut.is_some(), has_mut);
}
}

Codegen Tests

Add a get test that has a nested field

/// Verify that we can use the get attribute
mod get {
use super::*;
fn bridge_module_tokens() -> TokenStream {
quote! {
#[swift_bridge::bridge]
mod ffi {
extern "Rust" {
type SomeType;
#[swift_bridge(get(field))]
fn some_function(&self) -> u16;
#[swift_bridge(get(&field))]
fn some_function_ref(&self) -> i16;
#[swift_bridge(get(&mut field))]
fn some_function_ref_mut(&mut self) -> u8;
}
}
}
}
fn expected_rust_tokens() -> ExpectedRustTokens {
ExpectedRustTokens::ContainsMany(vec![
quote! {
pub extern "C" fn __swift_bridge__SomeType_some_function(
this: *mut super::SomeType
) -> u16 {
(unsafe { &*this }).field
}
},
quote! {
pub extern "C" fn __swift_bridge__SomeType_some_function_ref(
this: *mut super::SomeType
) -> i16 {
&(unsafe { &*this }).field
}
},
quote! {
pub extern "C" fn __swift_bridge__SomeType_some_function_ref_mut(
this: *mut super::SomeType
) -> u8 {
&mut (unsafe { &mut *this }).field
}
},
])
}
fn expected_swift_code() -> ExpectedSwiftCode {
ExpectedSwiftCode::SkipTest
}
fn expected_c_header() -> ExpectedCHeader {
ExpectedCHeader::SkipTest
}
#[test]
fn get() {
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();
}
}

Add a get_with test that has a nested field

/// Verify that we can use the get attribute
mod get_with {
use super::*;
fn bridge_module_tokens() -> TokenStream {
quote! {
#[swift_bridge::bridge]
mod ffi {
extern "Rust" {
type SomeType;
#[swift_bridge(get_with(field = a::b::c))]
fn some_function(&self);
#[swift_bridge(get_with(&field = a::b::c))]
fn some_function_ref(&self);
#[swift_bridge(get_with(&mut field = a::b::c))]
fn some_function_ref_mut(&mut self);
}
}
}
}
fn expected_rust_tokens() -> ExpectedRustTokens {
ExpectedRustTokens::ContainsMany(vec![
quote! {
pub extern "C" fn __swift_bridge__SomeType_some_function(
this: *mut super::SomeType
) {
super::a::b::c( (unsafe { &*this }).field )
}
},
quote! {
pub extern "C" fn __swift_bridge__SomeType_some_function_ref(
this: *mut super::SomeType
) {
super::a::b::c( & (unsafe { &*this }).field )
}
},
quote! {
pub extern "C" fn __swift_bridge__SomeType_some_function_ref_mut(
this: *mut super::SomeType
) {
super::a::b::c( &mut (unsafe { &mut *this }).field )
}
},
])
}
fn expected_swift_code() -> ExpectedSwiftCode {
ExpectedSwiftCode::SkipTest
}
fn expected_c_header() -> ExpectedCHeader {
ExpectedCHeader::SkipTest
}
#[test]
fn get_with() {
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();
}
}

Integration Tests

Add a get function that has a nested field

#[swift_bridge::bridge]
mod ffi {
extern "Rust" {
type SomeTypeGet;
#[swift_bridge(init)]
fn new() -> SomeTypeGet;
#[swift_bridge(get(my_u8))]
fn my_u8(&self) -> u8;
#[swift_bridge(get(&my_string))]
fn my_string_reference(&self) -> &str;
#[swift_bridge(get(my_opt_static_str))]
fn my_opt_static_str(&self) -> Option<&'static str>;
}
}

Add a get_with function that has a nested field

#[swift_bridge::bridge]
mod ffi {
extern "Rust" {
type SomeTypeGetWith;
// Returns ui_to_i16(self.my_u8)
#[swift_bridge(get_with(my_u8 = u8_to_i16))]
fn my_u8_converted(&self) -> i16;
// Returns Clone::clone(&self.my_string)
#[swift_bridge(get_with(&my_string = Clone::clone))]
fn my_string_cloned(&self) -> String;
// Returns string_to_u32(&self.my_string)
#[swift_bridge(get_with(&my_string = string_to_u32))]
fn my_string_parsed(&self) -> u32;
}
}

Get Tests Passing

Once all of the tests are passing and the crates/swift-integration-tests can successfully compile then this issue is complete.

@chinedufn chinedufn added the good first issue Good for newcomers label Jan 27, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
good first issue Good for newcomers
Projects
None yet
Development

No branches or pull requests

1 participant