perf(tauri): move blocking command work off async runtime
This commit is contained in:
1 parent
a5d9addbf3
commit
348f39ff35
11 files changed
+135
-64
No files matched your search
@@ -1,4 +1,4 @@
|
||||
use std::sync::Arc;
|
||||
use std::{path::PathBuf, sync::Arc};
|
||||
|
||||
use dbx_core::storage::DesktopSettings;
|
||||
use tauri::{AppHandle, Manager, State};
|
||||
@@ -37,9 +37,15 @@ pub async fn save_pinned_tree_node_ids(state: State<'_, Arc<AppState>>, ids: Vec
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn load_native_debug_logs(app: AppHandle) -> Result<String, String> {
|
||||
let log_dir = app.path().app_log_dir().map_err(|e| e.to_string())?;
|
||||
tauri::async_runtime::spawn_blocking(move || load_native_debug_logs_from_dir(log_dir))
|
||||
.await
|
||||
.map_err(|err| err.to_string())?
|
||||
}
|
||||
|
||||
fn load_native_debug_logs_from_dir(log_dir: PathBuf) -> Result<String, String> {
|
||||
const MAX_FILES: usize = 6;
|
||||
const MAX_FILE_BYTES: u64 = 512 * 1024;
|
||||
let log_dir = app.path().app_log_dir().map_err(|e| e.to_string())?;
|
||||
if !log_dir.exists() {
|
||||
return Ok(format!("Native log dir does not exist yet: {}", log_dir.display()));
|
||||
}
|
||||
|
||||
@@ -15,9 +15,13 @@ pub struct QueryResultCsvExportRequest {
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn export_query_result_csv(request: QueryResultCsvExportRequest) -> Result<(), String> {
|
||||
let csv = format_csv(&request.columns, &request.rows);
|
||||
std::fs::write(&request.file_path, format!("\u{FEFF}{csv}")).map_err(|err| err.to_string())
|
||||
pub async fn export_query_result_csv(request: QueryResultCsvExportRequest) -> Result<(), String> {
|
||||
tauri::async_runtime::spawn_blocking(move || {
|
||||
let csv = format_csv(&request.columns, &request.rows);
|
||||
std::fs::write(&request.file_path, format!("\u{FEFF}{csv}")).map_err(|err| err.to_string())
|
||||
})
|
||||
.await
|
||||
.map_err(|err| err.to_string())?
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
|
||||
@@ -12,8 +12,8 @@ pub fn pending_open_sql_files(state: tauri::State<'_, ExternalSqlOpenState>) ->
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn read_external_sql_file(path: String) -> Result<String, String> {
|
||||
read_external_sql_file_content(Path::new(&path))
|
||||
pub async fn read_external_sql_file(path: String) -> Result<String, String> {
|
||||
read_external_sql_file_content_async(PathBuf::from(path)).await
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@@ -62,7 +62,8 @@ pub fn is_sql_file_path(path: &Path) -> bool {
|
||||
path.extension().and_then(|ext| ext.to_str()).map(|ext| ext.eq_ignore_ascii_case("sql")).unwrap_or(false)
|
||||
}
|
||||
|
||||
pub fn read_external_sql_file_content(path: &Path) -> Result<String, String> {
|
||||
#[cfg(test)]
|
||||
fn read_external_sql_file_content(path: &Path) -> Result<String, String> {
|
||||
if !is_sql_file_path(path) {
|
||||
return Err("Only .sql files can be opened this way".to_string());
|
||||
}
|
||||
@@ -70,6 +71,14 @@ pub fn read_external_sql_file_content(path: &Path) -> Result<String, String> {
|
||||
decode_sql_file_bytes(&bytes)
|
||||
}
|
||||
|
||||
async fn read_external_sql_file_content_async(path: PathBuf) -> Result<String, String> {
|
||||
if !is_sql_file_path(&path) {
|
||||
return Err("Only .sql files can be opened this way".to_string());
|
||||
}
|
||||
let bytes = tokio::fs::read(&path).await.map_err(|e| format!("Failed to read SQL file: {e}"))?;
|
||||
decode_sql_file_bytes(&bytes)
|
||||
}
|
||||
|
||||
fn dedupe_paths(paths: Vec<String>) -> Vec<String> {
|
||||
let mut unique = Vec::new();
|
||||
for path in paths {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use std::process::Command;
|
||||
|
||||
/// Read a macOS Keychain generic password by service name.
|
||||
/// Triggers a system authorization dialog (Touch ID / password) for each unique service.
|
||||
#[tauri::command]
|
||||
@@ -12,28 +10,34 @@ pub async fn read_keychain_password(service: String, account: Option<String>) ->
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
let mut cmd = Command::new("security");
|
||||
cmd.args(["find-generic-password", "-s", &service, "-w"]);
|
||||
if let Some(ref acct) = account {
|
||||
cmd.args(["-a", acct]);
|
||||
}
|
||||
tauri::async_runtime::spawn_blocking(move || read_keychain_password_blocking(service, account))
|
||||
.await
|
||||
.map_err(|err| err.to_string())?
|
||||
}
|
||||
}
|
||||
|
||||
let output = cmd.output().map_err(|e| format!("Failed to run security command: {e}"))?;
|
||||
#[cfg(target_os = "macos")]
|
||||
fn read_keychain_password_blocking(service: String, account: Option<String>) -> Result<String, String> {
|
||||
let mut cmd = std::process::Command::new("security");
|
||||
cmd.args(["find-generic-password", "-s", &service, "-w"]);
|
||||
if let Some(ref acct) = account {
|
||||
cmd.args(["-a", acct]);
|
||||
}
|
||||
|
||||
if output.status.success() {
|
||||
let password = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
||||
Ok(password)
|
||||
let output = cmd.output().map_err(|e| format!("Failed to run security command: {e}"))?;
|
||||
|
||||
if output.status.success() {
|
||||
let password = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
||||
Ok(password)
|
||||
} else {
|
||||
// Exit code 44 = user cancelled the authorization dialog
|
||||
let stderr = String::from_utf8_lossy(&output.stderr).to_string();
|
||||
if output.status.code() == Some(44) || stderr.contains("User canceled") {
|
||||
Ok(String::new())
|
||||
} else if stderr.contains("could not be found") || stderr.contains("The specified item could not be found") {
|
||||
Ok(String::new())
|
||||
} else {
|
||||
// Exit code 44 = user cancelled the authorization dialog
|
||||
let stderr = String::from_utf8_lossy(&output.stderr).to_string();
|
||||
if output.status.code() == Some(44) || stderr.contains("User canceled") {
|
||||
Ok(String::new()) // Return empty instead of error for cancelled
|
||||
} else if stderr.contains("could not be found") || stderr.contains("The specified item could not be found")
|
||||
{
|
||||
Ok(String::new()) // No entry found — not an error
|
||||
} else {
|
||||
Err(format!("Keychain read failed: {}", stderr.trim()))
|
||||
}
|
||||
Err(format!("Keychain read failed: {}", stderr.trim()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,11 +31,17 @@ struct NpmLatestPackage {
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn check_mcp_server_status() -> Result<McpServerStatus, String> {
|
||||
let npm_available = command_success("npm", &["--version"]);
|
||||
let node_version = command_stdout("node", &["--version"]).ok().and_then(first_non_empty_line);
|
||||
let current_version = if npm_available { installed_mcp_version() } else { None };
|
||||
let bin_path = locate_mcp_bin();
|
||||
let latest_version = fetch_latest_mcp_version().await.ok();
|
||||
let local_status = tauri::async_runtime::spawn_blocking(|| {
|
||||
let npm_available = command_success("npm", &["--version"]);
|
||||
let node_version = command_stdout("node", &["--version"]).ok().and_then(first_non_empty_line);
|
||||
let current_version = if npm_available { installed_mcp_version() } else { None };
|
||||
let bin_path = locate_mcp_bin();
|
||||
(npm_available, node_version, current_version, bin_path)
|
||||
});
|
||||
let latest_version = fetch_latest_mcp_version();
|
||||
let (local_status, latest_version) = tokio::join!(local_status, latest_version);
|
||||
let (npm_available, node_version, current_version, bin_path) = local_status.map_err(|err| err.to_string())?;
|
||||
let latest_version = latest_version.ok();
|
||||
let update_available = current_version
|
||||
.as_deref()
|
||||
.zip(latest_version.as_deref())
|
||||
@@ -58,7 +64,9 @@ pub async fn check_mcp_server_status() -> Result<McpServerStatus, String> {
|
||||
|
||||
async fn fetch_latest_mcp_version() -> Result<String, String> {
|
||||
let mut builder = reqwest::Client::builder().timeout(Duration::from_secs(10)).user_agent("dbx-mcp-status-checker");
|
||||
if let Some(proxy_url) = dbx_core::update::system_proxy_url() {
|
||||
let proxy_url =
|
||||
tauri::async_runtime::spawn_blocking(dbx_core::update::system_proxy_url).await.map_err(|e| e.to_string())?;
|
||||
if let Some(proxy_url) = proxy_url {
|
||||
let proxy = reqwest::Proxy::all(&proxy_url).map_err(|e| format!("Invalid system proxy URL: {e}"))?;
|
||||
builder = builder.proxy(proxy);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,10 @@ use super::connection::AppState;
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn list_plugins(state: State<'_, Arc<AppState>>) -> Result<Vec<InstalledPlugin>, String> {
|
||||
state.plugins.list_installed()
|
||||
let root_dir = state.plugins.root_dir().to_path_buf();
|
||||
tauri::async_runtime::spawn_blocking(move || dbx_core::plugins::PluginRegistry::new(root_dir).list_installed())
|
||||
.await
|
||||
.map_err(|err| err.to_string())?
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
@@ -31,12 +34,18 @@ pub async fn install_jdbc_plugin_local(
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn uninstall_jdbc_plugin(state: State<'_, Arc<AppState>>) -> Result<JdbcPluginStatus, String> {
|
||||
jdbc::uninstall_jdbc_plugin(state.plugins.root_dir())
|
||||
let root_dir = state.plugins.root_dir().to_path_buf();
|
||||
tauri::async_runtime::spawn_blocking(move || jdbc::uninstall_jdbc_plugin(&root_dir))
|
||||
.await
|
||||
.map_err(|err| err.to_string())?
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn list_jdbc_drivers(state: State<'_, Arc<AppState>>) -> Result<Vec<JdbcDriverInfo>, String> {
|
||||
jdbc::list_jdbc_drivers(state.plugins.root_dir())
|
||||
let root_dir = state.plugins.root_dir().to_path_buf();
|
||||
tauri::async_runtime::spawn_blocking(move || jdbc::list_jdbc_drivers(&root_dir))
|
||||
.await
|
||||
.map_err(|err| err.to_string())?
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
@@ -44,10 +53,16 @@ pub async fn import_jdbc_drivers(
|
||||
state: State<'_, Arc<AppState>>,
|
||||
paths: Vec<String>,
|
||||
) -> Result<Vec<JdbcDriverInfo>, String> {
|
||||
jdbc::import_jdbc_drivers(state.plugins.root_dir(), &paths)
|
||||
let root_dir = state.plugins.root_dir().to_path_buf();
|
||||
tauri::async_runtime::spawn_blocking(move || jdbc::import_jdbc_drivers(&root_dir, &paths))
|
||||
.await
|
||||
.map_err(|err| err.to_string())?
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn delete_jdbc_driver(state: State<'_, Arc<AppState>>, path: String) -> Result<Vec<JdbcDriverInfo>, String> {
|
||||
jdbc::delete_jdbc_driver(state.plugins.root_dir(), &path)
|
||||
let root_dir = state.plugins.root_dir().to_path_buf();
|
||||
tauri::async_runtime::spawn_blocking(move || jdbc::delete_jdbc_driver(&root_dir, &path))
|
||||
.await
|
||||
.map_err(|err| err.to_string())?
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
#[tauri::command]
|
||||
pub fn list_system_fonts() -> Result<Vec<String>, String> {
|
||||
Ok(dbx_core::jdbc::list_system_fonts())
|
||||
pub async fn list_system_fonts() -> Result<Vec<String>, String> {
|
||||
tauri::async_runtime::spawn_blocking(|| Ok(dbx_core::jdbc::list_system_fonts()))
|
||||
.await
|
||||
.map_err(|err| err.to_string())?
|
||||
}
|
||||
@@ -11,19 +11,29 @@ pub struct QueryResultTextExportRequest {
|
||||
}
|
||||
|
||||
impl QueryResultTextExportRequest {
|
||||
fn data(&self) -> QueryResultTextExportData {
|
||||
QueryResultTextExportData { columns: self.columns.clone(), rows: self.rows.clone() }
|
||||
fn into_data(self) -> QueryResultTextExportData {
|
||||
QueryResultTextExportData { columns: self.columns, rows: self.rows }
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn export_query_result_json(request: QueryResultTextExportRequest) -> Result<(), String> {
|
||||
let content = format_json(&request.data())?;
|
||||
std::fs::write(&request.file_path, format!("\u{FEFF}{content}")).map_err(|err| err.to_string())
|
||||
pub async fn export_query_result_json(request: QueryResultTextExportRequest) -> Result<(), String> {
|
||||
tauri::async_runtime::spawn_blocking(move || {
|
||||
let file_path = request.file_path.clone();
|
||||
let content = format_json(&request.into_data())?;
|
||||
std::fs::write(file_path, format!("\u{FEFF}{content}")).map_err(|err| err.to_string())
|
||||
})
|
||||
.await
|
||||
.map_err(|err| err.to_string())?
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn export_query_result_markdown(request: QueryResultTextExportRequest) -> Result<(), String> {
|
||||
let content = format_markdown(&request.data());
|
||||
std::fs::write(&request.file_path, format!("\u{FEFF}{content}")).map_err(|err| err.to_string())
|
||||
pub async fn export_query_result_markdown(request: QueryResultTextExportRequest) -> Result<(), String> {
|
||||
tauri::async_runtime::spawn_blocking(move || {
|
||||
let file_path = request.file_path.clone();
|
||||
let content = format_markdown(&request.into_data());
|
||||
std::fs::write(file_path, format!("\u{FEFF}{content}")).map_err(|err| err.to_string())
|
||||
})
|
||||
.await
|
||||
.map_err(|err| err.to_string())?
|
||||
}
|
||||
@@ -10,6 +10,6 @@ pub async fn check_for_updates() -> Result<UpdateInfo, String> {
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn get_system_proxy_url() -> Option<String> {
|
||||
dbx_core::update::system_proxy_url()
|
||||
pub async fn get_system_proxy_url() -> Option<String> {
|
||||
tauri::async_runtime::spawn_blocking(dbx_core::update::system_proxy_url).await.ok().flatten()
|
||||
}
|
||||
@@ -12,11 +12,15 @@ pub struct QueryResultXlsxExportRequest {
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn export_query_result_xlsx(request: QueryResultXlsxExportRequest) -> Result<(), String> {
|
||||
let workbook = build_xlsx_workbook(&XlsxWorksheetData {
|
||||
sheet_name: request.sheet_name,
|
||||
columns: request.columns,
|
||||
rows: request.rows,
|
||||
})?;
|
||||
std::fs::write(&request.file_path, workbook).map_err(|err| err.to_string())
|
||||
pub async fn export_query_result_xlsx(request: QueryResultXlsxExportRequest) -> Result<(), String> {
|
||||
tauri::async_runtime::spawn_blocking(move || {
|
||||
let workbook = build_xlsx_workbook(&XlsxWorksheetData {
|
||||
sheet_name: request.sheet_name,
|
||||
columns: request.columns,
|
||||
rows: request.rows,
|
||||
})?;
|
||||
std::fs::write(&request.file_path, workbook).map_err(|err| err.to_string())
|
||||
})
|
||||
.await
|
||||
.map_err(|err| err.to_string())?
|
||||
}
|
||||
Reference in new issue
Block a user