Unable to invoke tauri command with struct argument from yew frontend

Hi all,

I am new to Rust and I wanted to build a MacOS app to learn the language better.
I decided to use Tauri + Yew to fully emerge on the Rust ecosystem.
My knowledge on web development is quite limited as well.

As a first step, I wanted to introduce a new type that could be shared between the frontend and the backend, thus, I introduced the following struct:

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Person<'a> {
  pub name: &'a str,
  pub age: u8,
}

Then, I introduced a simple command that expects the type as argument:

#[tauri::command]
fn greet_type(person: Person, storage : State<utils::Storage>) -> String {
    println!("Message from rust greet_str{} {}", person.name, person.age);
    format!("Hello,  You've been greeted from Rust!")
}

and I generated a handler for the command:

invoke_handler(tauri::generate_handler![greet, greet_expanded, greet_type])

Finally, I extended the frontend to invoke the command using the following way:

let args = to_value(&Person { name :"Kappa" , age : 0 }).unwrap();
let new_msg = invoke("greet_type", args).await.as_string().unwrap();

However, I see the following error:
"panicked at 'unexpected exception: JsValue("invalid args person for command greet_type: command greet_type missing required key person")'

The error is unclear to me, since the greet_type command does expect a Person as an argument.

As a next experiment, I introduced the following command:

#[tauri::command]
fn greet_expanded(name : &str, age : u8, storage : State<utils::Storage>) -> String {    
    println!("Message from greet_expanded: Hello, {}!", "Kappa");
    format!("Hello, {}! You've been greeted from Rust!", "Kappa")
}

And to my surprise this works..

Could somebody explain this behaviour and how to approach such issues in general?
References I could check/read are also highly appreciated.

Thanks in advance for your help!

I'm unfamiliar with tauri so this is somewhat of a shot in the dark.

This looks like it would take a Person<'_> by value:

#[tauri::command]
fn greet_type(person: Person, storage : State<utils::Storage>) -> String {

But this looks like you're passing in a reference:

let args = to_value(&Person { name :"Kappa" , age : 0 }).unwrap();
//                  ^

Try removing the &.


I also suggest using #![deny(elided_lifetimes_in_paths)] which would require this change:

 #[tauri::command]
-fn greet_type(person: Person, storage : State<utils::Storage>) -> String {
+fn greet_type(person: Person<'_>, storage : State<utils::Storage>) -> String {

Since the function is being processed by tauri's command macro, it's possible this will help your issues, but that's probably a long shot. However I recommend the lint be turned on in general.

From the tauri's guide :

The second parameter of invoke expect a js value with each key corresponding to one argument of the target function.

// In frontend
#[derive(Serialize, Deserialize)]
struct Args<'a> {
  a: &'a str,
  b: u32,
}

invoke("greet", to_value(&Args { a: "hello", b: 42 }).unwrap())

// In backend

#[tauri::command]
fn greet(a: &str, b: u32) {
    //...
}

Here greet have two arguments a and b, so the argument object passed to invoke must have a field a and a field b.

So the correct way to call greet_type is:

#[derive(Serialize, Deserialize)]
struct Args {
  person: Person
}

invoke("greet_type", to_value(&Args { person: Person { ... }}).unwrap())

Much appreciated!

Adding the Args struct indeed did solve my issue!
Thanks

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.