-
Notifications
You must be signed in to change notification settings - Fork 113
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
RFC: Add support for exporting functions #249
Conversation
Also might generate something like type foo = (bar: string, baz: number) => boolean |
My thinking is that we need to generate a struct which has fields corresponding to the function's arguments. This struct must derive
|
I have changed |
Still not sure on the names though... Maybe "normal" and "destructured"? |
This attribute doesn't require |
It also requires |
Interesting! I haven't thought about this idea as much as I should have, so I'm still a bit fuzzy on what the concrete use-cases are. For methods, we could include the methods in the the For wasm, users already get TS bindings for their functions, though the parameters will be primitives or For tauri, I'm completely oblivious how this works there, so I'd appreciate if you could give me an overview there. |
Honestly, not entirely sure, mostly Tauri I think.
Most likely something along the lines of const myFunction: Something = <some function implementation>
So tauri exposes a function called The problem is, the definition of declare function invoke<T>(command: string, args: Record<string, unknown>): Promise<T> Where This proposal could potentially allow for: #[ts_rs::tr_rs_fn(args = "inlined", rename_all = "camelCase")] // This "inlined" name is still a WIP
#[tauri::command]
async fn my_command(my_arg: u8) -> String {
String::new()
} // Generated file:
type MyCommand = (args: { myArg: number, }) => Promise<string>
// User's code
const myCommand: MyCommand = args => invoke('my_command', args) // Still needs to type 'my_command' :/
myCommand({ myArg: 42 }) // This gets LSP support :D |
I'm the other way around, I use Tauri a lot, but I have no idea how wasm works lol |
I'm pretty sure methods wouldn't work with this (or at least I've got no idea how to handle them - just discard Anyway, I do agree with you, the use cases for this are pretty narrow, even Tauri would struggle to benefit from it as you'd still have to write the command's name without any intellisense checking for typos. I just saw the issue and decided to play around with it to see if I could come up with something useful, but exporting a Rust function's signature really does seem like a somewhat useless feature outside of slightly helping with Tauri, so I can see this either being dropped or feature gated if added |
Maybe we could start with something tauri-specific? If the only focus was on tauri, we might be able to generate everything a user would want: export function my_command(args: { myArg: number, }): Promise<string> {
return invoke('my_command', args);
} Not sure how to nicely set that up, though. Maybe it'd make sense to have the core functionality (like you've already done) inside |
To illustrate this idea, here's an example of how this could work: Then, |
That might be interesting! We will also need an extra import in the file to do this import { invoke } from "@tauri-apps/api" |
Right! Also, arguments or return types might need to be imported. |
I think the current state of the PR handles that already. Another weird edge case is that |
Another thing, currently, this PR depends on We could have all the logic in |
Like the direction you're going in here, we could implement generating function signatures in So far we're on the same page i think, and now the interesting question is: How do these crates do their "extra stuff"? Instead of these separate crates being big proc-macro colossus, i think it'd be pretty awesome if their proc-macros were minimal, or if they could be implemented completely without proc macros. In Now, for these separate crates to do anything meaningfull, they need a good description of the function signature. In that vein, trait TsFn {
const ASYNC: bool;
const NAME: &'static str;
type ReturnType: TS;
fn arguments() -> impl TypeList;
} With that description in hand, the rest of the work could (at some point ^^) be done completely outside of the proc macro in the "frontend" crates. trait TauriCommand: TsFn {
fn generate() -> String;
}
impl<F> TauriCommand for F where F: TsFn {
fn generate(export: bool, inline_args: bool) -> String {
let sync = if Self::ASYNC { "async " } else { "" };
// ...
let export = if export { "export" } else { "" };
format!("\
{export} {sync} function {name}({args}): {return_type} {{ \
return invoke('{name}'); \
}\
")
}
} All this is definetely a long-term vision, and we'll need to find small steps to get us there. Footnotes
|
… function_bindings
… function_bindings
I'll close this one as it really does need way more thought to be put into it than I initailly thought and I think the CLI should be our priority for now |
This PR aims to add support for exporting function signatures through
ts-rs
. Everything here is a work in progress and probably needs some heavy refactoring.The idea is to create an attribute macro to be used on a function such as
And have the following signature be generated:
...writing this I now realize "named" and "positional" are terrible names, but as I said, work in progress
Closes #92