How to create global variable for tauri-command functions?

Just started out with tauri. Trying to create some sort of todo app. Also trying to make it internet connection independent so for database i use .txt file

For now i got this:

// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]

use serde::{Deserialize, Serialize};
use std::fs;

fn main() {
    let mut check_items = get_check_items();

    change_check_item_state(&mut check_items, 2);
    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![create_check_item, change_check_item_state, delete_check_item])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

#[derive(Serialize, Deserialize, Debug)]
struct CheckItem {
    id: u128,
    title: String,
    is_completed: bool,
    created_at: String,
}

fn get_check_items() -> Vec<CheckItem> {
    let strings_container = fs::read_to_string("file.txt").unwrap();
    let deserialized_check_items: Vec<CheckItem> =
        serde_json::from_str(&strings_container).unwrap();

    return deserialized_check_items;
}

#[tauri::command]
fn create_check_item(check_items: &mut Vec<CheckItem>) {
    let check_items = 

    check_items.push(CheckItem {
        id: 4,
        title: "from tauri".to_string(),
        is_completed: true,
        created_at: "2024.06.10.07:12".to_string(),
    });

    let serialized_check_items = serde_json::to_string_pretty(&check_items).unwrap();

    let _ = fs::write("file.txt", serialized_check_items);
}

#[tauri::command]
fn delete_check_item(check_items: &mut Vec<CheckItem>, id: u128) {
    check_items.retain(|check_item| check_item.id != id);

    let serialized_check_items = serde_json::to_string_pretty(&check_items).unwrap();

    let _ = fs::write("file.txt", serialized_check_items);
}

#[tauri::command]
fn change_check_item_state(check_items: &mut Vec<CheckItem>, id: u128) {
    let check_item = check_items
        .iter_mut()
        .find(|check_item| check_item.id == id)
        .unwrap();

    check_item.is_completed = !check_item.is_completed;

    let serialized_check_items = serde_json::to_string_pretty(&check_items).unwrap();

    let _ = fs::write("file.txt", serialized_check_items);
}

It does not compile because &Vec<> does not implement CommandArg<, tauri_runtime_wry::Wry>.

This problem solves easily by copying check_items variable, but i don't want to do like this, because for every command i will have to copy full vector.

So i came to the conclusion that I need to create global variable for txt database

Is there any better solution or mine is good? If it's good what should i use to make it work?

PS
I understood that i can't even pass my db through params into functions because this functions will be called in frontend. So my only ways are creating global variable or read file every time i call command from frontend i guess

you can implement any trait on a wrapper around vec using the newtype pattern

if you just want to use global state, std::sync::Mutex should let you do that safely.

1 Like

If you are in higher Rust version, try OnceLock. If you are sure it's thread safe, try UnsafeCell.

If you want to use global db pool:

use std::sync::OnceLock;

fn db() -> DbPool {
    static POOL: OnceLock<DbPool> = OnceLock::new();
    POOL.get_or_init(DbPool::new).clone()
}

Then when you want a DbPool, call db().

PS: clone() is because many DbPool are actually behind an Arc

1 Like

Use managed state in your commands

See the manager method of the Builder

Passing a mutex in the command is still no good, you're right that you need a "global variable". Managed state is how Tauri supports this use case.

If you're using v2, you will need to find the v2 equivalent feature

4 Likes

And if you are curiosity about how Tauri get State in

This is called dependency injection, you can learn it here

1 Like