Reputation: 2755
As a Tauri and Rust beginner, I am trying to develop a basic Tauri 2 application with React and Typescript frontend and Rust backend. I want a basic database manager-like app, where users can enter database connection data, including the DB engine. After the app is connected to the DB, users can execute predefined queries.
I want to support MySQL, PostgreSQL, and SQLite so I use sqlx crate, that supports all of them. The engine is selected by the user during runtime.
I've been struggling for days how to solve this simple task. What I figured out until now is that I need a Tauri state to store the connection and use this later for query execution.
So my main tauri code manages DatabaseManager:
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
//let db_state = Arc::new(Mutex::new(DatabaseState::new()));
tauri::Builder::default()
.setup(|app| {
#[cfg(debug_assertions)]
app.get_webview_window("main").unwrap().open_devtools();
Ok(())
})
.plugin(tauri_plugin_shell::init())
.manage(DatabaseManager::new())
.invoke_handler(tauri::generate_handler![initialize_database_connection])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
My DatabaseManager looks like this:
pub struct DatabaseManager {
pub connection: Option<Arc<Mutex<DatabaseConnection>>>,
}
impl DatabaseManager {
pub fn new() -> Self {
DatabaseManager { connection: None }
}
pub fn get_connection(&self) -> Option<Arc<Mutex<DatabaseConnection>>> {
self.connection.clone()
}
pub fn set_connection(&mut self, connection: DatabaseConnection) {
self.connection = Some(Arc::new(Mutex::new(connection)));
}
}
Where DatabaseConnection is:
pub enum DatabaseConnection {
MySql(Box<MySqlConnection>),
Postgres(Box<PgConnection>),
Sqlite(Box<SqliteConnection>),
}
impl DatabaseConnection {
pub async fn new(
engine: &str,
host: &str,
port: &str,
database: &str,
username: &str,
password: &str,
) -> Result<Self, Box<dyn Error>> {
let connection = match engine {
"mysql" | "mariadb" => {
let connection = MySqlConnection::connect(&format!(
"mysql://{}:{}@{}:{}/{}",
username, password, host, port, database
))
.await?;
DatabaseConnection::MySql(Box::new(connection))
}
"postgres" => {
let connection = PgConnection::connect(&format!(
"postgres://{}:{}@{}:{}/{}",
username, password, host, port, database
))
.await?;
DatabaseConnection::Postgres(Box::new(connection))
}
"sqlite" => {
let connection = SqliteConnection::connect(&format!(
"sqlite://{}",
database
))
.await?;
DatabaseConnection::Sqlite(Box::new(connection))
}
_ => return Err("Unsupported engine type".into()),
};
Ok(connection)
}
}
And the commend to invoke on the frontend looks like this:
#[tauri::command]
pub async fn initialize_database_connection(
engine: String,
host: String,
port: String,
database: String,
username: String,
password: String,
state: State<'_, Arc<Mutex<DatabaseManager>>>,
) -> Result<String, String> {
let connection = DatabaseConnection::new(
&engine, &host, &port, &database, &username, &password,
).await;
match connection {
Ok(conn) => {
let mut state = state.lock().await;
state.set_connection(conn);
Ok("Database connection established".into())
}
Err(e) => Err(format!("Failed to connect: {}", e)),
}
}
The code doesn't compile as future returned by initialize_database_connection is not Send
The full error message is:
error: future cannot be sent between threads safely
--> src\commands\db_commands.rs:7:1
|
7 | #[tauri::command]
| ^^^^^^^^^^^^^^^^^ future returned by `initialize_database_connection` is not `Send`
|
::: src\lib.rs:23:25
|
23 | .invoke_handler(tauri::generate_handler![initialize_database_connection])
| -------------------------------------------------------- in this macro invocation
|
= help: the trait `Send` is not implemented for `dyn std::error::Error`, which is required by `impl Future<Output = Result<std::string::String, std::string::String>>: Send`
note: future is not `Send` as this value is used across an await
--> src\commands\db_commands.rs:25:42
|
19 | let connection = DatabaseConnection::new(
| ---------- has type `Result<DatabaseConnection, Box<dyn std::error::Error>>` which is not `Send`
...
25 | let mut state = state.lock().await;
| ^^^^^ await occurs here, with `connection` maybe used later
note: required by a bound in `ResultFutureTag::future`
--> C:\Users\Balázs\.cargo\registry\src\index.crates.io-6f17d22bba15001f\tauri-2.0.6\src\ipc\command.rs:331:42
|
324 | pub fn future<T, E, F>(
| ------ required by a bound in this associated function
...
331 | F: Future<Output = Result<T, E>> + Send,
| ^^^^ required by this bound in `ResultFutureTag::future`
= note: this error originates in the macro `__cmd__initialize_database_connection` which comes from the expansion of the macro `tauri::generate_handler` (in Nightly builds, run with -Z macro-backtrace for more info)
I don't really know what to do with this error. To be honest I don't even know if the above concept is a good starting point at all.
Upvotes: 0
Views: 181