swift-bridge 0.1.5

Generate FFI bindings for safe interop between Rust and Swift.
Documentation
use std::path::PathBuf;

fn main() {
    println!("cargo:rerun-if-env-changed=SWIFT_BRIDGE_OUT_DIR");

    if let Ok(out_dir) = std::env::var("SWIFT_BRIDGE_OUT_DIR") {
        let out_dir = PathBuf::from(out_dir);

        let generated = swift_bridge_build::parse_bridges(vec![
            //
            manifest_dir().join("src/std_bridge/string.rs"),
        ]);
        let generated_swift = generated.concat_swift();
        let generated_c = generated.concat_c();

        let core_swift_out = out_dir.join("SwiftBridgeCore.swift");
        let mut swift = core_swift();
        swift += "\n";
        swift += &generated_swift;

        std::fs::write(core_swift_out, swift).unwrap();

        let core_c_header_out = out_dir.join("SwiftBridgeCore.h");
        let mut c_header = core_c_header().to_string();
        c_header += "\n";
        c_header += &generated_c;

        std::fs::write(core_c_header_out, c_header).unwrap();
    }
}

fn core_swift() -> String {
    let mut core_swift = "".to_string();

    core_swift += include_str!("src/std_bridge/string.swift");
    core_swift += include_str!("src/std_bridge/rust_vec.swift");
    core_swift += include_str!("src/std_bridge/option.swift");

    for path in vec![
        "src/std_bridge/string.swift",
        "src/std_bridge/rust_vec.swift",
        "src/std_bridge/option.swift",
    ] {
        println!(
            "cargo:rerun-if-changed={}",
            PathBuf::from(path).to_str().unwrap()
        )
    }

    for (swift_ty, rust_ty, unused_value) in vec![
        ("UInt8", "u8", "123"),
        ("UInt16", "u16", "123"),
        ("UInt32", "u32", "123"),
        ("UInt64", "u64", "123"),
        ("UInt", "usize", "123"),
        //
        ("Int8", "i8", "123"),
        ("Int16", "i16", "123"),
        ("Int32", "i32", "123"),
        ("Int64", "i64", "123"),
        ("Int", "isize", "123"),
        //
        ("Bool", "bool", "false"),
    ] {
        core_swift += &conform_to_vectorizable(swift_ty, rust_ty);
        core_swift += &conform_to_ffi_option(swift_ty, unused_value);
    }

    core_swift
}

fn core_c_header() -> String {
    let mut header = r#"#include <stdint.h>
#include <stdbool.h> 
typedef struct RustStr { uint8_t* const start; uintptr_t len; } RustStr;
typedef struct __private__FfiSlice { void* const start; uintptr_t len; } __private__FfiSlice;
bool _get_option_arg(uint8_t is_some);
void _set_option_arg(uint8_t idx, bool is_some);
bool _get_option_return();
void _set_option_return(bool is_some);
"#
    .to_string();

    for (rust_ty, c_ty) in vec![
        ("u8", "uint8_t"),
        ("u16", "uint16_t"),
        ("u32", "uint32_t"),
        ("u64", "uint64_t"),
        ("usize", "uintptr_t"),
        //
        ("i8", "int8_t"),
        ("i16", "int16_t"),
        ("i32", "int32_t"),
        ("i64", "int64_t"),
        ("isize", "intptr_t"),
        //
        ("bool", "bool"),
    ] {
        header += &vec_of_primitive_headers(rust_ty, c_ty);
    }

    header
}

/// Headers for Vec<T> where T is a primitive such as u8, i32, bool
fn vec_of_primitive_headers(rust_ty: &str, c_ty: &str) -> String {
    format!(
        r#"
void* __swift_bridge__$Vec_{rust_ty}$new();
void __swift_bridge__$Vec_{rust_ty}$_free(void* const vec);
uintptr_t __swift_bridge__$Vec_{rust_ty}$len(void* const vec);
void __swift_bridge__$Vec_{rust_ty}$push(void* const vec, {c_ty} val);
{c_ty} __swift_bridge__$Vec_{rust_ty}$pop(void* const vec);
{c_ty} __swift_bridge__$Vec_{rust_ty}$get(void* const vec, uintptr_t index);
{c_ty} const * __swift_bridge__$Vec_{rust_ty}$as_ptr(void* const vec);
"#,
        rust_ty = rust_ty,
        c_ty = c_ty
    )
}

fn conform_to_vectorizable(swift_ty: &str, rust_ty: &str) -> String {
    format!(
        r#"
extension {swift_ty}: Vectorizable {{
    static func vecOfSelfNew() -> UnsafeMutableRawPointer {{
        __swift_bridge__$Vec_{rust_ty}$new()
    }}
    
    static func vecOfSelfFree(vecPtr: UnsafeMutableRawPointer) {{
        __swift_bridge__$Vec_{rust_ty}$_free(vecPtr)
    }}
    
    static func vecOfSelfPush(vecPtr: UnsafeMutableRawPointer, value: Self) {{
        __swift_bridge__$Vec_{rust_ty}$push(vecPtr, value)
    }}
    
    static func vecOfSelfPop(vecPtr: UnsafeMutableRawPointer) -> Optional<Self> {{
        __swift_bridge__$Vec_{rust_ty}$pop(vecPtr)
    }}
    
    static func vecOfSelfGet(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional<Self> {{
        __swift_bridge__$Vec_{rust_ty}$get(vecPtr, index)
    }}
    
    static func vecOfSelfLen(vecPtr: UnsafeMutableRawPointer) -> UInt {{
        __swift_bridge__$Vec_{rust_ty}$len(vecPtr)
    }}
    
    static func vecOfSelfAsPtr(vecPtr: UnsafeMutableRawPointer) -> UnsafePointer<Self> {{
        UnsafePointer(__swift_bridge__$Vec_{rust_ty}$as_ptr(vecPtr))
    }}

}}
    "#,
        rust_ty = rust_ty,
        swift_ty = swift_ty
    )
}

fn conform_to_ffi_option(swift_ty: &str, unused_value: &str) -> String {
    format!(
        r#"
extension {swift_ty}: FfiOption {{
    static func unusedValue() -> Self {{
        {value}
    }}
}}
    "#,
        swift_ty = swift_ty,
        value = unused_value
    )
}

fn manifest_dir() -> PathBuf {
    PathBuf::from(env!("CARGO_MANIFEST_DIR"))
}