Skip to content

Commit

Permalink
add string property for swift class
Browse files Browse the repository at this point in the history
  • Loading branch information
sbag13 committed Aug 27, 2024
1 parent f493331 commit 4d49f70
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 26 deletions.
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ Tool for generating bindings from Rust code.
Rust glue code and bindings in target languages are generated for Rust code that is marked with the `#[ffi]` macro.

```rust
#[ffi]
use hi_ffi::ffi;

#[ffi]
Expand Down
6 changes: 5 additions & 1 deletion src/swift.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,12 @@ typedef float f32;
typedef double f64;
void* {RUST_STRING_DATA_FN_NAME}(void* self);
int {RUST_STRING_LEN_FN_NAME}(void* self);
unsigned int {RUST_STRING_LEN_FN_NAME}(void* self);
void {RUST_STRING_DROP_FN_NAME}(void* self);
void* {SLICE_GET_PTR_FN_NAME}(void* self);
unsigned int {SLICE_GET_LEN_FN_NAME}(void* self);
void {SLICE_DROP_FN_NAME}(void* self);
"#
)
}
Expand Down
67 changes: 54 additions & 13 deletions src/wrapper/swift/class_definition.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use std::fmt::Display;

use super::*;
use crate::wrapper::base::{SLICE_DROP_FN_NAME, SLICE_GET_LEN_FN_NAME, SLICE_GET_PTR_FN_NAME};
use quote::ToTokens;

pub fn gen_class_header(struct_wrapper: &StructWrapper) -> String {
let destructor_extern_fn = &struct_wrapper.drop_ext_fn_name;
let getters_and_setters = gen_getters_and_setters(struct_wrapper);
let getters_and_setters = gen_getters_and_setters_externs(struct_wrapper);
let default_constructor = gen_default_constructor_ext(struct_wrapper);

format!(
Expand All @@ -31,15 +32,15 @@ void* {default_constructor_ext_fn_name}();
}
}

fn gen_getters_and_setters(struct_wrapper: &StructWrapper) -> String {
fn gen_getters_and_setters_externs(struct_wrapper: &StructWrapper) -> String {
struct_wrapper
.fields
.iter()
.map(gen_getter_and_setter)
.map(gen_getter_and_setter_externs)
.collect()
}

fn gen_getter_and_setter(field: &FieldWrapper) -> String {
fn gen_getter_and_setter_externs(field: &FieldWrapper) -> String {
let field_type = &field.field_type.to_token_stream().to_string();
let (getter, setter) = match field {
FieldWrapper {
Expand All @@ -63,20 +64,31 @@ fn gen_getter_and_setter(field: &FieldWrapper) -> String {
}
FieldWrapper {
wrapper_type: FieldWrapperType::String,
setter,
getter,
..
} => {
(None, None) // TODO
}
} => (
getter.as_ref().map(|g| map_string_getter_as_extern_fn(g)),
setter.as_ref().map(|g| map_string_setter_as_extern_fn(g)),
),
};

match (getter, setter) {
(Some(getter), Some(setter)) => format!("{}\n{}", getter, setter),
(Some(getter), None) => getter,
(None, Some(setter)) => setter,
(Some(getter), Some(setter)) => format!("\n{}\n{}", getter, setter),
(Some(getter), None) => format!("\n{}", getter),
(None, Some(setter)) => format!("\n{}", setter),
(None, None) => String::new(),
}
}

fn map_string_getter_as_extern_fn(Getter { extern_fn_name, .. }: &Getter) -> String {
format!("void* {extern_fn_name}(void*);")
}

fn map_string_setter_as_extern_fn(Setter { extern_fn_name, .. }: &Setter) -> String {
format!("void {extern_fn_name}(void*, const char*, unsigned int);")
}

fn map_primitive_getter_as_extern_fn(
Getter { extern_fn_name, .. }: &Getter,
field_type: impl Display,
Expand Down Expand Up @@ -149,10 +161,13 @@ fn gen_property(field: &FieldWrapper) -> String {
}
FieldWrapper {
wrapper_type: FieldWrapperType::String,
setter,
getter,
..
} => {
(None, None) // TODO
}
} => (
getter.as_ref().map(map_string_getter),
setter.as_ref().map(map_string_setter),
),
};

let field_name = &field.field_name;
Expand Down Expand Up @@ -207,3 +222,29 @@ fn map_primitive_setter(Setter { extern_fn_name, .. }: &Setter) -> String {
}}"#,
)
}

fn map_string_getter(Getter { extern_fn_name, .. }: &Getter) -> String {
format!(
r#"
get {{
let slice_ptr = {extern_fn_name}(self.rawPtr())
let c_str_ptr = {SLICE_GET_PTR_FN_NAME}(slice_ptr)
let c_str_len = {SLICE_GET_LEN_FN_NAME}(slice_ptr)
let typed_pointer = c_str_ptr!.assumingMemoryBound(to: UInt8.self)
let bytes: UnsafeBufferPointer<UInt8> = UnsafeBufferPointer(start: typed_pointer, count: Int(c_str_len))
let result = String(bytes: bytes, encoding: .utf8)!
{SLICE_DROP_FN_NAME}(slice_ptr)
return result
}}"#,
)
}

fn map_string_setter(Setter { extern_fn_name, .. }: &Setter) -> String {
format!(
r#"
set {{
let str_ptr = newValue.utf8CString.withUnsafeBufferPointer({{ ptr in return UnsafeMutableRawPointer(mutating: ptr.baseAddress!) }})
{extern_fn_name}(self.rawPtr(), str_ptr, UInt32(newValue.utf8CString.count))
}}"#,
)
}
20 changes: 10 additions & 10 deletions tests/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 11 additions & 1 deletion tests/swift/ModuleTest/Sources/ModuleTest/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import Foundation
import FfiModule

// TODO assertions

func run () {
print("Swift FFI Test Suite")

Expand All @@ -26,6 +28,8 @@ func run () {
let str_result = function_return_string()
print(str_result)

print(combo_function("Combo!", "Don't print me", true))

print("Creating a struct")
let s = TestStruct()

Expand All @@ -37,7 +41,13 @@ func run () {
s.i32_field = 42
print("i32_field: \(s.i32_field)")

print(combo_function("Combo!", "Don't print me", true))
print("Getting string field")
let string_field = s.string_field
print("string_field (should be empty): \(string_field)")

print("Setting string field")
s.string_field = "Hello, World!"
print("updated string_field: \(s.string_field)")
}

run()

0 comments on commit 4d49f70

Please sign in to comment.