From 22fcd84b8dec69208fc918065f1276dd1b4cda5a Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Sat, 4 Jan 2025 11:58:52 +1000 Subject: [PATCH] refactor: Clean up code formatting in utils module --- src-tauri/src/api/clipboard.rs | 236 ++++++++++++++++++-------------- src-tauri/src/api/hotkeys.rs | 30 ++-- src-tauri/src/api/tray.rs | 68 ++++----- src-tauri/src/api/updater.rs | 33 ++++- src-tauri/src/db/database.rs | 41 +++--- src-tauri/src/db/history.rs | 151 ++++++++++---------- src-tauri/src/db/settings.rs | 69 +++++----- src-tauri/src/main.rs | 91 ++++++------ src-tauri/src/utils/commands.rs | 86 ++++++------ src-tauri/src/utils/favicon.rs | 2 +- src-tauri/src/utils/keys.rs | 4 +- src-tauri/src/utils/logger.rs | 58 ++++---- src-tauri/src/utils/mod.rs | 2 +- src-tauri/src/utils/types.rs | 8 +- 14 files changed, 485 insertions(+), 394 deletions(-) diff --git a/src-tauri/src/api/clipboard.rs b/src-tauri/src/api/clipboard.rs index 59ac7b1..381a7d9 100644 --- a/src-tauri/src/api/clipboard.rs +++ b/src-tauri/src/api/clipboard.rs @@ -1,14 +1,14 @@ use tauri_plugin_aptabase::EventTracker; -use base64::{engine::general_purpose::STANDARD, Engine}; +use base64::{ engine::general_purpose::STANDARD, Engine }; // use hyperpolyglot; use lazy_static::lazy_static; -use rdev::{simulate, EventType, Key}; +use rdev::{ simulate, EventType, Key }; use regex::Regex; use sqlx::SqlitePool; use std::fs; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::{thread, time::Duration}; -use tauri::{AppHandle, Emitter, Listener, Manager}; +use std::sync::atomic::{ AtomicBool, Ordering }; +use std::{ thread, time::Duration }; +use tauri::{ AppHandle, Emitter, Listener, Manager }; use tauri_plugin_clipboard::Clipboard; use tokio::runtime::Runtime as TokioRuntime; use url::Url; @@ -17,7 +17,7 @@ use uuid::Uuid; use crate::db; use crate::utils::commands::get_app_info; use crate::utils::favicon::fetch_favicon_as_base64; -use crate::utils::types::{ContentType, HistoryItem}; +use crate::utils::types::{ ContentType, HistoryItem }; lazy_static! { static ref IS_PROGRAMMATIC_PASTE: AtomicBool = AtomicBool::new(false); @@ -27,16 +27,14 @@ lazy_static! { pub async fn write_and_paste( app_handle: AppHandle, content: String, - content_type: String, + content_type: String ) -> Result<(), String> { let clipboard = app_handle.state::(); match content_type.as_str() { "text" => clipboard.write_text(content).map_err(|e| e.to_string())?, "image" => { - clipboard - .write_image_base64(content) - .map_err(|e| e.to_string())?; + clipboard.write_image_base64(content).map_err(|e| e.to_string())?; } "files" => { clipboard @@ -44,11 +42,13 @@ pub async fn write_and_paste( content .split(", ") .map(|file| file.to_string()) - .collect::>(), + .collect::>() ) .map_err(|e| e.to_string())?; } - _ => return Err("Unsupported content type".to_string()), + _ => { + return Err("Unsupported content type".to_string()); + } } IS_PROGRAMMATIC_PASTE.store(true, Ordering::SeqCst); @@ -65,7 +65,7 @@ pub async fn write_and_paste( EventType::KeyPress(modifier_key), EventType::KeyPress(Key::KeyV), EventType::KeyRelease(Key::KeyV), - EventType::KeyRelease(modifier_key), + EventType::KeyRelease(modifier_key) ]; for event in events { @@ -81,9 +81,12 @@ pub async fn write_and_paste( IS_PROGRAMMATIC_PASTE.store(false, Ordering::SeqCst); }); - let _ = app_handle.track_event("clipboard_paste", Some(serde_json::json!({ + let _ = app_handle.track_event( + "clipboard_paste", + Some(serde_json::json!({ "content_type": content_type - }))); + })) + ); Ok(()) } @@ -92,79 +95,92 @@ pub fn setup(app: &AppHandle) { let app_handle = app.clone(); let runtime = TokioRuntime::new().expect("Failed to create Tokio runtime"); - app_handle.clone().listen( - "plugin:clipboard://clipboard-monitor/update", - move |_event| { - let app_handle = app_handle.clone(); - runtime.block_on(async move { - if IS_PROGRAMMATIC_PASTE.load(Ordering::SeqCst) { - return; - } + app_handle.clone().listen("plugin:clipboard://clipboard-monitor/update", move |_event| { + let app_handle = app_handle.clone(); + runtime.block_on(async move { + if IS_PROGRAMMATIC_PASTE.load(Ordering::SeqCst) { + return; + } - let clipboard = app_handle.state::(); - let available_types = clipboard.available_types().unwrap(); + let clipboard = app_handle.state::(); + let available_types = clipboard.available_types().unwrap(); - let (app_name, app_icon) = get_app_info(); + let (app_name, app_icon) = get_app_info(); - match get_pool(&app_handle).await { - Ok(pool) => { - if available_types.image { - println!("Handling image change"); - if let Ok(image_data) = clipboard.read_image_base64() { - let file_path = save_image_to_file(&app_handle, &image_data) - .await - .map_err(|e| e.to_string()) - .unwrap_or_else(|e| e); + match get_pool(&app_handle).await { + Ok(pool) => { + if available_types.image { + println!("Handling image change"); + if let Ok(image_data) = clipboard.read_image_base64() { + let file_path = save_image_to_file(&app_handle, &image_data).await + .map_err(|e| e.to_string()) + .unwrap_or_else(|e| e); + let _ = db::history::add_history_item( + app_handle.clone(), + pool, + HistoryItem::new( + app_name, + ContentType::Image, + file_path, + None, + app_icon, + None + ) + ).await; + } + } else if available_types.files { + println!("Handling files change"); + if let Ok(files) = clipboard.read_files() { + for file in files { let _ = db::history::add_history_item( app_handle.clone(), - pool, - HistoryItem::new(app_name, ContentType::Image, file_path, None, app_icon, None) + pool.clone(), + HistoryItem::new( + app_name.clone(), + ContentType::File, + file, + None, + app_icon.clone(), + None + ) ).await; } - } else if available_types.files { - println!("Handling files change"); - if let Ok(files) = clipboard.read_files() { - for file in files { + } + } else if available_types.text { + println!("Handling text change"); + if let Ok(text) = clipboard.read_text() { + let text = text.to_string(); + let url_regex = Regex::new( + r"^https?://(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&//=]*)$" + ).unwrap(); + + if url_regex.is_match(&text) { + if let Ok(url) = Url::parse(&text) { + let favicon = match fetch_favicon_as_base64(url).await { + Ok(Some(f)) => Some(f), + _ => None, + }; + let _ = db::history::add_history_item( app_handle.clone(), - pool.clone(), + pool, HistoryItem::new( - app_name.clone(), - ContentType::File, - file, - None, - app_icon.clone(), + app_name, + ContentType::Link, + text, + favicon, + app_icon, None - ), + ) ).await; } - } - } else if available_types.text { - println!("Handling text change"); - if let Ok(text) = clipboard.read_text() { - let text = text.to_string(); - let url_regex = Regex::new(r"^https?://(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&//=]*)$").unwrap(); + } else { + if text.is_empty() { + return; + } - if url_regex.is_match(&text) { - if let Ok(url) = Url::parse(&text) { - let favicon = match fetch_favicon_as_base64(url).await { - Ok(Some(f)) => Some(f), - _ => None, - }; - - let _ = db::history::add_history_item( - app_handle.clone(), - pool, - HistoryItem::new(app_name, ContentType::Link, text, favicon, app_icon, None) - ).await; - } - } else { - if text.is_empty() { - return; - } - - // Temporarily disabled code detection - /*if let Some(detection) = hyperpolyglot::detect_from_text(&text) { + // Temporarily disabled code detection + /*if let Some(detection) = hyperpolyglot::detect_from_text(&text) { let language = match detection { hyperpolyglot::Detection::Heuristics(lang) => lang.to_string(), _ => detection.language().to_string(), @@ -175,43 +191,61 @@ pub fn setup(app: &AppHandle) { HistoryItem::new(app_name, ContentType::Code, text, None, app_icon, Some(language)) ).await; } else*/ if crate::utils::commands::detect_color(&text) { - let _ = db::history::add_history_item( - app_handle.clone(), - pool, - HistoryItem::new(app_name, ContentType::Color, text, None, app_icon, None) - ).await; - } else { - let _ = db::history::add_history_item( - app_handle.clone(), - pool, - HistoryItem::new(app_name, ContentType::Text, text.clone(), None, app_icon, None) - ).await; - } + let _ = db::history::add_history_item( + app_handle.clone(), + pool, + HistoryItem::new( + app_name, + ContentType::Color, + text, + None, + app_icon, + None + ) + ).await; + } else { + let _ = db::history::add_history_item( + app_handle.clone(), + pool, + HistoryItem::new( + app_name, + ContentType::Text, + text.clone(), + None, + app_icon, + None + ) + ).await; } } - } else { - println!("Unknown clipboard content type"); } - } - Err(e) => { - println!("Failed to get database pool: {}", e); + } else { + println!("Unknown clipboard content type"); } } + Err(e) => { + println!("Failed to get database pool: {}", e); + } + } - let _ = app_handle.emit("clipboard-content-updated", ()); - let _ = app_handle.track_event("clipboard_copied", Some(serde_json::json!({ + let _ = app_handle.emit("clipboard-content-updated", ()); + let _ = app_handle.track_event( + "clipboard_copied", + Some( + serde_json::json!({ "content_type": if available_types.image { "image" } else if available_types.files { "files" } else if available_types.text { "text" } else { "unknown" } - }))); - }); - }, - ); + }) + ) + ); + }); + }); } async fn get_pool( - app_handle: &AppHandle, + app_handle: &AppHandle ) -> Result, Box> { Ok(app_handle.state::()) } @@ -219,9 +253,7 @@ async fn get_pool( #[tauri::command] pub fn start_monitor(app_handle: AppHandle) -> Result<(), String> { let clipboard = app_handle.state::(); - clipboard - .start_monitor(app_handle.clone()) - .map_err(|e| e.to_string())?; + clipboard.start_monitor(app_handle.clone()).map_err(|e| e.to_string())?; app_handle .emit("plugin:clipboard://clipboard-monitor/status", true) .map_err(|e| e.to_string())?; @@ -230,7 +262,7 @@ pub fn start_monitor(app_handle: AppHandle) -> Result<(), String> { async fn save_image_to_file( app_handle: &AppHandle, - base64_data: &str, + base64_data: &str ) -> Result> { let app_data_dir = app_handle.path().app_data_dir().unwrap(); let images_dir = app_data_dir.join("images"); diff --git a/src-tauri/src/api/hotkeys.rs b/src-tauri/src/api/hotkeys.rs index 69ee336..a0084eb 100644 --- a/src-tauri/src/api/hotkeys.rs +++ b/src-tauri/src/api/hotkeys.rs @@ -1,13 +1,15 @@ use crate::utils::commands::center_window_on_current_monitor; use crate::utils::keys::KeyCode; use global_hotkey::{ - hotkey::{Code, HotKey, Modifiers}, - GlobalHotKeyEvent, GlobalHotKeyManager, HotKeyState, + hotkey::{ Code, HotKey, Modifiers }, + GlobalHotKeyEvent, + GlobalHotKeyManager, + HotKeyState, }; use lazy_static::lazy_static; use std::str::FromStr; use std::sync::Mutex; -use tauri::{AppHandle, Listener, Manager}; +use tauri::{ AppHandle, Listener, Manager }; use tauri_plugin_aptabase::EventTracker; lazy_static! { @@ -110,10 +112,18 @@ fn parse_hotkey(shortcut: &[String]) -> Result modifiers |= Modifiers::CONTROL, - "AltLeft" => modifiers |= Modifiers::ALT, - "ShiftLeft" => modifiers |= Modifiers::SHIFT, - "MetaLeft" => modifiers |= Modifiers::META, + "ControlLeft" => { + modifiers |= Modifiers::CONTROL; + } + "AltLeft" => { + modifiers |= Modifiers::ALT; + } + "ShiftLeft" => { + modifiers |= Modifiers::SHIFT; + } + "MetaLeft" => { + modifiers |= Modifiers::META; + } key => { code = Some(Code::from(KeyCode::from_str(key)?)); } @@ -144,8 +154,10 @@ fn handle_hotkey_event(app_handle: &AppHandle) { let _ = app_handle.track_event( "hotkey_triggered", - Some(serde_json::json!({ + Some( + serde_json::json!({ "action": if window.is_visible().unwrap() { "hide" } else { "show" } - })), + }) + ) ); } diff --git a/src-tauri/src/api/tray.rs b/src-tauri/src/api/tray.rs index 7e579a4..2ae2bfa 100644 --- a/src-tauri/src/api/tray.rs +++ b/src-tauri/src/api/tray.rs @@ -1,16 +1,15 @@ -use tauri::{ - menu::{MenuBuilder, MenuItemBuilder}, - tray::TrayIconBuilder, - Emitter, Manager, -}; +use tauri::{ menu::{ MenuBuilder, MenuItemBuilder }, tray::TrayIconBuilder, Emitter, Manager }; use tauri_plugin_aptabase::EventTracker; pub fn setup(app: &mut tauri::App) -> Result<(), Box> { let window = app.get_webview_window("main").unwrap(); let is_visible = window.is_visible().unwrap(); - let _ = app.track_event("tray_toggle", Some(serde_json::json!({ + let _ = app.track_event( + "tray_toggle", + Some(serde_json::json!({ "action": if is_visible { "hide" } else { "show" } - }))); + })) + ); let icon_bytes = include_bytes!("../../icons/Square71x71Logo.png"); let icon = tauri::image::Image::from_bytes(icon_bytes).unwrap(); @@ -18,37 +17,42 @@ pub fn setup(app: &mut tauri::App) -> Result<(), Box> { let _tray = TrayIconBuilder::new() .menu( &MenuBuilder::new(app) - .items(&[&MenuItemBuilder::with_id("app_name", "Qopy") - .enabled(false) - .build(app)?]) + .items(&[&MenuItemBuilder::with_id("app_name", "Qopy").enabled(false).build(app)?]) .items(&[&MenuItemBuilder::with_id("show", "Show/Hide").build(app)?]) .items(&[&MenuItemBuilder::with_id("settings", "Settings").build(app)?]) .items(&[&MenuItemBuilder::with_id("quit", "Quit").build(app)?]) - .build()?, + .build()? ) - .on_menu_event(move |_app, event| match event.id().as_ref() { - "quit" => { - let _ = _app.track_event("app_quit", None); - std::process::exit(0); - } - "show" => { - let _ = _app.track_event("tray_toggle", Some(serde_json::json!({ - "action": if is_visible { "hide" } else { "show" } - }))); - let is_visible = window.is_visible().unwrap(); - if is_visible { - window.hide().unwrap(); - } else { - window.show().unwrap(); - window.set_focus().unwrap(); + .on_menu_event(move |_app, event| { + match event.id().as_ref() { + "quit" => { + let _ = _app.track_event("app_quit", None); + std::process::exit(0); } - window.emit("main_route", ()).unwrap(); + "show" => { + let _ = _app.track_event( + "tray_toggle", + Some( + serde_json::json!({ + "action": if is_visible { "hide" } else { "show" } + }) + ) + ); + let is_visible = window.is_visible().unwrap(); + if is_visible { + window.hide().unwrap(); + } else { + window.show().unwrap(); + window.set_focus().unwrap(); + } + window.emit("main_route", ()).unwrap(); + } + "settings" => { + let _ = _app.track_event("tray_settings", None); + window.emit("settings", ()).unwrap(); + } + _ => (), } - "settings" => { - let _ = _app.track_event("tray_settings", None); - window.emit("settings", ()).unwrap(); - } - _ => (), }) .icon(icon) .build(app)?; diff --git a/src-tauri/src/api/updater.rs b/src-tauri/src/api/updater.rs index a5e16f9..f3d4cb0 100644 --- a/src-tauri/src/api/updater.rs +++ b/src-tauri/src/api/updater.rs @@ -1,5 +1,5 @@ -use tauri::{async_runtime, AppHandle}; -use tauri_plugin_dialog::{DialogExt, MessageDialogButtons, MessageDialogKind}; +use tauri::{ async_runtime, AppHandle }; +use tauri_plugin_dialog::{ DialogExt, MessageDialogButtons, MessageDialogKind }; use tauri_plugin_updater::UpdaterExt; pub async fn check_for_updates(app: AppHandle) { @@ -21,18 +21,35 @@ pub async fn check_for_updates(app: AppHandle) { app.dialog() .message(msg) .title("Qopy Update Available") - .buttons(MessageDialogButtons::OkCancelCustom(String::from("Install"), String::from("Cancel"))) + .buttons( + MessageDialogButtons::OkCancelCustom( + String::from("Install"), + String::from("Cancel") + ) + ) .show(move |response| { if !response { return; } async_runtime::spawn(async move { - match update.download_and_install(|_, _| {}, || {}).await { + match + update.download_and_install( + |_, _| {}, + || {} + ).await + { Ok(_) => { app.dialog() - .message("Update installed successfully. The application needs to restart to apply the changes.") + .message( + "Update installed successfully. The application needs to restart to apply the changes." + ) .title("Qopy Update Installed") - .buttons(MessageDialogButtons::OkCancelCustom(String::from("Restart"), String::from("Cancel"))) + .buttons( + MessageDialogButtons::OkCancelCustom( + String::from("Restart"), + String::from("Cancel") + ) + ) .show(move |response| { if response { app.restart(); @@ -42,7 +59,9 @@ pub async fn check_for_updates(app: AppHandle) { Err(e) => { println!("Error installing new update: {:?}", e); app.dialog() - .message("Failed to install new update. The new update can be downloaded from Github") + .message( + "Failed to install new update. The new update can be downloaded from Github" + ) .kind(MessageDialogKind::Error) .show(|_| {}); } diff --git a/src-tauri/src/db/database.rs b/src-tauri/src/db/database.rs index fb7ce4f..264d308 100644 --- a/src-tauri/src/db/database.rs +++ b/src-tauri/src/db/database.rs @@ -1,5 +1,5 @@ -use include_dir::{include_dir, Dir}; -use sqlx::sqlite::{SqlitePool, SqlitePoolOptions}; +use include_dir::{ include_dir, Dir }; +use sqlx::sqlite::{ SqlitePool, SqlitePoolOptions }; use std::fs; use tauri::Manager; use tokio::runtime::Runtime as TokioRuntime; @@ -25,8 +25,7 @@ pub fn setup(app: &mut tauri::App) -> Result<(), Box> { let pool = rt.block_on(async { SqlitePoolOptions::new() .max_connections(5) - .connect(&db_url) - .await + .connect(&db_url).await .expect("Failed to create pool") }); @@ -49,24 +48,22 @@ pub fn setup(app: &mut tauri::App) -> Result<(), Box> { } async fn apply_migrations(pool: &SqlitePool) -> Result<(), Box> { - sqlx::query( - "CREATE TABLE IF NOT EXISTS schema_version ( + sqlx + ::query( + "CREATE TABLE IF NOT EXISTS schema_version ( version INTEGER PRIMARY KEY, applied_at DATETIME DEFAULT CURRENT_TIMESTAMP - );", - ) - .execute(pool) - .await?; + );" + ) + .execute(pool).await?; - let current_version: Option = - sqlx::query_scalar("SELECT MAX(version) FROM schema_version") - .fetch_one(pool) - .await?; + let current_version: Option = sqlx + ::query_scalar("SELECT MAX(version) FROM schema_version") + .fetch_one(pool).await?; let current_version = current_version.unwrap_or(0); - let mut migration_files: Vec<(i64, &str)> = MIGRATIONS_DIR - .files() + let mut migration_files: Vec<(i64, &str)> = MIGRATIONS_DIR.files() .filter_map(|file| { let file_name = file.path().file_name()?.to_str()?; if file_name.ends_with(".sql") && file_name.starts_with("v") { @@ -93,16 +90,16 @@ async fn apply_migrations(pool: &SqlitePool) -> Result<(), Box Result<(), Box> { - let id: String = thread_rng() - .sample_iter(&Alphanumeric) - .take(16) - .map(char::from) - .collect(); + let id: String = thread_rng().sample_iter(&Alphanumeric).take(16).map(char::from).collect(); - sqlx::query( - "INSERT INTO history (id, source, content_type, content, timestamp) VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP)" - ) - .bind(id) - .bind("System") - .bind("text") - .bind("Welcome to your clipboard history!") - .execute(pool) - .await?; + sqlx + ::query( + "INSERT INTO history (id, source, content_type, content, timestamp) VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP)" + ) + .bind(id) + .bind("System") + .bind("text") + .bind("Welcome to your clipboard history!") + .execute(pool).await?; Ok(()) } #[tauri::command] pub async fn get_history(pool: tauri::State<'_, SqlitePool>) -> Result, String> { - let rows = sqlx::query( - "SELECT id, source, source_icon, content_type, content, favicon, timestamp, language FROM history ORDER BY timestamp DESC", - ) - .fetch_all(&*pool) - .await - .map_err(|e| e.to_string())?; + let rows = sqlx + ::query( + "SELECT id, source, source_icon, content_type, content, favicon, timestamp, language FROM history ORDER BY timestamp DESC" + ) + .fetch_all(&*pool).await + .map_err(|e| e.to_string())?; let items = rows .iter() @@ -56,50 +52,53 @@ pub async fn get_history(pool: tauri::State<'_, SqlitePool>) -> Result, - item: HistoryItem, + item: HistoryItem ) -> Result<(), String> { let (id, source, source_icon, content_type, content, favicon, timestamp, language) = item.to_row(); - let existing = sqlx::query("SELECT id FROM history WHERE content = ? AND content_type = ?") + let existing = sqlx + ::query("SELECT id FROM history WHERE content = ? AND content_type = ?") .bind(&content) .bind(&content_type) - .fetch_optional(&*pool) - .await + .fetch_optional(&*pool).await .map_err(|e| e.to_string())?; match existing { Some(_) => { - sqlx::query( - "UPDATE history SET timestamp = strftime('%Y-%m-%dT%H:%M:%f+00:00', 'now') WHERE content = ? AND content_type = ?" - ) - .bind(&content) - .bind(&content_type) - .execute(&*pool) - .await - .map_err(|e| e.to_string())?; + sqlx + ::query( + "UPDATE history SET timestamp = strftime('%Y-%m-%dT%H:%M:%f+00:00', 'now') WHERE content = ? AND content_type = ?" + ) + .bind(&content) + .bind(&content_type) + .execute(&*pool).await + .map_err(|e| e.to_string())?; } None => { - sqlx::query( - "INSERT INTO history (id, source, source_icon, content_type, content, favicon, timestamp, language) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" - ) - .bind(id) - .bind(source) - .bind(source_icon) - .bind(content_type) - .bind(content) - .bind(favicon) - .bind(timestamp) - .bind(language) - .execute(&*pool) - .await - .map_err(|e| e.to_string())?; + sqlx + ::query( + "INSERT INTO history (id, source, source_icon, content_type, content, favicon, timestamp, language) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" + ) + .bind(id) + .bind(source) + .bind(source_icon) + .bind(content_type) + .bind(content) + .bind(favicon) + .bind(timestamp) + .bind(language) + .execute(&*pool).await + .map_err(|e| e.to_string())?; } } - let _ = app_handle.track_event("history_item_added", Some(serde_json::json!({ + let _ = app_handle.track_event( + "history_item_added", + Some(serde_json::json!({ "content_type": item.content_type.to_string() - }))); + })) + ); Ok(()) } @@ -107,16 +106,16 @@ pub async fn add_history_item( #[tauri::command] pub async fn search_history( pool: tauri::State<'_, SqlitePool>, - query: String, + query: String ) -> Result, String> { let query = format!("%{}%", query); - let rows = sqlx::query( - "SELECT id, source, source_icon, content_type, content, favicon, timestamp, language FROM history WHERE content LIKE ? ORDER BY timestamp DESC" - ) - .bind(query) - .fetch_all(&*pool) - .await - .map_err(|e| e.to_string())?; + let rows = sqlx + ::query( + "SELECT id, source, source_icon, content_type, content, favicon, timestamp, language FROM history WHERE content LIKE ? ORDER BY timestamp DESC" + ) + .bind(query) + .fetch_all(&*pool).await + .map_err(|e| e.to_string())?; let items = rows .iter() @@ -139,16 +138,16 @@ pub async fn search_history( pub async fn load_history_chunk( pool: tauri::State<'_, SqlitePool>, offset: i64, - limit: i64, + limit: i64 ) -> Result, String> { - let rows = sqlx::query( - "SELECT id, source, source_icon, content_type, content, favicon, timestamp, language FROM history ORDER BY timestamp DESC LIMIT ? OFFSET ?" - ) - .bind(limit) - .bind(offset) - .fetch_all(&*pool) - .await - .map_err(|e| e.to_string())?; + let rows = sqlx + ::query( + "SELECT id, source, source_icon, content_type, content, favicon, timestamp, language FROM history ORDER BY timestamp DESC LIMIT ? OFFSET ?" + ) + .bind(limit) + .bind(offset) + .fetch_all(&*pool).await + .map_err(|e| e.to_string())?; let items = rows .iter() @@ -171,12 +170,12 @@ pub async fn load_history_chunk( pub async fn delete_history_item( app_handle: tauri::AppHandle, pool: tauri::State<'_, SqlitePool>, - id: String, + id: String ) -> Result<(), String> { - sqlx::query("DELETE FROM history WHERE id = ?") + sqlx + ::query("DELETE FROM history WHERE id = ?") .bind(id) - .execute(&*pool) - .await + .execute(&*pool).await .map_err(|e| e.to_string())?; let _ = app_handle.track_event("history_item_deleted", None); @@ -189,9 +188,9 @@ pub async fn clear_history( app_handle: tauri::AppHandle, pool: tauri::State<'_, SqlitePool> ) -> Result<(), String> { - sqlx::query("DELETE FROM history") - .execute(&*pool) - .await + sqlx + ::query("DELETE FROM history") + .execute(&*pool).await .map_err(|e| e.to_string())?; let _ = app_handle.track_event("history_cleared", None); diff --git a/src-tauri/src/db/settings.rs b/src-tauri/src/db/settings.rs index 7fedf8c..caee425 100644 --- a/src-tauri/src/db/settings.rs +++ b/src-tauri/src/db/settings.rs @@ -1,8 +1,8 @@ -use serde::{Deserialize, Serialize}; +use serde::{ Deserialize, Serialize }; use serde_json; use sqlx::Row; use sqlx::SqlitePool; -use tauri::{Emitter, Manager}; +use tauri::{ Emitter, Manager }; use tauri_plugin_aptabase::EventTracker; #[derive(Deserialize, Serialize)] @@ -16,10 +16,10 @@ pub async fn initialize_settings(pool: &SqlitePool) -> Result<(), Box Result<(), Box, - keybind: Vec, + keybind: Vec ) -> Result<(), String> { - app_handle - .emit("update-shortcut", &keybind) - .map_err(|e| e.to_string())?; + app_handle.emit("update-shortcut", &keybind).map_err(|e| e.to_string())?; let json = serde_json::to_string(&keybind).map_err(|e| e.to_string())?; - sqlx::query("INSERT OR REPLACE INTO settings (key, value) VALUES ('keybind', ?)") + sqlx + ::query("INSERT OR REPLACE INTO settings (key, value) VALUES ('keybind', ?)") .bind(json) - .execute(&*pool) - .await + .execute(&*pool).await .map_err(|e| e.to_string())?; - let _ = app_handle.track_event("keybind_saved", Some(serde_json::json!({ + let _ = app_handle.track_event( + "keybind_saved", + Some(serde_json::json!({ "keybind": keybind - }))); + })) + ); Ok(()) } @@ -52,12 +53,12 @@ pub async fn save_keybind( #[tauri::command] pub async fn get_setting( pool: tauri::State<'_, SqlitePool>, - key: String, + key: String ) -> Result { - let row = sqlx::query("SELECT value FROM settings WHERE key = ?") + let row = sqlx + ::query("SELECT value FROM settings WHERE key = ?") .bind(key) - .fetch_optional(&*pool) - .await + .fetch_optional(&*pool).await .map_err(|e| e.to_string())?; Ok(row.map(|r| r.get("value")).unwrap_or_default()) @@ -68,18 +69,21 @@ pub async fn save_setting( app_handle: tauri::AppHandle, pool: tauri::State<'_, SqlitePool>, key: String, - value: String, + value: String ) -> Result<(), String> { - sqlx::query("INSERT OR REPLACE INTO settings (key, value) VALUES (?, ?)") + sqlx + ::query("INSERT OR REPLACE INTO settings (key, value) VALUES (?, ?)") .bind(key.clone()) .bind(value) - .execute(&*pool) - .await + .execute(&*pool).await .map_err(|e| e.to_string())?; - let _ = app_handle.track_event("setting_saved", Some(serde_json::json!({ + let _ = app_handle.track_event( + "setting_saved", + Some(serde_json::json!({ "key": key - }))); + })) + ); Ok(()) } @@ -88,15 +92,18 @@ pub async fn save_setting( pub async fn get_keybind(app_handle: tauri::AppHandle) -> Result, String> { let pool = app_handle.state::(); - let row = sqlx::query("SELECT value FROM settings WHERE key = 'keybind'") - .fetch_optional(&*pool) - .await + let row = sqlx + ::query("SELECT value FROM settings WHERE key = 'keybind'") + .fetch_optional(&*pool).await .map_err(|e| e.to_string())?; - let json = row.map(|r| r.get::("value")).unwrap_or_else(|| { - serde_json::to_string(&vec!["MetaLeft".to_string(), "KeyV".to_string()]) - .expect("Failed to serialize default keybind") - }); + let json = row + .map(|r| r.get::("value")) + .unwrap_or_else(|| { + serde_json + ::to_string(&vec!["MetaLeft".to_string(), "KeyV".to_string()]) + .expect("Failed to serialize default keybind") + }); serde_json::from_str::>(&json).map_err(|e| e.to_string()) } diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 7f9fb79..7f06282 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -1,7 +1,4 @@ -#![cfg_attr( - all(not(debug_assertions), target_os = "windows"), - windows_subsystem = "windows" -)] +#![cfg_attr(all(not(debug_assertions), target_os = "windows"), windows_subsystem = "windows")] mod api; mod db; @@ -10,7 +7,7 @@ mod utils; use sqlx::sqlite::SqlitePoolOptions; use std::fs; use tauri::Manager; -use tauri_plugin_aptabase::{EventTracker, InitOptions}; +use tauri_plugin_aptabase::{ EventTracker, InitOptions }; use tauri_plugin_autostart::MacosLauncher; use tauri_plugin_prevent_default::Flags; @@ -18,7 +15,8 @@ fn main() { let runtime = tokio::runtime::Runtime::new().expect("Failed to create Tokio runtime"); let _guard = runtime.enter(); - tauri::Builder::default() + tauri::Builder + ::default() .plugin(tauri_plugin_clipboard::init()) .plugin(tauri_plugin_os::init()) .plugin(tauri_plugin_sql::Builder::default().build()) @@ -26,34 +24,37 @@ fn main() { .plugin(tauri_plugin_fs::init()) .plugin(tauri_plugin_updater::Builder::default().build()) .plugin( - tauri_plugin_aptabase::Builder::new("A-SH-8937252746") + tauri_plugin_aptabase::Builder + ::new("A-SH-8937252746") .with_options(InitOptions { host: Some("https://aptabase.pandadev.net".to_string()), flush_interval: None, }) - .with_panic_hook(Box::new(|client, info, msg| { - let location = info - .location() - .map(|loc| format!("{}:{}:{}", loc.file(), loc.line(), loc.column())) - .unwrap_or_else(|| "".to_string()); + .with_panic_hook( + Box::new(|client, info, msg| { + let location = info + .location() + .map(|loc| format!("{}:{}:{}", loc.file(), loc.line(), loc.column())) + .unwrap_or_else(|| "".to_string()); - let _ = client.track_event( - "panic", - Some(serde_json::json!({ + let _ = client.track_event( + "panic", + Some( + serde_json::json!({ "info": format!("{} ({})", msg, location), - })), - ); - })) - .build(), + }) + ) + ); + }) + ) + .build() ) - .plugin(tauri_plugin_autostart::init( - MacosLauncher::LaunchAgent, - Some(vec![]), - )) + .plugin(tauri_plugin_autostart::init(MacosLauncher::LaunchAgent, Some(vec![]))) .plugin( - tauri_plugin_prevent_default::Builder::new() + tauri_plugin_prevent_default::Builder + ::new() .with_flags(Flags::all().difference(Flags::CONTEXT_MENU)) - .build(), + .build() ) .setup(|app| { let app_data_dir = app.path().app_data_dir().unwrap(); @@ -75,8 +76,7 @@ fn main() { tauri::async_runtime::spawn(async move { let pool = SqlitePoolOptions::new() .max_connections(5) - .connect(&db_url) - .await + .connect(&db_url).await .expect("Failed to create pool"); app_handle_clone.manage(pool); @@ -91,7 +91,10 @@ fn main() { let _ = api::clipboard::start_monitor(app_handle.clone()); utils::commands::center_window_on_current_monitor(main_window.as_ref().unwrap()); - main_window.as_ref().map(|w| w.hide()).unwrap_or(Ok(()))?; + main_window + .as_ref() + .map(|w| w.hide()) + .unwrap_or(Ok(()))?; let _ = app.track_event("app_started", None); @@ -109,21 +112,23 @@ fn main() { } } }) - .invoke_handler(tauri::generate_handler![ - api::clipboard::write_and_paste, - db::history::get_history, - db::history::add_history_item, - db::history::search_history, - db::history::load_history_chunk, - db::history::delete_history_item, - db::history::clear_history, - db::history::read_image, - db::settings::get_setting, - db::settings::save_setting, - db::settings::save_keybind, - db::settings::get_keybind, - utils::commands::fetch_page_meta, - ]) + .invoke_handler( + tauri::generate_handler![ + api::clipboard::write_and_paste, + db::history::get_history, + db::history::add_history_item, + db::history::search_history, + db::history::load_history_chunk, + db::history::delete_history_item, + db::history::clear_history, + db::history::read_image, + db::settings::get_setting, + db::settings::save_setting, + db::settings::save_keybind, + db::settings::get_keybind, + utils::commands::fetch_page_meta + ] + ) .run(tauri::generate_context!()) .expect("error while running tauri application"); } diff --git a/src-tauri/src/utils/commands.rs b/src-tauri/src/utils/commands.rs index f87a866..48c84e5 100644 --- a/src-tauri/src/utils/commands.rs +++ b/src-tauri/src/utils/commands.rs @@ -1,34 +1,37 @@ use active_win_pos_rs::get_active_window; -use base64::{engine::general_purpose::STANDARD, Engine}; +use base64::{ engine::general_purpose::STANDARD, Engine }; use image::codecs::png::PngEncoder; use tauri::PhysicalPosition; use meta_fetcher; pub fn center_window_on_current_monitor(window: &tauri::WebviewWindow) { - if let Some(monitor) = window.available_monitors().unwrap().iter().find(|m| { - let primary_monitor = window - .primary_monitor() + if + let Some(monitor) = window + .available_monitors() .unwrap() - .expect("Failed to get primary monitor"); - let mouse_position = primary_monitor.position(); - let monitor_position = m.position(); - let monitor_size = m.size(); - mouse_position.x >= monitor_position.x - && mouse_position.x < monitor_position.x + monitor_size.width as i32 - && mouse_position.y >= monitor_position.y - && mouse_position.y < monitor_position.y + monitor_size.height as i32 - }) { + .iter() + .find(|m| { + let primary_monitor = window + .primary_monitor() + .unwrap() + .expect("Failed to get primary monitor"); + let mouse_position = primary_monitor.position(); + let monitor_position = m.position(); + let monitor_size = m.size(); + mouse_position.x >= monitor_position.x && + mouse_position.x < monitor_position.x + (monitor_size.width as i32) && + mouse_position.y >= monitor_position.y && + mouse_position.y < monitor_position.y + (monitor_size.height as i32) + }) + { let monitor_size = monitor.size(); let window_size = window.outer_size().unwrap(); - let x = (monitor_size.width as i32 - window_size.width as i32) / 2; - let y = (monitor_size.height as i32 - window_size.height as i32) / 2; + let x = ((monitor_size.width as i32) - (window_size.width as i32)) / 2; + let y = ((monitor_size.height as i32) - (window_size.height as i32)) / 2; window - .set_position(PhysicalPosition::new( - monitor.position().x + x, - monitor.position().y + y, - )) + .set_position(PhysicalPosition::new(monitor.position().x + x, monitor.position().y + y)) .unwrap(); } } @@ -51,59 +54,64 @@ fn _process_icon_to_base64(path: &str) -> Result bool { let color = color.trim().to_lowercase(); - + // hex if color.starts_with('#') && color.len() == color.trim_end_matches(char::is_whitespace).len() { let hex = &color[1..]; return match hex.len() { 3 | 6 | 8 => hex.chars().all(|c| c.is_ascii_hexdigit()), - _ => false + _ => false, }; } - + // rgb/rgba - if (color.starts_with("rgb(") || color.starts_with("rgba(")) && color.ends_with(")") && !color[..color.len()-1].contains(")") { + if + (color.starts_with("rgb(") || color.starts_with("rgba(")) && + color.ends_with(")") && + !color[..color.len() - 1].contains(")") + { let values = color .trim_start_matches("rgba(") .trim_start_matches("rgb(") .trim_end_matches(')') .split(',') .collect::>(); - + return match values.len() { 3 | 4 => values.iter().all(|v| v.trim().parse::().is_ok()), - _ => false + _ => false, }; } - - // hsl/hsla - if (color.starts_with("hsl(") || color.starts_with("hsla(")) && color.ends_with(")") && !color[..color.len()-1].contains(")") { + + // hsl/hsla + if + (color.starts_with("hsl(") || color.starts_with("hsla(")) && + color.ends_with(")") && + !color[..color.len() - 1].contains(")") + { let values = color .trim_start_matches("hsla(") .trim_start_matches("hsl(") .trim_end_matches(')') .split(',') .collect::>(); - + return match values.len() { 3 | 4 => values.iter().all(|v| v.trim().parse::().is_ok()), - _ => false + _ => false, }; } - + false } #[tauri::command] pub async fn fetch_page_meta(url: String) -> Result<(String, Option), String> { - let metadata = meta_fetcher::fetch_metadata(&url) + let metadata = meta_fetcher + ::fetch_metadata(&url) .map_err(|e| format!("Failed to fetch metadata: {}", e))?; - - Ok(( - metadata.title.unwrap_or_else(|| "No title found".to_string()), - metadata.image - )) -} \ No newline at end of file + + Ok((metadata.title.unwrap_or_else(|| "No title found".to_string()), metadata.image)) +} diff --git a/src-tauri/src/utils/favicon.rs b/src-tauri/src/utils/favicon.rs index 38321f3..45568dc 100644 --- a/src-tauri/src/utils/favicon.rs +++ b/src-tauri/src/utils/favicon.rs @@ -5,7 +5,7 @@ use reqwest; use url::Url; pub async fn fetch_favicon_as_base64( - url: Url, + url: Url ) -> Result, Box> { let client = reqwest::Client::new(); let favicon_url = format!("https://favicone.com/{}", url.host_str().unwrap()); diff --git a/src-tauri/src/utils/keys.rs b/src-tauri/src/utils/keys.rs index fffacdd..01b6836 100644 --- a/src-tauri/src/utils/keys.rs +++ b/src-tauri/src/utils/keys.rs @@ -105,7 +105,9 @@ impl FromStr for KeyCode { "F10" => Code::F10, "F11" => Code::F11, "F12" => Code::F12, - _ => return Err(format!("Unknown key code: {}", s)), + _ => { + return Err(format!("Unknown key code: {}", s)); + } }; Ok(KeyCode(code)) } diff --git a/src-tauri/src/utils/logger.rs b/src-tauri/src/utils/logger.rs index 114b2c6..bd39108 100644 --- a/src-tauri/src/utils/logger.rs +++ b/src-tauri/src/utils/logger.rs @@ -1,6 +1,6 @@ use chrono; -use log::{LevelFilter, SetLoggerError}; -use std::fs::{File, OpenOptions}; +use log::{ LevelFilter, SetLoggerError }; +use std::fs::{ File, OpenOptions }; use std::io::Write; use std::panic; @@ -16,7 +16,7 @@ impl log::Log for FileLogger { fn log(&self, record: &log::Record) { if self.enabled(record.metadata()) { let mut file = self.file.try_clone().expect("Failed to clone file handle"); - + // Format: timestamp [LEVEL] target: message (file:line) writeln!( file, @@ -50,32 +50,38 @@ pub fn init_logger(app_data_dir: &std::path::Path) -> Result<(), SetLoggerError> // Set up panic hook let panic_file = file.try_clone().expect("Failed to clone file handle"); - panic::set_hook(Box::new(move |panic_info| { - let mut file = panic_file.try_clone().expect("Failed to clone file handle"); - - let location = panic_info.location() - .map(|loc| format!("{}:{}:{}", loc.file(), loc.line(), loc.column())) - .unwrap_or_else(|| "unknown location".to_string()); + panic::set_hook( + Box::new(move |panic_info| { + let mut file = panic_file.try_clone().expect("Failed to clone file handle"); - let message = match panic_info.payload().downcast_ref::<&str>() { - Some(s) => *s, - None => match panic_info.payload().downcast_ref::() { - Some(s) => s.as_str(), - None => "Unknown panic message", - }, - }; + let location = panic_info + .location() + .map(|loc| format!("{}:{}:{}", loc.file(), loc.line(), loc.column())) + .unwrap_or_else(|| "unknown location".to_string()); - let _ = writeln!( - file, - "{} [PANIC] rust_panic: {} ({})", - chrono::Local::now().format("%Y-%m-%d %H:%M:%S"), - message, - location - ); - })); + let message = match panic_info.payload().downcast_ref::<&str>() { + Some(s) => *s, + None => + match panic_info.payload().downcast_ref::() { + Some(s) => s.as_str(), + None => "Unknown panic message", + } + }; + + let _ = writeln!( + file, + "{} [PANIC] rust_panic: {} ({})", + chrono::Local::now().format("%Y-%m-%d %H:%M:%S"), + message, + location + ); + }) + ); let logger = Box::new(FileLogger { file }); - unsafe { log::set_logger_racy(Box::leak(logger))? }; + unsafe { + log::set_logger_racy(Box::leak(logger))?; + } log::set_max_level(LevelFilter::Debug); Ok(()) -} \ No newline at end of file +} diff --git a/src-tauri/src/utils/mod.rs b/src-tauri/src/utils/mod.rs index 9554229..78e50cb 100644 --- a/src-tauri/src/utils/mod.rs +++ b/src-tauri/src/utils/mod.rs @@ -2,4 +2,4 @@ pub mod commands; pub mod favicon; pub mod types; pub mod logger; -pub mod keys; \ No newline at end of file +pub mod keys; diff --git a/src-tauri/src/utils/types.rs b/src-tauri/src/utils/types.rs index 76c846e..4e6886f 100644 --- a/src-tauri/src/utils/types.rs +++ b/src-tauri/src/utils/types.rs @@ -1,5 +1,5 @@ -use chrono::{DateTime, Utc}; -use serde::{Deserialize, Serialize}; +use chrono::{ DateTime, Utc }; +use serde::{ Deserialize, Serialize }; use std::fmt; use uuid::Uuid; @@ -115,7 +115,7 @@ impl HistoryItem { content: String, favicon: Option, source_icon: Option, - language: Option, + language: Option ) -> Self { Self { id: Uuid::new_v4().to_string(), @@ -130,7 +130,7 @@ impl HistoryItem { } pub fn to_row( - &self, + &self ) -> ( String, String,