From 3d96a575c0cccaedf0a5b497ef04ec2065d055d5 Mon Sep 17 00:00:00 2001 From: Tunglies Date: Tue, 14 Oct 2025 12:39:22 +0800 Subject: [PATCH] refactor: streamline profile import logic and enhance error handling (#5051) --- UPDATELOG.md | 1 + src-tauri/src/cmd/profile.rs | 102 +++++++++---------------- src-tauri/src/config/draft.rs | 24 ++++++ src-tauri/src/config/profiles.rs | 100 ++++++++++-------------- src-tauri/src/process/async_handler.rs | 6 +- 5 files changed, 103 insertions(+), 130 deletions(-) diff --git a/UPDATELOG.md b/UPDATELOG.md index 112f6e5c..722430ef 100644 --- a/UPDATELOG.md +++ b/UPDATELOG.md @@ -47,6 +47,7 @@ - 修复 Windows 深色模式下首次启动客户端标题栏颜色异常 - 修复静默启动不加载完整 WebView 的问题 - 修复 Linux WebKit 网络进程的崩溃 +- 修复无法导入订阅 - 修复实际导入成功但显示导入失败的问题 - 修复 macOS 连接界面显示异常 diff --git a/src-tauri/src/cmd/profile.rs b/src-tauri/src/cmd/profile.rs index d1d2b55b..6a926fae 100644 --- a/src-tauri/src/cmd/profile.rs +++ b/src-tauri/src/cmd/profile.rs @@ -97,80 +97,48 @@ pub async fn enhance_profiles() -> CmdResult { pub async fn import_profile(url: String, option: Option) -> CmdResult { logging!(info, Type::Cmd, "[导入订阅] 开始导入: {}", url); - let import_result = tokio::time::timeout(Duration::from_secs(60), async { - let item = PrfItem::from_url(&url, None, None, option).await?; - logging!(info, Type::Cmd, "[导入订阅] 下载完成,开始保存配置"); - - let profiles = Config::profiles().await; - let pre_count = profiles - .latest_ref() - .items - .as_ref() - .map_or(0, |items| items.len()); - - let result = profiles_append_item_safe(item.clone()).await; - result?; - - let post_count = profiles - .latest_ref() - .items - .as_ref() - .map_or(0, |items| items.len()); - if post_count <= pre_count { - logging!(error, Type::Cmd, "[导入订阅] 配置未增加,导入可能失败"); - return Err(anyhow::anyhow!("配置导入后数量未增加")); + // 直接依赖 PrfItem::from_url 自身的超时/重试逻辑,不再使用 tokio::time::timeout 包裹 + let item = match PrfItem::from_url(&url, None, None, option).await { + Ok(it) => { + logging!(info, Type::Cmd, "[导入订阅] 下载完成,开始保存配置"); + it } - - logging!( - info, - Type::Cmd, - "[导入订阅] 配置保存成功,数量: {} -> {}", - pre_count, - post_count - ); - - // 立即发送配置变更通知 - if let Some(uid) = &item.uid { - logging!(info, Type::Cmd, "[导入订阅] 发送配置变更通知: {}", uid); - handle::Handle::notify_profile_changed(uid.clone()); + Err(e) => { + logging!(error, Type::Cmd, "[导入订阅] 下载失败: {}", e); + return Err(format!("导入订阅失败: {}", e)); } + }; - // 异步保存配置文件并发送全局通知 - let uid_clone = item.uid.clone(); - crate::process::AsyncHandler::spawn(move || async move { - // 使用Send-safe helper函数 - if let Err(e) = profiles_save_file_safe().await { - logging!(error, Type::Cmd, "[导入订阅] 保存配置文件失败: {}", e); - } else { + match profiles_append_item_safe(item.clone()).await { + Ok(_) => match profiles_save_file_safe().await { + Ok(_) => { logging!(info, Type::Cmd, "[导入订阅] 配置文件保存成功"); - - // 发送全局配置更新通知 - if let Some(uid) = uid_clone { - // 延迟发送,确保文件已完全写入 - tokio::time::sleep(Duration::from_millis(100)).await; - handle::Handle::notify_profile_changed(uid); - } } - }); - - Ok(()) - }) - .await; - - match import_result { - Ok(Ok(())) => { - logging!(info, Type::Cmd, "[导入订阅] 导入完成: {}", url); - Ok(()) - } - Ok(Err(e)) => { - logging!(error, Type::Cmd, "[导入订阅] 导入失败: {}", e); - Err(format!("导入订阅失败: {e}")) - } - Err(_) => { - logging!(error, Type::Cmd, "[导入订阅] 导入超时(60秒): {}", url); - Err("导入订阅超时,请检查网络连接".into()) + Err(e) => { + logging!(error, Type::Cmd, "[导入订阅] 保存配置文件失败: {}", e); + } + }, + Err(e) => { + logging!(error, Type::Cmd, "[导入订阅] 保存配置失败: {}", e); + return Err(format!("导入订阅失败: {}", e)); } } + // 立即发送配置变更通知 + if let Some(uid) = &item.uid { + logging!(info, Type::Cmd, "[导入订阅] 发送配置变更通知: {}", uid); + handle::Handle::notify_profile_changed(uid.clone()); + } + + // 异步保存配置文件并发送全局通知 + let uid_clone = item.uid.clone(); + if let Some(uid) = uid_clone { + // 延迟发送,确保文件已完全写入 + tokio::time::sleep(Duration::from_millis(100)).await; + handle::Handle::notify_profile_changed(uid); + } + + logging!(info, Type::Cmd, "[导入订阅] 导入完成: {}", url); + Ok(()) } /// 调整profile的顺序 diff --git a/src-tauri/src/config/draft.rs b/src-tauri/src/config/draft.rs index ac82d685..3da3029c 100644 --- a/src-tauri/src/config/draft.rs +++ b/src-tauri/src/config/draft.rs @@ -81,6 +81,30 @@ impl Draft> { pub fn discard(&self) -> Option> { self.inner.write().1.take() } + + /// 异步修改正式数据,闭包直接获得 Box 所有权 + 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, + { + // 克隆正式数据 + let local = { + let guard = self.inner.read(); + guard.0.clone() + }; + + // 异步闭包执行,返回修改后的 Box 和业务结果 R + let (new_local, res) = f(local).await?; + + // 写回正式数据 + let mut guard = self.inner.write(); + guard.0 = new_local; + + Ok(res) + } } #[test] diff --git a/src-tauri/src/config/profiles.rs b/src-tauri/src/config/profiles.rs index 032489da..a33ff0f0 100644 --- a/src-tauri/src/config/profiles.rs +++ b/src-tauri/src/config/profiles.rs @@ -1,7 +1,6 @@ use super::{PrfOption, prfitem::PrfItem}; use crate::{ logging_error, - process::AsyncHandler, utils::{dirs, help, logging::Type}, }; use anyhow::{Context, Result, bail}; @@ -164,7 +163,8 @@ impl IProfiles { items.push(item) } - self.save_file().await + // self.save_file().await + Ok(()) } /// reorder items @@ -730,86 +730,66 @@ pub async fn profiles_append_item_with_filedata_safe( item: PrfItem, file_data: Option, ) -> Result<()> { - AsyncHandler::spawn_blocking(move || { - AsyncHandler::handle().block_on(async { - let item = PrfItem::from(item, file_data).await?; - let profiles = Config::profiles().await; - let mut profiles_guard = profiles.data_mut(); - profiles_guard.append_item(item).await - }) - }) - .await - .map_err(|e| anyhow::anyhow!("Task join error: {}", e))? + let item = PrfItem::from(item, file_data).await?; + profiles_append_item_safe(item).await } pub async fn profiles_append_item_safe(item: PrfItem) -> Result<()> { - AsyncHandler::spawn_blocking(move || { - AsyncHandler::handle().block_on(async { - let profiles = Config::profiles().await; - let mut profiles_guard = profiles.data_mut(); - profiles_guard.append_item(item).await + Config::profiles() + .await + .with_data_modify(|mut profiles| async move { + profiles.append_item(item).await?; + Ok((profiles, ())) }) - }) - .await - .map_err(|e| anyhow::anyhow!("Task join error: {}", e))? + .await } pub async fn profiles_patch_item_safe(index: String, item: PrfItem) -> Result<()> { - AsyncHandler::spawn_blocking(move || { - AsyncHandler::handle().block_on(async { - let profiles = Config::profiles().await; - let mut profiles_guard = profiles.data_mut(); - profiles_guard.patch_item(index, item).await + Config::profiles() + .await + .with_data_modify(|mut profiles| async move { + profiles.patch_item(index, item).await?; + Ok((profiles, ())) }) - }) - .await - .map_err(|e| anyhow::anyhow!("Task join error: {}", e))? + .await } pub async fn profiles_delete_item_safe(index: String) -> Result { - AsyncHandler::spawn_blocking(move || { - AsyncHandler::handle().block_on(async { - let profiles = Config::profiles().await; - let mut profiles_guard = profiles.data_mut(); - profiles_guard.delete_item(index).await + Config::profiles() + .await + .with_data_modify(|mut profiles| async move { + let deleted = profiles.delete_item(index).await?; + Ok((profiles, deleted)) }) - }) - .await - .map_err(|e| anyhow::anyhow!("Task join error: {}", e))? + .await } pub async fn profiles_reorder_safe(active_id: String, over_id: String) -> Result<()> { - AsyncHandler::spawn_blocking(move || { - AsyncHandler::handle().block_on(async { - let profiles = Config::profiles().await; - let mut profiles_guard = profiles.data_mut(); - profiles_guard.reorder(active_id, over_id).await + Config::profiles() + .await + .with_data_modify(|mut profiles| async move { + profiles.reorder(active_id, over_id).await?; + Ok((profiles, ())) }) - }) - .await - .map_err(|e| anyhow::anyhow!("Task join error: {}", e))? + .await } pub async fn profiles_save_file_safe() -> Result<()> { - AsyncHandler::spawn_blocking(move || { - AsyncHandler::handle().block_on(async { - let profiles = Config::profiles().await; - let profiles_guard = profiles.data_mut(); - profiles_guard.save_file().await + Config::profiles() + .await + .with_data_modify(|profiles| async move { + profiles.save_file().await?; + Ok((profiles, ())) }) - }) - .await - .map_err(|e| anyhow::anyhow!("Task join error: {}", e))? + .await } pub async fn profiles_draft_update_item_safe(index: String, item: PrfItem) -> Result<()> { - AsyncHandler::spawn_blocking(move || { - AsyncHandler::handle().block_on(async { - let profiles = Config::profiles().await; - let mut profiles_guard = profiles.draft_mut(); - profiles_guard.update_item(index, item).await + Config::profiles() + .await + .with_data_modify(|mut profiles| async move { + profiles.update_item(index, item).await?; + Ok((profiles, ())) }) - }) - .await - .map_err(|e| anyhow::anyhow!("Task join error: {}", e))? + .await } diff --git a/src-tauri/src/process/async_handler.rs b/src-tauri/src/process/async_handler.rs index 1b796485..eeb60cf5 100644 --- a/src-tauri/src/process/async_handler.rs +++ b/src-tauri/src/process/async_handler.rs @@ -8,9 +8,9 @@ use tauri::{async_runtime, async_runtime::JoinHandle}; pub struct AsyncHandler; impl AsyncHandler { - pub fn handle() -> async_runtime::RuntimeHandle { - async_runtime::handle() - } + // pub fn handle() -> async_runtime::RuntimeHandle { + // async_runtime::handle() + // } #[track_caller] pub fn spawn(f: F) -> JoinHandle<()>