fix: clippy errors with new config (#4428)

* refactor: improve code quality with clippy fixes and standardized logging

- Replace dangerous unwrap()/expect() calls with proper error handling
- Standardize logging from log:: to logging\! macro with Type:: classifications
- Fix app handle panics with graceful fallback patterns
- Improve error resilience across 35+ modules without breaking functionality
- Reduce clippy warnings from 300+ to 0 in main library code

* chore: update Cargo.toml configuration

* refactor: resolve all clippy warnings
- Fix Arc clone warnings using explicit Arc::clone syntax across 9 files
- Add #[allow(clippy::expect_used)] to test functions for appropriate expect usage
- Remove no-effect statements from debug code cleanup
- Apply clippy auto-fixes for dbg\! macro removals and path statements
- Achieve zero clippy warnings on all targets with -D warnings flag

* chore: update Cargo.toml clippy configuration

* refactor: simplify macOS job configuration and improve caching

* refactor: remove unnecessary async/await from service and proxy functions

* refactor: streamline pnpm installation in CI configuration

* refactor: simplify error handling and remove unnecessary else statements

* refactor: replace async/await with synchronous locks for core management

* refactor: add workflow_dispatch trigger to clippy job

* refactor: convert async functions to synchronous for service management

* refactor: convert async functions to synchronous for UWP tool invocation

* fix: change wrong logging

* refactor: convert proxy restoration functions to async

* Revert "refactor: convert proxy restoration functions to async"

This reverts commit b82f5d250b2af7151e4dfd7dd411630b34ed2c18.

* refactor: update proxy restoration functions to return Result types

* fix: handle errors during proxy restoration and update async function signatures

* fix: handle errors during proxy restoration and update async function signatures

* refactor: update restore_pac_proxy and restore_sys_proxy functions to async

* fix: convert restore_pac_proxy and restore_sys_proxy functions to async

* fix: await restore_sys_proxy calls in proxy restoration logic

* fix: suppress clippy warnings for unused async functions in proxy restoration

* fix: suppress clippy warnings for unused async functions in proxy restoration
This commit is contained in:
Tunglies
2025-08-18 02:02:25 +08:00
committed by GitHub
parent a5fdd3f1a2
commit 537d27d10b
49 changed files with 1275 additions and 923 deletions

View File

@@ -117,8 +117,7 @@ impl WebDavClient {
attempt.follow()
}
}))
.build()
.unwrap(),
.build()?,
)
.set_host(config.url)
.set_auth(reqwest_dav::Auth::Basic(config.username, config.password))
@@ -243,12 +242,17 @@ pub fn create_backup() -> Result<(String, PathBuf), Error> {
let options = SimpleFileOptions::default().compression_method(zip::CompressionMethod::Stored);
if let Ok(entries) = fs::read_dir(dirs::app_profiles_dir()?) {
for entry in entries {
let entry = entry.unwrap();
let entry = entry?;
let path = entry.path();
if path.is_file() {
let backup_path = format!("profiles/{}", entry.file_name().to_str().unwrap());
let file_name_os = entry.file_name();
let file_name = file_name_os
.to_str()
.ok_or_else(|| anyhow::Error::msg("Invalid file name encoding"))?;
let backup_path = format!("profiles/{}", file_name);
zip.start_file(backup_path, options)?;
zip.write_all(fs::read(path).unwrap().as_slice())?;
let file_content = fs::read(&path)?;
zip.write_all(&file_content)?;
}
}
}

View File

@@ -14,6 +14,7 @@ use crate::{
};
use anyhow::Result;
use chrono::Local;
use parking_lot::Mutex;
use std::{
fmt,
fs::{create_dir_all, File},
@@ -22,7 +23,6 @@ use std::{
sync::Arc,
};
use tauri_plugin_shell::{process::CommandChild, ShellExt};
use tokio::sync::Mutex;
#[derive(Debug)]
pub struct CoreManager {
@@ -135,7 +135,7 @@ impl CoreManager {
Ok(false)
}
/// 使用默认配置
pub async fn use_default_config(&self, msg_type: &str, msg_content: &str) -> Result<()> {
pub fn use_default_config(&self, msg_type: &str, msg_content: &str) -> Result<()> {
let runtime_path = dirs::app_home_dir()?.join(RUNTIME_CONFIG);
*Config::runtime().draft_mut() = Box::new(IRuntime {
config: Some(Config::clash().latest_ref().0.clone()),
@@ -185,7 +185,7 @@ impl CoreManager {
"检测到Merge文件仅进行语法检查: {}",
config_path
);
return self.validate_file_syntax(config_path).await;
return self.validate_file_syntax(config_path);
}
// 检查是否为脚本文件
@@ -217,7 +217,7 @@ impl CoreManager {
"检测到脚本文件使用JavaScript验证: {}",
config_path
);
return self.validate_script_file(config_path).await;
return self.validate_script_file(config_path);
}
// 对YAML配置文件使用Clash内核验证
@@ -249,7 +249,11 @@ impl CoreManager {
let clash_core = Config::verge().latest_ref().get_valid_clash_core();
logging!(info, Type::Config, true, "使用内核: {}", clash_core);
let app_handle = handle::Handle::global().app_handle().unwrap();
let app_handle = handle::Handle::global().app_handle().ok_or_else(|| {
let msg = "Failed to get app handle";
logging!(error, Type::Core, true, "{}", msg);
anyhow::anyhow!(msg)
})?;
let app_dir = dirs::app_home_dir()?;
let app_dir_str = dirs::path_to_str(&app_dir)?;
logging!(info, Type::Config, true, "验证目录: {}", app_dir_str);
@@ -297,7 +301,7 @@ impl CoreManager {
}
}
/// 只进行文件语法检查,不进行完整验证
async fn validate_file_syntax(&self, config_path: &str) -> Result<(bool, String)> {
fn validate_file_syntax(&self, config_path: &str) -> Result<(bool, String)> {
logging!(info, Type::Config, true, "开始检查文件: {}", config_path);
// 读取文件内容
@@ -325,7 +329,7 @@ impl CoreManager {
}
}
/// 验证脚本文件语法
async fn validate_script_file(&self, path: &str) -> Result<(bool, String)> {
fn validate_script_file(&self, path: &str) -> Result<(bool, String)> {
// 读取脚本内容
let content = match std::fs::read_to_string(path) {
Ok(content) => content,
@@ -382,7 +386,7 @@ impl CoreManager {
// 1. 先生成新的配置内容
logging!(info, Type::Config, true, "生成新的配置内容");
Config::generate().await?;
Config::generate()?;
// 2. 验证配置
match self.validate_config().await {
@@ -435,7 +439,7 @@ impl CoreManager {
// 获取当前管理的进程 PID
let current_pid = {
let child_guard = self.child_sidecar.lock().await;
let child_guard = self.child_sidecar.lock();
child_guard.as_ref().map(|child| child.pid())
};
@@ -729,7 +733,7 @@ impl CoreManager {
}
}
async fn start_core_by_sidecar(&self) -> Result<()> {
fn start_core_by_sidecar(&self) -> Result<()> {
logging!(trace, Type::Core, true, "Running core by sidecar");
let config_file = &Config::generate_file(ConfigType::Run)?;
let app_handle = handle::Handle::global()
@@ -783,14 +787,14 @@ impl CoreManager {
"Started core by sidecar pid: {}",
pid
);
*self.child_sidecar.lock().await = Some(child);
self.set_running_mode(RunningMode::Sidecar).await;
*self.child_sidecar.lock() = Some(child);
self.set_running_mode(RunningMode::Sidecar);
Ok(())
}
async fn stop_core_by_sidecar(&self) -> Result<()> {
fn stop_core_by_sidecar(&self) -> Result<()> {
logging!(trace, Type::Core, true, "Stopping core by sidecar");
if let Some(child) = self.child_sidecar.lock().await.take() {
if let Some(child) = self.child_sidecar.lock().take() {
let pid = child.pid();
child.kill()?;
logging!(
@@ -801,7 +805,7 @@ impl CoreManager {
pid
);
}
self.set_running_mode(RunningMode::NotRunning).await;
self.set_running_mode(RunningMode::NotRunning);
Ok(())
}
}
@@ -811,13 +815,13 @@ impl CoreManager {
logging!(trace, Type::Core, true, "Running core by service");
let config_file = &Config::generate_file(ConfigType::Run)?;
service::run_core_by_service(config_file).await?;
self.set_running_mode(RunningMode::Service).await;
self.set_running_mode(RunningMode::Service);
Ok(())
}
async fn stop_core_by_service(&self) -> Result<()> {
logging!(trace, Type::Core, true, "Stopping core by service");
service::stop_core_by_service().await?;
self.set_running_mode(RunningMode::NotRunning).await;
self.set_running_mode(RunningMode::NotRunning);
Ok(())
}
}
@@ -839,7 +843,7 @@ impl CoreManager {
async fn attempt_service_init(&self) -> Result<()> {
if service::check_service_needs_reinstall().await {
logging!(info, Type::Core, true, "服务版本不匹配或状态异常,执行重装");
if let Err(e) = service::reinstall_service().await {
if let Err(e) = service::reinstall_service() {
logging!(
warn,
Type::Core,
@@ -944,7 +948,7 @@ impl CoreManager {
true,
"用户偏好Sidecar模式或先前服务启动失败使用Sidecar模式启动"
);
self.start_core_by_sidecar().await?;
self.start_core_by_sidecar()?;
// 如果 sidecar 启动成功,我们可以认为核心初始化流程到此结束
// 后续的 Tray::global().subscribe_traffic().await 仍然会执行
} else {
@@ -956,7 +960,7 @@ impl CoreManager {
true,
"无服务安装记录 (首次运行或状态重置),尝试安装服务"
);
match service::install_service().await {
match service::install_service() {
Ok(_) => {
logging!(info, Type::Core, true, "服务安装成功(首次尝试)");
let mut new_state = service::ServiceState::default();
@@ -980,7 +984,7 @@ impl CoreManager {
final_state.last_error =
Some("Newly installed service failed to start".to_string());
final_state.save()?;
self.start_core_by_sidecar().await?;
self.start_core_by_sidecar()?;
}
} else {
logging!(
@@ -996,7 +1000,7 @@ impl CoreManager {
.to_string(),
);
final_state.save()?;
self.start_core_by_sidecar().await?;
self.start_core_by_sidecar()?;
}
}
Err(err) => {
@@ -1007,7 +1011,7 @@ impl CoreManager {
..Default::default()
};
new_state.save()?;
self.start_core_by_sidecar().await?;
self.start_core_by_sidecar()?;
}
}
} else {
@@ -1036,7 +1040,7 @@ impl CoreManager {
}));
final_state.save()?;
}
self.start_core_by_sidecar().await?;
self.start_core_by_sidecar()?;
}
}
}
@@ -1047,13 +1051,13 @@ impl CoreManager {
Ok(())
}
pub async fn set_running_mode(&self, mode: RunningMode) {
let mut guard = self.running.lock().await;
pub fn set_running_mode(&self, mode: RunningMode) {
let mut guard = self.running.lock();
*guard = mode;
}
pub async fn get_running_mode(&self) -> RunningMode {
let guard = self.running.lock().await;
pub fn get_running_mode(&self) -> RunningMode {
let guard = self.running.lock();
(*guard).clone()
}
@@ -1061,7 +1065,7 @@ impl CoreManager {
pub async fn start_core(&self) -> Result<()> {
if service::is_service_available().await.is_ok() {
if service::check_service_needs_reinstall().await {
service::reinstall_service().await?;
service::reinstall_service()?;
}
logging!(info, Type::Core, true, "服务可用,使用服务模式启动");
self.start_core_by_service().await?;
@@ -1075,10 +1079,10 @@ impl CoreManager {
true,
"服务不可用根据用户偏好使用Sidecar模式"
);
self.start_core_by_sidecar().await?;
self.start_core_by_sidecar()?;
} else {
logging!(info, Type::Core, true, "服务不可用使用Sidecar模式");
self.start_core_by_sidecar().await?;
self.start_core_by_sidecar()?;
}
}
Ok(())
@@ -1086,9 +1090,9 @@ impl CoreManager {
/// 停止核心运行
pub async fn stop_core(&self) -> Result<()> {
match self.get_running_mode().await {
match self.get_running_mode() {
RunningMode::Service => self.stop_core_by_service().await,
RunningMode::Sidecar => self.stop_core_by_sidecar().await,
RunningMode::Sidecar => self.stop_core_by_sidecar(),
RunningMode::NotRunning => Ok(()),
}
}
@@ -1108,8 +1112,12 @@ impl CoreManager {
logging!(error, Type::Core, true, "{}", error_message);
return Err(error_message.to_string());
}
let core: &str = &clash_core.clone().unwrap();
if !IVerge::VALID_CLASH_CORES.contains(&core) {
let core = clash_core.as_ref().ok_or_else(|| {
let msg = "Clash core should not be None";
logging!(error, Type::Core, true, "{}", msg);
msg.to_string()
})?;
if !IVerge::VALID_CLASH_CORES.contains(&core.as_str()) {
let error_message = format!("Clash core invalid name: {core}");
logging!(error, Type::Core, true, "{}", error_message);
return Err(error_message);

View File

@@ -97,7 +97,7 @@ impl EventDrivenProxyManager {
let (event_tx, event_rx) = mpsc::unbounded_channel();
let (query_tx, query_rx) = mpsc::unbounded_channel();
Self::start_event_loop(state.clone(), event_rx, query_rx);
Self::start_event_loop(Arc::clone(&state), event_rx, query_rx);
Self {
state,
@@ -218,7 +218,7 @@ impl EventDrivenProxyManager {
Self::enable_system_proxy(state).await;
}
ProxyEvent::DisableProxy => {
Self::disable_system_proxy(state).await;
Self::disable_system_proxy(state);
}
ProxyEvent::SwitchToPac => {
Self::switch_proxy_mode(state, true).await;
@@ -307,7 +307,9 @@ impl EventDrivenProxyManager {
if !current.enable || current.url != expected.url {
log::info!(target: "app", "PAC代理设置异常正在恢复...");
Self::restore_pac_proxy(&expected.url).await;
if let Err(e) = Self::restore_pac_proxy(&expected.url).await {
log::error!(target: "app", "恢复PAC代理失败: {}", e);
}
sleep(Duration::from_millis(500)).await;
let restored = Self::get_auto_proxy_with_timeout().await;
@@ -329,7 +331,9 @@ impl EventDrivenProxyManager {
if !current.enable || current.host != expected.host || current.port != expected.port {
log::info!(target: "app", "系统代理设置异常,正在恢复...");
Self::restore_sys_proxy(&expected).await;
if let Err(e) = Self::restore_sys_proxy(&expected).await {
log::error!(target: "app", "恢复系统代理失败: {}", e);
}
sleep(Duration::from_millis(500)).await;
let restored = Self::get_sys_proxy_with_timeout().await;
@@ -350,16 +354,20 @@ impl EventDrivenProxyManager {
if pac_enabled {
let expected = Self::get_expected_pac_config();
Self::restore_pac_proxy(&expected.url).await;
if let Err(e) = Self::restore_pac_proxy(&expected.url).await {
log::error!(target: "app", "启用PAC代理失败: {}", e);
}
} else {
let expected = Self::get_expected_sys_proxy();
Self::restore_sys_proxy(&expected).await;
if let Err(e) = Self::restore_sys_proxy(&expected).await {
log::error!(target: "app", "启用系统代理失败: {}", e);
}
}
Self::check_and_restore_proxy(state).await;
}
async fn disable_system_proxy(_state: &Arc<RwLock<ProxyState>>) {
fn disable_system_proxy(_state: &Arc<RwLock<ProxyState>>) {
log::info!(target: "app", "禁用系统代理");
#[cfg(not(target_os = "windows"))]
@@ -380,13 +388,17 @@ impl EventDrivenProxyManager {
logging_error!(Type::System, true, disabled_sys.set_system_proxy());
let expected = Self::get_expected_pac_config();
Self::restore_pac_proxy(&expected.url).await;
if let Err(e) = Self::restore_pac_proxy(&expected.url).await {
log::error!(target: "app", "切换到PAC模式失败: {}", e);
}
} else {
let disabled_auto = Autoproxy::default();
logging_error!(Type::System, true, disabled_auto.set_auto_proxy());
let expected = Self::get_expected_sys_proxy();
Self::restore_sys_proxy(&expected).await;
if let Err(e) = Self::restore_sys_proxy(&expected).await {
log::error!(target: "app", "切换到HTTP代理模式失败: {}", e);
}
}
Self::update_state_timestamp(state, |s| s.pac_enabled = to_pac);
@@ -506,37 +518,45 @@ impl EventDrivenProxyManager {
}
}
async fn restore_pac_proxy(expected_url: &str) {
#[cfg(not(target_os = "windows"))]
#[cfg(target_os = "windows")]
async fn restore_pac_proxy(expected_url: &str) -> Result<(), anyhow::Error> {
Self::execute_sysproxy_command(&["pac", expected_url]).await
}
#[allow(clippy::unused_async)]
#[cfg(not(target_os = "windows"))]
async fn restore_pac_proxy(expected_url: &str) -> Result<(), anyhow::Error> {
{
let new_autoproxy = Autoproxy {
enable: true,
url: expected_url.to_string(),
};
logging_error!(Type::System, true, new_autoproxy.set_auto_proxy());
}
#[cfg(target_os = "windows")]
{
Self::execute_sysproxy_command(&["pac", expected_url]).await;
}
}
async fn restore_sys_proxy(expected: &Sysproxy) {
#[cfg(not(target_os = "windows"))]
{
logging_error!(Type::System, true, expected.set_system_proxy());
}
#[cfg(target_os = "windows")]
{
let address = format!("{}:{}", expected.host, expected.port);
Self::execute_sysproxy_command(&["global", &address, &expected.bypass]).await;
// logging_error!(Type::System, true, new_autoproxy.set_auto_proxy());
new_autoproxy
.set_auto_proxy()
.map_err(|e| anyhow::anyhow!("Failed to set auto proxy: {}", e))
}
}
#[cfg(target_os = "windows")]
async fn execute_sysproxy_command(args: &[&str]) {
async fn restore_sys_proxy(expected: &Sysproxy) -> Result<(), anyhow::Error> {
let address = format!("{}:{}", expected.host, expected.port);
Self::execute_sysproxy_command(&["global", &address, &expected.bypass]).await
}
#[allow(clippy::unused_async)]
#[cfg(not(target_os = "windows"))]
async fn restore_sys_proxy(expected: &Sysproxy) -> Result<(), anyhow::Error> {
{
// logging_error!(Type::System, true, expected.set_system_proxy());
expected
.set_system_proxy()
.map_err(|e| anyhow::anyhow!("Failed to set system proxy: {}", e))
}
}
#[cfg(target_os = "windows")]
async fn execute_sysproxy_command(args: &[&str]) -> Result<(), anyhow::Error> {
use crate::utils::dirs;
#[allow(unused_imports)] // creation_flags必须
use std::os::windows::process::CommandExt;
@@ -546,37 +566,22 @@ impl EventDrivenProxyManager {
Ok(path) => path,
Err(e) => {
log::error!(target: "app", "获取服务路径失败: {e}");
return;
return Err(e);
}
};
let sysproxy_exe = binary_path.with_file_name("sysproxy.exe");
if !sysproxy_exe.exists() {
log::error!(target: "app", "sysproxy.exe 不存在");
return;
}
anyhow::ensure!(sysproxy_exe.exists(), "sysproxy.exe does not exist");
let output = Command::new(sysproxy_exe)
let _output = Command::new(sysproxy_exe)
.args(args)
.creation_flags(0x08000000) // CREATE_NO_WINDOW - 隐藏窗口
.output()
.await;
.await?;
match output {
Ok(output) => {
if !output.status.success() {
log::error!(target: "app", "执行sysproxy命令失败: {args:?}");
let stderr = String::from_utf8_lossy(&output.stderr);
if !stderr.is_empty() {
log::error!(target: "app", "sysproxy错误输出: {stderr}");
}
} else {
log::debug!(target: "app", "成功执行sysproxy命令: {args:?}");
}
}
Err(e) => {
log::error!(target: "app", "执行sysproxy命令出错: {e}");
}
}
Ok(())
}
}

View File

@@ -82,121 +82,123 @@ impl NotificationSystem {
*self.last_emit_time.write() = Instant::now();
self.worker_handle = Some(
thread::Builder::new()
.name("frontend-notifier".into())
.spawn(move || {
let handle = Handle::global();
match thread::Builder::new()
.name("frontend-notifier".into())
.spawn(move || {
let handle = Handle::global();
while !handle.is_exiting() {
match rx.recv_timeout(Duration::from_millis(100)) {
Ok(event) => {
let system_guard = handle.notification_system.read();
if system_guard.as_ref().is_none() {
log::warn!("NotificationSystem not found in handle while processing event.");
continue;
}
let system = system_guard.as_ref().unwrap();
while !handle.is_exiting() {
match rx.recv_timeout(Duration::from_millis(100)) {
Ok(event) => {
let system_guard = handle.notification_system.read();
let Some(system) = system_guard.as_ref() else {
log::warn!("NotificationSystem not found in handle while processing event.");
continue;
};
let is_emergency = *system.emergency_mode.read();
let is_emergency = *system.emergency_mode.read();
if is_emergency {
if let FrontendEvent::NoticeMessage { ref status, .. } = event {
if status == "info" {
log::warn!(
"Emergency mode active, skipping info message"
);
continue;
}
if is_emergency {
if let FrontendEvent::NoticeMessage { ref status, .. } = event {
if status == "info" {
log::warn!(
"Emergency mode active, skipping info message"
);
continue;
}
}
}
if let Some(window) = handle.get_window() {
*system.last_emit_time.write() = Instant::now();
if let Some(window) = handle.get_window() {
*system.last_emit_time.write() = Instant::now();
let (event_name_str, payload_result) = match event {
FrontendEvent::RefreshClash => {
("verge://refresh-clash-config", Ok(serde_json::json!("yes")))
}
FrontendEvent::RefreshVerge => {
("verge://refresh-verge-config", Ok(serde_json::json!("yes")))
}
FrontendEvent::NoticeMessage { status, message } => {
match serde_json::to_value((status, message)) {
Ok(p) => ("verge://notice-message", Ok(p)),
Err(e) => {
log::error!("Failed to serialize NoticeMessage payload: {e}");
("verge://notice-message", Err(e))
}
}
}
FrontendEvent::ProfileChanged { current_profile_id } => {
("profile-changed", Ok(serde_json::json!(current_profile_id)))
}
FrontendEvent::TimerUpdated { profile_index } => {
("verge://timer-updated", Ok(serde_json::json!(profile_index)))
}
FrontendEvent::StartupCompleted => {
("verge://startup-completed", Ok(serde_json::json!(null)))
}
FrontendEvent::ProfileUpdateStarted { uid } => {
("profile-update-started", Ok(serde_json::json!({ "uid": uid })))
}
FrontendEvent::ProfileUpdateCompleted { uid } => {
("profile-update-completed", Ok(serde_json::json!({ "uid": uid })))
}
};
if let Ok(payload) = payload_result {
match window.emit(event_name_str, payload) {
Ok(_) => {
system.stats.total_sent.fetch_add(1, Ordering::SeqCst);
// 记录成功发送的事件
if log::log_enabled!(log::Level::Debug) {
log::debug!("Successfully emitted event: {event_name_str}");
}
}
let (event_name_str, payload_result) = match event {
FrontendEvent::RefreshClash => {
("verge://refresh-clash-config", Ok(serde_json::json!("yes")))
}
FrontendEvent::RefreshVerge => {
("verge://refresh-verge-config", Ok(serde_json::json!("yes")))
}
FrontendEvent::NoticeMessage { status, message } => {
match serde_json::to_value((status, message)) {
Ok(p) => ("verge://notice-message", Ok(p)),
Err(e) => {
log::warn!("Failed to emit event {event_name_str}: {e}");
system.stats.total_errors.fetch_add(1, Ordering::SeqCst);
*system.stats.last_error_time.write() = Some(Instant::now());
let errors = system.stats.total_errors.load(Ordering::SeqCst);
const EMIT_ERROR_THRESHOLD: u64 = 10;
if errors > EMIT_ERROR_THRESHOLD && !*system.emergency_mode.read() {
log::warn!(
"Reached {EMIT_ERROR_THRESHOLD} emit errors, entering emergency mode"
);
*system.emergency_mode.write() = true;
}
log::error!("Failed to serialize NoticeMessage payload: {e}");
("verge://notice-message", Err(e))
}
}
}
FrontendEvent::ProfileChanged { current_profile_id } => {
("profile-changed", Ok(serde_json::json!(current_profile_id)))
}
FrontendEvent::TimerUpdated { profile_index } => {
("verge://timer-updated", Ok(serde_json::json!(profile_index)))
}
FrontendEvent::StartupCompleted => {
("verge://startup-completed", Ok(serde_json::json!(null)))
}
FrontendEvent::ProfileUpdateStarted { uid } => {
("profile-update-started", Ok(serde_json::json!({ "uid": uid })))
}
FrontendEvent::ProfileUpdateCompleted { uid } => {
("profile-update-completed", Ok(serde_json::json!({ "uid": uid })))
}
};
if let Ok(payload) = payload_result {
match window.emit(event_name_str, payload) {
Ok(_) => {
system.stats.total_sent.fetch_add(1, Ordering::SeqCst);
// 记录成功发送的事件
if log::log_enabled!(log::Level::Debug) {
log::debug!("Successfully emitted event: {event_name_str}");
}
}
Err(e) => {
log::warn!("Failed to emit event {event_name_str}: {e}");
system.stats.total_errors.fetch_add(1, Ordering::SeqCst);
*system.stats.last_error_time.write() = Some(Instant::now());
let errors = system.stats.total_errors.load(Ordering::SeqCst);
const EMIT_ERROR_THRESHOLD: u64 = 10;
if errors > EMIT_ERROR_THRESHOLD && !*system.emergency_mode.read() {
log::warn!(
"Reached {EMIT_ERROR_THRESHOLD} emit errors, entering emergency mode"
);
*system.emergency_mode.write() = true;
}
}
} else {
system.stats.total_errors.fetch_add(1, Ordering::SeqCst);
*system.stats.last_error_time.write() = Some(Instant::now());
log::warn!("Skipped emitting event due to payload serialization error for {event_name_str}");
}
} else {
log::warn!("No window found, skipping event emit.");
system.stats.total_errors.fetch_add(1, Ordering::SeqCst);
*system.stats.last_error_time.write() = Some(Instant::now());
log::warn!("Skipped emitting event due to payload serialization error for {event_name_str}");
}
thread::sleep(Duration::from_millis(20));
}
Err(mpsc::RecvTimeoutError::Timeout) => {
continue;
}
Err(mpsc::RecvTimeoutError::Disconnected) => {
log::info!(
"Notification channel disconnected, exiting worker thread"
);
break;
} else {
log::warn!("No window found, skipping event emit.");
}
thread::sleep(Duration::from_millis(20));
}
Err(mpsc::RecvTimeoutError::Timeout) => {
}
Err(mpsc::RecvTimeoutError::Disconnected) => {
log::info!(
"Notification channel disconnected, exiting worker thread"
);
break;
}
}
}
log::info!("Notification worker thread exiting");
})
.expect("Failed to start notification worker thread"),
);
log::info!("Notification worker thread exiting");
}) {
Ok(handle) => {
self.worker_handle = Some(handle);
}
Err(e) => {
log::error!("Failed to start notification worker thread: {e}");
}
}
}
/// 发送事件到队列

View File

@@ -195,7 +195,9 @@ impl Hotkey {
hotkey: &str,
function: HotkeyFunction,
) -> Result<()> {
let app_handle = handle::Handle::global().app_handle().unwrap();
let app_handle = handle::Handle::global()
.app_handle()
.ok_or_else(|| anyhow::anyhow!("Failed to get app handle for hotkey registration"))?;
let manager = app_handle.global_shortcut();
logging!(
@@ -351,7 +353,9 @@ impl Hotkey {
}
pub fn reset(&self) -> Result<()> {
let app_handle = handle::Handle::global().app_handle().unwrap();
let app_handle = handle::Handle::global()
.app_handle()
.ok_or_else(|| anyhow::anyhow!("Failed to get app handle for hotkey registration"))?;
let manager = app_handle.global_shortcut();
manager.unregister_all()?;
Ok(())
@@ -364,7 +368,9 @@ impl Hotkey {
}
pub fn unregister(&self, hotkey: &str) -> Result<()> {
let app_handle = handle::Handle::global().app_handle().unwrap();
let app_handle = handle::Handle::global()
.app_handle()
.ok_or_else(|| anyhow::anyhow!("Failed to get app handle for hotkey registration"))?;
let manager = app_handle.global_shortcut();
manager.unregister(hotkey)?;
logging!(debug, Type::Hotkey, "Unregister hotkey {}", hotkey);
@@ -438,7 +444,17 @@ impl Hotkey {
impl Drop for Hotkey {
fn drop(&mut self) {
let app_handle = handle::Handle::global().app_handle().unwrap();
let app_handle = match handle::Handle::global().app_handle() {
Some(handle) => handle,
None => {
logging!(
error,
Type::Hotkey,
"Failed to get app handle during hotkey cleanup"
);
return;
}
};
if let Err(e) = app_handle.global_shortcut().unregister_all() {
logging!(
error,

View File

@@ -112,7 +112,7 @@ pub struct JsonResponse {
}
#[cfg(target_os = "windows")]
pub async fn uninstall_service() -> Result<()> {
pub fn uninstall_service() -> Result<()> {
logging!(info, Type::Service, true, "uninstall service");
use deelevate::{PrivilegeLevel, Token};
@@ -138,7 +138,7 @@ pub async fn uninstall_service() -> Result<()> {
if !status.success() {
bail!(
"failed to uninstall service with status {}",
status.code().unwrap()
status.code().unwrap_or(-1)
);
}
@@ -146,7 +146,7 @@ pub async fn uninstall_service() -> Result<()> {
}
#[cfg(target_os = "windows")]
pub async fn install_service() -> Result<()> {
pub fn install_service() -> Result<()> {
logging!(info, Type::Service, true, "install service");
use deelevate::{PrivilegeLevel, Token};
@@ -172,7 +172,7 @@ pub async fn install_service() -> Result<()> {
if !status.success() {
bail!(
"failed to install service with status {}",
status.code().unwrap()
status.code().unwrap_or(-1)
);
}
@@ -180,7 +180,7 @@ pub async fn install_service() -> Result<()> {
}
#[cfg(target_os = "windows")]
pub async fn reinstall_service() -> Result<()> {
pub fn reinstall_service() -> Result<()> {
logging!(info, Type::Service, true, "reinstall service");
// 获取当前服务状态
@@ -198,7 +198,7 @@ pub async fn reinstall_service() -> Result<()> {
}
// 先卸载服务
if let Err(err) = uninstall_service().await {
if let Err(err) = uninstall_service() {
logging!(
warn,
Type::Service,
@@ -209,7 +209,7 @@ pub async fn reinstall_service() -> Result<()> {
}
// 再安装服务
match install_service().await {
match install_service() {
Ok(_) => {
// 记录安装信息并保存
service_state.record_install();
@@ -228,7 +228,7 @@ pub async fn reinstall_service() -> Result<()> {
}
#[cfg(target_os = "linux")]
pub async fn uninstall_service() -> Result<()> {
pub fn uninstall_service() -> Result<()> {
logging!(info, Type::Service, true, "uninstall service");
use users::get_effective_uid;
@@ -254,13 +254,13 @@ pub async fn uninstall_service() -> Result<()> {
Type::Service,
true,
"uninstall status code:{}",
status.code().unwrap()
status.code().unwrap_or(-1)
);
if !status.success() {
bail!(
"failed to uninstall service with status {}",
status.code().unwrap()
status.code().unwrap_or(-1)
);
}
@@ -268,7 +268,7 @@ pub async fn uninstall_service() -> Result<()> {
}
#[cfg(target_os = "linux")]
pub async fn install_service() -> Result<()> {
pub fn install_service() -> Result<()> {
logging!(info, Type::Service, true, "install service");
use users::get_effective_uid;
@@ -294,13 +294,13 @@ pub async fn install_service() -> Result<()> {
Type::Service,
true,
"install status code:{}",
status.code().unwrap()
status.code().unwrap_or(-1)
);
if !status.success() {
bail!(
"failed to install service with status {}",
status.code().unwrap()
status.code().unwrap_or(-1)
);
}
@@ -308,7 +308,7 @@ pub async fn install_service() -> Result<()> {
}
#[cfg(target_os = "linux")]
pub async fn reinstall_service() -> Result<()> {
pub fn reinstall_service() -> Result<()> {
logging!(info, Type::Service, true, "reinstall service");
// 获取当前服务状态
@@ -326,7 +326,7 @@ pub async fn reinstall_service() -> Result<()> {
}
// 先卸载服务
if let Err(err) = uninstall_service().await {
if let Err(err) = uninstall_service() {
logging!(
warn,
Type::Service,
@@ -337,7 +337,7 @@ pub async fn reinstall_service() -> Result<()> {
}
// 再安装服务
match install_service().await {
match install_service() {
Ok(_) => {
// 记录安装信息并保存
service_state.record_install();
@@ -356,7 +356,7 @@ pub async fn reinstall_service() -> Result<()> {
}
#[cfg(target_os = "macos")]
pub async fn uninstall_service() -> Result<()> {
pub fn uninstall_service() -> Result<()> {
use crate::utils::i18n::t;
logging!(info, Type::Service, true, "uninstall service");
@@ -384,7 +384,7 @@ pub async fn uninstall_service() -> Result<()> {
if !status.success() {
bail!(
"failed to uninstall service with status {}",
status.code().unwrap()
status.code().unwrap_or(-1)
);
}
@@ -392,7 +392,7 @@ pub async fn uninstall_service() -> Result<()> {
}
#[cfg(target_os = "macos")]
pub async fn install_service() -> Result<()> {
pub fn install_service() -> Result<()> {
use crate::utils::i18n::t;
logging!(info, Type::Service, true, "install service");
@@ -420,7 +420,7 @@ pub async fn install_service() -> Result<()> {
if !status.success() {
bail!(
"failed to install service with status {}",
status.code().unwrap()
status.code().unwrap_or(-1)
);
}
@@ -428,7 +428,7 @@ pub async fn install_service() -> Result<()> {
}
#[cfg(target_os = "macos")]
pub async fn reinstall_service() -> Result<()> {
pub fn reinstall_service() -> Result<()> {
logging!(info, Type::Service, true, "reinstall service");
// 获取当前服务状态
@@ -446,7 +446,7 @@ pub async fn reinstall_service() -> Result<()> {
}
// 先卸载服务
if let Err(err) = uninstall_service().await {
if let Err(err) = uninstall_service() {
logging!(
warn,
Type::Service,
@@ -457,7 +457,7 @@ pub async fn reinstall_service() -> Result<()> {
}
// 再安装服务
match install_service().await {
match install_service() {
Ok(_) => {
// 记录安装信息并保存
service_state.record_install();
@@ -856,17 +856,14 @@ pub(super) async fn run_core_by_service(config_file: &PathBuf) -> Result<()> {
if let Ok(()) = start_with_existing_service(config_file).await {
log::info!(target: "app", "尽管版本不匹配,但成功启动了服务");
return Ok(());
} else {
bail!("服务版本不匹配且无法重装,启动失败");
}
bail!("服务版本不匹配且无法重装,启动失败");
}
log::info!(target: "app", "开始重装服务");
if let Err(err) = reinstall_service().await {
if let Err(err) = reinstall_service() {
log::warn!(target: "app", "服务重装失败: {err}");
log::info!(target: "app", "尝试使用现有服务");
return start_with_existing_service(config_file).await;
bail!("Failed to reinstall service: {}", err);
}
log::info!(target: "app", "服务重装成功,尝试启动");
@@ -890,7 +887,7 @@ pub(super) async fn run_core_by_service(config_file: &PathBuf) -> Result<()> {
if check_service_needs_reinstall().await {
log::info!(target: "app", "服务需要重装");
if let Err(err) = reinstall_service().await {
if let Err(err) = reinstall_service() {
log::warn!(target: "app", "服务重装失败: {err}");
bail!("Failed to reinstall service: {}", err);
}
@@ -970,7 +967,7 @@ pub async fn is_service_available() -> Result<()> {
}
/// 强制重装服务UI修复按钮
pub async fn force_reinstall_service() -> Result<()> {
pub fn force_reinstall_service() -> Result<()> {
log::info!(target: "app", "用户请求强制重装服务");
let service_state = ServiceState::default();
@@ -978,7 +975,7 @@ pub async fn force_reinstall_service() -> Result<()> {
log::info!(target: "app", "已重置服务状态,开始执行重装");
match reinstall_service().await {
match reinstall_service() {
Ok(()) => {
log::info!(target: "app", "服务重装成功");
Ok(())

View File

@@ -149,7 +149,9 @@ impl Sysopt {
use anyhow::bail;
use tauri_plugin_shell::ShellExt;
let app_handle = Handle::global().app_handle().unwrap();
let app_handle = Handle::global()
.app_handle()
.ok_or_else(|| anyhow::anyhow!("App handle not available"))?;
let binary_path = dirs::service_path()?;
let sysproxy_exe = binary_path.with_file_name("sysproxy.exe");
@@ -160,23 +162,27 @@ impl Sysopt {
let shell = app_handle.shell();
let output = if pac_enable {
let address = format!("http://{proxy_host}:{pac_port}/commands/pac");
let output = shell
.command(sysproxy_exe.as_path().to_str().unwrap())
let sysproxy_str = sysproxy_exe
.as_path()
.to_str()
.ok_or_else(|| anyhow::anyhow!("Invalid sysproxy.exe path"))?;
shell
.command(sysproxy_str)
.args(["pac", address.as_str()])
.output()
.await
.unwrap();
output
.await?
} else {
let address = format!("{proxy_host}:{port}");
let bypass = get_bypass();
let output = shell
.command(sysproxy_exe.as_path().to_str().unwrap())
let sysproxy_str = sysproxy_exe
.as_path()
.to_str()
.ok_or_else(|| anyhow::anyhow!("Invalid sysproxy.exe path"))?;
shell
.command(sysproxy_str)
.args(["global", address.as_str(), bypass.as_ref()])
.output()
.await
.unwrap();
output
.await?
};
if !output.status.success() {
@@ -218,7 +224,9 @@ impl Sysopt {
use anyhow::bail;
use tauri_plugin_shell::ShellExt;
let app_handle = Handle::global().app_handle().unwrap();
let app_handle = Handle::global()
.app_handle()
.ok_or_else(|| anyhow::anyhow!("App handle not available"))?;
let binary_path = dirs::service_path()?;
let sysproxy_exe = binary_path.with_file_name("sysproxy.exe");
@@ -228,12 +236,15 @@ impl Sysopt {
}
let shell = app_handle.shell();
let sysproxy_str = sysproxy_exe
.as_path()
.to_str()
.ok_or_else(|| anyhow::anyhow!("Invalid sysproxy.exe path"))?;
let output = shell
.command(sysproxy_exe.as_path().to_str().unwrap())
.command(sysproxy_str)
.args(["set", "1"])
.output()
.await
.unwrap();
.await?;
if !output.status.success() {
bail!("sysproxy exe run failed");
@@ -279,7 +290,10 @@ impl Sysopt {
/// 尝试使用原来的自启动方法
fn try_original_autostart_method(&self, is_enable: bool) {
let app_handle = Handle::global().app_handle().unwrap();
let Some(app_handle) = Handle::global().app_handle() else {
log::error!(target: "app", "App handle not available for autostart");
return;
};
let autostart_manager = app_handle.autolaunch();
if is_enable {
@@ -306,7 +320,9 @@ impl Sysopt {
}
// 回退到原来的方法
let app_handle = Handle::global().app_handle().unwrap();
let app_handle = Handle::global()
.app_handle()
.ok_or_else(|| anyhow::anyhow!("App handle not available"))?;
let autostart_manager = app_handle.autolaunch();
match autostart_manager.is_enabled() {

View File

@@ -71,9 +71,10 @@ impl TrayState {
let verge = Config::verge().latest_ref().clone();
let is_common_tray_icon = verge.common_tray_icon.unwrap_or(false);
if is_common_tray_icon {
if let Some(common_icon_path) = find_target_icons("common").unwrap() {
let icon_data = fs::read(common_icon_path).unwrap();
return (true, icon_data);
if let Ok(Some(common_icon_path)) = find_target_icons("common") {
if let Ok(icon_data) = fs::read(common_icon_path) {
return (true, icon_data);
}
}
}
#[cfg(target_os = "macos")]
@@ -105,9 +106,10 @@ impl TrayState {
let verge = Config::verge().latest_ref().clone();
let is_sysproxy_tray_icon = verge.sysproxy_tray_icon.unwrap_or(false);
if is_sysproxy_tray_icon {
if let Some(sysproxy_icon_path) = find_target_icons("sysproxy").unwrap() {
let icon_data = fs::read(sysproxy_icon_path).unwrap();
return (true, icon_data);
if let Ok(Some(sysproxy_icon_path)) = find_target_icons("sysproxy") {
if let Ok(icon_data) = fs::read(sysproxy_icon_path) {
return (true, icon_data);
}
}
}
#[cfg(target_os = "macos")]
@@ -139,9 +141,10 @@ impl TrayState {
let verge = Config::verge().latest_ref().clone();
let is_tun_tray_icon = verge.tun_tray_icon.unwrap_or(false);
if is_tun_tray_icon {
if let Some(tun_icon_path) = find_target_icons("tun").unwrap() {
let icon_data = fs::read(tun_icon_path).unwrap();
return (true, icon_data);
if let Ok(Some(tun_icon_path)) = find_target_icons("tun") {
if let Ok(icon_data) = fs::read(tun_icon_path) {
return (true, icon_data);
}
}
}
#[cfg(target_os = "macos")]
@@ -188,10 +191,14 @@ impl Tray {
/// 更新托盘点击行为
pub fn update_click_behavior(&self) -> Result<()> {
let app_handle = handle::Handle::global().app_handle().unwrap();
let app_handle = handle::Handle::global()
.app_handle()
.ok_or_else(|| anyhow::anyhow!("Failed to get app handle for tray update"))?;
let tray_event = { Config::verge().latest_ref().tray_event.clone() };
let tray_event: String = tray_event.unwrap_or("main_window".into());
let tray = app_handle.tray_by_id("main").unwrap();
let tray = app_handle
.tray_by_id("main")
.ok_or_else(|| anyhow::anyhow!("Failed to get main tray"))?;
match tray_event.as_str() {
"tray_menu" => tray.set_show_menu_on_left_click(true)?,
_ => tray.set_show_menu_on_left_click(false)?,
@@ -360,8 +367,12 @@ impl Tray {
/// 更新托盘显示状态的函数
pub fn update_tray_display(&self) -> Result<()> {
let app_handle = handle::Handle::global().app_handle().unwrap();
let _tray = app_handle.tray_by_id("main").unwrap();
let app_handle = handle::Handle::global()
.app_handle()
.ok_or_else(|| anyhow::anyhow!("Failed to get app handle for tray update"))?;
let _tray = app_handle
.tray_by_id("main")
.ok_or_else(|| anyhow::anyhow!("Failed to get main tray"))?;
// 更新菜单
self.update_menu()?;
@@ -562,9 +573,8 @@ fn create_tray_menu(
is_current_profile,
None::<&str>,
)
.unwrap()
})
.collect();
.collect::<Result<Vec<_>, _>>()?;
let profile_menu_items: Vec<&dyn IsMenuItem<Wry>> = profile_menu_items
.iter()
.map(|item| item as &dyn IsMenuItem<Wry>)
@@ -576,8 +586,7 @@ fn create_tray_menu(
t("Dashboard"),
true,
hotkeys.get("open_or_close_dashboard").map(|s| s.as_str()),
)
.unwrap();
)?;
let rule_mode = &CheckMenuItem::with_id(
app_handle,
@@ -586,8 +595,7 @@ fn create_tray_menu(
true,
mode == "rule",
hotkeys.get("clash_mode_rule").map(|s| s.as_str()),
)
.unwrap();
)?;
let global_mode = &CheckMenuItem::with_id(
app_handle,
@@ -596,8 +604,7 @@ fn create_tray_menu(
true,
mode == "global",
hotkeys.get("clash_mode_global").map(|s| s.as_str()),
)
.unwrap();
)?;
let direct_mode = &CheckMenuItem::with_id(
app_handle,
@@ -606,8 +613,7 @@ fn create_tray_menu(
true,
mode == "direct",
hotkeys.get("clash_mode_direct").map(|s| s.as_str()),
)
.unwrap();
)?;
let profiles = &Submenu::with_id_and_items(
app_handle,
@@ -615,8 +621,7 @@ fn create_tray_menu(
t("Profiles"),
true,
&profile_menu_items,
)
.unwrap();
)?;
let system_proxy = &CheckMenuItem::with_id(
app_handle,
@@ -625,8 +630,7 @@ fn create_tray_menu(
true,
system_proxy_enabled,
hotkeys.get("toggle_system_proxy").map(|s| s.as_str()),
)
.unwrap();
)?;
let tun_mode = &CheckMenuItem::with_id(
app_handle,
@@ -635,8 +639,7 @@ fn create_tray_menu(
true,
tun_mode_enabled,
hotkeys.get("toggle_tun_mode").map(|s| s.as_str()),
)
.unwrap();
)?;
let lighteweight_mode = &CheckMenuItem::with_id(
app_handle,
@@ -645,11 +648,9 @@ fn create_tray_menu(
true,
is_lightweight_mode,
hotkeys.get("entry_lightweight_mode").map(|s| s.as_str()),
)
.unwrap();
)?;
let copy_env =
&MenuItem::with_id(app_handle, "copy_env", t("Copy Env"), true, None::<&str>).unwrap();
let copy_env = &MenuItem::with_id(app_handle, "copy_env", t("Copy Env"), true, None::<&str>)?;
let open_app_dir = &MenuItem::with_id(
app_handle,
@@ -657,8 +658,7 @@ fn create_tray_menu(
t("Conf Dir"),
true,
None::<&str>,
)
.unwrap();
)?;
let open_core_dir = &MenuItem::with_id(
app_handle,
@@ -666,8 +666,7 @@ fn create_tray_menu(
t("Core Dir"),
true,
None::<&str>,
)
.unwrap();
)?;
let open_logs_dir = &MenuItem::with_id(
app_handle,
@@ -675,8 +674,7 @@ fn create_tray_menu(
t("Logs Dir"),
true,
None::<&str>,
)
.unwrap();
)?;
let open_dir = &Submenu::with_id_and_items(
app_handle,
@@ -684,8 +682,7 @@ fn create_tray_menu(
t("Open Dir"),
true,
&[open_app_dir, open_core_dir, open_logs_dir],
)
.unwrap();
)?;
let restart_clash = &MenuItem::with_id(
app_handle,
@@ -693,8 +690,7 @@ fn create_tray_menu(
t("Restart Clash Core"),
true,
None::<&str>,
)
.unwrap();
)?;
let restart_app = &MenuItem::with_id(
app_handle,
@@ -702,8 +698,7 @@ fn create_tray_menu(
t("Restart App"),
true,
None::<&str>,
)
.unwrap();
)?;
let app_version = &MenuItem::with_id(
app_handle,
@@ -711,8 +706,7 @@ fn create_tray_menu(
format!("{} {version}", t("Verge Version")),
true,
None::<&str>,
)
.unwrap();
)?;
let more = &Submenu::with_id_and_items(
app_handle,
@@ -720,13 +714,11 @@ fn create_tray_menu(
t("More"),
true,
&[restart_clash, restart_app, app_version],
)
.unwrap();
)?;
let quit =
&MenuItem::with_id(app_handle, "quit", t("Exit"), true, Some("CmdOrControl+Q")).unwrap();
let quit = &MenuItem::with_id(app_handle, "quit", t("Exit"), true, Some("CmdOrControl+Q"))?;
let separator = &PredefinedMenuItem::separator(app_handle).unwrap();
let separator = &PredefinedMenuItem::separator(app_handle)?;
let menu = tauri::menu::MenuBuilder::new(app_handle)
.items(&[
@@ -748,8 +740,7 @@ fn create_tray_menu(
separator,
quit,
])
.build()
.unwrap();
.build()?;
Ok(menu)
}

View File

@@ -6,7 +6,7 @@ use deelevate::{PrivilegeLevel, Token};
use runas::Command as RunasCommand;
use std::process::Command as StdCommand;
pub async fn invoke_uwptools() -> Result<()> {
pub fn invoke_uwptools() -> Result<()> {
let resource_dir = dirs::app_resources_dir()?;
let tool_path = resource_dir.join("enableLoopback.exe");