use crate::{ config::{Config, IVerge}, core::{CoreManager, handle, hotkey, sysopt, tray}, module::{auto_backup::AutoBackupManager, lightweight}, }; use anyhow::Result; use bitflags::bitflags; use clash_verge_draft::SharedDraft; use clash_verge_logging::{Type, logging, logging_error}; use serde_yaml_ng::Mapping; /// Patch Clash configuration pub async fn patch_clash(patch: &Mapping) -> Result<()> { Config::clash().await.edit_draft(|d| d.patch_config(patch)); let res = { // 激活订阅 if patch.get("secret").is_some() || patch.get("external-controller").is_some() { Config::generate().await?; CoreManager::global().restart_core().await?; } else { if patch.get("mode").is_some() { logging_error!(Type::Tray, tray::Tray::global().update_menu().await); logging_error!( Type::Tray, tray::Tray::global() .update_icon(&Config::verge().await.data_arc()) .await ); } Config::runtime().await.edit_draft(|d| d.patch_config(patch)); CoreManager::global().update_config().await?; } handle::Handle::refresh_clash(); >::Ok(()) }; match res { Ok(()) => { Config::clash().await.apply(); // 分离数据获取和异步调用 let clash_data = Config::clash().await.data_arc(); clash_data.save_config().await?; Ok(()) } Err(err) => { Config::clash().await.discard(); Err(err) } } } // Define update flags as bitflags for better performance bitflags! { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] struct UpdateFlags: u16 { const RESTART_CORE = 1 << 0; const CLASH_CONFIG = 1 << 1; const VERGE_CONFIG = 1 << 2; const LAUNCH = 1 << 3; const SYS_PROXY = 1 << 4; const SYSTRAY_ICON = 1 << 5; const HOTKEY = 1 << 6; const SYSTRAY_MENU = 1 << 7; const SYSTRAY_TOOLTIP = 1 << 8; const SYSTRAY_CLICK_BEHAVIOR = 1 << 9; const LIGHT_WEIGHT = 1 << 10; const LANGUAGE = 1 << 11; const GROUP_SYS_TRAY = Self::SYSTRAY_MENU.bits() | Self::SYSTRAY_TOOLTIP.bits() | Self::SYSTRAY_ICON.bits(); } } fn determine_update_flags(patch: &IVerge) -> UpdateFlags { let tun_mode = patch.enable_tun_mode; let auto_launch = patch.enable_auto_launch; let system_proxy = patch.enable_system_proxy; let pac = patch.proxy_auto_config; let pac_content = &patch.pac_file_content; let proxy_bypass = &patch.system_proxy_bypass; let language = &patch.language; let mixed_port = patch.verge_mixed_port; #[cfg(target_os = "macos")] let tray_icon = &patch.tray_icon; #[cfg(not(target_os = "macos"))] let tray_icon: Option = None; let common_tray_icon = patch.common_tray_icon; let sysproxy_tray_icon = patch.sysproxy_tray_icon; let tun_tray_icon = patch.tun_tray_icon; #[cfg(not(target_os = "windows"))] let redir_enabled = patch.verge_redir_enabled; #[cfg(not(target_os = "windows"))] let redir_port = patch.verge_redir_port; #[cfg(target_os = "linux")] let tproxy_enabled = patch.verge_tproxy_enabled; #[cfg(target_os = "linux")] let tproxy_port = patch.verge_tproxy_port; let socks_enabled = patch.verge_socks_enabled; let socks_port = patch.verge_socks_port; let http_enabled = patch.verge_http_enabled; let http_port = patch.verge_port; let enable_tray_speed = patch.enable_tray_speed; // let enable_tray_icon = patch.enable_tray_icon; let enable_global_hotkey = patch.enable_global_hotkey; let tray_event = &patch.tray_event; let home_cards = patch.home_cards.as_ref(); let enable_auto_light_weight = patch.enable_auto_light_weight_mode; let enable_external_controller = patch.enable_external_controller; let tray_proxy_groups_display_mode = &patch.tray_proxy_groups_display_mode; let tray_inline_outbound_modes = patch.tray_inline_outbound_modes; let enable_proxy_guard = patch.enable_proxy_guard; let proxy_guard_duration = patch.proxy_guard_duration; #[cfg(target_os = "windows")] let restart_core_needed = socks_enabled.is_some() || http_enabled.is_some() || socks_port.is_some() || http_port.is_some() || mixed_port.is_some() || enable_external_controller.is_some(); #[cfg(not(target_os = "windows"))] let mut restart_core_needed = socks_enabled.is_some() || http_enabled.is_some() || socks_port.is_some() || http_port.is_some() || mixed_port.is_some() || enable_external_controller.is_some(); #[cfg(not(target_os = "windows"))] { restart_core_needed |= redir_enabled.is_some() || redir_port.is_some(); } #[cfg(target_os = "linux")] { restart_core_needed |= tproxy_enabled.is_some() || tproxy_port.is_some(); } let mut update_flags = UpdateFlags::empty(); if restart_core_needed { update_flags.insert(UpdateFlags::RESTART_CORE); } if tun_mode.is_some() { update_flags.insert(UpdateFlags::CLASH_CONFIG | UpdateFlags::GROUP_SYS_TRAY); } if enable_global_hotkey.is_some() || home_cards.is_some() { update_flags.insert(UpdateFlags::VERGE_CONFIG); } if auto_launch.is_some() { update_flags.insert(UpdateFlags::LAUNCH); } if system_proxy.is_some() { update_flags.insert(UpdateFlags::SYS_PROXY | UpdateFlags::GROUP_SYS_TRAY); } if proxy_bypass.is_some() || pac_content.is_some() || pac.is_some() || enable_proxy_guard.is_some() || proxy_guard_duration.is_some() { update_flags.insert(UpdateFlags::SYS_PROXY); } if language.is_some() { update_flags.insert(UpdateFlags::LANGUAGE | UpdateFlags::SYSTRAY_MENU | UpdateFlags::SYSTRAY_TOOLTIP); } if common_tray_icon.is_some() || sysproxy_tray_icon.is_some() || tun_tray_icon.is_some() || tray_icon.is_some() || enable_tray_speed.is_some() { update_flags.insert(UpdateFlags::SYSTRAY_ICON); } if patch.hotkeys.is_some() { update_flags.insert(UpdateFlags::HOTKEY | UpdateFlags::SYSTRAY_MENU); } if tray_event.is_some() { update_flags.insert(UpdateFlags::SYSTRAY_CLICK_BEHAVIOR); } if enable_auto_light_weight.is_some() { update_flags.insert(UpdateFlags::LIGHT_WEIGHT); } if tray_proxy_groups_display_mode.is_some() { update_flags.insert(UpdateFlags::SYSTRAY_MENU); } if tray_inline_outbound_modes.is_some() { update_flags.insert(UpdateFlags::SYSTRAY_MENU); } update_flags } #[allow(clippy::cognitive_complexity)] async fn process_terminated_flags(update_flags: UpdateFlags, patch: &IVerge) -> Result<()> { // Process updates based on flags if update_flags.contains(UpdateFlags::RESTART_CORE) { Config::generate().await?; CoreManager::global().restart_core().await?; } if update_flags.contains(UpdateFlags::CLASH_CONFIG) { CoreManager::global().update_config().await?; handle::Handle::refresh_clash(); } if update_flags.contains(UpdateFlags::VERGE_CONFIG) { Config::verge() .await .edit_draft(|d| d.enable_global_hotkey = patch.enable_global_hotkey); handle::Handle::refresh_verge(); } if update_flags.contains(UpdateFlags::LAUNCH) { sysopt::Sysopt::global().update_launch().await?; } if update_flags.contains(UpdateFlags::LANGUAGE) && let Some(language) = &patch.language { clash_verge_i18n::set_locale(language.as_str()); } if update_flags.contains(UpdateFlags::SYS_PROXY) { sysopt::Sysopt::global().update_sysproxy().await?; sysopt::Sysopt::global().refresh_guard().await; } if update_flags.contains(UpdateFlags::HOTKEY) && let Some(hotkeys) = &patch.hotkeys { hotkey::Hotkey::global().update(hotkeys.to_owned()).await?; } if update_flags.contains(UpdateFlags::SYSTRAY_MENU) { tray::Tray::global().update_menu().await?; } if update_flags.contains(UpdateFlags::SYSTRAY_ICON) { tray::Tray::global() .update_icon(&Config::verge().await.latest_arc()) .await?; } if update_flags.contains(UpdateFlags::SYSTRAY_TOOLTIP) { tray::Tray::global().update_tooltip().await?; } if update_flags.contains(UpdateFlags::SYSTRAY_CLICK_BEHAVIOR) { tray::Tray::global().update_click_behavior().await?; } if update_flags.contains(UpdateFlags::LIGHT_WEIGHT) { if patch.enable_auto_light_weight_mode.unwrap_or(false) { lightweight::enable_auto_light_weight_mode().await; } else { lightweight::disable_auto_light_weight_mode(); } } Ok(()) } pub async fn patch_verge(patch: &IVerge, not_save_file: bool) -> Result<()> { Config::verge().await.edit_draft(|d| d.patch_config(patch)); let update_flags = determine_update_flags(patch); logging!(debug, Type::Setup, "Determined update flags: {:?}", update_flags); let process_flag_result: std::result::Result<(), anyhow::Error> = { process_terminated_flags(update_flags, patch).await?; Ok(()) }; if let Err(err) = process_flag_result { Config::verge().await.discard(); return Err(err); } Config::verge().await.apply(); logging_error!(Type::Backup, AutoBackupManager::global().refresh_settings().await); if !not_save_file { // 分离数据获取和异步调用 let verge_data = Config::verge().await.data_arc(); logging!(debug, Type::Setup, "Saving Verge configuration to file..."); verge_data.save_file().await?; } Ok(()) } pub async fn fetch_verge_config() -> Result> { let draft = Config::verge().await; let data = draft.data_arc(); Ok(data) }