Skip to content

Simplify Command Matching #1157

Closed
Closed
@parker-codes

Description

@parker-codes

The Problem

The main.rs file gets pretty bulky. Yes, things can be split apart into simpler chunks, and that's what I'm proposing - I just think Tauri can assist with some "sugar".
This probably isn't a priority. I just think of how much simpler it could be, especially for beginners to Rust.

Specifically I'd like to point out:

  • deserializing the command from a string
  • matching commands inline
  • move ||
  • destructuring and passing along Tauri-specific values like the callback and error fields

image
Link to code

Preferred Solution

Describe the solution you'd like
A clear and concise description of what you want to happen.

I see a few options:

  1. Create a macro that converts all variant matches into methods of the same name, like GetAllTodos into get_all_todos(). The problem then becomes passing the value into the method. If there are multiple fields in the variant, would they be passed in order? That doesn't seem right. It would be cleanest to pass the command itself, but enum variants aren't real types, so the value can't be destructured and passed in. Here's a playground example of how the command can be passed, but it still needs to be matched on the other end, which detracts from the desired cleanliness.
  2. Deserialize the command into a tuple variant. I think the syntax is quite ugly and confusing, but all of the RFCs have been closed or slow moving. See Types for enum variants rust-lang/rfcs#1450 and Enum variant types rust-lang/rfcs#2593. Also see this Stack Overflow post for clarification on why this is ugly. Serde has some great features that may be able to deserialize this, like flatten. This way would probably be pretty simple.

Both of these together could look like:

#[derive(Deserialize)]
struct GetAllTodos {
    callback: String,
    error: String,
}

#[derive(Deserialize)]
struct CreateTodo {
    title: String,
    callback: String,
    error: String,
}

// some type of flatten?
#[derive(Deserialize)]
#[serde(tag = "cmd", rename_all = "camelCase", flatten)]
pub enum Cmd {
    GetAllTodos(GetAllTodos),

    CreateTodo(CreateTodo),

    // .. more
}

And then potentially an attribute or macro to generate this, if desired:

match command {
    Cmd::GetAllTodos(cmd) => get_all_todos(cmd),
    Cmd::CreateTodo(cmd) => create_todo(cmd),
}

And the usage could look like:

fn get_all_todos(cmd: GetAllTodos) {
    // stuff here
}

Another thing to think about here would be handling responses and return types like via tauri::execute_promise(). Is there any way for these methods and params to be abstracted or hidden as well (like success and error callbacks)? Perhaps the commands or functions could have an attribute like #[tauri(type = "execute_promise")] which acts like a decorator?

Conclusion

This is all to hopefully help simplify how people use it - not necessarily code cleanliness. If there is a "cleaner" structure that can be done without any API changes, perhaps it's worth showcasing that in the examples instead of the very straightforward examples that are there currently.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions