From 9e4e0c81a4265a480da75c398f95831f95ef17e4 Mon Sep 17 00:00:00 2001 From: Tunglies Date: Sun, 4 Jan 2026 19:21:45 +0800 Subject: [PATCH] refactor: clean up and improve code structure (#6010) --- src-tauri/src/constants.rs | 5 -- src-tauri/src/core/backup.rs | 17 +++--- src-tauri/src/core/handle.rs | 80 +++++--------------------- src-tauri/src/core/logger.rs | 2 - src-tauri/src/core/notification.rs | 79 ++++--------------------- src-tauri/src/core/tray/mod.rs | 13 ++--- src-tauri/src/core/tray/speed_rate.rs | 1 - src-tauri/src/process/async_handler.rs | 46 --------------- src-tauri/src/utils/network.rs | 72 +++-------------------- src-tauri/src/utils/window_manager.rs | 2 - 10 files changed, 49 insertions(+), 268 deletions(-) delete mode 100644 src-tauri/src/core/tray/speed_rate.rs diff --git a/src-tauri/src/constants.rs b/src-tauri/src/constants.rs index 93ef8e872..094604b37 100644 --- a/src-tauri/src/constants.rs +++ b/src-tauri/src/constants.rs @@ -25,7 +25,6 @@ pub mod timing { pub const CONFIG_UPDATE_DEBOUNCE: Duration = Duration::from_millis(300); pub const EVENT_EMIT_DELAY: Duration = Duration::from_millis(20); pub const STARTUP_ERROR_DELAY: Duration = Duration::from_secs(2); - pub const ERROR_BATCH_DELAY: Duration = Duration::from_millis(300); #[cfg(target_os = "windows")] pub const SERVICE_WAIT_MAX: Duration = Duration::from_millis(3000); @@ -33,10 +32,6 @@ pub mod timing { pub const SERVICE_WAIT_INTERVAL: Duration = Duration::from_millis(200); } -pub mod retry { - pub const EVENT_EMIT_THRESHOLD: u64 = 10; -} - pub mod files { pub const RUNTIME_CONFIG: &str = "clash-verge.yaml"; pub const CHECK_CONFIG: &str = "clash-verge-check.yaml"; diff --git a/src-tauri/src/core/backup.rs b/src-tauri/src/core/backup.rs index 1c3c36650..2ddaa9bd6 100644 --- a/src-tauri/src/core/backup.rs +++ b/src-tauri/src/core/backup.rs @@ -53,16 +53,16 @@ impl Operation { } pub struct WebDavClient { - config: Arc>, - clients: Arc>>, + config: ArcSwapOption, + clients: ArcSwap>, } impl WebDavClient { pub fn global() -> &'static Self { static WEBDAV_CLIENT: OnceCell = OnceCell::new(); WEBDAV_CLIENT.get_or_init(|| Self { - config: Arc::new(ArcSwapOption::new(None)), - clients: Arc::new(ArcSwap::new(Arc::new(HashMap::new()))), + config: ArcSwapOption::new(None), + clients: ArcSwap::new(Arc::new(HashMap::new())), }) } @@ -147,11 +147,12 @@ impl WebDavClient { } } - // 缓存客户端(替换 Arc>> 的写法) { - let mut map = (**self.clients.load()).clone(); - map.insert(op, client.clone()); - self.clients.store(map.into()); + self.clients.rcu(|clients_map| { + let mut new_map = (**clients_map).clone(); + new_map.insert(op, client.clone()); + Arc::new(new_map) + }); } Ok(client) diff --git a/src-tauri/src/core/handle.rs b/src-tauri/src/core/handle.rs index a5c973a61..1a027f68a 100644 --- a/src-tauri/src/core/handle.rs +++ b/src-tauri/src/core/handle.rs @@ -1,24 +1,19 @@ -use crate::{APP_HANDLE, constants::timing, singleton}; +use crate::{APP_HANDLE, singleton, utils::window_manager::WindowManager}; use parking_lot::RwLock; use smartstring::alias::String; -use std::{ - sync::{ - Arc, - atomic::{AtomicBool, Ordering}, - }, - thread, +use std::sync::{ + Arc, + atomic::{AtomicBool, Ordering}, }; use tauri::{AppHandle, Manager as _, WebviewWindow}; use tauri_plugin_mihomo::{Mihomo, MihomoExt as _}; use tokio::sync::RwLockReadGuard; -use super::notification::{ErrorMessage, FrontendEvent, NotificationSystem}; +use super::notification::{FrontendEvent, NotificationSystem}; #[derive(Debug)] pub struct Handle { is_exiting: AtomicBool, - startup_errors: Arc>>, - startup_completed: AtomicBool, pub(crate) notification_system: Arc>>, } @@ -26,8 +21,6 @@ impl Default for Handle { fn default() -> Self { Self { is_exiting: AtomicBool::new(false), - startup_errors: Arc::new(RwLock::new(Vec::new())), - startup_completed: AtomicBool::new(false), notification_system: Arc::new(RwLock::new(Some(NotificationSystem::new()))), } } @@ -47,7 +40,7 @@ impl Handle { let mut system_opt = self.notification_system.write(); if let Some(system) = system_opt.as_mut() - && !system.is_running + && !system.is_running() { system.start(); } @@ -111,21 +104,19 @@ impl Handle { // TODO 利用 &str 等缩短 Clone pub fn notice_message, M: Into>(status: S, msg: M) { let handle = Self::global(); - let status_str = status.into(); - let msg_str = msg.into(); - - if !handle.startup_completed.load(Ordering::Acquire) { - handle.startup_errors.write().push(ErrorMessage { - status: status_str, - message: msg_str, - }); - return; - } if handle.is_exiting() { return; } + // We only send notice when main window exists + if WindowManager::get_main_window().is_none() { + return; + } + + let status_str = status.into(); + let msg_str = msg.into(); + Self::send_event(FrontendEvent::NoticeMessage { status: status_str, message: msg_str, @@ -144,49 +135,6 @@ impl Handle { } } - pub fn mark_startup_completed(&self) { - self.startup_completed.store(true, Ordering::Release); - self.send_startup_errors(); - } - - fn send_startup_errors(&self) { - let errors = { - let mut errors = self.startup_errors.write(); - std::mem::take(&mut *errors) - }; - - if errors.is_empty() { - return; - } - - let _ = thread::Builder::new() - .name("startup-errors-sender".into()) - .spawn(move || { - thread::sleep(timing::STARTUP_ERROR_DELAY); - - let handle = Self::global(); - if handle.is_exiting() { - return; - } - - let system_opt = handle.notification_system.read(); - if let Some(system) = system_opt.as_ref() { - for error in errors { - if handle.is_exiting() { - break; - } - - system.send_event(FrontendEvent::NoticeMessage { - status: error.status, - message: error.message, - }); - - thread::sleep(timing::ERROR_BATCH_DELAY); - } - } - }); - } - pub fn set_is_exiting(&self) { self.is_exiting.store(true, Ordering::Release); diff --git a/src-tauri/src/core/logger.rs b/src-tauri/src/core/logger.rs index 9b2edc2ba..2820207a8 100644 --- a/src-tauri/src/core/logger.rs +++ b/src-tauri/src/core/logger.rs @@ -134,7 +134,6 @@ impl Logger { /// only update app log level pub fn update_log_level(&self, level: LevelFilter) -> Result<()> { - println!("refresh log level"); *self.log_level.write() = level; let log_level = self.log_level.read().to_owned(); if let Some(handle) = self.handle.lock().as_mut() { @@ -149,7 +148,6 @@ impl Logger { /// update app and mihomo core log config pub async fn update_log_config(&self, log_max_size: u64, log_max_count: usize) -> Result<()> { - println!("refresh log file"); self.log_max_size.store(log_max_size, Ordering::SeqCst); self.log_max_count.store(log_max_count, Ordering::SeqCst); if let Some(handle) = self.handle.lock().as_ref() { diff --git a/src-tauri/src/core/notification.rs b/src-tauri/src/core/notification.rs index aa8246ff1..e6c0e0a75 100644 --- a/src-tauri/src/core/notification.rs +++ b/src-tauri/src/core/notification.rs @@ -1,16 +1,8 @@ use super::handle::Handle; -use crate::constants::{retry, timing}; +use crate::constants::timing; use clash_verge_logging::{Type, logging}; -use parking_lot::RwLock; use smartstring::alias::String; -use std::{ - sync::{ - atomic::{AtomicBool, AtomicU64, Ordering}, - mpsc, - }, - thread, - time::Instant, -}; +use std::{sync::mpsc, thread}; use tauri::{Emitter as _, WebviewWindow}; // TODO 重构或优化,避免 Clone 过多 @@ -25,27 +17,11 @@ pub enum FrontendEvent { ProfileUpdateCompleted { uid: String }, } -#[derive(Debug, Default)] -struct EventStats { - total_sent: AtomicU64, - total_errors: AtomicU64, - last_error_time: RwLock>, -} - -#[derive(Debug, Clone)] -pub struct ErrorMessage { - pub status: String, - pub message: String, -} - #[derive(Debug)] pub struct NotificationSystem { sender: Option>, #[allow(clippy::type_complexity)] worker_handle: Option>, - pub(super) is_running: bool, - stats: EventStats, - emergency_mode: AtomicBool, } impl Default for NotificationSystem { @@ -55,25 +31,26 @@ impl Default for NotificationSystem { } impl NotificationSystem { - pub fn new() -> Self { + pub const fn new() -> Self { Self { sender: None, worker_handle: None, - is_running: false, - stats: EventStats::default(), - emergency_mode: AtomicBool::new(false), } } + pub const fn is_running(&self) -> bool { + self.sender.is_some() && self.worker_handle.is_some() + } + pub fn start(&mut self) { - if self.is_running { + if self.is_running() { return; } let (tx, rx) = mpsc::channel(); self.sender = Some(tx); - self.is_running = true; + //? Do we have to create a new thread for this? let result = thread::Builder::new() .name("frontend-notifier".into()) .spawn(move || Self::worker_loop(rx)); @@ -107,10 +84,6 @@ impl NotificationSystem { None => return, }; - if system.should_skip_event(&event) { - return; - } - if let Some(window) = super::handle::Handle::get_window() { system.emit_to_window(&window, event); drop(binding); @@ -118,30 +91,15 @@ impl NotificationSystem { } } - fn should_skip_event(&self, event: &FrontendEvent) -> bool { - let is_emergency = self.emergency_mode.load(Ordering::Acquire); - matches!( - (is_emergency, event), - (true, FrontendEvent::NoticeMessage { status, .. }) if status == "info" - ) - } - fn emit_to_window(&self, window: &WebviewWindow, event: FrontendEvent) { let (event_name, payload) = self.serialize_event(event); let Ok(payload) = payload else { - self.stats.total_errors.fetch_add(1, Ordering::Relaxed); return; }; - match window.emit(event_name, payload) { - Ok(_) => { - self.stats.total_sent.fetch_add(1, Ordering::Relaxed); - } - Err(e) => { - logging!(warn, Type::Frontend, "Event emit failed: {}", e); - self.handle_emit_error(); - } + if let Err(e) = window.emit(event_name, payload) { + logging!(warn, Type::Frontend, "Event emit failed: {}", e); } } @@ -161,19 +119,8 @@ impl NotificationSystem { } } - fn handle_emit_error(&self) { - self.stats.total_errors.fetch_add(1, Ordering::Relaxed); - *self.stats.last_error_time.write() = Some(Instant::now()); - - let errors = self.stats.total_errors.load(Ordering::Relaxed); - if errors > retry::EVENT_EMIT_THRESHOLD && !self.emergency_mode.load(Ordering::Acquire) { - logging!(warn, Type::Frontend, "Entering emergency mode after {} errors", errors); - self.emergency_mode.store(true, Ordering::Release); - } - } - pub fn send_event(&self, event: FrontendEvent) -> bool { - if self.should_skip_event(&event) { + if !self.is_running() { return false; } @@ -185,8 +132,6 @@ impl NotificationSystem { } pub fn shutdown(&mut self) { - self.is_running = false; - if let Some(sender) = self.sender.take() { drop(sender); } diff --git a/src-tauri/src/core/tray/mod.rs b/src-tauri/src/core/tray/mod.rs index 21b3d3010..54bf49783 100644 --- a/src-tauri/src/core/tray/mod.rs +++ b/src-tauri/src/core/tray/mod.rs @@ -1,10 +1,3 @@ -use governor::{DefaultDirectRateLimiter, Quota, RateLimiter}; -use tauri::tray::TrayIconBuilder; -use tauri_plugin_clash_verge_sysinfo::is_current_app_handle_admin; -use tauri_plugin_mihomo::models::Proxies; -use tokio::fs; -#[cfg(target_os = "macos")] -pub mod speed_rate; use crate::config::{IProfilePreview, IVerge}; use crate::core::service; use crate::core::tray::menu_def::TrayAction; @@ -16,6 +9,11 @@ use crate::{ Type, cmd, config::Config, feat, logging, module::lightweight::is_in_lightweight_mode, utils::dirs::find_target_icons, }; +use governor::{DefaultDirectRateLimiter, Quota, RateLimiter}; +use tauri::tray::TrayIconBuilder; +use tauri_plugin_clash_verge_sysinfo::is_current_app_handle_admin; +use tauri_plugin_mihomo::models::Proxies; +use tokio::fs; use super::handle; use anyhow::Result; @@ -955,7 +953,6 @@ fn on_menu_event(_: &AppHandle, event: MenuEvent) { } MenuIds::COPY_ENV => feat::copy_clash_env().await, MenuIds::CONF_DIR => { - println!("Open directory submenu clicked"); let _ = cmd::open_app_dir().await; } MenuIds::CORE_DIR => { diff --git a/src-tauri/src/core/tray/speed_rate.rs b/src-tauri/src/core/tray/speed_rate.rs deleted file mode 100644 index 8b1378917..000000000 --- a/src-tauri/src/core/tray/speed_rate.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src-tauri/src/process/async_handler.rs b/src-tauri/src/process/async_handler.rs index f277ec6aa..3978869d9 100644 --- a/src-tauri/src/process/async_handler.rs +++ b/src-tauri/src/process/async_handler.rs @@ -1,17 +1,9 @@ -#[cfg(feature = "tokio-trace")] -use std::any::type_name; use std::future::Future; -#[cfg(feature = "tokio-trace")] -use std::panic::Location; use tauri::{async_runtime, async_runtime::JoinHandle}; pub struct AsyncHandler; impl AsyncHandler { - // pub fn handle() -> async_runtime::RuntimeHandle { - // async_runtime::handle() - // } - #[inline] #[track_caller] pub fn spawn(f: F) -> JoinHandle<()> @@ -19,8 +11,6 @@ impl AsyncHandler { F: FnOnce() -> Fut + Send + 'static, Fut: Future + Send + 'static, { - #[cfg(feature = "tokio-trace")] - Self::log_task_info(&f); async_runtime::spawn(f()) } @@ -31,8 +21,6 @@ impl AsyncHandler { F: FnOnce() -> T + Send + 'static, T: Send + 'static, { - #[cfg(feature = "tokio-trace")] - Self::log_task_info(&f); async_runtime::spawn_blocking(f) } @@ -42,40 +30,6 @@ impl AsyncHandler { where Fut: Future + Send + 'static, { - #[cfg(feature = "tokio-trace")] - Self::log_task_info(&fut); async_runtime::block_on(fut) } - - #[cfg(feature = "tokio-trace")] - #[track_caller] - fn log_task_info(f: &F) - where - F: ?Sized, - { - const TRACE_SPECIAL_SIZE: [usize; 3] = [0, 4, 24]; - let size = std::mem::size_of_val(f); - if TRACE_SPECIAL_SIZE.contains(&size) { - return; - } - - let location = Location::caller(); - let type_str = type_name::(); - let size_str = format!("{} bytes", size); - let loc_str = format!("{}:{}:{}", location.file(), location.line(), location.column()); - - println!( - "┌────────────────────┬─────────────────────────────────────────────────────────────────────────────┐" - ); - println!("│ {:<18} │ {:<80} │", "Field", "Value"); - println!( - "├────────────────────┼─────────────────────────────────────────────────────────────────────────────┤" - ); - println!("│ {:<18} │ {:<80} │", "Type of task", type_str); - println!("│ {:<18} │ {:<80} │", "Size of task", size_str); - println!("│ {:<18} │ {:<80} │", "Called from", loc_str); - println!( - "└────────────────────┴─────────────────────────────────────────────────────────────────────────────┘" - ); - } } diff --git a/src-tauri/src/utils/network.rs b/src-tauri/src/utils/network.rs index 837da05bb..245fd72a7 100644 --- a/src-tauri/src/utils/network.rs +++ b/src-tauri/src/utils/network.rs @@ -6,10 +6,9 @@ use reqwest::{ header::{HeaderMap, HeaderValue, USER_AGENT}, }; use smartstring::alias::String; -use std::time::{Duration, Instant}; +use std::time::Duration; use sysproxy::Sysproxy; use tauri::Url; -use tokio::sync::Mutex; #[derive(Debug)] pub struct HttpResponse { @@ -43,13 +42,7 @@ pub enum ProxyType { System, } -pub struct NetworkManager { - self_proxy_client: Mutex>, - system_proxy_client: Mutex>, - no_proxy_client: Mutex>, - last_connection_error: Mutex>, - connection_error_count: Mutex, -} +pub struct NetworkManager; impl Default for NetworkManager { fn default() -> Self { @@ -58,44 +51,8 @@ impl Default for NetworkManager { } impl NetworkManager { - pub fn new() -> Self { - Self { - self_proxy_client: Mutex::new(None), - system_proxy_client: Mutex::new(None), - no_proxy_client: Mutex::new(None), - last_connection_error: Mutex::new(None), - connection_error_count: Mutex::new(0), - } - } - - async fn record_connection_error(&self, error: &str) { - *self.last_connection_error.lock().await = Some((Instant::now(), error.into())); - - let mut count = self.connection_error_count.lock().await; - *count += 1; - } - - async fn should_reset_clients(&self) -> bool { - let count = *self.connection_error_count.lock().await; - if count > 5 { - return true; - } - - if let Some((time, _)) = &*self.last_connection_error.lock().await - && time.elapsed() < Duration::from_secs(30) - && count > 2 - { - return true; - } - - false - } - - pub async fn reset_clients(&self) { - *self.self_proxy_client.lock().await = None; - *self.system_proxy_client.lock().await = None; - *self.no_proxy_client.lock().await = None; - *self.connection_error_count.lock().await = 0; + pub const fn new() -> Self { + Self } fn build_client( @@ -192,11 +149,7 @@ impl NetworkManager { user_agent: Option, accept_invalid_certs: bool, ) -> Result { - if self.should_reset_clients().await { - self.reset_clients().await; - } - - let parsed = Url::parse(url)?; + let mut parsed = Url::parse(url)?; let mut extra_headers = HeaderMap::new(); if !parsed.username().is_empty() @@ -207,19 +160,15 @@ impl NetworkManager { extra_headers.insert("Authorization", HeaderValue::from_str(&format!("Basic {}", encoded))?); } - let clean_url = { - let mut no_auth = parsed.clone(); - no_auth.set_username("").ok(); - no_auth.set_password(None).ok(); - no_auth.to_string() - }; + parsed.set_username("").ok(); + parsed.set_password(None).ok(); // 创建请求 let client = self .create_request(proxy_type, timeout_secs, user_agent, accept_invalid_certs) .await?; - let mut request_builder = client.get(&clean_url); + let mut request_builder = client.get(parsed); for (key, value) in extra_headers.iter() { request_builder = request_builder.header(key, value); @@ -228,18 +177,15 @@ impl NetworkManager { let response = match request_builder.send().await { Ok(resp) => resp, Err(e) => { - self.record_connection_error(&format!("Request failed: {}", e)).await; return Err(anyhow::anyhow!("Request failed: {}", e)); } }; let status = response.status(); - let headers = response.headers().clone(); + let headers = response.headers().to_owned(); let body = match response.text().await { Ok(text) => text.into(), Err(e) => { - self.record_connection_error(&format!("Failed to read response body: {}", e)) - .await; return Err(anyhow::anyhow!("Failed to read response body: {}", e)); } }; diff --git a/src-tauri/src/utils/window_manager.rs b/src-tauri/src/utils/window_manager.rs index c008efb64..aa9081f62 100644 --- a/src-tauri/src/utils/window_manager.rs +++ b/src-tauri/src/utils/window_manager.rs @@ -303,8 +303,6 @@ impl WindowManager { return false; } - handle::Handle::global().mark_startup_completed(); - true }) }