chinedufn / swift-bridge

swift-bridge facilitates Rust and Swift interop.
https://chinedufn.github.io/swift-bridge
Apache License 2.0
803 stars 58 forks source link

Automatically renaming Swift functions to camelCase without needing to manually annotate them #133

Open NiwakaDev opened 1 year ago

NiwakaDev commented 1 year ago

New Feature

//Rust side 

extern "Rust" {
    fn get_value()->i16;
}
//build.rs

use std::path::PathBuf;

fn main() {
    let out_dir = PathBuf::from("./generated");

    let bridges = vec!["src/lib.rs"];
    for path in &bridges {
        println!("cargo:rerun-if-changed={}", path);
    }

    let support_camel_case = true  // option to convert snake case to camel case

    swift_bridge_build::parse_bridges(bridges, support_camel_case)
        .write_all_concatenated(out_dir, env!("CARGO_PKG_NAME"));
}
//Swift side 

let value = getValue()

Why

the Swift API Design Guidelines recommend using camel case.

chinedufn commented 1 year ago

I think that if we introduce build options they should be as fine grained as possible.

So, if we went this route, we would want something along the lines of

struct BuildConfig {
    rename_exported_rust_fictions: Option<RenameExportedRustFunctions> 
}

enum RenameExportedRustFunctions {
    CamelCase,
    Custom(Box<dyn FnMut(&str) -> String)
}

This is just a quick idea off the top of my head. I haven't thought through what this approach might look like.

I don't think that we should go this route though.


My hesitation with introducing build configuration for this is that since it lives away from the code it will surprise people.

It would mean that if you were reading a bridge module you wouldn't know that all of the functions that you see are getting renamed.

Right now my biggest application that uses swift-bridge currently has 17 bridge modules.

Most swift-bridge users have one bridge module (at least from what I see publicly on GitHub). Having more than a few dozen will probably be extremely uncommon.

So, we might be able to start with just letting you annotate your bridge modules:

#[swift_bridge::bridge(rename_exported_rust_functions = "camelCase"))]
mod ffi {
    extern "Rust" {
        // Shows up on the Swift side as `someFunction`.
        fn some_function();
        // Shows up on the Swift side as `anotherFunction`.
        fn another_function();
    }
}

So, the majority of users would get camel case Swift names by just annotating their one module.

Users with many modules would have to add the annotation once per module, but it should be easy to remember since you'll get reminded when you see on the Swift side that your function names are snake_case.


Given that Swift's API design guidelines recommend camel case, one might ask if we should just default to renaming things to camel case.

Let's pretend that a new swift-bridge user is exporting a Rust function and then going over to the generated Swift code to find it.

If that user is Ctrl/Cmd+F searching for the function, I would expect them to type in the Rust function's original name, not a camel case name. That is to say that I think that the default expectation of a new user is that a function's name shows up exactly as you exported it.

Now, a more experienced user might want all functions to show up with idiomatic Swift names. The majority of these experienced users can just add a single annotation to their single bridge module.

Folks that have lot of bridge modules would need to add multiple annotations (one per module), but I think that's fine for now. If that becomes a hassle for people we can consider a build option.


I mainly use swift-bridge for applications and not a public library. In my use case I quite like keeping my snake_case names since on the Swift side you can easily see that what you're calling is a Rust function. So, I personally wouldn't use this feature.

However, this feature would be super useful for folks that want to generate more idiomatic Swift code for whatever reason (i.e. if you're creating a publicly available Swift Package.. or you're more familiar with Swift than Rust and want to generate Swift code that you find comfortable to use), so I'd love to land something here.


Your thoughts?

NiwakaDev commented 1 year ago

@chinedufn, thank you for your polite response.

So, we might be able to start with just letting you annotate your bridge modules:

Sounds good to me !

If that user is Ctrl/Cmd+F searching for the function, I would expect them to type in the Rust function's original name, not a camel case name. That is to say that I think that the default expectation of a new user is that a function's name shows up exactly as you exported it.

I see… This may be a serious problem. I can't come up with a solution to this.

However, this feature would be super useful for folks that want to generate more idiomatic Swift code for whatever reason (i.e. if you're creating a publicly available Swift Package.. or you're more familiar with Swift than Rust and want to generate Swift code that you find comfortable to use), so I'd love to land something here.

Yes, I really want to use camel case in Swift. However, it may not be necessary to implement this feature with the problems mentioned earlier

Your thoughts?

After reading your opinion, I honestly feel that my suggested features is subtle. If it is to be implemented, it would be better to implement it with the annotation you mentioned.

chinedufn commented 1 year ago

Yes, I really want to use camel case in Swift.

Just in case you didn't know, you can use camel case in Swift today using the swift_name attribute:

https://github.com/chinedufn/swift-bridge/blob/36c90e7d3aed7013575311af810f268faf263b51/book/src/bridge-module/functions/README.md?plain=1#L283-L302

You just have to add the annotation to every function.

I'd like to hold off one adding a way to do it in bulk (i.e. an attribute on the bridge module, or build config, etc) until you or someone else that does a lot of renaming can give more context as to what it feels like to have to add the annotation manually every time and what sort of user experience you want around this.

NiwakaDev commented 1 year ago

Just in case you didn't know, you can use camel case in Swift today using the swift_name attribute:

I didn't know this. Thanks! However, there should be the only one way to convert snake-case to camel-case. Doing this manually might seem a little tedious.

chinedufn commented 1 year ago

Yeah definitely.

Once some people have use cases that need a lot of renaming their feedback will make it easier to decide on if we should solve this problem and, if so, how to solve it (since there are multiple possible solutions).