refactor: streamline profile import logic and enhance error handling (#5051)
This commit is contained in:
committed by
GitHub
Unverified
parent
db091f5d2e
commit
3d96a575c0
@@ -47,6 +47,7 @@
|
||||
- 修复 Windows 深色模式下首次启动客户端标题栏颜色异常
|
||||
- 修复静默启动不加载完整 WebView 的问题
|
||||
- 修复 Linux WebKit 网络进程的崩溃
|
||||
- 修复无法导入订阅
|
||||
- 修复实际导入成功但显示导入失败的问题
|
||||
- 修复 macOS 连接界面显示异常
|
||||
|
||||
|
||||
@@ -97,80 +97,48 @@ pub async fn enhance_profiles() -> CmdResult {
|
||||
pub async fn import_profile(url: String, option: Option<PrfOption>) -> 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的顺序
|
||||
|
||||
@@ -81,6 +81,30 @@ impl<T: Clone + ToOwned> Draft<Box<T>> {
|
||||
pub fn discard(&self) -> Option<Box<T>> {
|
||||
self.inner.write().1.take()
|
||||
}
|
||||
|
||||
/// 异步修改正式数据,闭包直接获得 Box<T> 所有权
|
||||
pub async fn with_data_modify<F, Fut, R, E>(&self, f: F) -> Result<R, E>
|
||||
where
|
||||
T: Send + Sync + 'static,
|
||||
F: FnOnce(Box<T>) -> Fut + Send,
|
||||
Fut: std::future::Future<Output = Result<(Box<T>, R), E>> + Send,
|
||||
E: From<anyhow::Error>,
|
||||
{
|
||||
// 克隆正式数据
|
||||
let local = {
|
||||
let guard = self.inner.read();
|
||||
guard.0.clone()
|
||||
};
|
||||
|
||||
// 异步闭包执行,返回修改后的 Box<T> 和业务结果 R
|
||||
let (new_local, res) = f(local).await?;
|
||||
|
||||
// 写回正式数据
|
||||
let mut guard = self.inner.write();
|
||||
guard.0 = new_local;
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -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<String>,
|
||||
) -> 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<bool> {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -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, Fut>(f: F) -> JoinHandle<()>
|
||||
|
||||
Reference in New Issue
Block a user