Reputation: 65
in short, Tauri application using Rust backend and Vue frontend. The #[tauri::command] is good and all for pure functions, but what happens if we need to preserve some kind of state in the backend, lets say that of a struct instance? This is my example:
Tauri entry point: main.rs
mod random_number_generator;
use random_number_generator::RandomNumberGenerator;
fn main() {
// instance to manage the vector state
let ng = RandomNumberGenerator::new();
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![generate]) <-- how to access the "generate" of ng?
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
A simple struct and its implementation: random_number_generator.rs.
use rand::Rng;
pub struct RandomNumberGenerator
{
used_numbers_repository: Vec<i64>,
}
impl RandomNumberGenerator {
pub fn new() -> Self {
RandomNumberGenerator {
used_numbers_repository: Vec::new()
}
}
#[tauri::command] <-- this is not allowed
pub fn generate(&mut self, minimum: i64, maximum: i64) -> String {
if minimum < 0 {
return format!("Random number between {} and {}: minimum must be positive.", minimum, maximum);
}
if minimum >= maximum {
return format!("Random number between {} and {}: maximum must be more than minimum.", minimum, maximum);
}
let mut rng = rand::thread_rng();
let mut number: i64 = rng.gen_range(minimum..maximum + 1);
while self.used_numbers_repository.contains(&number) {
number = rng.gen_range(minimum..maximum + 1);
}
self.used_numbers_repository.push(number);
format!("A random number between {} and {}? Here you go: {}.", minimum, maximum, number)
}
#[tauri::command] <-- this is not allowed
pub fn used_numbers(&self) -> String {
self.used_numbers_repository.iter().map( |&id| id.to_string() + " ").collect()
}
}
A simple vue component: RandomGenerator.vue
<script setup lang="ts">
import { ref } from "vue";
import { invoke } from "@tauri-apps/api/tauri";
const generatedMessage = ref("");
const minimum = ref(0);
const maximum = ref(999);
async function generate() {
generatedMessage.value = await invoke("generate", { minimum: minimum.value, maximum: maximum.value })
}
</script>
<template>
<div class="card">
<input type="number" v-model="minimum" placeholder="minimum" />
<input type="number" v-model="maximum" placeholder="maximum" />
<button type="button" @click="generate()">Generate</button>
</div>
<p>{{ generatedMessage }}</p>
</template>
#[tauri::command] is not allowed to struct methods.
Could that be possible without using some other packages, for state management for example?
Upvotes: 1
Views: 758
Reputation: 65
After the accepted answer, the code in main.rs became like this:
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use tauri::State;
use std::sync::Mutex;
use number_generator::RandomNumberGenerator;
mod number_generator;
#[tauri::command]
fn generate(rng: State<Mutex<RandomNumberGenerator>>, minimum: i64, maximum: i64) -> String {
rng.lock().unwrap().generate(minimum, maximum)
}
#[tauri::command]
fn numbers(rng: State<Mutex<RandomNumberGenerator>>) -> String {
rng.lock().unwrap().used_numbers()
}
fn main() {
let rng = Mutex::new(RandomNumberGenerator::new());
tauri::Builder::default()
.manage(rng)
.invoke_handler(tauri::generate_handler![generate, numbers])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
And added in Generate.vue:
...
const generatedMessage = ref("");
const usedNumbers = ref("");
...
generatedMessage.value = await invoke("generate", { minimum: minimum.value, maximum: maximum.value })
usedNumbers.value = await invoke("numbers")
...
<h4>Used numbers</h4>
<p>{{ usedNumbers }}</p>
Upvotes: 1
Reputation: 2632
The
#[tauri::command]
is good and all for pure functions, but what happens if we need to preserve some kind of state in the backend, lets say that of a struct instance?
In Tauri you'd do this not with methods, but with State
handled by your Tauri app with the Manager::manage
method (similar to how endpoints in Rust web frameworks like Actix, Axum or Rocket work). Read more about state management in tauri in this section of the guide.
I.e. this could be a setup that works for Tauri using your RandomNumberGenerator
as state while exposing your generate
command to Vue.js:
use tauri::State;
pub struct RandomNumberGenerator
{
used_numbers_repository: Vec<i64>,
}
#[tauri::command]
pub fn generate(rng: State<RandomNumberGenerator>, minimum: i64, maximum: i64) -> String {
todo!();
}
fn main() {
let ng = RandomNumberGenerator::new();
tauri::Builder::default()
.manage(ng) // manage ng as app state
.invoke_handler(tauri::generate_handler![generate])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
In Vue you'd invoke this command like you do in your example:
await invoke("generate", { minimum: minimum.value, maximum: maximum.value })
Upvotes: 1