diff --git a/src-tauri/benches/draft_benchmark.rs b/src-tauri/benches/draft_benchmark.rs index 3942382f..c8a07c53 100644 --- a/src-tauri/benches/draft_benchmark.rs +++ b/src-tauri/benches/draft_benchmark.rs @@ -7,13 +7,13 @@ use app_lib::config::IVerge; use app_lib::utils::Draft as DraftNew; /// 创建测试数据 -fn make_draft() -> DraftNew> { - let verge = Box::new(IVerge { +fn make_draft() -> DraftNew { + let verge = IVerge { enable_auto_launch: Some(true), enable_tun_mode: Some(false), ..Default::default() - }); - DraftNew::from(verge) + }; + DraftNew::new(verge) } pub fn bench_draft(c: &mut Criterion) { @@ -30,18 +30,17 @@ pub fn bench_draft(c: &mut Criterion) { group.bench_function("data_mut", |b| { b.iter(|| { let draft = black_box(make_draft()); - let mut data = draft.data_mut(); - data.enable_tun_mode = Some(true); - black_box(&data.enable_tun_mode); + draft.edit_draft(|d| d.enable_tun_mode = Some(true)); + black_box(&draft.latest_arc().enable_tun_mode); }); }); group.bench_function("draft_mut_first", |b| { b.iter(|| { let draft = black_box(make_draft()); - let mut d = draft.draft_mut(); - d.enable_auto_launch = Some(false); - black_box(&d.enable_auto_launch); + draft.edit_draft(|d| d.enable_auto_launch = Some(false)); + let latest = draft.latest_arc(); + black_box(&latest.enable_auto_launch); }); }); @@ -49,20 +48,24 @@ pub fn bench_draft(c: &mut Criterion) { b.iter(|| { let draft = black_box(make_draft()); { - let mut first = draft.draft_mut(); - first.enable_tun_mode = Some(true); - black_box(&first.enable_tun_mode); + draft.edit_draft(|d| { + d.enable_tun_mode = Some(true); + }); + let latest1 = draft.latest_arc(); + black_box(&latest1.enable_tun_mode); } - let mut second = draft.draft_mut(); - second.enable_tun_mode = Some(false); - black_box(&second.enable_tun_mode); + draft.edit_draft(|d| { + d.enable_tun_mode = Some(false); + }); + let latest2 = draft.latest_arc(); + black_box(&latest2.enable_tun_mode); }); }); - group.bench_function("latest_ref", |b| { + group.bench_function("latest_arc", |b| { b.iter(|| { let draft = black_box(make_draft()); - let latest = draft.latest_ref(); + let latest = draft.latest_arc(); black_box(&latest.enable_auto_launch); }); }); @@ -71,8 +74,9 @@ pub fn bench_draft(c: &mut Criterion) { b.iter(|| { let draft = black_box(make_draft()); { - let mut d = draft.draft_mut(); - d.enable_auto_launch = Some(false); + draft.edit_draft(|d| { + d.enable_auto_launch = Some(false); + }); } draft.apply(); black_box(&draft); @@ -83,8 +87,9 @@ pub fn bench_draft(c: &mut Criterion) { b.iter(|| { let draft = black_box(make_draft()); { - let mut d = draft.draft_mut(); - d.enable_auto_launch = Some(false); + draft.edit_draft(|d| { + d.enable_auto_launch = Some(false); + }); } draft.discard(); black_box(&draft); @@ -95,7 +100,7 @@ pub fn bench_draft(c: &mut Criterion) { b.to_async(&rt).iter(|| async { let draft = black_box(make_draft()); let _: Result<(), anyhow::Error> = draft - .with_data_modify::<_, _, _, anyhow::Error>(|mut box_data| async move { + .with_data_modify::<_, _, _>(|mut box_data| async move { box_data.enable_auto_launch = Some(!box_data.enable_auto_launch.unwrap_or(false)); Ok((box_data, ())) diff --git a/src-tauri/src/cmd/clash.rs b/src-tauri/src/cmd/clash.rs index 1bcadea9..b51afce5 100644 --- a/src-tauri/src/cmd/clash.rs +++ b/src-tauri/src/cmd/clash.rs @@ -2,11 +2,11 @@ use super::CmdResult; use crate::utils::dirs; use crate::{ cmd::StringifyErr, - config::Config, + config::{ClashInfo, Config}, constants, core::{CoreManager, handle, validate::CoreConfigValidator}, }; -use crate::{config::*, feat, logging, utils::logging::Type}; +use crate::{feat, logging, utils::logging::Type}; use compact_str::CompactString; use serde_yaml_ng::Mapping; use smartstring::alias::String; @@ -22,7 +22,7 @@ pub async fn copy_clash_env() -> CmdResult { /// 获取Clash信息 #[tauri::command] pub async fn get_clash_info() -> CmdResult { - Ok(Config::clash().await.latest_ref().get_client_info()) + Ok(Config::clash().await.latest_arc().get_client_info()) } /// 修改Clash配置 @@ -141,12 +141,6 @@ pub async fn save_dns_config(dns_config: Mapping) -> CmdResult { /// 应用或撤销DNS配置 #[tauri::command] pub async fn apply_dns_config(apply: bool) -> CmdResult { - use crate::{ - config::Config, - core::{CoreManager, handle}, - utils::dirs, - }; - if apply { // 读取DNS配置文件 let dns_path = dirs::app_home_dir() @@ -175,7 +169,9 @@ pub async fn apply_dns_config(apply: bool) -> CmdResult { patch.insert("dns".into(), patch_config.into()); // 应用DNS配置到运行时配置 - Config::runtime().await.draft_mut().patch_config(patch); + Config::runtime().await.edit_draft(|d| { + d.patch_config(patch); + }); // 重新生成配置 Config::generate().await.stringify_err_log(|err| { diff --git a/src-tauri/src/cmd/profile.rs b/src-tauri/src/cmd/profile.rs index c6a04fd6..e2bcf3b6 100644 --- a/src-tauri/src/cmd/profile.rs +++ b/src-tauri/src/cmd/profile.rs @@ -1,5 +1,6 @@ use super::CmdResult; use super::StringifyErr; +use crate::utils::draft::SharedBox; use crate::{ config::{ Config, IProfiles, PrfItem, PrfOption, @@ -23,11 +24,11 @@ use std::time::Duration; static CURRENT_SWITCHING_PROFILE: AtomicBool = AtomicBool::new(false); #[tauri::command] -pub async fn get_profiles() -> CmdResult { +pub async fn get_profiles() -> CmdResult> { logging!(debug, Type::Cmd, "获取配置文件列表"); let draft = Config::profiles().await; - let latest = draft.latest_ref(); - Ok((**latest).clone()) + let latest = draft.latest_arc(); + Ok(latest) } /// 增强配置文件 @@ -172,7 +173,7 @@ async fn validate_new_profile(new_profile: &String) -> Result<(), ()> { // 获取目标配置文件路径 let config_file_result = { let profiles_config = Config::profiles().await; - let profiles_data = profiles_config.latest_ref(); + let profiles_data = profiles_config.latest_arc(); match profiles_data.get_item(new_profile) { Ok(item) => { if let Some(file) = &item.file { @@ -282,8 +283,7 @@ async fn restore_previous_profile(prev_profile: String) -> CmdResult<()> { }; Config::profiles() .await - .draft_mut() - .patch_config(&restore_profiles) + .edit_draft(|d| d.patch_config(&restore_profiles)) .stringify_err()?; Config::profiles().await.apply(); crate::process::AsyncHandler::spawn(|| async move { @@ -392,7 +392,7 @@ pub async fn patch_profiles_config(profiles: IProfiles) -> CmdResult { ); // 保存当前配置,以便在验证失败时恢复 - let current_profile = Config::profiles().await.latest_ref().current.clone(); + let current_profile = Config::profiles().await.latest_arc().current.clone(); logging!(info, Type::Cmd, "当前配置: {:?}", current_profile); // 如果要切换配置,先检查目标配置文件是否有语法错误 @@ -403,7 +403,10 @@ pub async fn patch_profiles_config(profiles: IProfiles) -> CmdResult { return Ok(false); } - let _ = Config::profiles().await.draft_mut().patch_config(&profiles); + let _ = Config::profiles() + .await + .edit_draft(|d| d.patch_config(&profiles)); + let current_value = profiles.current.clone(); perform_config_update(current_value, current_profile).await @@ -426,7 +429,7 @@ pub async fn patch_profiles_config_by_profile_index(profile_index: String) -> Cm pub async fn patch_profile(index: String, profile: PrfItem) -> CmdResult { // 保存修改前检查是否有更新 update_interval let profiles = Config::profiles().await; - let should_refresh_timer = if let Ok(old_profile) = profiles.latest_ref().get_item(&index) + let should_refresh_timer = if let Ok(old_profile) = profiles.latest_arc().get_item(&index) && let Some(new_option) = profile.option.as_ref() { let old_interval = old_profile.option.as_ref().and_then(|o| o.update_interval); @@ -465,7 +468,7 @@ pub async fn patch_profile(index: String, profile: PrfItem) -> CmdResult { #[tauri::command] pub async fn view_profile(index: String) -> CmdResult { let profiles = Config::profiles().await; - let profiles_ref = profiles.latest_ref(); + let profiles_ref = profiles.latest_arc(); let file = profiles_ref .get_item(&index) .stringify_err()? @@ -488,7 +491,7 @@ pub async fn view_profile(index: String) -> CmdResult { pub async fn read_profile_file(index: String) -> CmdResult { let item = { let profiles = Config::profiles().await; - let profiles_ref = profiles.latest_ref(); + let profiles_ref = profiles.latest_arc(); PrfItem { file: profiles_ref.get_item(&index).stringify_err()?.file.clone(), ..Default::default() diff --git a/src-tauri/src/cmd/runtime.rs b/src-tauri/src/cmd/runtime.rs index 05ae1252..5ecfe954 100644 --- a/src-tauri/src/cmd/runtime.rs +++ b/src-tauri/src/cmd/runtime.rs @@ -8,14 +8,14 @@ use std::collections::HashMap; /// 获取运行时配置 #[tauri::command] pub async fn get_runtime_config() -> CmdResult> { - Ok(Config::runtime().await.latest_ref().config.clone()) + Ok(Config::runtime().await.latest_arc().config.clone()) } /// 获取运行时YAML配置 #[tauri::command] pub async fn get_runtime_yaml() -> CmdResult { let runtime = Config::runtime().await; - let runtime = runtime.latest_ref(); + let runtime = runtime.latest_arc(); let config = runtime.config.as_ref(); config @@ -31,19 +31,19 @@ pub async fn get_runtime_yaml() -> CmdResult { /// 获取运行时存在的键 #[tauri::command] pub async fn get_runtime_exists() -> CmdResult> { - Ok(Config::runtime().await.latest_ref().exists_keys.clone()) + Ok(Config::runtime().await.latest_arc().exists_keys.clone()) } /// 获取运行时日志 #[tauri::command] pub async fn get_runtime_logs() -> CmdResult>> { - Ok(Config::runtime().await.latest_ref().chain_logs.clone()) + Ok(Config::runtime().await.latest_arc().chain_logs.clone()) } #[tauri::command] pub async fn get_runtime_proxy_chain_config(proxy_chain_exit_node: String) -> CmdResult { let runtime = Config::runtime().await; - let runtime = runtime.latest_ref(); + let runtime = runtime.latest_arc(); let config = runtime .config @@ -98,9 +98,7 @@ pub async fn update_proxy_chain_config_in_runtime( ) -> CmdResult<()> { { let runtime = Config::runtime().await; - let mut draft = runtime.draft_mut(); - draft.update_proxy_chain_config(proxy_chain_config); - drop(draft); + runtime.edit_draft(|d| d.update_proxy_chain_config(proxy_chain_config)); runtime.apply(); } diff --git a/src-tauri/src/cmd/save_profile.rs b/src-tauri/src/cmd/save_profile.rs index 1e905e63..a5246295 100644 --- a/src-tauri/src/cmd/save_profile.rs +++ b/src-tauri/src/cmd/save_profile.rs @@ -20,7 +20,7 @@ pub async fn save_profile_file(index: String, file_data: Option) -> CmdR // 在异步操作前获取必要元数据并释放锁 let (rel_path, is_merge_file) = { let profiles = Config::profiles().await; - let profiles_guard = profiles.latest_ref(); + let profiles_guard = profiles.latest_arc(); let item = profiles_guard.get_item(&index).stringify_err()?; let is_merge = item.itype.as_ref().is_some_and(|t| t == "merge"); let path = item.file.clone().ok_or("file field is null")?; diff --git a/src-tauri/src/cmd/verge.rs b/src-tauri/src/cmd/verge.rs index cce4043d..9944305f 100644 --- a/src-tauri/src/cmd/verge.rs +++ b/src-tauri/src/cmd/verge.rs @@ -1,16 +1,16 @@ use super::CmdResult; -use crate::{cmd::StringifyErr, config::*, feat}; +use crate::{ + cmd::StringifyErr, + config::{Config, IVerge}, + feat, + utils::draft::SharedBox, +}; /// 获取Verge配置 #[tauri::command] -pub async fn get_verge_config() -> CmdResult { +pub async fn get_verge_config() -> CmdResult> { let verge = Config::verge().await; - let verge_data = { - let ref_data = verge.latest_ref(); - ref_data.clone() - }; - let verge_response = IVergeResponse::from(verge_data); - Ok(verge_response) + Ok(verge.latest_arc()) } /// 修改Verge配置 diff --git a/src-tauri/src/cmd/webdav.rs b/src-tauri/src/cmd/webdav.rs index 1de27b70..209f39e7 100644 --- a/src-tauri/src/cmd/webdav.rs +++ b/src-tauri/src/cmd/webdav.rs @@ -1,5 +1,9 @@ use super::CmdResult; -use crate::{cmd::StringifyErr, config::*, core, feat}; +use crate::{ + cmd::StringifyErr, + config::{Config, IVerge}, + core, feat, +}; use reqwest_dav::list_cmd::ListFile; use smartstring::alias::String; @@ -12,15 +16,11 @@ pub async fn save_webdav_config(url: String, username: String, password: String) webdav_password: Some(password), ..IVerge::default() }; - Config::verge().await.draft_mut().patch_config(&patch); + Config::verge().await.edit_draft(|e| e.patch_config(&patch)); Config::verge().await.apply(); - // 分离数据获取和异步调用 - let verge_data = Config::verge().await.latest_ref().clone(); - verge_data - .save_file() - .await - .map_err(|err| err.to_string())?; + let verge_data = Config::verge().await.latest_arc(); + verge_data.save_file().await.stringify_err()?; core::backup::WebDavClient::global().reset(); Ok(()) } diff --git a/src-tauri/src/config/clash.rs b/src-tauri/src/config/clash.rs index 8b1c072a..195ec6c9 100644 --- a/src-tauri/src/config/clash.rs +++ b/src-tauri/src/config/clash.rs @@ -300,7 +300,7 @@ impl IClashTemp { // 检查 enable_external_controller 设置,用于运行时配置生成 let enable_external_controller = Config::verge() .await - .latest_ref() + .latest_arc() .enable_external_controller .unwrap_or(false); diff --git a/src-tauri/src/config/config.rs b/src-tauri/src/config/config.rs index f9bbcf0a..c523bbda 100644 --- a/src-tauri/src/config/config.rs +++ b/src-tauri/src/config/config.rs @@ -15,10 +15,10 @@ use tokio::sync::OnceCell; use tokio::time::sleep; pub struct Config { - clash_config: Draft>, - verge_config: Draft>, - profiles_config: Draft>, - runtime_config: Draft>, + clash_config: Draft, + verge_config: Draft, + profiles_config: Draft, + runtime_config: Draft, } impl Config { @@ -27,28 +27,28 @@ impl Config { CONFIG .get_or_init(|| async { Config { - clash_config: Draft::from(Box::new(IClashTemp::new().await)), - verge_config: Draft::from(Box::new(IVerge::new().await)), - profiles_config: Draft::from(Box::new(IProfiles::new().await)), - runtime_config: Draft::from(Box::new(IRuntime::new())), + clash_config: Draft::new(IClashTemp::new().await), + verge_config: Draft::new(IVerge::new().await), + profiles_config: Draft::new(IProfiles::new().await), + runtime_config: Draft::new(IRuntime::new()), } }) .await } - pub async fn clash() -> Draft> { + pub async fn clash() -> Draft { Self::global().await.clash_config.clone() } - pub async fn verge() -> Draft> { + pub async fn verge() -> Draft { Self::global().await.verge_config.clone() } - pub async fn profiles() -> Draft> { + pub async fn profiles() -> Draft { Self::global().await.profiles_config.clone() } - pub async fn runtime() -> Draft> { + pub async fn runtime() -> Draft { Self::global().await.runtime_config.clone() } @@ -61,12 +61,14 @@ impl Config { && service::is_service_available().await.is_err() { let verge = Config::verge().await; - verge.draft_mut().enable_tun_mode = Some(false); + verge.edit_draft(|d| { + d.enable_tun_mode = Some(false); + }); verge.apply(); let _ = tray::Tray::global().update_tray_display().await; // 分离数据获取和异步调用避免Send问题 - let verge_data = Config::verge().await.latest_ref().clone(); + let verge_data = Config::verge().await.latest_arc(); logging_error!(Type::Core, verge_data.save_file().await); } @@ -83,11 +85,11 @@ impl Config { // Ensure "Merge" and "Script" profile items exist, adding them if missing. async fn ensure_default_profile_items() -> Result<()> { let profiles = Self::profiles().await; - if profiles.latest_ref().get_item("Merge").is_err() { + if profiles.latest_arc().get_item("Merge").is_err() { let merge_item = &mut PrfItem::from_merge(Some("Merge".into()))?; profiles_append_item_safe(merge_item).await?; } - if profiles.latest_ref().get_item("Script").is_err() { + if profiles.latest_arc().get_item("Script").is_err() { let script_item = &mut PrfItem::from_script(Some("Script".into()))?; profiles_append_item_safe(script_item).await?; } @@ -154,7 +156,7 @@ impl Config { let runtime = Config::runtime().await; let config = runtime - .latest_ref() + .latest_arc() .config .as_ref() .ok_or_else(|| anyhow!("failed to get runtime config"))? @@ -168,11 +170,13 @@ impl Config { pub async fn generate() -> Result<()> { let (config, exists_keys, logs) = enhance::enhance().await; - **Config::runtime().await.draft_mut() = IRuntime { - config: Some(config), - exists_keys, - chain_logs: logs, - }; + Config::runtime().await.edit_draft(|d| { + *d = IRuntime { + config: Some(config), + exists_keys, + chain_logs: logs, + } + }); Ok(()) } @@ -187,7 +191,7 @@ impl Config { }; let operation = || async { - if Config::runtime().await.latest_ref().config.is_some() { + if Config::runtime().await.latest_arc().config.is_some() { return Ok::<(), BackoffError>(()); } @@ -228,7 +232,7 @@ mod tests { #[test] #[allow(unused_variables)] fn test_draft_size_non_boxed() { - let draft = Draft::from(IRuntime::new()); + let draft = Draft::new(IRuntime::new()); let iruntime_size = std::mem::size_of_val(&draft); assert_eq!(iruntime_size, std::mem::size_of::>()); } @@ -236,7 +240,7 @@ mod tests { #[test] #[allow(unused_variables)] fn test_draft_size_boxed() { - let draft = Draft::from(Box::new(IRuntime::new())); + let draft = Draft::new(Box::new(IRuntime::new())); let box_iruntime_size = std::mem::size_of_val(&draft); assert_eq!( box_iruntime_size, diff --git a/src-tauri/src/config/verge.rs b/src-tauri/src/config/verge.rs index 1e37cee4..64af34c2 100644 --- a/src-tauri/src/config/verge.rs +++ b/src-tauri/src/config/verge.rs @@ -313,7 +313,9 @@ impl IVerge { ); let config_draft = Config::verge().await; - **config_draft.draft_mut() = updated_config; + config_draft.edit_draft(|d| { + *d = updated_config; + }); config_draft.apply(); Ok(()) @@ -696,9 +698,3 @@ impl From for IVergeResponse { } } } - -impl From> for IVergeResponse { - fn from(verge: Box) -> Self { - IVergeResponse::from(*verge) - } -} diff --git a/src-tauri/src/core/backup.rs b/src-tauri/src/core/backup.rs index 6e48a137..3e94a733 100644 --- a/src-tauri/src/core/backup.rs +++ b/src-tauri/src/core/backup.rs @@ -87,7 +87,7 @@ impl WebDavClient { (*cfg_arc).clone() } else { // 释放锁后获取异步配置 - let verge = Config::verge().await.latest_ref().clone(); + let verge = Config::verge().await.latest_arc(); if verge.webdav_url.is_none() || verge.webdav_username.is_none() || verge.webdav_password.is_none() @@ -99,11 +99,13 @@ impl WebDavClient { let config = WebDavConfig { url: verge .webdav_url + .as_ref() + .cloned() .unwrap_or_default() .trim_end_matches('/') .into(), - username: verge.webdav_username.unwrap_or_default(), - password: verge.webdav_password.unwrap_or_default(), + username: verge.webdav_username.as_ref().cloned().unwrap_or_default(), + password: verge.webdav_password.as_ref().cloned().unwrap_or_default(), }; // 存储配置到 ArcSwapOption diff --git a/src-tauri/src/core/event_driven_proxy.rs b/src-tauri/src/core/event_driven_proxy.rs index 18a6f155..c55593b9 100644 --- a/src-tauri/src/core/event_driven_proxy.rs +++ b/src-tauri/src/core/event_driven_proxy.rs @@ -387,7 +387,7 @@ impl EventDrivenProxyManager { async fn get_proxy_config() -> ProxyConfig { let (sys_enabled, pac_enabled, guard_enabled, guard_duration) = { let verge_config = Config::verge().await; - let verge = verge_config.latest_ref(); + let verge = verge_config.latest_arc(); ( verge.enable_system_proxy.unwrap_or(false), verge.proxy_auto_config.unwrap_or(false), @@ -406,7 +406,7 @@ impl EventDrivenProxyManager { async fn get_expected_pac_config() -> Autoproxy { let proxy_host = { let verge_config = Config::verge().await; - let verge = verge_config.latest_ref(); + let verge = verge_config.latest_arc(); verge .proxy_host .clone() @@ -424,13 +424,13 @@ impl EventDrivenProxyManager { let (verge_mixed_port, proxy_host) = { let verge_config = Config::verge().await; - let verge_ref = verge_config.latest_ref(); + let verge_ref = verge_config.latest_arc(); (verge_ref.verge_mixed_port, verge_ref.proxy_host.clone()) }; let default_port = { let clash_config = Config::clash().await; - clash_config.latest_ref().get_mixed_port() + clash_config.latest_arc().get_mixed_port() }; let port = verge_mixed_port.unwrap_or(default_port); @@ -450,7 +450,7 @@ impl EventDrivenProxyManager { use crate::constants::bypass; let verge_config = Config::verge().await; - let verge = verge_config.latest_ref(); + let verge = verge_config.latest_arc(); let use_default = verge.use_default_bypass.unwrap_or(true); let custom = verge.system_proxy_bypass.as_deref().unwrap_or(""); diff --git a/src-tauri/src/core/hotkey.rs b/src-tauri/src/core/hotkey.rs index f33a2cbc..0735c9e4 100755 --- a/src-tauri/src/core/hotkey.rs +++ b/src-tauri/src/core/hotkey.rs @@ -237,7 +237,7 @@ impl Hotkey { let is_enable_global_hotkey = Config::verge() .await - .latest_ref() + .latest_arc() .enable_global_hotkey .unwrap_or(true); @@ -274,7 +274,7 @@ singleton_with_logging!(Hotkey, INSTANCE, "Hotkey"); impl Hotkey { pub async fn init(&self, skip: bool) -> Result<()> { let verge = Config::verge().await; - let enable_global_hotkey = !skip && verge.latest_ref().enable_global_hotkey.unwrap_or(true); + let enable_global_hotkey = !skip && verge.latest_arc().enable_global_hotkey.unwrap_or(true); logging!( debug, @@ -284,7 +284,7 @@ impl Hotkey { ); // Extract hotkeys data before async operations - let hotkeys = verge.latest_ref().hotkeys.as_ref().cloned(); + let hotkeys = verge.latest_arc().hotkeys.as_ref().cloned(); if let Some(hotkeys) = hotkeys { logging!( diff --git a/src-tauri/src/core/manager/config.rs b/src-tauri/src/core/manager/config.rs index b0e79bca..e0f9bd51 100644 --- a/src-tauri/src/core/manager/config.rs +++ b/src-tauri/src/core/manager/config.rs @@ -17,13 +17,15 @@ impl CoreManager { use crate::constants::files::RUNTIME_CONFIG; let runtime_path = dirs::app_home_dir()?.join(RUNTIME_CONFIG); - let clash_config = Config::clash().await.latest_ref().0.clone(); + let clash_config = &Config::clash().await.latest_arc().0; - **Config::runtime().await.draft_mut() = IRuntime { - config: Some(clash_config.clone()), - exists_keys: vec![], - chain_logs: Default::default(), - }; + Config::runtime().await.edit_draft(|d| { + *d = IRuntime { + config: Some(clash_config.to_owned()), + exists_keys: vec![], + chain_logs: Default::default(), + } + }); help::save_yaml(&runtime_path, &clash_config, Some("# Clash Verge Runtime")).await?; handle::Handle::notice_message(error_key, error_msg); diff --git a/src-tauri/src/core/manager/lifecycle.rs b/src-tauri/src/core/manager/lifecycle.rs index 8f0c6a88..c299c1ef 100644 --- a/src-tauri/src/core/manager/lifecycle.rs +++ b/src-tauri/src/core/manager/lifecycle.rs @@ -47,10 +47,12 @@ impl CoreManager { return Err(format!("Invalid clash core: {}", clash_core).into()); } - Config::verge().await.draft_mut().clash_core = clash_core.to_owned().into(); + Config::verge().await.edit_draft(|d| { + d.clash_core = Some(clash_core.to_owned()); + }); Config::verge().await.apply(); - let verge_data = Config::verge().await.latest_ref().clone(); + let verge_data = Config::verge().await.latest_arc(); verge_data.save_file().await.map_err(|e| e.to_string())?; let run_path = Config::generate_file(ConfigType::Run) @@ -82,7 +84,7 @@ impl CoreManager { let needs_service = Config::verge() .await - .latest_ref() + .latest_arc() .enable_tun_mode .unwrap_or(false); diff --git a/src-tauri/src/core/manager/state.rs b/src-tauri/src/core/manager/state.rs index af92ce18..f13bc543 100644 --- a/src-tauri/src/core/manager/state.rs +++ b/src-tauri/src/core/manager/state.rs @@ -32,7 +32,7 @@ impl CoreManager { let config_file = Config::generate_file(crate::config::ConfigType::Run).await?; let app_handle = handle::Handle::app_handle(); - let clash_core = Config::verge().await.latest_ref().get_valid_clash_core(); + let clash_core = Config::verge().await.latest_arc().get_valid_clash_core(); let config_dir = dirs::app_home_dir()?; let (mut rx, child) = app_handle diff --git a/src-tauri/src/core/service.rs b/src-tauri/src/core/service.rs index 04ded18a..40a01b3a 100644 --- a/src-tauri/src/core/service.rs +++ b/src-tauri/src/core/service.rs @@ -353,7 +353,7 @@ pub(super) async fn start_with_existing_service(config_file: &PathBuf) -> Result logging!(info, Type::Service, "尝试使用现有服务启动核心"); let verge_config = Config::verge().await; - let clash_core = verge_config.latest_ref().get_valid_clash_core(); + let clash_core = verge_config.latest_arc().get_valid_clash_core(); drop(verge_config); let bin_ext = if cfg!(windows) { ".exe" } else { "" }; diff --git a/src-tauri/src/core/sysopt.rs b/src-tauri/src/core/sysopt.rs index c24d8be9..bc280578 100644 --- a/src-tauri/src/core/sysopt.rs +++ b/src-tauri/src/core/sysopt.rs @@ -31,12 +31,12 @@ static DEFAULT_BYPASS: &str = "127.0.0.1,192.168.0.0/16,10.0.0.0/8,172.16.0.0/12 async fn get_bypass() -> String { let use_default = Config::verge() .await - .latest_ref() + .latest_arc() .use_default_bypass .unwrap_or(true); let res = { let verge = Config::verge().await; - let verge = verge.latest_ref(); + let verge = verge.latest_arc(); verge.system_proxy_bypass.clone() }; let custom_bypass = match res { @@ -124,17 +124,17 @@ impl Sysopt { } let port = { - let verge_port = Config::verge().await.latest_ref().verge_mixed_port; + let verge_port = Config::verge().await.latest_arc().verge_mixed_port; match verge_port { Some(port) => port, - None => Config::clash().await.latest_ref().get_mixed_port(), + None => Config::clash().await.latest_arc().get_mixed_port(), } }; let pac_port = IVerge::get_singleton_port(); let (sys_enable, pac_enable, proxy_host) = { let verge = Config::verge().await; - let verge = verge.latest_ref(); + let verge = verge.latest_arc(); ( verge.enable_system_proxy.unwrap_or(false), verge.proxy_auto_config.unwrap_or(false), @@ -266,7 +266,7 @@ impl Sysopt { /// update the startup pub async fn update_launch(&self) -> Result<()> { - let enable_auto_launch = { Config::verge().await.latest_ref().enable_auto_launch }; + let enable_auto_launch = { Config::verge().await.latest_arc().enable_auto_launch }; let is_enable = enable_auto_launch.unwrap_or(false); logging!( info, diff --git a/src-tauri/src/core/timer.rs b/src-tauri/src/core/timer.rs index e8506df1..e88f863d 100644 --- a/src-tauri/src/core/timer.rs +++ b/src-tauri/src/core/timer.rs @@ -100,7 +100,7 @@ impl Timer { // Collect profiles that need immediate update let profiles_to_update = - if let Some(items) = Config::profiles().await.latest_ref().get_items() { + if let Some(items) = Config::profiles().await.latest_arc().get_items() { items .iter() .filter_map(|item| { @@ -273,7 +273,7 @@ impl Timer { async fn gen_map(&self) -> HashMap { let mut new_map = HashMap::new(); - if let Some(items) = Config::profiles().await.latest_ref().get_items() { + if let Some(items) = Config::profiles().await.latest_arc().get_items() { for item in items.iter() { if let Some(option) = item.option.as_ref() && let Some(allow_auto_update) = option.allow_auto_update @@ -427,7 +427,7 @@ impl Timer { // Get the profile updated timestamp - now safe to await let items = { let profiles = Config::profiles().await; - let profiles_guard = profiles.latest_ref(); + let profiles_guard = profiles.latest_arc(); match profiles_guard.get_items() { Some(i) => i.clone(), None => { @@ -489,7 +489,7 @@ impl Timer { match tokio::time::timeout(std::time::Duration::from_secs(40), async { Self::emit_update_event(uid, true); - let is_current = Config::profiles().await.latest_ref().current.as_ref() == Some(uid); + let is_current = Config::profiles().await.latest_arc().current.as_ref() == Some(uid); logging!( info, Type::Timer, diff --git a/src-tauri/src/core/tray/mod.rs b/src-tauri/src/core/tray/mod.rs index ef300bbe..e7cc1fda 100644 --- a/src-tauri/src/core/tray/mod.rs +++ b/src-tauri/src/core/tray/mod.rs @@ -86,7 +86,7 @@ pub struct Tray { impl TrayState { pub async fn get_common_tray_icon() -> (bool, Vec) { - let verge = Config::verge().await.latest_ref().clone(); + let verge = Config::verge().await.latest_arc(); let is_common_tray_icon = verge.common_tray_icon.unwrap_or(false); if is_common_tray_icon && let Ok(Some(common_icon_path)) = find_target_icons("common") @@ -123,7 +123,7 @@ impl TrayState { } pub async fn get_sysproxy_tray_icon() -> (bool, Vec) { - let verge = Config::verge().await.latest_ref().clone(); + let verge = Config::verge().await.latest_arc(); let is_sysproxy_tray_icon = verge.sysproxy_tray_icon.unwrap_or(false); if is_sysproxy_tray_icon && let Ok(Some(sysproxy_icon_path)) = find_target_icons("sysproxy") @@ -160,7 +160,7 @@ impl TrayState { } pub async fn get_tun_tray_icon() -> (bool, Vec) { - let verge = Config::verge().await.latest_ref().clone(); + let verge = Config::verge().await.latest_arc(); let is_tun_tray_icon = verge.tun_tray_icon.unwrap_or(false); if is_tun_tray_icon && let Ok(Some(tun_icon_path)) = find_target_icons("tun") @@ -243,7 +243,7 @@ impl Tray { } let app_handle = handle::Handle::app_handle(); - let tray_event = { Config::verge().await.latest_ref().tray_event.clone() }; + let tray_event = { Config::verge().await.latest_arc().tray_event.clone() }; let tray_event = tray_event.unwrap_or_else(|| "main_window".into()); let tray = app_handle .tray_by_id("main") @@ -303,7 +303,7 @@ impl Tray { } async fn update_menu_internal(&self, app_handle: &AppHandle) -> Result<()> { - let verge = Config::verge().await.latest_ref().clone(); + let verge = Config::verge().await.latest_arc(); let system_proxy = verge.enable_system_proxy.as_ref().unwrap_or(&false); let tun_mode = verge.enable_tun_mode.as_ref().unwrap_or(&false); let tun_mode_available = cmd::system::is_admin().unwrap_or_default() @@ -311,7 +311,7 @@ impl Tray { let mode = { Config::clash() .await - .latest_ref() + .latest_arc() .0 .get("mode") .map(|val| val.as_str().unwrap_or("rule")) @@ -320,7 +320,7 @@ impl Tray { }; let profile_uid_and_name = Config::profiles() .await - .data_mut() + .latest_arc() .all_profile_uid_and_name() .unwrap_or_default(); let is_lightweight_mode = is_in_lightweight_mode(); @@ -375,7 +375,7 @@ impl Tray { } }; - let verge = Config::verge().await.latest_ref().clone(); + let verge = Config::verge().await.latest_arc(); let system_mode = verge.enable_system_proxy.as_ref().unwrap_or(&false); let tun_mode = verge.enable_tun_mode.as_ref().unwrap_or(&false); @@ -418,7 +418,7 @@ impl Tray { } }; - let verge = Config::verge().await.latest_ref().clone(); + let verge = Config::verge().await.latest_arc(); let system_mode = verge.enable_system_proxy.as_ref().unwrap_or(&false); let tun_mode = verge.enable_tun_mode.as_ref().unwrap_or(&false); @@ -460,7 +460,7 @@ impl Tray { let app_handle = handle::Handle::app_handle(); - let verge = Config::verge().await.latest_ref().clone(); + let verge = Config::verge().await.latest_arc(); let system_proxy = verge.enable_system_proxy.as_ref().unwrap_or(&false); let tun_mode = verge.enable_tun_mode.as_ref().unwrap_or(&false); @@ -474,7 +474,7 @@ impl Tray { let mut current_profile_name = "None".into(); { let profiles = Config::profiles().await; - let profiles = profiles.latest_ref(); + let profiles = profiles.latest_arc(); if let Some(current_profile_uid) = profiles.get_current() && let Ok(profile) = profiles.get_item(¤t_profile_uid) { @@ -552,7 +552,7 @@ impl Tray { #[cfg(any(target_os = "macos", target_os = "windows"))] let show_menu_on_left_click = { - let tray_event = { Config::verge().await.latest_ref().tray_event.clone() }; + let tray_event = { Config::verge().await.latest_arc().tray_event.clone() }; let tray_event: String = tray_event.unwrap_or_else(|| "main_window".into()); tray_event.as_str() == "tray_menu" }; @@ -583,7 +583,7 @@ impl Tray { } AsyncHandler::spawn(|| async move { - let tray_event = { Config::verge().await.latest_ref().tray_event.clone() }; + let tray_event = { Config::verge().await.latest_arc().tray_event.clone() }; let tray_event: String = tray_event.unwrap_or_else(|| "main_window".into()); logging!(debug, Type::Tray, "tray event: {tray_event:?}"); @@ -675,7 +675,7 @@ async fn create_profile_menu_item( async move { let is_current_profile = Config::profiles() .await - .latest_ref() + .latest_arc() .is_current_profile_index(profile_uid); CheckMenuItem::with_id( &app_handle, @@ -878,7 +878,7 @@ async fn create_tray_menu( // 获取当前配置文件的选中代理组信息 let current_profile_selected = { let profiles_config = Config::profiles().await; - let profiles_ref = profiles_config.latest_ref(); + let profiles_ref = profiles_config.latest_arc(); profiles_ref .get_current() .and_then(|uid| profiles_ref.get_item(&uid).ok()) @@ -924,7 +924,7 @@ async fn create_tray_menu( .collect::>() }); - let verge_settings = Config::verge().await.latest_ref().clone(); + let verge_settings = Config::verge().await.latest_arc(); let show_proxy_groups_inline = verge_settings.tray_inline_proxy_groups.unwrap_or(false); let version = env!("CARGO_PKG_VERSION"); diff --git a/src-tauri/src/core/validate.rs b/src-tauri/src/core/validate.rs index 204497a0..562ebd24 100644 --- a/src-tauri/src/core/validate.rs +++ b/src-tauri/src/core/validate.rs @@ -266,7 +266,7 @@ impl CoreConfigValidator { logging!(info, Type::Validate, "开始验证配置文件: {}", config_path); - let clash_core = Config::verge().await.latest_ref().get_valid_clash_core(); + let clash_core = Config::verge().await.latest_arc().get_valid_clash_core(); logging!(info, Type::Validate, "使用内核: {}", clash_core); let app_handle = handle::Handle::app_handle(); diff --git a/src-tauri/src/enhance/mod.rs b/src-tauri/src/enhance/mod.rs index e9183170..ad1a52d1 100644 --- a/src-tauri/src/enhance/mod.rs +++ b/src-tauri/src/enhance/mod.rs @@ -45,11 +45,11 @@ struct ProfileItems { } async fn get_config_values() -> ConfigValues { - let clash_config = { Config::clash().await.latest_ref().0.clone() }; + let clash_config = { Config::clash().await.latest_arc().0.clone() }; let (clash_core, enable_tun, enable_builtin, socks_enabled, http_enabled, enable_dns_settings) = { let verge = Config::verge().await; - let verge = verge.latest_ref(); + let verge = verge.latest_arc(); ( Some(verge.get_valid_clash_core()), verge.enable_tun_mode.unwrap_or(false), @@ -63,14 +63,14 @@ async fn get_config_values() -> ConfigValues { #[cfg(not(target_os = "windows"))] let redir_enabled = { let verge = Config::verge().await; - let verge = verge.latest_ref(); + let verge = verge.latest_arc(); verge.verge_redir_enabled.unwrap_or(false) }; #[cfg(target_os = "linux")] let tproxy_enabled = { let verge = Config::verge().await; - let verge = verge.latest_ref(); + let verge = verge.latest_arc(); verge.verge_tproxy_enabled.unwrap_or(false) }; @@ -103,12 +103,12 @@ async fn collect_profile_items() -> ProfileItems { ) = { let current = { let profiles = Config::profiles().await; - let profiles_clone = profiles.latest_ref().clone(); + let profiles_clone = profiles.latest_arc(); profiles_clone.current_mapping().await.unwrap_or_default() }; let profiles = Config::profiles().await; - let profiles_ref = profiles.latest_ref(); + let profiles_ref = profiles.latest_arc(); let merge_uid = profiles_ref.current_merge().unwrap_or_default(); let script_uid = profiles_ref.current_script().unwrap_or_default(); @@ -139,7 +139,7 @@ async fn collect_profile_items() -> ProfileItems { let merge_item = { let item = { let profiles = Config::profiles().await; - let profiles = profiles.latest_ref(); + let profiles = profiles.latest_arc(); profiles.get_item(merge_uid).ok().cloned() }; if let Some(item) = item { @@ -156,7 +156,7 @@ async fn collect_profile_items() -> ProfileItems { let script_item = { let item = { let profiles = Config::profiles().await; - let profiles = profiles.latest_ref(); + let profiles = profiles.latest_arc(); profiles.get_item(script_uid).ok().cloned() }; if let Some(item) = item { @@ -173,7 +173,7 @@ async fn collect_profile_items() -> ProfileItems { let rules_item = { let item = { let profiles = Config::profiles().await; - let profiles = profiles.latest_ref(); + let profiles = profiles.latest_arc(); profiles.get_item(rules_uid).ok().cloned() }; if let Some(item) = item { @@ -190,7 +190,7 @@ async fn collect_profile_items() -> ProfileItems { let proxies_item = { let item = { let profiles = Config::profiles().await; - let profiles = profiles.latest_ref(); + let profiles = profiles.latest_arc(); profiles.get_item(proxies_uid).ok().cloned() }; if let Some(item) = item { @@ -207,7 +207,7 @@ async fn collect_profile_items() -> ProfileItems { let groups_item = { let item = { let profiles = Config::profiles().await; - let profiles = profiles.latest_ref(); + let profiles = profiles.latest_arc(); profiles.get_item(groups_uid).ok().cloned() }; if let Some(item) = item { @@ -224,7 +224,7 @@ async fn collect_profile_items() -> ProfileItems { let global_merge = { let item = { let profiles = Config::profiles().await; - let profiles = profiles.latest_ref(); + let profiles = profiles.latest_arc(); profiles.get_item("Merge").ok().cloned() }; if let Some(item) = item { @@ -241,7 +241,7 @@ async fn collect_profile_items() -> ProfileItems { let global_script = { let item = { let profiles = Config::profiles().await; - let profiles = profiles.latest_ref(); + let profiles = profiles.latest_arc(); profiles.get_item("Script").ok().cloned() }; if let Some(item) = item { @@ -394,7 +394,7 @@ async fn merge_default_config( if key.as_str() == Some("external-controller") { let enable_external_controller = Config::verge() .await - .latest_ref() + .latest_arc() .enable_external_controller .unwrap_or(false); diff --git a/src-tauri/src/feat/backup.rs b/src-tauri/src/feat/backup.rs index 4ccdecab..2aac5505 100644 --- a/src-tauri/src/feat/backup.rs +++ b/src-tauri/src/feat/backup.rs @@ -78,7 +78,7 @@ pub async fn delete_webdav_backup(filename: String) -> Result<()> { /// Restore WebDAV backup pub async fn restore_webdav_backup(filename: String) -> Result<()> { let verge = Config::verge().await; - let verge_data = verge.latest_ref().clone(); + let verge_data = verge.latest_arc(); let webdav_url = verge_data.webdav_url.clone(); let webdav_username = verge_data.webdav_username.clone(); let webdav_password = verge_data.webdav_password.clone(); @@ -243,7 +243,7 @@ pub async fn restore_local_backup(filename: String) -> Result<()> { let (webdav_url, webdav_username, webdav_password) = { let verge = Config::verge().await; - let verge = verge.latest_ref(); + let verge = verge.latest_arc(); ( verge.webdav_url.clone(), verge.webdav_username.clone(), diff --git a/src-tauri/src/feat/clash.rs b/src-tauri/src/feat/clash.rs index cf031d78..6551d440 100644 --- a/src-tauri/src/feat/clash.rs +++ b/src-tauri/src/feat/clash.rs @@ -72,10 +72,12 @@ pub async fn change_clash_mode(mode: String) { { Ok(_) => { // 更新订阅 - Config::clash().await.data_mut().patch_config(mapping); + Config::clash() + .await + .edit_draft(|d| d.patch_config(mapping)); // 分离数据获取和异步调用 - let clash_data = Config::clash().await.data_mut().clone(); + let clash_data = Config::clash().await.data_arc(); if clash_data.save_config().await.is_ok() { handle::Handle::refresh_clash(); logging_error!(Type::Tray, tray::Tray::global().update_menu().await); @@ -84,7 +86,7 @@ pub async fn change_clash_mode(mode: String) { let is_auto_close_connection = Config::verge() .await - .data_mut() + .data_arc() .auto_close_connection .unwrap_or(false); if is_auto_close_connection { @@ -102,7 +104,7 @@ pub async fn test_delay(url: String) -> anyhow::Result { let tun_mode = Config::verge() .await - .latest_ref() + .latest_arc() .enable_tun_mode .unwrap_or(false); diff --git a/src-tauri/src/feat/config.rs b/src-tauri/src/feat/config.rs index 1ca9669c..f4ab591a 100644 --- a/src-tauri/src/feat/config.rs +++ b/src-tauri/src/feat/config.rs @@ -12,8 +12,7 @@ use serde_yaml_ng::Mapping; pub async fn patch_clash(patch: Mapping) -> Result<()> { Config::clash() .await - .draft_mut() - .patch_config(patch.clone()); + .edit_draft(|d| d.patch_config(patch.clone())); let res = { // 激活订阅 @@ -25,7 +24,9 @@ pub async fn patch_clash(patch: Mapping) -> Result<()> { logging_error!(Type::Tray, tray::Tray::global().update_menu().await); logging_error!(Type::Tray, tray::Tray::global().update_icon().await); } - Config::runtime().await.draft_mut().patch_config(patch); + Config::runtime() + .await + .edit_draft(|d| d.patch_config(patch)); CoreManager::global().update_config().await?; } handle::Handle::refresh_clash(); @@ -35,7 +36,7 @@ pub async fn patch_clash(patch: Mapping) -> Result<()> { Ok(()) => { Config::clash().await.apply(); // 分离数据获取和异步调用 - let clash_data = Config::clash().await.data_mut().clone(); + let clash_data = Config::clash().await.data_arc(); clash_data.save_config().await?; Ok(()) } @@ -190,7 +191,9 @@ async fn process_terminated_flags(update_flags: i32, patch: &IVerge) -> Result<( handle::Handle::refresh_clash(); } if (update_flags & (UpdateFlags::VergeConfig as i32)) != 0 { - Config::verge().await.draft_mut().enable_global_hotkey = patch.enable_global_hotkey; + Config::verge() + .await + .edit_draft(|d| d.enable_global_hotkey = patch.enable_global_hotkey); handle::Handle::refresh_verge(); } if (update_flags & (UpdateFlags::Launch as i32)) != 0 { @@ -227,7 +230,7 @@ async fn process_terminated_flags(update_flags: i32, patch: &IVerge) -> Result<( } pub async fn patch_verge(patch: &IVerge, not_save_file: bool) -> Result<()> { - Config::verge().await.draft_mut().patch_config(patch); + Config::verge().await.edit_draft(|d| d.patch_config(patch)); let update_flags = determine_update_flags(patch); let process_flag_result: std::result::Result<(), anyhow::Error> = { @@ -242,7 +245,7 @@ pub async fn patch_verge(patch: &IVerge, not_save_file: bool) -> Result<()> { Config::verge().await.apply(); if !not_save_file { // 分离数据获取和异步调用 - let verge_data = Config::verge().await.data_ref().clone(); + let verge_data = Config::verge().await.data_arc(); verge_data.save_file().await?; } Ok(()) diff --git a/src-tauri/src/feat/profile.rs b/src-tauri/src/feat/profile.rs index f3832e38..6bf267e5 100644 --- a/src-tauri/src/feat/profile.rs +++ b/src-tauri/src/feat/profile.rs @@ -28,7 +28,7 @@ async fn should_update_profile( ignore_auto_update: bool, ) -> Result)>> { let profiles = Config::profiles().await; - let profiles = profiles.latest_ref(); + let profiles = profiles.latest_arc(); let item = profiles.get_item(uid)?; let is_remote = item.itype.as_ref().is_some_and(|s| s == "remote"); @@ -89,12 +89,12 @@ async fn perform_profile_update( let mut merged_opt = PrfOption::merge(opt, option); let is_current = { let profiles = Config::profiles().await; - profiles.latest_ref().is_current_profile_index(uid) + profiles.latest_arc().is_current_profile_index(uid) }; let profile_name = { let profiles = Config::profiles().await; profiles - .latest_ref() + .latest_arc() .get_name_by_uid(uid) .unwrap_or_default() }; diff --git a/src-tauri/src/feat/proxy.rs b/src-tauri/src/feat/proxy.rs index 731cec20..9f7d513e 100644 --- a/src-tauri/src/feat/proxy.rs +++ b/src-tauri/src/feat/proxy.rs @@ -10,8 +10,8 @@ use tauri_plugin_clipboard_manager::ClipboardExt; /// Toggle system proxy on/off pub async fn toggle_system_proxy() { let verge = Config::verge().await; - let enable = verge.latest_ref().enable_system_proxy.unwrap_or(false); - let auto_close_connection = verge.latest_ref().auto_close_connection.unwrap_or(false); + let enable = verge.latest_arc().enable_system_proxy.unwrap_or(false); + let auto_close_connection = verge.latest_arc().auto_close_connection.unwrap_or(false); // 如果当前系统代理即将关闭,且自动关闭连接设置为true,则关闭所有连接 if enable @@ -42,7 +42,7 @@ pub async fn toggle_system_proxy() { /// Toggle TUN mode on/off pub async fn toggle_tun_mode(not_save_file: Option) { - let enable = Config::verge().await.data_mut().enable_tun_mode; + let enable = Config::verge().await.latest_arc().enable_tun_mode; let enable = enable.unwrap_or(false); match super::patch_verge( @@ -66,7 +66,7 @@ pub async fn copy_clash_env() { Ok(ip) => ip.into(), Err(_) => Config::verge() .await - .latest_ref() + .latest_arc() .proxy_host .clone() .unwrap_or_else(|| "127.0.0.1".into()), @@ -76,7 +76,7 @@ pub async fn copy_clash_env() { let port = { Config::verge() .await - .latest_ref() + .latest_arc() .verge_mixed_port .unwrap_or(7897) }; @@ -84,7 +84,7 @@ pub async fn copy_clash_env() { let socks5_proxy = format!("socks5://{clash_verge_rev_ip}:{port}"); let cliboard = app_handle.clipboard(); - let env_type = { Config::verge().await.latest_ref().env_type.clone() }; + let env_type = { Config::verge().await.latest_arc().env_type.clone() }; let env_type = match env_type { Some(env_type) => env_type, None => { diff --git a/src-tauri/src/feat/window.rs b/src-tauri/src/feat/window.rs index a23a41f2..8352fea1 100644 --- a/src-tauri/src/feat/window.rs +++ b/src-tauri/src/feat/window.rs @@ -47,7 +47,7 @@ pub async fn clean_async() -> bool { let tun_task = async { let tun_enabled = Config::verge() .await - .latest_ref() + .latest_arc() .enable_tun_mode .unwrap_or(false); @@ -100,7 +100,7 @@ pub async fn clean_async() -> bool { // 检查系统代理是否开启 let sys_proxy_enabled = Config::verge() .await - .latest_ref() + .latest_arc() .enable_system_proxy .unwrap_or(false); @@ -176,7 +176,7 @@ pub async fn clean_async() -> bool { { let sys_proxy_enabled = Config::verge() .await - .latest_ref() + .latest_arc() .enable_system_proxy .unwrap_or(false); @@ -316,7 +316,7 @@ pub async fn hide() { let enable_auto_light_weight_mode = Config::verge() .await - .data_mut() + .latest_arc() .enable_auto_light_weight_mode .unwrap_or(false); diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index da6c5710..7555e477 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -319,7 +319,7 @@ pub fn run() { AsyncHandler::spawn(move || async move { let is_enable_global_hotkey = Config::verge() .await - .latest_ref() + .latest_arc() .enable_global_hotkey .unwrap_or(true); @@ -360,7 +360,7 @@ pub fn run() { let _ = hotkey::Hotkey::global().unregister_system_hotkey(SystemHotkey::CmdW); let is_enable_global_hotkey = Config::verge() .await - .latest_ref() + .latest_arc() .enable_global_hotkey .unwrap_or(true); if !is_enable_global_hotkey { diff --git a/src-tauri/src/module/lightweight.rs b/src-tauri/src/module/lightweight.rs index 9390639f..802b83da 100644 --- a/src-tauri/src/module/lightweight.rs +++ b/src-tauri/src/module/lightweight.rs @@ -87,11 +87,11 @@ async fn refresh_lightweight_tray_state() { pub async fn auto_lightweight_boot() -> Result<()> { let verge_config = Config::verge().await; let enable_auto = verge_config - .data_mut() + .latest_arc() .enable_auto_light_weight_mode .unwrap_or(false); let is_silent_start = verge_config - .latest_ref() + .latest_arc() .enable_silent_start .unwrap_or(false); @@ -236,7 +236,7 @@ async fn setup_light_weight_timer() -> Result<()> { let once_by_minutes = Config::verge() .await - .latest_ref() + .latest_arc() .auto_light_weight_minutes .unwrap_or(10); diff --git a/src-tauri/src/utils/draft.rs b/src-tauri/src/utils/draft.rs index c34bbfb6..52d9119d 100644 --- a/src-tauri/src/utils/draft.rs +++ b/src-tauri/src/utils/draft.rs @@ -1,179 +1,368 @@ +use parking_lot::RwLock; use std::sync::Arc; -use parking_lot::{ - MappedRwLockReadGuard, MappedRwLockWriteGuard, RwLock, RwLockReadGuard, - RwLockUpgradableReadGuard, RwLockWriteGuard, -}; +pub type SharedBox = Arc>; +type DraftInner = (SharedBox, Option>); +/// Draft 管理:committed 与 optional draft 都以 Arc> 存储, +// (committed_snapshot, optional_draft_snapshot) #[derive(Debug, Clone)] -pub struct Draft { - inner: Arc)>>, +pub struct Draft { + inner: Arc>>, } -impl From for Draft { - fn from(data: T) -> Self { +impl Draft { + pub fn new(data: T) -> Self { Self { - inner: Arc::new(RwLock::new((data, None))), + inner: Arc::new(RwLock::new((Arc::new(Box::new(data)), None))), } } -} - -/// Implements draft management for `Box`, allowing for safe concurrent editing and committing of draft data. -/// # Type Parameters -/// - `T`: The underlying data type, which must implement `Clone` and `ToOwned`. -/// -/// # Methods -/// - `data_mut`: Returns a mutable reference to the committed data. -/// - `draft_mut`: Creates or retrieves a mutable reference to the draft data, cloning the committed data if no draft exists. -/// - `latest_ref`: Returns an immutable reference to the draft data if it exists, otherwise to the committed data. -/// - `apply`: Commits the draft data, replacing the committed data and returning the old committed value if a draft existed. -/// - `discard`: Discards the draft data and returns it if it existed. -impl Draft> { - /// 正式数据视图 - pub fn data_ref(&self) -> MappedRwLockReadGuard<'_, Box> { - RwLockReadGuard::map(self.inner.read(), |inner| &inner.0) + /// 以 Arc> 的形式获取当前“已提交(正式)”数据的快照(零拷贝,仅 clone Arc) + pub fn data_arc(&self) -> SharedBox { + let guard = self.inner.read(); + Arc::clone(&guard.0) } - /// 可写正式数据 - pub fn data_mut(&self) -> MappedRwLockWriteGuard<'_, Box> { - RwLockWriteGuard::map(self.inner.write(), |inner| &mut inner.0) + /// 获取当前(草稿若存在则返回草稿,否则返回已提交)的快照 + /// 这也是零拷贝:只 clone Arc,不 clone T + pub fn latest_arc(&self) -> SharedBox { + let guard = self.inner.read(); + guard + .1 + .as_ref() + .cloned() + .unwrap_or_else(|| Arc::clone(&guard.0)) } - /// 创建或获取草稿并返回可写引用 - pub fn draft_mut(&self) -> MappedRwLockWriteGuard<'_, Box> { - let guard = self.inner.upgradable_read(); + /// 通过闭包以可变方式编辑草稿(在闭包中我们给出 &mut T) + /// - 延迟拷贝:如果只有这一个 Arc 引用,则直接修改,不会克隆 T; + /// - 若草稿被其他读者共享,Arc::make_mut 会做一次 T.clone(最小必要拷贝)。 + pub fn edit_draft(&self, f: F) -> R + where + F: FnOnce(&mut T) -> R, + { + // 先获得写锁以创建或取出草稿 Arc 的可变引用位置 + let mut guard = self.inner.write(); if guard.1.is_none() { - let mut guard = RwLockUpgradableReadGuard::upgrade(guard); - guard.1 = Some(guard.0.clone()); - return RwLockWriteGuard::map(guard, |inner| { - inner.1.as_mut().unwrap_or_else(|| { - unreachable!("Draft was just created above, this should never fail") - }) - }); + // 创建草稿 snapshot(Arc clone,cheap) + guard.1 = Some(Arc::clone(&guard.0)); } - // 已存在草稿,升级为写锁映射 - RwLockWriteGuard::map(RwLockUpgradableReadGuard::upgrade(guard), |inner| { - inner - .1 - .as_mut() - .unwrap_or_else(|| unreachable!("Draft should exist when guard.1.is_some()")) - }) + // 此时 guaranteed: guard.1 is Some(Arc>) + #[allow(clippy::unwrap_used)] + let arc_box = guard.1.as_mut().unwrap(); + // Arc::make_mut: 如果只有一个引用则返回可变引用;否则会克隆底层 Box(要求 T: Clone) + let boxed = Arc::make_mut(arc_box); // &mut Box + // 对 Box 解引用得到 &mut T + + f(&mut **boxed) } - /// 零拷贝只读视图:返回草稿(若存在)或正式值 - pub fn latest_ref(&self) -> MappedRwLockReadGuard<'_, Box> { - RwLockReadGuard::map(self.inner.read(), |inner| { - inner.1.as_ref().unwrap_or(&inner.0) - }) - } - - /// 提交草稿,返回旧正式数据 + /// 将草稿提交到已提交位置(替换),并清除草稿 pub fn apply(&self) { - let guard = self.inner.upgradable_read(); - if guard.1.is_none() { - return; - } - - let mut guard = RwLockUpgradableReadGuard::upgrade(guard); - if let Some(draft) = guard.1.take() { - guard.0 = draft; + let mut guard = self.inner.write(); + if let Some(d) = guard.1.take() { + guard.0 = d; } } - /// 丢弃草稿,返回被丢弃的草稿 + /// 丢弃草稿(如果存在) pub fn discard(&self) { - self.inner.write().1.take(); + let mut guard = self.inner.write(); + guard.1 = None; } - /// 异步修改正式数据,闭包直接获得 Box 所有权 - pub async fn with_data_modify(&self, f: F) -> Result + /// 异步地以拥有 Box 的方式修改已提交数据:将克隆一次已提交数据到本地, + /// 异步闭包返回新的 Box(替换已提交数据)和业务返回值 R。 + pub async fn with_data_modify(&self, f: F) -> Result where T: Send + Sync + 'static, F: FnOnce(Box) -> Fut + Send, - Fut: std::future::Future, R), E>> + Send, - E: From, + Fut: std::future::Future, R), anyhow::Error>> + Send, { - // 克隆正式数据 - let local = { + // 读取已提交快照(cheap Arc clone, 然后得到 Box 所有权 via clone) + // 注意:为了让闭包接收 Box 所有权,我们需要 clone 底层 T(不可避免) + let local: Box = { let guard = self.inner.read(); - guard.0.clone() + // 将 Arc> 的 Box clone 出来(会调用 T: Clone) + (*guard.0).clone() }; - // 异步闭包执行,返回修改后的 Box 和业务结果 R let (new_local, res) = f(local).await?; - // 写回正式数据 + // 将新的 Box 放到已提交位置(包进 Arc) let mut guard = self.inner.write(); - guard.0 = new_local; + guard.0 = Arc::new(new_local); Ok(res) } } -#[test] -fn test_draft_box() { - use crate::config::IVerge; +#[cfg(test)] +mod tests { + use super::*; + use anyhow::anyhow; + use std::future::Future; + use std::pin::Pin; + use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; - // 1. 创建 Draft> - let verge = Box::new(IVerge { - enable_auto_launch: Some(true), - enable_tun_mode: Some(false), - ..IVerge::default() - }); - let draft = Draft::from(verge); - - // 2. 读取正式数据(data_mut) - { - let data = draft.data_mut(); - assert_eq!(data.enable_auto_launch, Some(true)); - assert_eq!(data.enable_tun_mode, Some(false)); + #[derive(Clone, Debug, Default, PartialEq)] + struct IVerge { + enable_auto_launch: Option, + enable_tun_mode: Option, } - // 3. 初次获取草稿(draft_mut 会自动 clone 一份) - { - let draft_view = draft.draft_mut(); - assert_eq!(draft_view.enable_auto_launch, Some(true)); - assert_eq!(draft_view.enable_tun_mode, Some(false)); + // Minimal single-threaded executor for immediately-ready futures + fn block_on_ready(fut: F) -> F::Output { + fn no_op_raw_waker() -> RawWaker { + fn clone(_: *const ()) -> RawWaker { + no_op_raw_waker() + } + fn wake(_: *const ()) {} + fn wake_by_ref(_: *const ()) {} + fn drop(_: *const ()) {} + static VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop); + RawWaker::new(std::ptr::null(), &VTABLE) + } + + let waker = unsafe { Waker::from_raw(no_op_raw_waker()) }; + let mut cx = Context::from_waker(&waker); + let mut fut = Box::pin(fut); + loop { + match Pin::as_mut(&mut fut).poll(&mut cx) { + Poll::Ready(v) => return v, + Poll::Pending => std::thread::yield_now(), + } + } } - // 4. 修改草稿 - { - let mut d = draft.draft_mut(); - d.enable_auto_launch = Some(false); - d.enable_tun_mode = Some(true); + #[test] + fn test_draft_basic_flow() { + let verge = IVerge { + enable_auto_launch: Some(true), + enable_tun_mode: Some(false), + }; + let draft = Draft::new(verge); + + // 读取正式数据(data_arc) + { + let data = draft.data_arc(); + assert_eq!(data.enable_auto_launch, Some(true)); + assert_eq!(data.enable_tun_mode, Some(false)); + } + + // 修改草稿(使用 edit_draft) + draft.edit_draft(|d| { + d.enable_auto_launch = Some(false); + d.enable_tun_mode = Some(true); + }); + + // 正式数据未变 + { + let data = draft.data_arc(); + assert_eq!(data.enable_auto_launch, Some(true)); + assert_eq!(data.enable_tun_mode, Some(false)); + } + + // 草稿已变 + { + let latest = draft.latest_arc(); + assert_eq!(latest.enable_auto_launch, Some(false)); + assert_eq!(latest.enable_tun_mode, Some(true)); + } + + // 提交草稿 + draft.apply(); + + // 正式数据已更新 + { + let data = draft.data_arc(); + assert_eq!(data.enable_auto_launch, Some(false)); + assert_eq!(data.enable_tun_mode, Some(true)); + } + + // 新一轮草稿并修改 + draft.edit_draft(|d| { + d.enable_auto_launch = Some(true); + }); + { + let latest = draft.latest_arc(); + assert_eq!(latest.enable_auto_launch, Some(true)); + assert_eq!(latest.enable_tun_mode, Some(true)); + } + + // 丢弃草稿 + draft.discard(); + + // 丢弃后再次创建草稿,会从已提交重新 clone + { + draft.edit_draft(|d| { + // 原 committed 是 enable_auto_launch = Some(false) + assert_eq!(d.enable_auto_launch, Some(false)); + // 再修改一下 + d.enable_tun_mode = Some(false); + }); + // 草稿中值已修改,但正式数据仍是 apply 后的值 + let data = draft.data_arc(); + assert_eq!(data.enable_auto_launch, Some(false)); + assert_eq!(data.enable_tun_mode, Some(true)); + } } - // 正式数据未变 - assert_eq!(draft.data_mut().enable_auto_launch, Some(true)); - assert_eq!(draft.data_mut().enable_tun_mode, Some(false)); + #[test] + fn test_arc_pointer_behavior_on_edit_and_apply() { + let draft = Draft::new(IVerge { + enable_auto_launch: Some(true), + enable_tun_mode: Some(false), + }); - // 草稿已变 - { - let latest = draft.latest_ref(); - assert_eq!(latest.enable_auto_launch, Some(false)); + // 初始 latest == committed + let committed = draft.data_arc(); + let latest = draft.latest_arc(); + assert!(std::sync::Arc::ptr_eq(&committed, &latest)); + + // 第一次 edit:由于与 committed 共享,Arc::make_mut 会克隆 + draft.edit_draft(|d| d.enable_tun_mode = Some(true)); + let committed_after_first_edit = draft.data_arc(); + let draft_after_first_edit = draft.latest_arc(); + assert!(!std::sync::Arc::ptr_eq( + &committed_after_first_edit, + &draft_after_first_edit + )); + // 提交会把 committed 指向草稿的 Arc + let prev_draft_ptr = std::sync::Arc::as_ptr(&draft_after_first_edit); + draft.apply(); + let committed_after_apply = draft.data_arc(); + assert_eq!( + std::sync::Arc::as_ptr(&committed_after_apply), + prev_draft_ptr + ); + + // 第二次编辑:此时草稿唯一持有(无其它引用),不应再克隆 + // 获取草稿 Arc 的指针并立即丢弃本地引用,避免增加 strong_count + draft.edit_draft(|d| d.enable_auto_launch = Some(false)); + let latest1 = draft.latest_arc(); + let latest1_ptr = std::sync::Arc::as_ptr(&latest1); + drop(latest1); // 确保只有 Draft 内部持有草稿 Arc + + // 再次编辑(unique,Arc::make_mut 不应克隆) + draft.edit_draft(|d| d.enable_tun_mode = Some(false)); + let latest2 = draft.latest_arc(); + let latest2_ptr = std::sync::Arc::as_ptr(&latest2); + + assert_eq!(latest1_ptr, latest2_ptr, "Unique edit should not clone Arc"); + assert_eq!(latest2.enable_auto_launch, Some(false)); + assert_eq!(latest2.enable_tun_mode, Some(false)); + } + + #[test] + fn test_discard_restores_latest_to_committed() { + let draft = Draft::new(IVerge { + enable_auto_launch: Some(false), + enable_tun_mode: Some(false), + }); + + // 创建草稿并修改 + draft.edit_draft(|d| d.enable_auto_launch = Some(true)); + let committed = draft.data_arc(); + let latest = draft.latest_arc(); + assert!(!std::sync::Arc::ptr_eq(&committed, &latest)); + + // 丢弃草稿后 latest 应回到 committed + draft.discard(); + let committed2 = draft.data_arc(); + let latest2 = draft.latest_arc(); + assert!(std::sync::Arc::ptr_eq(&committed2, &latest2)); + assert_eq!(latest2.enable_auto_launch, Some(false)); + } + + #[test] + fn test_edit_draft_returns_closure_result() { + let draft = Draft::new(IVerge::default()); + let ret = draft.edit_draft(|d| { + d.enable_tun_mode = Some(true); + 123usize + }); + assert_eq!(ret, 123); + let latest = draft.latest_arc(); assert_eq!(latest.enable_tun_mode, Some(true)); } - // 5. 提交草稿 - draft.apply(); + #[test] + fn test_with_data_modify_ok_and_replaces_committed() { + let draft = Draft::new(IVerge { + enable_auto_launch: Some(false), + enable_tun_mode: Some(false), + }); - // 正式数据已更新 - { - let data = draft.data_mut(); - assert_eq!(data.enable_auto_launch, Some(false)); - assert_eq!(data.enable_tun_mode, Some(true)); + // 使用 with_data_modify 异步(立即就绪)地更新 committed + let res = block_on_ready(draft.with_data_modify(|mut v| async move { + v.enable_auto_launch = Some(true); + Ok((Box::new(*v), "done")) // Dereference v to get Box + })); + assert_eq!( + { + #[allow(clippy::unwrap_used)] + res.unwrap() + }, + "done" + ); + + let committed = draft.data_arc(); + assert_eq!(committed.enable_auto_launch, Some(true)); + assert_eq!(committed.enable_tun_mode, Some(false)); } - // 6. 新建并修改下一轮草稿 - { - let mut d = draft.draft_mut(); - d.enable_auto_launch = Some(true); + #[test] + fn test_with_data_modify_error_propagation() { + let draft = Draft::new(IVerge::default()); + + #[allow(clippy::unwrap_used)] + let err = block_on_ready(draft.with_data_modify(|v| async move { + drop(v); + Err::<(Box, ()), _>(anyhow!("boom")) + })) + .unwrap_err(); + + assert_eq!(format!("{err}"), "boom"); } - assert_eq!(draft.draft_mut().enable_auto_launch, Some(true)); - // 7. 丢弃草稿 - draft.discard(); + #[test] + fn test_with_data_modify_does_not_touch_existing_draft() { + let draft = Draft::new(IVerge { + enable_auto_launch: Some(false), + enable_tun_mode: Some(false), + }); - // 8. 草稿已被丢弃,新的 draft_mut() 会重新 clone - assert_eq!(draft.draft_mut().enable_auto_launch, Some(false)); + // 创建草稿并修改 + draft.edit_draft(|d| { + d.enable_auto_launch = Some(true); + d.enable_tun_mode = Some(true); + }); + let draft_before = draft.latest_arc(); + let draft_before_ptr = std::sync::Arc::as_ptr(&draft_before); + + // 同时通过 with_data_modify 修改 committed + #[allow(clippy::unwrap_used)] + block_on_ready(draft.with_data_modify(|mut v| async move { + v.enable_auto_launch = Some(false); // 与草稿不同 + Ok((Box::new(*v), ())) // Dereference v to get Box + })) + .unwrap(); + + // 草稿应保持不变 + let draft_after = draft.latest_arc(); + assert_eq!( + std::sync::Arc::as_ptr(&draft_after), + draft_before_ptr, + "Existing draft should not be replaced by with_data_modify" + ); + assert_eq!(draft_after.enable_auto_launch, Some(true)); + assert_eq!(draft_after.enable_tun_mode, Some(true)); + + // 丢弃草稿后 latest == committed,且 committed 为异步修改结果 + draft.discard(); + let latest = draft.latest_arc(); + assert_eq!(latest.enable_auto_launch, Some(false)); + assert_eq!(latest.enable_tun_mode, Some(false)); + } } diff --git a/src-tauri/src/utils/i18n.rs b/src-tauri/src/utils/i18n.rs index 83f6140e..b1c55232 100644 --- a/src-tauri/src/utils/i18n.rs +++ b/src-tauri/src/utils/i18n.rs @@ -43,7 +43,7 @@ pub fn get_supported_languages() -> Vec { pub async fn current_language() -> String { Config::verge() .await - .latest_ref() + .latest_arc() .language .as_deref() .map(String::from) diff --git a/src-tauri/src/utils/init.rs b/src-tauri/src/utils/init.rs index 97211210..ff29d937 100644 --- a/src-tauri/src/utils/init.rs +++ b/src-tauri/src/utils/init.rs @@ -31,7 +31,7 @@ pub async fn init_logger() -> Result<()> { // TODO 提供 runtime 级别实时修改 let (log_level, log_max_size, log_max_count) = { let verge_guard = Config::verge().await; - let verge = verge_guard.latest_ref(); + let verge = verge_guard.latest_arc(); ( verge.get_log_level(), verge.app_log_max_size.unwrap_or(128), @@ -81,7 +81,7 @@ pub async fn init_logger() -> Result<()> { pub async fn sidecar_writer() -> Result { let (log_max_size, log_max_count) = { let verge_guard = Config::verge().await; - let verge = verge_guard.latest_ref(); + let verge = verge_guard.latest_arc(); ( verge.app_log_max_size.unwrap_or(128), verge.app_log_max_count.unwrap_or(8), @@ -109,7 +109,7 @@ pub async fn sidecar_writer() -> Result { pub async fn service_writer_config() -> Result { let (log_max_size, log_max_count) = { let verge_guard = Config::verge().await; - let verge = verge_guard.latest_ref(); + let verge = verge_guard.latest_arc(); ( verge.app_log_max_size.unwrap_or(128), verge.app_log_max_count.unwrap_or(8), @@ -134,7 +134,7 @@ pub async fn delete_log() -> Result<()> { let auto_log_clean = { let verge = Config::verge().await; - let verge = verge.latest_ref(); + let verge = verge.latest_arc(); verge.auto_log_clean.unwrap_or(0) }; @@ -509,7 +509,7 @@ pub async fn startup_script() -> Result<()> { let app_handle = handle::Handle::app_handle(); let script_path = { let verge = Config::verge().await; - let verge = verge.latest_ref(); + let verge = verge.latest_arc(); verge.startup_script.clone().unwrap_or_else(|| "".into()) }; diff --git a/src-tauri/src/utils/network.rs b/src-tauri/src/utils/network.rs index 65ef63a2..d2353673 100644 --- a/src-tauri/src/utils/network.rs +++ b/src-tauri/src/utils/network.rs @@ -165,10 +165,10 @@ impl NetworkManager { ProxyType::None => None, ProxyType::Localhost => { let port = { - let verge_port = Config::verge().await.latest_ref().verge_mixed_port; + let verge_port = Config::verge().await.latest_arc().verge_mixed_port; match verge_port { Some(port) => port, - None => Config::clash().await.latest_ref().get_mixed_port(), + None => Config::clash().await.latest_arc().get_mixed_port(), } }; let proxy_scheme = format!("http://127.0.0.1:{port}"); diff --git a/src-tauri/src/utils/resolve/mod.rs b/src-tauri/src/utils/resolve/mod.rs index 4055ecd0..5a355616 100644 --- a/src-tauri/src/utils/resolve/mod.rs +++ b/src-tauri/src/utils/resolve/mod.rs @@ -179,7 +179,7 @@ pub(super) async fn refresh_tray_menu() { pub(super) async fn init_window() { let is_silent_start = Config::verge() .await - .latest_ref() + .latest_arc() .enable_silent_start .unwrap_or(false); #[cfg(target_os = "macos")] diff --git a/src-tauri/src/utils/resolve/window.rs b/src-tauri/src/utils/resolve/window.rs index 387145ec..cf8cecd5 100644 --- a/src-tauri/src/utils/resolve/window.rs +++ b/src-tauri/src/utils/resolve/window.rs @@ -22,7 +22,7 @@ pub async fn build_new_window() -> Result { let app_handle = handle::Handle::app_handle(); let config = Config::verge().await; - let latest = config.latest_ref(); + let latest = config.latest_arc(); let start_page = latest.start_page.as_deref().unwrap_or("/"); match tauri::WebviewWindowBuilder::new( diff --git a/src-tauri/src/utils/server.rs b/src-tauri/src/utils/server.rs index c5062a8c..ef2f951b 100644 --- a/src-tauri/src/utils/server.rs +++ b/src-tauri/src/utils/server.rs @@ -89,15 +89,15 @@ pub fn embed_server() { let clash_config = Config::clash().await; let pac_content = verge_config - .latest_ref() + .latest_arc() .pac_file_content .clone() .unwrap_or_else(|| DEFAULT_PAC.into()); let pac_port = verge_config - .latest_ref() + .latest_arc() .verge_mixed_port - .unwrap_or_else(|| clash_config.latest_ref().get_mixed_port()); + .unwrap_or_else(|| clash_config.latest_arc().get_mixed_port()); let pac = warp::path!("commands" / "pac").map(move || { let processed_content = pac_content.replace("%mixed-port%", &format!("{pac_port}"));