perf: utilize smartstring for string handling (#5149)

* perf: utilize smartstring for string handling

- Updated various modules to replace standard String with smartstring::alias::String for improved performance and memory efficiency.
- Adjusted string manipulations and conversions throughout the codebase to ensure compatibility with the new smartstring type.
- Enhanced readability and maintainability by using `.into()` for conversions where applicable.
- Ensured that all instances of string handling in configuration, logging, and network management leverage the benefits of smartstring.

* fix: replace wrap_err with stringify_err for better error handling in UWP tool invocation

* refactor: update import path for StringifyErr and adjust string handling in sysopt

* fix: correct import path for CmdResult in UWP module

* fix: update argument type for execute_sysproxy_command to use std::string::String

* fix: add missing CmdResult import in UWP platform module

* fix: improve string handling and error messaging across multiple files

* style: format code for improved readability and consistency across multiple files

* fix: remove unused file
This commit is contained in:
Tunglies
2025-10-22 16:25:44 +08:00
committed by GitHub
parent fe96a7030a
commit a05ea64bcd
50 changed files with 361 additions and 272 deletions

13
src-tauri/Cargo.lock generated
View File

@@ -1131,6 +1131,7 @@ dependencies = [
"serde_json", "serde_json",
"serde_yaml_ng", "serde_yaml_ng",
"sha2 0.10.9", "sha2 0.10.9",
"smartstring",
"sys-locale", "sys-locale",
"sysinfo", "sysinfo",
"sysproxy", "sysproxy",
@@ -6780,6 +6781,18 @@ version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "smartstring"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29"
dependencies = [
"autocfg",
"serde",
"static_assertions",
"version_check",
]
[[package]] [[package]]
name = "smol" name = "smol"
version = "1.3.0" version = "1.3.0"

View File

@@ -87,6 +87,7 @@ tauri-plugin-devtools = { version = "2.0.1" }
tauri-plugin-mihomo = { git = "https://github.com/clash-verge-rev/tauri-plugin-mihomo" } tauri-plugin-mihomo = { git = "https://github.com/clash-verge-rev/tauri-plugin-mihomo" }
clash_verge_logger = { version = "0.1.0", git = "https://github.com/clash-verge-rev/clash-verge-logger" } clash_verge_logger = { version = "0.1.0", git = "https://github.com/clash-verge-rev/clash-verge-logger" }
async-trait = "0.1.89" async-trait = "0.1.89"
smartstring = { version = "1.0.1", features = ["serde"] }
clash_verge_service_ipc = { version = "2.0.17", features = [ clash_verge_service_ipc = { version = "2.0.17", features = [
"client", "client",
], git = "https://github.com/clash-verge-rev/clash-verge-service-ipc" } ], git = "https://github.com/clash-verge-rev/clash-verge-service-ipc" }

View File

@@ -1,12 +1,14 @@
use super::CmdResult; use super::CmdResult;
use crate::core::sysopt::Sysopt;
use crate::{ use crate::{
cmd::StringifyErr,
feat, logging, feat, logging,
utils::{ utils::{
dirs::{self, PathBufExec}, dirs::{self, PathBufExec},
logging::Type, logging::Type,
}, },
wrap_err,
}; };
use smartstring::alias::String;
use std::path::Path; use std::path::Path;
use tauri::{AppHandle, Manager}; use tauri::{AppHandle, Manager};
use tokio::fs; use tokio::fs;
@@ -14,29 +16,29 @@ use tokio::fs;
/// 打开应用程序所在目录 /// 打开应用程序所在目录
#[tauri::command] #[tauri::command]
pub async fn open_app_dir() -> CmdResult<()> { pub async fn open_app_dir() -> CmdResult<()> {
let app_dir = wrap_err!(dirs::app_home_dir())?; let app_dir = dirs::app_home_dir().stringify_err()?;
wrap_err!(open::that(app_dir)) open::that(app_dir).stringify_err()
} }
/// 打开核心所在目录 /// 打开核心所在目录
#[tauri::command] #[tauri::command]
pub async fn open_core_dir() -> CmdResult<()> { pub async fn open_core_dir() -> CmdResult<()> {
let core_dir = wrap_err!(tauri::utils::platform::current_exe())?; let core_dir = tauri::utils::platform::current_exe().stringify_err()?;
let core_dir = core_dir.parent().ok_or("failed to get core dir")?; let core_dir = core_dir.parent().ok_or("failed to get core dir")?;
wrap_err!(open::that(core_dir)) open::that(core_dir).stringify_err()
} }
/// 打开日志目录 /// 打开日志目录
#[tauri::command] #[tauri::command]
pub async fn open_logs_dir() -> CmdResult<()> { pub async fn open_logs_dir() -> CmdResult<()> {
let log_dir = wrap_err!(dirs::app_logs_dir())?; let log_dir = dirs::app_logs_dir().stringify_err()?;
wrap_err!(open::that(log_dir)) open::that(log_dir).stringify_err()
} }
/// 打开网页链接 /// 打开网页链接
#[tauri::command] #[tauri::command]
pub fn open_web_url(url: String) -> CmdResult<()> { pub fn open_web_url(url: String) -> CmdResult<()> {
wrap_err!(open::that(url)) open::that(url.as_str()).stringify_err()
} }
/// 打开/关闭开发者工具 /// 打开/关闭开发者工具
@@ -73,22 +75,27 @@ pub fn get_portable_flag() -> CmdResult<bool> {
/// 获取应用目录 /// 获取应用目录
#[tauri::command] #[tauri::command]
pub fn get_app_dir() -> CmdResult<String> { pub fn get_app_dir() -> CmdResult<String> {
let app_home_dir = wrap_err!(dirs::app_home_dir())?.to_string_lossy().into(); let app_home_dir = dirs::app_home_dir()
.stringify_err()?
.to_string_lossy()
.into();
Ok(app_home_dir) Ok(app_home_dir)
} }
/// 获取当前自启动状态 /// 获取当前自启动状态
#[tauri::command] #[tauri::command]
pub fn get_auto_launch_status() -> CmdResult<bool> { pub fn get_auto_launch_status() -> CmdResult<bool> {
use crate::core::sysopt::Sysopt; Sysopt::global().get_launch_status().stringify_err()
wrap_err!(Sysopt::global().get_launch_status())
} }
/// 下载图标缓存 /// 下载图标缓存
#[tauri::command] #[tauri::command]
pub async fn download_icon_cache(url: String, name: String) -> CmdResult<String> { pub async fn download_icon_cache(url: String, name: String) -> CmdResult<String> {
let icon_cache_dir = wrap_err!(dirs::app_home_dir())?.join("icons").join("cache"); let icon_cache_dir = dirs::app_home_dir()
let icon_path = icon_cache_dir.join(&name); .stringify_err()?
.join("icons")
.join("cache");
let icon_path = icon_cache_dir.join(name.as_str());
if icon_path.exists() { if icon_path.exists() {
return Ok(icon_path.to_string_lossy().into()); return Ok(icon_path.to_string_lossy().into());
@@ -98,9 +105,9 @@ pub async fn download_icon_cache(url: String, name: String) -> CmdResult<String>
let _ = std::fs::create_dir_all(&icon_cache_dir); let _ = std::fs::create_dir_all(&icon_cache_dir);
} }
let temp_path = icon_cache_dir.join(format!("{}.downloading", &name)); let temp_path = icon_cache_dir.join(format!("{}.downloading", name.as_str()));
let response = wrap_err!(reqwest::get(&url).await)?; let response = reqwest::get(url.as_str()).await.stringify_err()?;
let content_type = response let content_type = response
.headers() .headers()
@@ -110,7 +117,7 @@ pub async fn download_icon_cache(url: String, name: String) -> CmdResult<String>
let is_image = content_type.starts_with("image/"); let is_image = content_type.starts_with("image/");
let content = wrap_err!(response.bytes().await)?; let content = response.bytes().await.stringify_err()?;
let is_html = content.len() > 15 let is_html = content.len() > 15
&& (content.starts_with(b"<!DOCTYPE html") && (content.starts_with(b"<!DOCTYPE html")
@@ -129,7 +136,7 @@ pub async fn download_icon_cache(url: String, name: String) -> CmdResult<String>
} }
}; };
wrap_err!(std::io::copy(&mut content.as_ref(), &mut file))?; std::io::copy(&mut content.as_ref(), &mut file).stringify_err()?;
} }
if !icon_path.exists() { if !icon_path.exists() {
@@ -149,7 +156,7 @@ pub async fn download_icon_cache(url: String, name: String) -> CmdResult<String>
Ok(icon_path.to_string_lossy().into()) Ok(icon_path.to_string_lossy().into())
} else { } else {
let _ = temp_path.remove_if_exists().await; let _ = temp_path.remove_if_exists().await;
Err(format!("下载的内容不是有效图片: {url}")) Err(format!("下载的内容不是有效图片: {}", url.as_str()).into())
} }
} }
@@ -163,9 +170,9 @@ pub struct IconInfo {
/// 复制图标文件 /// 复制图标文件
#[tauri::command] #[tauri::command]
pub async fn copy_icon_file(path: String, icon_info: IconInfo) -> CmdResult<String> { pub async fn copy_icon_file(path: String, icon_info: IconInfo) -> CmdResult<String> {
let file_path = Path::new(&path); let file_path = Path::new(path.as_str());
let icon_dir = wrap_err!(dirs::app_home_dir())?.join("icons"); let icon_dir = dirs::app_home_dir().stringify_err()?.join("icons");
if !icon_dir.exists() { if !icon_dir.exists() {
let _ = fs::create_dir_all(&icon_dir).await; let _ = fs::create_dir_all(&icon_dir).await;
} }
@@ -176,17 +183,26 @@ pub async fn copy_icon_file(path: String, icon_info: IconInfo) -> CmdResult<Stri
let dest_path = icon_dir.join(format!( let dest_path = icon_dir.join(format!(
"{0}-{1}.{ext}", "{0}-{1}.{ext}",
icon_info.name, icon_info.current_t icon_info.name.as_str(),
icon_info.current_t.as_str()
)); ));
if file_path.exists() { if file_path.exists() {
if icon_info.previous_t.trim() != "" { if icon_info.previous_t.trim() != "" {
icon_dir icon_dir
.join(format!("{0}-{1}.png", icon_info.name, icon_info.previous_t)) .join(format!(
"{0}-{1}.png",
icon_info.name.as_str(),
icon_info.previous_t.as_str()
))
.remove_if_exists() .remove_if_exists()
.await .await
.unwrap_or_default(); .unwrap_or_default();
icon_dir icon_dir
.join(format!("{0}-{1}.ico", icon_info.name, icon_info.previous_t)) .join(format!(
"{0}-{1}.ico",
icon_info.name.as_str(),
icon_info.previous_t.as_str()
))
.remove_if_exists() .remove_if_exists()
.await .await
.unwrap_or_default(); .unwrap_or_default();
@@ -200,7 +216,7 @@ pub async fn copy_icon_file(path: String, icon_info: IconInfo) -> CmdResult<Stri
); );
match fs::copy(file_path, &dest_path).await { match fs::copy(file_path, &dest_path).await {
Ok(_) => Ok(dest_path.to_string_lossy().into()), Ok(_) => Ok(dest_path.to_string_lossy().into()),
Err(err) => Err(err.to_string()), Err(err) => Err(err.to_string().into()),
} }
} else { } else {
Err("file not found".into()) Err("file not found".into())
@@ -218,7 +234,7 @@ pub fn notify_ui_ready() -> CmdResult<()> {
/// UI加载阶段 /// UI加载阶段
#[tauri::command] #[tauri::command]
pub fn update_ui_stage(stage: String) -> CmdResult<()> { pub fn update_ui_stage(stage: String) -> CmdResult<()> {
log::info!(target: "app", "UI加载阶段更新: {stage}"); log::info!(target: "app", "UI加载阶段更新: {}", stage.as_str());
use crate::utils::resolve::ui::UiReadyStage; use crate::utils::resolve::ui::UiReadyStage;
@@ -229,8 +245,8 @@ pub fn update_ui_stage(stage: String) -> CmdResult<()> {
"ResourcesLoaded" => UiReadyStage::ResourcesLoaded, "ResourcesLoaded" => UiReadyStage::ResourcesLoaded,
"Ready" => UiReadyStage::Ready, "Ready" => UiReadyStage::Ready,
_ => { _ => {
log::warn!(target: "app", "未知的UI加载阶段: {stage}"); log::warn!(target: "app", "未知的UI加载阶段: {}", stage.as_str());
return Err(format!("未知的UI加载阶段: {stage}")); return Err(format!("未知的UI加载阶段: {}", stage.as_str()).into());
} }
}; };

View File

@@ -1,33 +1,34 @@
use super::CmdResult; use super::CmdResult;
use crate::{feat, wrap_err}; use crate::{cmd::StringifyErr, feat};
use feat::LocalBackupFile; use feat::LocalBackupFile;
use smartstring::alias::String;
/// Create a local backup /// Create a local backup
#[tauri::command] #[tauri::command]
pub async fn create_local_backup() -> CmdResult<()> { pub async fn create_local_backup() -> CmdResult<()> {
wrap_err!(feat::create_local_backup().await) feat::create_local_backup().await.stringify_err()
} }
/// List local backups /// List local backups
#[tauri::command] #[tauri::command]
pub fn list_local_backup() -> CmdResult<Vec<LocalBackupFile>> { pub fn list_local_backup() -> CmdResult<Vec<LocalBackupFile>> {
wrap_err!(feat::list_local_backup()) feat::list_local_backup().stringify_err()
} }
/// Delete local backup /// Delete local backup
#[tauri::command] #[tauri::command]
pub async fn delete_local_backup(filename: String) -> CmdResult<()> { pub async fn delete_local_backup(filename: String) -> CmdResult<()> {
wrap_err!(feat::delete_local_backup(filename).await) feat::delete_local_backup(filename).await.stringify_err()
} }
/// Restore local backup /// Restore local backup
#[tauri::command] #[tauri::command]
pub async fn restore_local_backup(filename: String) -> CmdResult<()> { pub async fn restore_local_backup(filename: String) -> CmdResult<()> {
wrap_err!(feat::restore_local_backup(filename).await) feat::restore_local_backup(filename).await.stringify_err()
} }
/// Export local backup to a user selected destination /// Export local backup to a user selected destination
#[tauri::command] #[tauri::command]
pub fn export_local_backup(filename: String, destination: String) -> CmdResult<()> { pub fn export_local_backup(filename: String, destination: String) -> CmdResult<()> {
wrap_err!(feat::export_local_backup(filename, destination)) feat::export_local_backup(filename, destination).stringify_err()
} }

View File

@@ -6,9 +6,10 @@ use crate::{
config::Config, config::Config,
core::{CoreManager, handle, validate::CoreConfigValidator}, core::{CoreManager, handle, validate::CoreConfigValidator},
}; };
use crate::{config::*, feat, logging, utils::logging::Type, wrap_err}; use crate::{config::*, feat, logging, utils::logging::Type};
use compact_str::CompactString; use compact_str::CompactString;
use serde_yaml_ng::Mapping; use serde_yaml_ng::Mapping;
use smartstring::alias::String;
/// 复制Clash环境变量 /// 复制Clash环境变量
#[tauri::command] #[tauri::command]
@@ -26,7 +27,7 @@ pub async fn get_clash_info() -> CmdResult<ClashInfo> {
/// 修改Clash配置 /// 修改Clash配置
#[tauri::command] #[tauri::command]
pub async fn patch_clash_config(payload: Mapping) -> CmdResult { pub async fn patch_clash_config(payload: Mapping) -> CmdResult {
wrap_err!(feat::patch_clash(payload).await) feat::patch_clash(payload).await.stringify_err()
} }
/// 修改Clash模式 /// 修改Clash模式
@@ -54,22 +55,23 @@ pub async fn change_clash_core(clash_core: String) -> CmdResult<Option<String>>
Type::Core, Type::Core,
"core changed and restarted to {clash_core}" "core changed and restarted to {clash_core}"
); );
handle::Handle::notice_message("config_core::change_success", &clash_core); handle::Handle::notice_message("config_core::change_success", clash_core);
handle::Handle::refresh_clash(); handle::Handle::refresh_clash();
Ok(None) Ok(None)
} }
Err(err) => { Err(err) => {
let error_msg = format!("Core changed but failed to restart: {err}"); let error_msg: String =
format!("Core changed but failed to restart: {err}").into();
handle::Handle::notice_message("config_core::change_error", error_msg.clone());
logging!(error, Type::Core, "{error_msg}"); logging!(error, Type::Core, "{error_msg}");
handle::Handle::notice_message("config_core::change_error", &error_msg);
Ok(Some(error_msg)) Ok(Some(error_msg))
} }
} }
} }
Err(err) => { Err(err) => {
let error_msg = err; let error_msg: String = err;
logging!(error, Type::Core, "failed to change core: {error_msg}"); logging!(error, Type::Core, "failed to change core: {error_msg}");
handle::Handle::notice_message("config_core::change_error", &error_msg); handle::Handle::notice_message("config_core::change_error", error_msg.clone());
Ok(Some(error_msg)) Ok(Some(error_msg))
} }
} }
@@ -78,7 +80,7 @@ pub async fn change_clash_core(clash_core: String) -> CmdResult<Option<String>>
/// 启动核心 /// 启动核心
#[tauri::command] #[tauri::command]
pub async fn start_core() -> CmdResult { pub async fn start_core() -> CmdResult {
let result = wrap_err!(CoreManager::global().start_core().await); let result = CoreManager::global().start_core().await.stringify_err();
if result.is_ok() { if result.is_ok() {
handle::Handle::refresh_clash(); handle::Handle::refresh_clash();
} }
@@ -88,7 +90,7 @@ pub async fn start_core() -> CmdResult {
/// 关闭核心 /// 关闭核心
#[tauri::command] #[tauri::command]
pub async fn stop_core() -> CmdResult { pub async fn stop_core() -> CmdResult {
let result = wrap_err!(CoreManager::global().stop_core().await); let result = CoreManager::global().stop_core().await.stringify_err();
if result.is_ok() { if result.is_ok() {
handle::Handle::refresh_clash(); handle::Handle::refresh_clash();
} }
@@ -98,7 +100,7 @@ pub async fn stop_core() -> CmdResult {
/// 重启核心 /// 重启核心
#[tauri::command] #[tauri::command]
pub async fn restart_core() -> CmdResult { pub async fn restart_core() -> CmdResult {
let result = wrap_err!(CoreManager::global().restart_core().await); let result = CoreManager::global().restart_core().await.stringify_err();
if result.is_ok() { if result.is_ok() {
handle::Handle::refresh_clash(); handle::Handle::refresh_clash();
} }
@@ -250,7 +252,7 @@ pub async fn get_dns_config_content() -> CmdResult<String> {
return Err("DNS config file not found".into()); return Err("DNS config file not found".into());
} }
let content = fs::read_to_string(&dns_path).await.stringify_err()?; let content = fs::read_to_string(&dns_path).await.stringify_err()?.into();
Ok(content) Ok(content)
} }

View File

@@ -1,4 +1,5 @@
use anyhow::Result; use anyhow::Result;
use smartstring::alias::String;
pub type CmdResult<T = ()> = Result<T, String>; pub type CmdResult<T = ()> = Result<T, String>;
@@ -47,7 +48,7 @@ pub trait StringifyErr<T> {
impl<T, E: std::fmt::Display> StringifyErr<T> for Result<T, E> { impl<T, E: std::fmt::Display> StringifyErr<T> for Result<T, E> {
fn stringify_err(self) -> CmdResult<T> { fn stringify_err(self) -> CmdResult<T> {
self.map_err(|e| e.to_string()) self.map_err(|e| e.to_string().into())
} }
fn stringify_err_log<F>(self, log_fn: F) -> CmdResult<T> fn stringify_err_log<F>(self, log_fn: F) -> CmdResult<T>
@@ -55,7 +56,7 @@ impl<T, E: std::fmt::Display> StringifyErr<T> for Result<T, E> {
F: Fn(&str), F: Fn(&str),
{ {
self.map_err(|e| { self.map_err(|e| {
let msg = e.to_string(); let msg = String::from(e.to_string());
log_fn(&msg); log_fn(&msg);
msg msg
}) })

View File

@@ -1,7 +1,7 @@
use super::CmdResult; use super::CmdResult;
use crate::cmd::StringifyErr;
use crate::core::{EventDrivenProxyManager, async_proxy_query::AsyncProxyQuery}; use crate::core::{EventDrivenProxyManager, async_proxy_query::AsyncProxyQuery};
use crate::process::AsyncHandler; use crate::process::AsyncHandler;
use crate::wrap_err;
use network_interface::NetworkInterface; use network_interface::NetworkInterface;
use serde_yaml_ng::Mapping; use serde_yaml_ng::Mapping;
@@ -82,7 +82,7 @@ pub fn get_network_interfaces_info() -> CmdResult<Vec<NetworkInterface>> {
use network_interface::{NetworkInterface, NetworkInterfaceConfig}; use network_interface::{NetworkInterface, NetworkInterfaceConfig};
let names = get_network_interfaces(); let names = get_network_interfaces();
let interfaces = wrap_err!(NetworkInterface::show())?; let interfaces = NetworkInterface::show().stringify_err()?;
let mut result = Vec::new(); let mut result = Vec::new();

View File

@@ -1,6 +1,6 @@
use super::CmdResult; use super::CmdResult;
use super::StringifyErr;
use crate::{ use crate::{
cmd::StringifyErr,
config::{ config::{
Config, IProfiles, PrfItem, PrfOption, Config, IProfiles, PrfItem, PrfOption,
profiles::{ profiles::{
@@ -14,8 +14,8 @@ use crate::{
process::AsyncHandler, process::AsyncHandler,
ret_err, ret_err,
utils::{dirs, help, logging::Type}, utils::{dirs, help, logging::Type},
wrap_err,
}; };
use smartstring::alias::String;
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::time::Duration; use std::time::Duration;
@@ -86,7 +86,7 @@ pub async fn enhance_profiles() -> CmdResult {
Ok(_) => {} Ok(_) => {}
Err(e) => { Err(e) => {
log::error!(target: "app", "{}", e); log::error!(target: "app", "{}", e);
return Err(e.to_string()); return Err(e.to_string().into());
} }
} }
handle::Handle::refresh_clash(); handle::Handle::refresh_clash();
@@ -95,7 +95,7 @@ pub async fn enhance_profiles() -> CmdResult {
/// 导入配置文件 /// 导入配置文件
#[tauri::command] #[tauri::command]
pub async fn import_profile(url: String, option: Option<PrfOption>) -> CmdResult { pub async fn import_profile(url: std::string::String, option: Option<PrfOption>) -> CmdResult {
logging!(info, Type::Cmd, "[导入订阅] 开始导入: {}", url); logging!(info, Type::Cmd, "[导入订阅] 开始导入: {}", url);
// 直接依赖 PrfItem::from_url 自身的超时/重试逻辑,不再使用 tokio::time::timeout 包裹 // 直接依赖 PrfItem::from_url 自身的超时/重试逻辑,不再使用 tokio::time::timeout 包裹
@@ -106,7 +106,7 @@ pub async fn import_profile(url: String, option: Option<PrfOption>) -> CmdResult
} }
Err(e) => { Err(e) => {
logging!(error, Type::Cmd, "[导入订阅] 下载失败: {}", e); logging!(error, Type::Cmd, "[导入订阅] 下载失败: {}", e);
return Err(format!("导入订阅失败: {}", e)); return Err(format!("导入订阅失败: {}", e).into());
} }
}; };
@@ -121,7 +121,7 @@ pub async fn import_profile(url: String, option: Option<PrfOption>) -> CmdResult
}, },
Err(e) => { Err(e) => {
logging!(error, Type::Cmd, "[导入订阅] 保存配置失败: {}", e); logging!(error, Type::Cmd, "[导入订阅] 保存配置失败: {}", e);
return Err(format!("导入订阅失败: {}", e)); return Err(format!("导入订阅失败: {}", e).into());
} }
} }
// 立即发送配置变更通知 // 立即发送配置变更通知
@@ -152,7 +152,7 @@ pub async fn reorder_profile(active_id: String, over_id: String) -> CmdResult {
} }
Err(err) => { Err(err) => {
log::error!(target: "app", "重新排序配置文件失败: {}", err); log::error!(target: "app", "重新排序配置文件失败: {}", err);
Err(format!("重新排序配置文件失败: {}", err)) Err(format!("重新排序配置文件失败: {}", err).into())
} }
} }
} }
@@ -172,7 +172,7 @@ pub async fn create_profile(item: PrfItem, file_data: Option<String>) -> CmdResu
} }
Err(err) => match err.to_string().as_str() { Err(err) => match err.to_string().as_str() {
"the file already exists" => Err("the file already exists".into()), "the file already exists" => Err("the file already exists".into()),
_ => Err(format!("add profile error: {err}")), _ => Err(format!("add profile error: {err}").into()),
}, },
} }
} }
@@ -184,7 +184,7 @@ pub async fn update_profile(index: String, option: Option<PrfOption>) -> CmdResu
Ok(_) => Ok(()), Ok(_) => Ok(()),
Err(e) => { Err(e) => {
log::error!(target: "app", "{}", e); log::error!(target: "app", "{}", e);
Err(e.to_string()) Err(e.to_string().into())
} }
} }
} }
@@ -194,7 +194,9 @@ pub async fn update_profile(index: String, option: Option<PrfOption>) -> CmdResu
pub async fn delete_profile(index: String) -> CmdResult { pub async fn delete_profile(index: String) -> CmdResult {
println!("delete_profile: {}", index); println!("delete_profile: {}", index);
// 使用Send-safe helper函数 // 使用Send-safe helper函数
let should_update = wrap_err!(profiles_delete_item_safe(index.clone()).await)?; let should_update = profiles_delete_item_safe(index.clone())
.await
.stringify_err()?;
profiles_save_file_safe().await.stringify_err()?; profiles_save_file_safe().await.stringify_err()?;
if should_update { if should_update {
@@ -207,7 +209,7 @@ pub async fn delete_profile(index: String) -> CmdResult {
} }
Err(e) => { Err(e) => {
log::error!(target: "app", "{}", e); log::error!(target: "app", "{}", e);
return Err(e.to_string()); return Err(e.to_string().into());
} }
} }
} }
@@ -264,7 +266,7 @@ pub async fn patch_profiles_config(profiles: IProfiles) -> CmdResult<bool> {
match profiles_data.get_item(new_profile) { match profiles_data.get_item(new_profile) {
Ok(item) => { Ok(item) => {
if let Some(file) = &item.file { if let Some(file) = &item.file {
let path = dirs::app_profiles_dir().map(|dir| dir.join(file)); let path = dirs::app_profiles_dir().map(|dir| dir.join(file.as_str()));
path.ok() path.ok()
} else { } else {
None None
@@ -321,7 +323,7 @@ pub async fn patch_profiles_config(profiles: IProfiles) -> CmdResult<bool> {
); );
handle::Handle::notice_message( handle::Handle::notice_message(
"config_validate::yaml_syntax_error", "config_validate::yaml_syntax_error",
&error_msg, error_msg.clone(),
); );
return Ok(false); return Ok(false);
} }
@@ -330,7 +332,7 @@ pub async fn patch_profiles_config(profiles: IProfiles) -> CmdResult<bool> {
logging!(error, Type::Cmd, "{}", error_msg); logging!(error, Type::Cmd, "{}", error_msg);
handle::Handle::notice_message( handle::Handle::notice_message(
"config_validate::yaml_parse_error", "config_validate::yaml_parse_error",
&error_msg, error_msg.clone(),
); );
return Ok(false); return Ok(false);
} }
@@ -339,7 +341,10 @@ pub async fn patch_profiles_config(profiles: IProfiles) -> CmdResult<bool> {
Ok(Err(err)) => { Ok(Err(err)) => {
let error_msg = format!("无法读取目标配置文件: {err}"); let error_msg = format!("无法读取目标配置文件: {err}");
logging!(error, Type::Cmd, "{}", error_msg); logging!(error, Type::Cmd, "{}", error_msg);
handle::Handle::notice_message("config_validate::file_read_error", &error_msg); handle::Handle::notice_message(
"config_validate::file_read_error",
error_msg.clone(),
);
return Ok(false); return Ok(false);
} }
Err(_) => { Err(_) => {
@@ -347,7 +352,7 @@ pub async fn patch_profiles_config(profiles: IProfiles) -> CmdResult<bool> {
logging!(error, Type::Cmd, "{}", error_msg); logging!(error, Type::Cmd, "{}", error_msg);
handle::Handle::notice_message( handle::Handle::notice_message(
"config_validate::file_read_timeout", "config_validate::file_read_timeout",
&error_msg, error_msg.clone(),
); );
return Ok(false); return Ok(false);
} }
@@ -479,12 +484,11 @@ pub async fn patch_profiles_config(profiles: IProfiles) -> CmdResult<bool> {
items: None, items: None,
}; };
// 静默恢复,不触发验证 // 静默恢复,不触发验证
wrap_err!({ Config::profiles()
Config::profiles() .await
.await .draft_mut()
.draft_mut() .patch_config(restore_profiles)
.patch_config(restore_profiles) .stringify_err()?;
})?;
Config::profiles().await.apply(); Config::profiles().await.apply();
crate::process::AsyncHandler::spawn(|| async move { crate::process::AsyncHandler::spawn(|| async move {
@@ -497,7 +501,7 @@ pub async fn patch_profiles_config(profiles: IProfiles) -> CmdResult<bool> {
} }
// 发送验证错误通知 // 发送验证错误通知
handle::Handle::notice_message("config_validate::error", &error_msg); handle::Handle::notice_message("config_validate::error", error_msg.to_string());
CURRENT_SWITCHING_PROFILE.store(false, Ordering::SeqCst); CURRENT_SWITCHING_PROFILE.store(false, Ordering::SeqCst);
Ok(false) Ok(false)
} }
@@ -539,12 +543,11 @@ pub async fn patch_profiles_config(profiles: IProfiles) -> CmdResult<bool> {
current: Some(prev_profile), current: Some(prev_profile),
items: None, items: None,
}; };
wrap_err!({ Config::profiles()
Config::profiles() .await
.await .draft_mut()
.draft_mut() .patch_config(restore_profiles)
.patch_config(restore_profiles) .stringify_err()?;
})?;
Config::profiles().await.apply(); Config::profiles().await.apply();
} }
@@ -585,7 +588,9 @@ pub async fn patch_profile(index: String, profile: PrfItem) -> CmdResult {
false false
}; };
wrap_err!(profiles_patch_item_safe(index.clone(), profile).await)?; profiles_patch_item_safe(index.clone(), profile)
.await
.stringify_err()?;
// 如果更新间隔或允许自动更新变更,异步刷新定时器 // 如果更新间隔或允许自动更新变更,异步刷新定时器
if should_refresh_timer { if should_refresh_timer {
@@ -609,19 +614,21 @@ pub async fn patch_profile(index: String, profile: PrfItem) -> CmdResult {
pub async fn view_profile(index: String) -> CmdResult { pub async fn view_profile(index: String) -> CmdResult {
let profiles = Config::profiles().await; let profiles = Config::profiles().await;
let profiles_ref = profiles.latest_ref(); let profiles_ref = profiles.latest_ref();
let file = { let file = profiles_ref
wrap_err!(profiles_ref.get_item(&index))? .get_item(&index)
.file .stringify_err()?
.clone() .file
.ok_or("the file field is null") .clone()
}?; .ok_or("the file field is null")?;
let path = wrap_err!(dirs::app_profiles_dir())?.join(file); let path = dirs::app_profiles_dir()
.stringify_err()?
.join(file.as_str());
if !path.exists() { if !path.exists() {
ret_err!("the file not found"); ret_err!("the file not found");
} }
wrap_err!(help::open_file(path)) help::open_file(path).stringify_err()
} }
/// 读取配置文件内容 /// 读取配置文件内容
@@ -629,8 +636,8 @@ pub async fn view_profile(index: String) -> CmdResult {
pub async fn read_profile_file(index: String) -> CmdResult<String> { pub async fn read_profile_file(index: String) -> CmdResult<String> {
let profiles = Config::profiles().await; let profiles = Config::profiles().await;
let profiles_ref = profiles.latest_ref(); let profiles_ref = profiles.latest_ref();
let item = wrap_err!(profiles_ref.get_item(&index))?; let item = profiles_ref.get_item(&index).stringify_err()?;
let data = wrap_err!(item.read_file())?; let data = item.read_file().stringify_err()?;
Ok(data) Ok(data)
} }

View File

@@ -14,7 +14,7 @@ pub async fn sync_tray_proxy_selection() -> CmdResult<()> {
} }
Err(e) => { Err(e) => {
logging!(error, Type::Cmd, "Failed to sync tray proxy selection: {e}"); logging!(error, Type::Cmd, "Failed to sync tray proxy selection: {e}");
Err(e.to_string()) Err(e.to_string().into())
} }
} }
} }

View File

@@ -1,7 +1,8 @@
use super::CmdResult; use super::CmdResult;
use crate::{config::*, core::CoreManager, log_err, wrap_err}; use crate::{cmd::StringifyErr, config::*, core::CoreManager, log_err};
use anyhow::Context; use anyhow::Context;
use serde_yaml_ng::Mapping; use serde_yaml_ng::Mapping;
use smartstring::alias::String;
use std::collections::HashMap; use std::collections::HashMap;
/// 获取运行时配置 /// 获取运行时配置
@@ -17,12 +18,14 @@ pub async fn get_runtime_yaml() -> CmdResult<String> {
let runtime = runtime.latest_ref(); let runtime = runtime.latest_ref();
let config = runtime.config.as_ref(); let config = runtime.config.as_ref();
wrap_err!( config
config .ok_or(anyhow::anyhow!("failed to parse config to yaml file"))
.ok_or(anyhow::anyhow!("failed to parse config to yaml file")) .and_then(|config| {
.and_then(|config| serde_yaml_ng::to_string(config) serde_yaml_ng::to_string(config)
.context("failed to convert config to yaml")) .context("failed to convert config to yaml")
) .map(|s| s.into())
})
.stringify_err()
} }
/// 获取运行时存在的键 /// 获取运行时存在的键
@@ -42,12 +45,11 @@ pub async fn get_runtime_proxy_chain_config(proxy_chain_exit_node: String) -> Cm
let runtime = Config::runtime().await; let runtime = Config::runtime().await;
let runtime = runtime.latest_ref(); let runtime = runtime.latest_ref();
let config = wrap_err!( let config = runtime
runtime .config
.config .as_ref()
.as_ref() .ok_or(anyhow::anyhow!("failed to parse config to yaml file"))
.ok_or(anyhow::anyhow!("failed to parse config to yaml file")) .stringify_err()?;
)?;
if let Some(serde_yaml_ng::Value::Sequence(proxies)) = config.get("proxies") { if let Some(serde_yaml_ng::Value::Sequence(proxies)) = config.get("proxies") {
let mut proxy_name = Some(Some(proxy_chain_exit_node.as_str())); let mut proxy_name = Some(Some(proxy_chain_exit_node.as_str()));
@@ -78,13 +80,14 @@ pub async fn get_runtime_proxy_chain_config(proxy_chain_exit_node: String) -> Cm
let mut config: HashMap<String, Vec<serde_yaml_ng::Value>> = HashMap::new(); let mut config: HashMap<String, Vec<serde_yaml_ng::Value>> = HashMap::new();
config.insert("proxies".to_string(), proxies_chain); config.insert("proxies".into(), proxies_chain);
wrap_err!(serde_yaml_ng::to_string(&config).context("YAML generation failed")) serde_yaml_ng::to_string(&config)
.context("YAML generation failed")
.map(|s| s.into())
.stringify_err()
} else { } else {
wrap_err!(Err(anyhow::anyhow!( Err("failed to get proxies or proxy-groups".into())
"failed to get proxies or proxy-groups".to_string()
)))
} }
} }
@@ -102,7 +105,9 @@ pub async fn update_proxy_chain_config_in_runtime(
} }
// 生成新的运行配置文件并通知 Clash 核心重新加载 // 生成新的运行配置文件并通知 Clash 核心重新加载
let run_path = wrap_err!(Config::generate_file(ConfigType::Run).await)?; let run_path = Config::generate_file(ConfigType::Run)
.await
.stringify_err()?;
log_err!(CoreManager::global().put_configs_force(run_path).await); log_err!(CoreManager::global().put_configs_force(run_path).await);
Ok(()) Ok(())

View File

@@ -1,11 +1,12 @@
use super::CmdResult; use super::CmdResult;
use crate::{ use crate::{
cmd::StringifyErr,
config::*, config::*,
core::{validate::CoreConfigValidator, *}, core::{validate::CoreConfigValidator, *},
logging, logging,
utils::{dirs, logging::Type}, utils::{dirs, logging::Type},
wrap_err,
}; };
use smartstring::alias::String;
use tokio::fs; use tokio::fs;
/// 保存profiles的配置 /// 保存profiles的配置
@@ -19,18 +20,18 @@ pub async fn save_profile_file(index: String, file_data: Option<String>) -> CmdR
let (file_path, original_content, is_merge_file) = { let (file_path, original_content, is_merge_file) = {
let profiles = Config::profiles().await; let profiles = Config::profiles().await;
let profiles_guard = profiles.latest_ref(); let profiles_guard = profiles.latest_ref();
let item = wrap_err!(profiles_guard.get_item(&index))?; let item = profiles_guard.get_item(&index).stringify_err()?;
// 确定是否为merge类型文件 // 确定是否为merge类型文件
let is_merge = item.itype.as_ref().is_some_and(|t| t == "merge"); let is_merge = item.itype.as_ref().is_some_and(|t| t == "merge");
let content = wrap_err!(item.read_file())?; let content = item.read_file().stringify_err()?;
let path = item.file.clone().ok_or("file field is null")?; let path = item.file.clone().ok_or("file field is null")?;
let profiles_dir = wrap_err!(dirs::app_profiles_dir())?; let profiles_dir = dirs::app_profiles_dir().stringify_err()?;
(profiles_dir.join(path), content, is_merge) (profiles_dir.join(path.as_str()), content, is_merge)
}; };
// 保存新的配置文件 // 保存新的配置文件
let file_data = file_data.ok_or("file_data is None")?; let file_data = file_data.ok_or("file_data is None")?;
wrap_err!(fs::write(&file_path, &file_data).await)?; fs::write(&file_path, &file_data).await.stringify_err()?;
let file_path_str = file_path.to_string_lossy().to_string(); let file_path_str = file_path.to_string_lossy().to_string();
logging!( logging!(
@@ -76,7 +77,9 @@ pub async fn save_profile_file(index: String, file_data: Option<String>) -> CmdR
error_msg error_msg
); );
// 恢复原始配置文件 // 恢复原始配置文件
wrap_err!(fs::write(&file_path, original_content).await)?; fs::write(&file_path, original_content)
.await
.stringify_err()?;
// 发送合并文件专用错误通知 // 发送合并文件专用错误通知
let result = (false, error_msg.clone()); let result = (false, error_msg.clone());
crate::cmd::validate::handle_yaml_validation_notice(&result, "合并配置文件"); crate::cmd::validate::handle_yaml_validation_notice(&result, "合并配置文件");
@@ -85,8 +88,10 @@ pub async fn save_profile_file(index: String, file_data: Option<String>) -> CmdR
Err(e) => { Err(e) => {
logging!(error, Type::Config, "[cmd配置save] 验证过程发生错误: {}", e); logging!(error, Type::Config, "[cmd配置save] 验证过程发生错误: {}", e);
// 恢复原始配置文件 // 恢复原始配置文件
wrap_err!(fs::write(&file_path, original_content).await)?; fs::write(&file_path, original_content)
return Err(e.to_string()); .await
.stringify_err()?;
return Err(e.to_string().into());
} }
} }
} }
@@ -100,7 +105,9 @@ pub async fn save_profile_file(index: String, file_data: Option<String>) -> CmdR
Ok((false, error_msg)) => { Ok((false, error_msg)) => {
logging!(warn, Type::Config, "[cmd配置save] 验证失败: {}", error_msg); logging!(warn, Type::Config, "[cmd配置save] 验证失败: {}", error_msg);
// 恢复原始配置文件 // 恢复原始配置文件
wrap_err!(fs::write(&file_path, original_content).await)?; fs::write(&file_path, original_content)
.await
.stringify_err()?;
// 智能判断错误类型 // 智能判断错误类型
let is_script_error = file_path_str.ends_with(".js") let is_script_error = file_path_str.ends_with(".js")
@@ -136,7 +143,7 @@ pub async fn save_profile_file(index: String, file_data: Option<String>) -> CmdR
Type::Config, Type::Config,
"[cmd配置save] 其他类型验证失败,发送一般通知" "[cmd配置save] 其他类型验证失败,发送一般通知"
); );
handle::Handle::notice_message("config_validate::error", &error_msg); handle::Handle::notice_message("config_validate::error", error_msg.to_owned());
} }
Ok(()) Ok(())
@@ -144,8 +151,10 @@ pub async fn save_profile_file(index: String, file_data: Option<String>) -> CmdR
Err(e) => { Err(e) => {
logging!(error, Type::Config, "[cmd配置save] 验证过程发生错误: {}", e); logging!(error, Type::Config, "[cmd配置save] 验证过程发生错误: {}", e);
// 恢复原始配置文件 // 恢复原始配置文件
wrap_err!(fs::write(&file_path, original_content).await)?; fs::write(&file_path, original_content)
Err(e.to_string()) .await
.stringify_err()?;
Err(e.to_string().into())
} }
} }
} }

View File

@@ -1,4 +1,4 @@
use super::CmdResult; use super::{CmdResult, StringifyErr};
use crate::{ use crate::{
core::service::{self, SERVICE_MANAGER, ServiceStatus}, core::service::{self, SERVICE_MANAGER, ServiceStatus},
utils::i18n::t, utils::i18n::t,
@@ -12,7 +12,7 @@ async fn execute_service_operation_sync(status: ServiceStatus, op_type: &str) ->
.await .await
{ {
let emsg = format!("{} Service failed: {}", op_type, e); let emsg = format!("{} Service failed: {}", op_type, e);
return Err(t(emsg.as_str()).await); return Err(t(emsg.as_str()).await.into());
} }
Ok(()) Ok(())
} }
@@ -39,8 +39,6 @@ pub async fn repair_service() -> CmdResult {
#[tauri::command] #[tauri::command]
pub async fn is_service_available() -> CmdResult<bool> { pub async fn is_service_available() -> CmdResult<bool> {
service::is_service_available() service::is_service_available().await.stringify_err()?;
.await Ok(true)
.map(|_| true)
.map_err(|e| e.to_string())
} }

View File

@@ -1,13 +1,14 @@
use super::CmdResult; use crate::cmd::CmdResult;
/// Platform-specific implementation for UWP functionality /// Platform-specific implementation for UWP functionality
#[cfg(windows)] #[cfg(windows)]
mod platform { mod platform {
use super::CmdResult; use crate::cmd::CmdResult;
use crate::{core::win_uwp, wrap_err}; use crate::cmd::StringifyErr;
use crate::core::win_uwp;
pub fn invoke_uwp_tool() -> CmdResult { pub fn invoke_uwp_tool() -> CmdResult {
wrap_err!(win_uwp::invoke_uwptools()) win_uwp::invoke_uwptools().stringify_err()
} }
} }

View File

@@ -4,11 +4,12 @@ use crate::{
logging, logging,
utils::logging::Type, utils::logging::Type,
}; };
use smartstring::alias::String;
/// 发送脚本验证通知消息 /// 发送脚本验证通知消息
#[tauri::command] #[tauri::command]
pub async fn script_validate_notice(status: String, msg: String) -> CmdResult { pub async fn script_validate_notice(status: String, msg: String) -> CmdResult {
handle::Handle::notice_message(&status, &msg); handle::Handle::notice_message(status.as_str(), msg.as_str());
Ok(()) Ok(())
} }
@@ -33,7 +34,7 @@ pub fn handle_script_validation_notice(result: &(bool, String), file_type: &str)
}; };
logging!(warn, Type::Config, "{} 验证失败: {}", file_type, error_msg); logging!(warn, Type::Config, "{} 验证失败: {}", file_type, error_msg);
handle::Handle::notice_message(status, error_msg); handle::Handle::notice_message(status, error_msg.to_owned());
} }
} }
@@ -117,6 +118,6 @@ pub fn handle_yaml_validation_notice(result: &(bool, String), file_type: &str) {
status, status,
error_msg error_msg
); );
handle::Handle::notice_message(status, error_msg); handle::Handle::notice_message(status, error_msg.to_owned());
} }
} }

View File

@@ -1,5 +1,5 @@
use super::CmdResult; use super::CmdResult;
use crate::{config::*, feat, wrap_err}; use crate::{cmd::StringifyErr, config::*, feat};
/// 获取Verge配置 /// 获取Verge配置
#[tauri::command] #[tauri::command]
@@ -16,5 +16,5 @@ pub async fn get_verge_config() -> CmdResult<IVergeResponse> {
/// 修改Verge配置 /// 修改Verge配置
#[tauri::command] #[tauri::command]
pub async fn patch_verge_config(payload: IVerge) -> CmdResult { pub async fn patch_verge_config(payload: IVerge) -> CmdResult {
wrap_err!(feat::patch_verge(payload, false).await) feat::patch_verge(payload, false).await.stringify_err()
} }

View File

@@ -1,6 +1,7 @@
use super::CmdResult; use super::CmdResult;
use crate::{config::*, core, feat, wrap_err}; use crate::{cmd::StringifyErr, config::*, core, feat};
use reqwest_dav::list_cmd::ListFile; use reqwest_dav::list_cmd::ListFile;
use smartstring::alias::String;
/// 保存 WebDAV 配置 /// 保存 WebDAV 配置
#[tauri::command] #[tauri::command]
@@ -30,23 +31,25 @@ pub async fn save_webdav_config(url: String, username: String, password: String)
/// 创建 WebDAV 备份并上传 /// 创建 WebDAV 备份并上传
#[tauri::command] #[tauri::command]
pub async fn create_webdav_backup() -> CmdResult<()> { pub async fn create_webdav_backup() -> CmdResult<()> {
wrap_err!(feat::create_backup_and_upload_webdav().await) feat::create_backup_and_upload_webdav()
.await
.stringify_err()
} }
/// 列出 WebDAV 上的备份文件 /// 列出 WebDAV 上的备份文件
#[tauri::command] #[tauri::command]
pub async fn list_webdav_backup() -> CmdResult<Vec<ListFile>> { pub async fn list_webdav_backup() -> CmdResult<Vec<ListFile>> {
wrap_err!(feat::list_wevdav_backup().await) feat::list_wevdav_backup().await.stringify_err()
} }
/// 删除 WebDAV 上的备份文件 /// 删除 WebDAV 上的备份文件
#[tauri::command] #[tauri::command]
pub async fn delete_webdav_backup(filename: String) -> CmdResult<()> { pub async fn delete_webdav_backup(filename: String) -> CmdResult<()> {
wrap_err!(feat::delete_webdav_backup(filename).await) feat::delete_webdav_backup(filename).await.stringify_err()
} }
/// 从 WebDAV 恢复备份文件 /// 从 WebDAV 恢复备份文件
#[tauri::command] #[tauri::command]
pub async fn restore_webdav_backup(filename: String) -> CmdResult<()> { pub async fn restore_webdav_backup(filename: String) -> CmdResult<()> {
wrap_err!(feat::restore_webdav_backup(filename).await) feat::restore_webdav_backup(filename).await.stringify_err()
} }

View File

@@ -8,6 +8,7 @@ use crate::{
}; };
use anyhow::{Result, anyhow}; use anyhow::{Result, anyhow};
use backoff::{Error as BackoffError, ExponentialBackoff}; use backoff::{Error as BackoffError, ExponentialBackoff};
use smartstring::alias::String;
use std::path::PathBuf; use std::path::PathBuf;
use tokio::sync::OnceCell; use tokio::sync::OnceCell;
use tokio::time::sleep; use tokio::time::sleep;
@@ -122,7 +123,7 @@ impl Config {
if let Some((msg_type, msg_content)) = validation_result { if let Some((msg_type, msg_content)) = validation_result {
sleep(timing::STARTUP_ERROR_DELAY).await; sleep(timing::STARTUP_ERROR_DELAY).await;
handle::Handle::notice_message(msg_type, &msg_content); handle::Handle::notice_message(msg_type, msg_content);
} }
Ok(()) Ok(())

View File

@@ -6,6 +6,7 @@ use crate::utils::{
use anyhow::{Context, Result, bail}; use anyhow::{Context, Result, bail};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_yaml_ng::Mapping; use serde_yaml_ng::Mapping;
use smartstring::alias::String;
use std::{fs, time::Duration}; use std::{fs, time::Duration};
#[derive(Debug, Clone, Deserialize, Serialize, Default)] #[derive(Debug, Clone, Deserialize, Serialize, Default)]
@@ -179,8 +180,8 @@ impl PrfItem {
file_data: Option<String>, file_data: Option<String>,
option: Option<PrfOption>, option: Option<PrfOption>,
) -> Result<PrfItem> { ) -> Result<PrfItem> {
let uid = help::get_uid("L"); let uid = help::get_uid("L").into();
let file = format!("{uid}.yaml"); let file = format!("{uid}.yaml").into();
let opt_ref = option.as_ref(); let opt_ref = option.as_ref();
let update_interval = opt_ref.and_then(|o| o.update_interval); let update_interval = opt_ref.and_then(|o| o.update_interval);
let mut merge = opt_ref.and_then(|o| o.merge.clone()); let mut merge = opt_ref.and_then(|o| o.merge.clone());
@@ -352,9 +353,9 @@ impl PrfItem {
None => None, None => None,
}; };
let uid = help::get_uid("R"); let uid = help::get_uid("R").into();
let file = format!("{uid}.yaml"); let file = format!("{uid}.yaml").into();
let name = name.unwrap_or(filename.unwrap_or("Remote File".into())); let name = name.unwrap_or(filename.unwrap_or("Remote File".into()).into());
let data = resp.text_with_charset()?; let data = resp.text_with_charset()?;
// process the charset "UTF-8 with BOM" // process the charset "UTF-8 with BOM"
@@ -422,13 +423,13 @@ impl PrfItem {
/// ## Merge type (enhance) /// ## Merge type (enhance)
/// create the enhanced item by using `merge` rule /// create the enhanced item by using `merge` rule
pub fn from_merge(uid: Option<String>) -> Result<PrfItem> { pub fn from_merge(uid: Option<String>) -> Result<PrfItem> {
let mut id = help::get_uid("m"); let mut id = help::get_uid("m").into();
let mut template = tmpl::ITEM_MERGE_EMPTY.into(); let mut template = tmpl::ITEM_MERGE_EMPTY.into();
if let Some(uid) = uid { if let Some(uid) = uid {
id = uid; id = uid;
template = tmpl::ITEM_MERGE.into(); template = tmpl::ITEM_MERGE.into();
} }
let file = format!("{id}.yaml"); let file = format!("{id}.yaml").into();
Ok(PrfItem { Ok(PrfItem {
uid: Some(id), uid: Some(id),
@@ -449,11 +450,11 @@ impl PrfItem {
/// ## Script type (enhance) /// ## Script type (enhance)
/// create the enhanced item by using javascript quick.js /// create the enhanced item by using javascript quick.js
pub fn from_script(uid: Option<String>) -> Result<PrfItem> { pub fn from_script(uid: Option<String>) -> Result<PrfItem> {
let mut id = help::get_uid("s"); let mut id = help::get_uid("s").into();
if let Some(uid) = uid { if let Some(uid) = uid {
id = uid; id = uid;
} }
let file = format!("{id}.js"); // js ext let file = format!("{id}.js").into(); // js ext
Ok(PrfItem { Ok(PrfItem {
uid: Some(id), uid: Some(id),
@@ -473,8 +474,8 @@ impl PrfItem {
/// ## Rules type (enhance) /// ## Rules type (enhance)
pub fn from_rules() -> Result<PrfItem> { pub fn from_rules() -> Result<PrfItem> {
let uid = help::get_uid("r"); let uid = help::get_uid("r").into();
let file = format!("{uid}.yaml"); // yaml ext let file = format!("{uid}.yaml").into(); // yaml ext
Ok(PrfItem { Ok(PrfItem {
uid: Some(uid), uid: Some(uid),
@@ -494,8 +495,8 @@ impl PrfItem {
/// ## Proxies type (enhance) /// ## Proxies type (enhance)
pub fn from_proxies() -> Result<PrfItem> { pub fn from_proxies() -> Result<PrfItem> {
let uid = help::get_uid("p"); let uid = help::get_uid("p").into();
let file = format!("{uid}.yaml"); // yaml ext let file = format!("{uid}.yaml").into(); // yaml ext
Ok(PrfItem { Ok(PrfItem {
uid: Some(uid), uid: Some(uid),
@@ -515,8 +516,8 @@ impl PrfItem {
/// ## Groups type (enhance) /// ## Groups type (enhance)
pub fn from_groups() -> Result<PrfItem> { pub fn from_groups() -> Result<PrfItem> {
let uid = help::get_uid("g"); let uid = help::get_uid("g").into();
let file = format!("{uid}.yaml"); // yaml ext let file = format!("{uid}.yaml").into(); // yaml ext
Ok(PrfItem { Ok(PrfItem {
uid: Some(uid), uid: Some(uid),
@@ -540,8 +541,9 @@ impl PrfItem {
.file .file
.clone() .clone()
.ok_or_else(|| anyhow::anyhow!("could not find the file"))?; .ok_or_else(|| anyhow::anyhow!("could not find the file"))?;
let path = dirs::app_profiles_dir()?.join(file); let path = dirs::app_profiles_dir()?.join(file.as_str());
fs::read_to_string(path).context("failed to read the file") let content = fs::read_to_string(path).context("failed to read the file")?;
Ok(content.into())
} }
/// save the file data /// save the file data
@@ -550,7 +552,7 @@ impl PrfItem {
.file .file
.clone() .clone()
.ok_or_else(|| anyhow::anyhow!("could not find the file"))?; .ok_or_else(|| anyhow::anyhow!("could not find the file"))?;
let path = dirs::app_profiles_dir()?.join(file); let path = dirs::app_profiles_dir()?.join(file.as_str());
fs::write(path, data.as_bytes()).context("failed to save the file") fs::write(path, data.as_bytes()).context("failed to save the file")
} }
} }

View File

@@ -6,6 +6,7 @@ use crate::utils::{
use anyhow::{Context, Result, bail}; use anyhow::{Context, Result, bail};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_yaml_ng::Mapping; use serde_yaml_ng::Mapping;
use smartstring::alias::String;
use std::collections::HashSet; use std::collections::HashSet;
use tokio::fs; use tokio::fs;
@@ -47,7 +48,7 @@ impl IProfiles {
if let Some(items) = profiles.items.as_mut() { if let Some(items) = profiles.items.as_mut() {
for item in items.iter_mut() { for item in items.iter_mut() {
if item.uid.is_none() { if item.uid.is_none() {
item.uid = Some(help::get_uid("d")); item.uid = Some(help::get_uid("d").into());
} }
} }
} }
@@ -142,7 +143,7 @@ impl IProfiles {
let file = item.file.clone().ok_or_else(|| { let file = item.file.clone().ok_or_else(|| {
anyhow::anyhow!("file field is required when file_data is provided") anyhow::anyhow!("file field is required when file_data is provided")
})?; })?;
let path = dirs::app_profiles_dir()?.join(&file); let path = dirs::app_profiles_dir()?.join(file.as_str());
fs::write(&path, file_data.as_bytes()) fs::write(&path, file_data.as_bytes())
.await .await
@@ -240,13 +241,13 @@ impl IProfiles {
// move the field value after save // move the field value after save
if let Some(file_data) = item.file_data.take() { if let Some(file_data) = item.file_data.take() {
let file = each.file.take(); let file = each.file.take();
let file = let file = file
file.unwrap_or(item.file.take().unwrap_or(format!("{}.yaml", &uid))); .unwrap_or(item.file.take().unwrap_or(format!("{}.yaml", &uid).into()));
// the file must exists // the file must exists
each.file = Some(file.clone()); each.file = Some(file.clone());
let path = dirs::app_profiles_dir()?.join(&file); let path = dirs::app_profiles_dir()?.join(file.as_str());
fs::write(&path, file_data.as_bytes()) fs::write(&path, file_data.as_bytes())
.await .await
@@ -291,7 +292,7 @@ impl IProfiles {
&& let Some(file) = items.remove(index).file && let Some(file) = items.remove(index).file
{ {
let _ = dirs::app_profiles_dir()? let _ = dirs::app_profiles_dir()?
.join(file) .join(file.as_str())
.remove_if_exists() .remove_if_exists()
.await; .await;
} }
@@ -306,7 +307,7 @@ impl IProfiles {
&& let Some(file) = items.remove(index).file && let Some(file) = items.remove(index).file
{ {
let _ = dirs::app_profiles_dir()? let _ = dirs::app_profiles_dir()?
.join(file) .join(file.as_str())
.remove_if_exists() .remove_if_exists()
.await; .await;
} }
@@ -321,7 +322,7 @@ impl IProfiles {
&& let Some(file) = items.remove(index).file && let Some(file) = items.remove(index).file
{ {
let _ = dirs::app_profiles_dir()? let _ = dirs::app_profiles_dir()?
.join(file) .join(file.as_str())
.remove_if_exists() .remove_if_exists()
.await; .await;
} }
@@ -336,7 +337,7 @@ impl IProfiles {
&& let Some(file) = items.remove(index).file && let Some(file) = items.remove(index).file
{ {
let _ = dirs::app_profiles_dir()? let _ = dirs::app_profiles_dir()?
.join(file) .join(file.as_str())
.remove_if_exists() .remove_if_exists()
.await; .await;
} }
@@ -351,7 +352,7 @@ impl IProfiles {
&& let Some(file) = items.remove(index).file && let Some(file) = items.remove(index).file
{ {
let _ = dirs::app_profiles_dir()? let _ = dirs::app_profiles_dir()?
.join(file) .join(file.as_str())
.remove_if_exists() .remove_if_exists()
.await; .await;
} }
@@ -366,7 +367,7 @@ impl IProfiles {
&& let Some(file) = items.remove(index).file && let Some(file) = items.remove(index).file
{ {
let _ = dirs::app_profiles_dir()? let _ = dirs::app_profiles_dir()?
.join(file) .join(file.as_str())
.remove_if_exists() .remove_if_exists()
.await; .await;
} }
@@ -392,7 +393,7 @@ impl IProfiles {
(Some(current), Some(items)) => { (Some(current), Some(items)) => {
if let Some(item) = items.iter().find(|e| e.uid.as_ref() == Some(current)) { if let Some(item) = items.iter().find(|e| e.uid.as_ref() == Some(current)) {
let file_path = match item.file.as_ref() { let file_path = match item.file.as_ref() {
Some(file) => dirs::app_profiles_dir()?.join(file), Some(file) => dirs::app_profiles_dir()?.join(file.as_str()),
None => bail!("failed to get the file field"), None => bail!("failed to get the file field"),
}; };
return help::read_mapping(&file_path).await; return help::read_mapping(&file_path).await;
@@ -544,7 +545,7 @@ impl IProfiles {
log::info!(target: "app", "已清理冗余文件: {file_name}"); log::info!(target: "app", "已清理冗余文件: {file_name}");
} }
Err(e) => { Err(e) => {
failed_deletions.push(format!("{file_name}: {e}")); failed_deletions.push(format!("{file_name}: {e}").into());
log::warn!(target: "app", "清理文件失败: {file_name} - {e}"); log::warn!(target: "app", "清理文件失败: {file_name} - {e}");
} }
} }

View File

@@ -1,7 +1,9 @@
use crate::enhance::field::use_keys; use crate::enhance::field::use_keys;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_yaml_ng::{Mapping, Value}; use serde_yaml_ng::{Mapping, Value};
use smartstring::alias::String;
use std::collections::HashMap; use std::collections::HashMap;
#[derive(Default, Debug, Clone, Deserialize, Serialize)] #[derive(Default, Debug, Clone, Deserialize, Serialize)]
pub struct IRuntime { pub struct IRuntime {
pub config: Option<Mapping>, pub config: Option<Mapping>,
@@ -30,15 +32,15 @@ impl IRuntime {
let patch_tun = patch.get("tun"); let patch_tun = patch.get("tun");
if patch_tun.is_some() { if patch_tun.is_some() {
let tun = config.get("tun"); let tun = config.get("tun");
let mut tun = tun.map_or(Mapping::new(), |val| { let mut tun: Mapping = tun.map_or(Mapping::new(), |val| {
val.as_mapping().cloned().unwrap_or(Mapping::new()) val.as_mapping().cloned().unwrap_or(Mapping::new())
}); });
let patch_tun = patch_tun.map_or(Mapping::new(), |val| { let patch_tun = patch_tun.map_or(Mapping::new(), |val| {
val.as_mapping().cloned().unwrap_or(Mapping::new()) val.as_mapping().cloned().unwrap_or(Mapping::new())
}); });
use_keys(&patch_tun).into_iter().for_each(|key| { use_keys(&patch_tun).into_iter().for_each(|key| {
if let Some(value) = patch_tun.get(&key).to_owned() { if let Some(value) = patch_tun.get(key.as_str()) {
tun.insert(key.into(), value.clone()); tun.insert(Value::from(key.as_str()), value.clone());
} }
}); });

View File

@@ -6,6 +6,7 @@ use crate::{
use anyhow::Result; use anyhow::Result;
use log::LevelFilter; use log::LevelFilter;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use smartstring::alias::String;
/// ### `verge.yaml` schema /// ### `verge.yaml` schema
#[derive(Default, Debug, Clone, Deserialize, Serialize)] #[derive(Default, Debug, Clone, Deserialize, Serialize)]
@@ -322,7 +323,7 @@ impl IVerge {
fn get_system_language() -> String { fn get_system_language() -> String {
let sys_lang = sys_locale::get_locale() let sys_lang = sys_locale::get_locale()
.unwrap_or_else(|| String::from("en")) .unwrap_or_else(|| "en".into())
.to_lowercase(); .to_lowercase();
let lang_code = sys_lang.split(['_', '-']).next().unwrap_or("en"); let lang_code = sys_lang.split(['_', '-']).next().unwrap_or("en");

View File

@@ -3,6 +3,7 @@ use anyhow::Error;
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use parking_lot::Mutex; use parking_lot::Mutex;
use reqwest_dav::list_cmd::{ListEntity, ListFile}; use reqwest_dav::list_cmd::{ListEntity, ListFile};
use smartstring::alias::String;
use std::{ use std::{
collections::HashMap, collections::HashMap,
env::{consts::OS, temp_dir}, env::{consts::OS, temp_dir},
@@ -123,8 +124,11 @@ impl WebDavClient {
})) }))
.build()?, .build()?,
) )
.set_host(config.url) .set_host(config.url.into())
.set_auth(reqwest_dav::Auth::Basic(config.username, config.password)) .set_auth(reqwest_dav::Auth::Basic(
config.username.into(),
config.password.into(),
))
.build()?; .build()?;
// 尝试检查目录是否存在,如果不存在尝试创建 // 尝试检查目录是否存在,如果不存在尝试创建
@@ -163,7 +167,7 @@ impl WebDavClient {
pub async fn upload(&self, file_path: PathBuf, file_name: String) -> Result<(), Error> { pub async fn upload(&self, file_path: PathBuf, file_name: String) -> Result<(), Error> {
let client = self.get_client(Operation::Upload).await?; let client = self.get_client(Operation::Upload).await?;
let webdav_path: String = format!("{}/{}", dirs::BACKUP_DIR, file_name); let webdav_path: String = format!("{}/{}", dirs::BACKUP_DIR, file_name).into();
// 读取文件并上传,如果失败尝试一次重试 // 读取文件并上传,如果失败尝试一次重试
let file_content = fs::read(&file_path)?; let file_content = fs::read(&file_path)?;
@@ -248,8 +252,8 @@ impl WebDavClient {
pub fn create_backup() -> Result<(String, PathBuf), Error> { pub fn create_backup() -> Result<(String, PathBuf), Error> {
let now = chrono::Local::now().format("%Y-%m-%d_%H-%M-%S").to_string(); let now = chrono::Local::now().format("%Y-%m-%d_%H-%M-%S").to_string();
let zip_file_name = format!("{OS}-backup-{now}.zip"); let zip_file_name: String = format!("{OS}-backup-{now}.zip").into();
let zip_path = temp_dir().join(&zip_file_name); let zip_path = temp_dir().join(zip_file_name.as_str());
let file = fs::File::create(&zip_path)?; let file = fs::File::create(&zip_path)?;
let mut zip = zip::ZipWriter::new(file); let mut zip = zip::ZipWriter::new(file);

View File

@@ -8,6 +8,7 @@ use crate::config::{Config, IVerge};
use crate::core::{async_proxy_query::AsyncProxyQuery, handle}; use crate::core::{async_proxy_query::AsyncProxyQuery, handle};
use crate::process::AsyncHandler; use crate::process::AsyncHandler;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use smartstring::alias::String;
use sysproxy::{Autoproxy, Sysproxy}; use sysproxy::{Autoproxy, Sysproxy};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -426,13 +427,15 @@ impl EventDrivenProxyManager {
}; };
let port = verge_mixed_port.unwrap_or(default_port); let port = verge_mixed_port.unwrap_or(default_port);
let host = proxy_host.unwrap_or_else(|| network::DEFAULT_PROXY_HOST.into()); let host = proxy_host
.unwrap_or_else(|| network::DEFAULT_PROXY_HOST.into())
.into();
Sysproxy { Sysproxy {
enable: true, enable: true,
host, host,
port, port,
bypass: Self::get_bypass_config().await, bypass: Self::get_bypass_config().await.into(),
} }
} }
@@ -445,9 +448,9 @@ impl EventDrivenProxyManager {
let custom = verge.system_proxy_bypass.as_deref().unwrap_or(""); let custom = verge.system_proxy_bypass.as_deref().unwrap_or("");
match (use_default, custom.is_empty()) { match (use_default, custom.is_empty()) {
(_, true) => bypass::DEFAULT.to_string(), (_, true) => bypass::DEFAULT.into(),
(true, false) => format!("{},{}", bypass::DEFAULT, custom), (true, false) => format!("{},{}", bypass::DEFAULT, custom).into(),
(false, false) => custom.to_string(), (false, false) => custom.into(),
} }
} }

View File

@@ -1,5 +1,6 @@
use crate::{APP_HANDLE, constants::timing, singleton}; use crate::{APP_HANDLE, constants::timing, singleton};
use parking_lot::RwLock; use parking_lot::RwLock;
use smartstring::alias::String;
use std::{sync::Arc, thread}; use std::{sync::Arc, thread};
use tauri::{AppHandle, Manager, WebviewWindow}; use tauri::{AppHandle, Manager, WebviewWindow};
use tauri_plugin_mihomo::{Mihomo, MihomoExt}; use tauri_plugin_mihomo::{Mihomo, MihomoExt};
@@ -199,7 +200,7 @@ impl Handle {
pub fn set_activation_policy(&self, policy: tauri::ActivationPolicy) -> Result<(), String> { pub fn set_activation_policy(&self, policy: tauri::ActivationPolicy) -> Result<(), String> {
Self::app_handle() Self::app_handle()
.set_activation_policy(policy) .set_activation_policy(policy)
.map_err(|e| e.to_string()) .map_err(|e| e.to_string().into())
} }
pub fn set_activation_policy_regular(&self) { pub fn set_activation_policy_regular(&self) {

View File

@@ -6,6 +6,7 @@ use crate::{
}; };
use anyhow::{Result, bail}; use anyhow::{Result, bail};
use parking_lot::Mutex; use parking_lot::Mutex;
use smartstring::alias::String;
use std::{collections::HashMap, fmt, str::FromStr, sync::Arc}; use std::{collections::HashMap, fmt, str::FromStr, sync::Arc};
use tauri::{AppHandle, Manager}; use tauri::{AppHandle, Manager};
use tauri_plugin_global_shortcut::{Code, GlobalShortcutExt, ShortcutState}; use tauri_plugin_global_shortcut::{Code, GlobalShortcutExt, ShortcutState};

View File

@@ -7,6 +7,7 @@ use crate::{
utils::{dirs, help, logging::Type}, utils::{dirs, help, logging::Type},
}; };
use anyhow::{Result, anyhow}; use anyhow::{Result, anyhow};
use smartstring::alias::String;
use std::{path::PathBuf, time::Instant}; use std::{path::PathBuf, time::Instant};
use tauri_plugin_mihomo::Error as MihomoError; use tauri_plugin_mihomo::Error as MihomoError;
use tokio::time::sleep; use tokio::time::sleep;

View File

@@ -8,6 +8,7 @@ use crate::{
utils::logging::Type, utils::logging::Type,
}; };
use anyhow::Result; use anyhow::Result;
use smartstring::alias::String;
impl CoreManager { impl CoreManager {
pub async fn start_core(&self) -> Result<()> { pub async fn start_core(&self) -> Result<()> {
@@ -48,7 +49,7 @@ impl CoreManager {
.ok_or_else(|| "Clash core cannot be None".to_string())?; .ok_or_else(|| "Clash core cannot be None".to_string())?;
if !IVerge::VALID_CLASH_CORES.contains(&core.as_str()) { if !IVerge::VALID_CLASH_CORES.contains(&core.as_str()) {
return Err(format!("Invalid clash core: {}", core)); return Err(format!("Invalid clash core: {}", core).into());
} }
Config::verge().await.draft_mut().clash_core = clash_core; Config::verge().await.draft_mut().clash_core = clash_core;
@@ -61,7 +62,9 @@ impl CoreManager {
.await .await
.map_err(|e| e.to_string())?; .map_err(|e| e.to_string())?;
self.apply_config(run_path).await.map_err(|e| e.to_string()) self.apply_config(run_path)
.await
.map_err(|e| e.to_string().into())
} }
async fn prepare_startup(&self) -> Result<()> { async fn prepare_startup(&self) -> Result<()> {

View File

@@ -37,7 +37,7 @@ impl CoreManager {
let (mut rx, child) = app_handle let (mut rx, child) = app_handle
.shell() .shell()
.sidecar(&clash_core)? .sidecar(clash_core.as_str())?
.args([ .args([
"-d", "-d",
dirs::path_to_str(&config_dir)?, dirs::path_to_str(&config_dir)?,

View File

@@ -4,6 +4,7 @@ use crate::{
utils::logging::Type, utils::logging::Type,
}; };
use parking_lot::RwLock; use parking_lot::RwLock;
use smartstring::alias::String;
use std::{ use std::{
sync::{ sync::{
atomic::{AtomicU64, Ordering}, atomic::{AtomicU64, Ordering},

View File

@@ -7,6 +7,7 @@ use crate::{
utils::logging::Type, utils::logging::Type,
}; };
use anyhow::Result; use anyhow::Result;
use smartstring::alias::String;
use std::sync::Arc; use std::sync::Arc;
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
use sysproxy::{Autoproxy, Sysproxy}; use sysproxy::{Autoproxy, Sysproxy};
@@ -45,7 +46,7 @@ async fn get_bypass() -> String {
if custom_bypass.is_empty() { if custom_bypass.is_empty() {
DEFAULT_BYPASS.into() DEFAULT_BYPASS.into()
} else if use_default { } else if use_default {
format!("{DEFAULT_BYPASS},{custom_bypass}") format!("{DEFAULT_BYPASS},{custom_bypass}").into()
} else { } else {
custom_bypass custom_bypass
} }
@@ -53,7 +54,7 @@ async fn get_bypass() -> String {
// Uses tokio Command with CREATE_NO_WINDOW flag to avoid DLL initialization issues during shutdown // Uses tokio Command with CREATE_NO_WINDOW flag to avoid DLL initialization issues during shutdown
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
async fn execute_sysproxy_command(args: Vec<String>) -> Result<()> { async fn execute_sysproxy_command(args: Vec<std::string::String>) -> Result<()> {
use crate::utils::dirs; use crate::utils::dirs;
use anyhow::bail; use anyhow::bail;
#[allow(unused_imports)] // Required for .creation_flags() method #[allow(unused_imports)] // Required for .creation_flags() method
@@ -68,7 +69,7 @@ async fn execute_sysproxy_command(args: Vec<String>) -> Result<()> {
} }
let output = Command::new(sysproxy_exe) let output = Command::new(sysproxy_exe)
.args(&args) .args(args)
.creation_flags(0x08000000) // CREATE_NO_WINDOW - 隐藏窗口 .creation_flags(0x08000000) // CREATE_NO_WINDOW - 隐藏窗口
.output() .output()
.await?; .await?;
@@ -132,9 +133,9 @@ impl Sysopt {
{ {
let mut sys = Sysproxy { let mut sys = Sysproxy {
enable: false, enable: false,
host: proxy_host.clone(), host: proxy_host.clone().into(),
port, port,
bypass: get_bypass().await, bypass: get_bypass().await.into(),
}; };
let mut auto = Autoproxy { let mut auto = Autoproxy {
enable: false, enable: false,
@@ -178,13 +179,13 @@ impl Sysopt {
return result; return result;
} }
let args = if pac_enable { let args: Vec<std::string::String> = if pac_enable {
let address = format!("http://{proxy_host}:{pac_port}/commands/pac"); let address = format!("http://{proxy_host}:{pac_port}/commands/pac");
vec!["pac".into(), address] vec!["pac".into(), address]
} else { } else {
let address = format!("{proxy_host}:{port}"); let address = format!("{proxy_host}:{port}");
let bypass = get_bypass().await; let bypass = get_bypass().await;
vec!["global".into(), address, bypass] vec!["global".into(), address, bypass.into()]
}; };
execute_sysproxy_command(args).await?; execute_sysproxy_command(args).await?;

View File

@@ -2,6 +2,7 @@ use crate::{config::Config, feat, logging, logging_error, singleton, utils::logg
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use delay_timer::prelude::{DelayTimer, DelayTimerBuilder, TaskBuilder}; use delay_timer::prelude::{DelayTimer, DelayTimerBuilder, TaskBuilder};
use parking_lot::RwLock; use parking_lot::RwLock;
use smartstring::alias::String;
use std::{ use std::{
collections::HashMap, collections::HashMap,
pin::Pin, pin::Pin,

View File

@@ -19,6 +19,7 @@ use super::handle;
use anyhow::Result; use anyhow::Result;
use futures::future::join_all; use futures::future::join_all;
use parking_lot::Mutex; use parking_lot::Mutex;
use smartstring::alias::String;
use std::collections::HashMap; use std::collections::HashMap;
use std::{ use std::{
fs, fs,
@@ -218,7 +219,7 @@ impl Tray {
let app_handle = handle::Handle::app_handle(); 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_ref().tray_event.clone() };
let tray_event: String = tray_event.unwrap_or("main_window".into()); let tray_event = tray_event.unwrap_or("main_window".into());
let tray = app_handle let tray = app_handle
.tray_by_id("main") .tray_by_id("main")
.ok_or_else(|| anyhow::anyhow!("Failed to get main tray"))?; .ok_or_else(|| anyhow::anyhow!("Failed to get main tray"))?;
@@ -625,7 +626,7 @@ async fn create_tray_menu(
.iter() .iter()
.filter_map(|group| group.get("name")) .filter_map(|group| group.get("name"))
.filter_map(|name| name.as_str()) .filter_map(|name| name.as_str())
.map(|name| name.to_string()) .map(|name| name.into())
.collect::<Vec<String>>() .collect::<Vec<String>>()
}) })
.unwrap_or_default() .unwrap_or_default()
@@ -671,7 +672,7 @@ async fn create_tray_menu(
let is_current_profile = Config::profiles() let is_current_profile = Config::profiles()
.await .await
.data_mut() .data_mut()
.is_current_profile_index(profile_uid.to_string()); .is_current_profile_index(profile_uid.clone());
CheckMenuItem::with_id( CheckMenuItem::with_id(
&app_handle, &app_handle,
format!("profiles_{profile_uid}"), format!("profiles_{profile_uid}"),
@@ -780,7 +781,7 @@ async fn create_tray_menu(
&group_items_refs, &group_items_refs,
) { ) {
let insertion_index = submenus.len(); let insertion_index = submenus.len();
submenus.push((group_name.to_string(), insertion_index, submenu)); submenus.push((group_name.into(), insertion_index, submenu));
} else { } else {
log::warn!(target: "app", "创建代理组子菜单失败: {}", group_name); log::warn!(target: "app", "创建代理组子菜单失败: {}", group_name);
} }

View File

@@ -1,4 +1,5 @@
use anyhow::Result; use anyhow::Result;
use smartstring::alias::String;
use std::path::Path; use std::path::Path;
use std::sync::Arc; use std::sync::Arc;
use tauri_plugin_shell::ShellExt; use tauri_plugin_shell::ShellExt;
@@ -124,7 +125,7 @@ impl CoreConfigValidator {
let content = match std::fs::read_to_string(config_path) { let content = match std::fs::read_to_string(config_path) {
Ok(content) => content, Ok(content) => content,
Err(err) => { Err(err) => {
let error_msg = format!("Failed to read file: {err}"); let error_msg = format!("Failed to read file: {err}").into();
logging!(error, Type::Validate, "无法读取文件: {}", error_msg); logging!(error, Type::Validate, "无法读取文件: {}", error_msg);
return Ok((false, error_msg)); return Ok((false, error_msg));
} }
@@ -138,7 +139,7 @@ impl CoreConfigValidator {
} }
Err(err) => { Err(err) => {
// 使用标准化的前缀,以便错误处理函数能正确识别 // 使用标准化的前缀,以便错误处理函数能正确识别
let error_msg = format!("YAML syntax error: {err}"); let error_msg = format!("YAML syntax error: {err}").into();
logging!(error, Type::Validate, "YAML语法错误: {}", error_msg); logging!(error, Type::Validate, "YAML语法错误: {}", error_msg);
Ok((false, error_msg)) Ok((false, error_msg))
} }
@@ -151,7 +152,7 @@ impl CoreConfigValidator {
let content = match std::fs::read_to_string(path) { let content = match std::fs::read_to_string(path) {
Ok(content) => content, Ok(content) => content,
Err(err) => { Err(err) => {
let error_msg = format!("Failed to read script file: {err}"); let error_msg = format!("Failed to read script file: {err}").into();
logging!(warn, Type::Validate, "脚本语法错误: {}", err); logging!(warn, Type::Validate, "脚本语法错误: {}", err);
//handle::Handle::notice_message("config_validate::script_syntax_error", &error_msg); //handle::Handle::notice_message("config_validate::script_syntax_error", &error_msg);
return Ok((false, error_msg)); return Ok((false, error_msg));
@@ -184,7 +185,7 @@ impl CoreConfigValidator {
Ok((true, String::new())) Ok((true, String::new()))
} }
Err(err) => { Err(err) => {
let error_msg = format!("Script syntax error: {err}"); let error_msg = format!("Script syntax error: {err}").into();
logging!(warn, Type::Validate, "脚本语法错误: {}", err); logging!(warn, Type::Validate, "脚本语法错误: {}", err);
//handle::Handle::notice_message("config_validate::script_syntax_error", &error_msg); //handle::Handle::notice_message("config_validate::script_syntax_error", &error_msg);
Ok((false, error_msg)) Ok((false, error_msg))
@@ -205,7 +206,7 @@ impl CoreConfigValidator {
// 检查文件是否存在 // 检查文件是否存在
if !std::path::Path::new(config_path).exists() { if !std::path::Path::new(config_path).exists() {
let error_msg = format!("File not found: {config_path}"); let error_msg = format!("File not found: {config_path}").into();
//handle::Handle::notice_message("config_validate::file_not_found", &error_msg); //handle::Handle::notice_message("config_validate::file_not_found", &error_msg);
return Ok((false, error_msg)); return Ok((false, error_msg));
} }
@@ -282,13 +283,13 @@ impl CoreConfigValidator {
// 使用子进程运行clash验证配置 // 使用子进程运行clash验证配置
let output = app_handle let output = app_handle
.shell() .shell()
.sidecar(clash_core)? .sidecar(clash_core.as_str())?
.args(["-t", "-d", app_dir_str, "-f", config_path]) .args(["-t", "-d", app_dir_str, "-f", config_path])
.output() .output()
.await?; .await?;
let stderr = String::from_utf8_lossy(&output.stderr); let stderr = std::string::String::from_utf8_lossy(&output.stderr);
let stdout = String::from_utf8_lossy(&output.stdout); let stdout = std::string::String::from_utf8_lossy(&output.stdout);
// 检查进程退出状态和错误输出 // 检查进程退出状态和错误输出
let error_keywords = ["FATA", "fatal", "Parse config error", "level=fatal"]; let error_keywords = ["FATA", "fatal", "Parse config error", "level=fatal"];
@@ -314,7 +315,7 @@ impl CoreConfigValidator {
}; };
logging!(info, Type::Validate, "-------- 验证结束 --------"); logging!(info, Type::Validate, "-------- 验证结束 --------");
Ok((false, error_msg)) // 返回错误消息给调用者处理 Ok((false, error_msg.into())) // 返回错误消息给调用者处理
} else { } else {
logging!(info, Type::Validate, "验证成功"); logging!(info, Type::Validate, "验证成功");
logging!(info, Type::Validate, "-------- 验证结束 --------"); logging!(info, Type::Validate, "-------- 验证结束 --------");

View File

@@ -4,6 +4,7 @@ use crate::{
utils::{dirs, help}, utils::{dirs, help},
}; };
use serde_yaml_ng::Mapping; use serde_yaml_ng::Mapping;
use smartstring::alias::String;
use std::fs; use std::fs;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -73,7 +74,7 @@ impl AsyncChainItemFrom for Option<ChainItem> {
let itype = item.itype.as_ref()?.as_str(); let itype = item.itype.as_ref()?.as_str();
let file = item.file.clone()?; let file = item.file.clone()?;
let uid = item.uid.clone().unwrap_or("".into()); let uid = item.uid.clone().unwrap_or("".into());
let path = dirs::app_profiles_dir().ok()?.join(file); let path = dirs::app_profiles_dir().ok()?.join(file.as_str());
if !path.exists() { if !path.exists() {
return None; return None;
@@ -82,7 +83,7 @@ impl AsyncChainItemFrom for Option<ChainItem> {
match itype { match itype {
"script" => Some(ChainItem { "script" => Some(ChainItem {
uid, uid,
data: ChainType::Script(fs::read_to_string(path).ok()?), data: ChainType::Script(fs::read_to_string(path).ok()?.into()),
}), }),
"merge" => Some(ChainItem { "merge" => Some(ChainItem {
uid, uid,

View File

@@ -1,4 +1,5 @@
use serde_yaml_ng::{Mapping, Value}; use serde_yaml_ng::{Mapping, Value};
use smartstring::alias::String;
use std::collections::HashSet; use std::collections::HashSet;
pub const HANDLE_FIELDS: [&str; 12] = [ pub const HANDLE_FIELDS: [&str; 12] = [
@@ -31,7 +32,7 @@ pub fn use_lowercase(config: Mapping) -> Mapping {
if let Some(key_str) = key.as_str() { if let Some(key_str) = key.as_str() {
let mut key_str = String::from(key_str); let mut key_str = String::from(key_str);
key_str.make_ascii_lowercase(); key_str.make_ascii_lowercase();
ret.insert(Value::from(key_str), value); ret.insert(Value::from(key_str.as_str()), value);
} }
} }
ret ret
@@ -70,8 +71,8 @@ pub fn use_keys(config: &Mapping) -> Vec<String> {
config config
.iter() .iter()
.filter_map(|(key, _)| key.as_str()) .filter_map(|(key, _)| key.as_str())
.map(|s| { .map(|s: &str| {
let mut s = s.to_string(); let mut s: String = s.into();
s.make_ascii_lowercase(); s.make_ascii_lowercase();
s s
}) })

View File

@@ -8,6 +8,7 @@ mod tun;
use self::{chain::*, field::*, merge::*, script::*, seq::*, tun::*}; use self::{chain::*, field::*, merge::*, script::*, seq::*, tun::*};
use crate::{config::Config, utils::tmpl}; use crate::{config::Config, utils::tmpl};
use serde_yaml_ng::Mapping; use serde_yaml_ng::Mapping;
use smartstring::alias::String;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
type ResultLog = Vec<(String, String)>; type ResultLog = Vec<(String, String)>;
@@ -192,7 +193,7 @@ pub async fn enhance() -> (Mapping, Vec<String>, HashMap<String, ResultLog>) {
let item = { let item = {
let profiles = Config::profiles().await; let profiles = Config::profiles().await;
let profiles = profiles.latest_ref(); let profiles = profiles.latest_ref();
profiles.get_item(&"Merge".to_string()).ok().cloned() profiles.get_item(&"Merge".into()).ok().cloned()
}; };
if let Some(item) = item { if let Some(item) = item {
<Option<ChainItem>>::from_async(&item).await <Option<ChainItem>>::from_async(&item).await
@@ -209,7 +210,7 @@ pub async fn enhance() -> (Mapping, Vec<String>, HashMap<String, ResultLog>) {
let item = { let item = {
let profiles = Config::profiles().await; let profiles = Config::profiles().await;
let profiles = profiles.latest_ref(); let profiles = profiles.latest_ref();
profiles.get_item(&"Script".to_string()).ok().cloned() profiles.get_item(&"Script".into()).ok().cloned()
}; };
if let Some(item) = item { if let Some(item) = item {
<Option<ChainItem>>::from_async(&item).await <Option<ChainItem>>::from_async(&item).await
@@ -253,7 +254,7 @@ pub async fn enhance() -> (Mapping, Vec<String>, HashMap<String, ResultLog>) {
config = res_config; config = res_config;
logs.extend(res_logs); logs.extend(res_logs);
} }
Err(err) => logs.push(("exception".into(), err.to_string())), Err(err) => logs.push(("exception".into(), err.to_string().into())),
} }
result_map.insert(global_script.uid, logs); result_map.insert(global_script.uid, logs);
@@ -280,13 +281,13 @@ pub async fn enhance() -> (Mapping, Vec<String>, HashMap<String, ResultLog>) {
if let ChainType::Script(script) = script_item.data { if let ChainType::Script(script) = script_item.data {
let mut logs = vec![]; let mut logs = vec![];
match use_script(script, config.to_owned(), profile_name.to_owned()) { match use_script(script, config.to_owned(), profile_name) {
Ok((res_config, res_logs)) => { Ok((res_config, res_logs)) => {
exists_keys.extend(use_keys(&res_config)); exists_keys.extend(use_keys(&res_config));
config = res_config; config = res_config;
logs.extend(res_logs); logs.extend(res_logs);
} }
Err(err) => logs.push(("exception".into(), err.to_string())), Err(err) => logs.push(("exception".into(), err.to_string().into())),
} }
result_map.insert(script_item.uid, logs); result_map.insert(script_item.uid, logs);
@@ -361,7 +362,7 @@ pub async fn enhance() -> (Mapping, Vec<String>, HashMap<String, ResultLog>) {
.for_each(|item| { .for_each(|item| {
log::debug!(target: "app", "run builtin script {}", item.uid); log::debug!(target: "app", "run builtin script {}", item.uid);
if let ChainType::Script(script) = item.data { if let ChainType::Script(script) = item.data {
match use_script(script, config.to_owned(), "".to_string()) { match use_script(script, config.to_owned(), "".into()) {
Ok((res_config, _)) => { Ok((res_config, _)) => {
config = res_config; config = res_config;
} }

View File

@@ -1,6 +1,7 @@
use super::use_lowercase; use super::use_lowercase;
use anyhow::{Error, Result}; use anyhow::{Error, Result};
use serde_yaml_ng::Mapping; use serde_yaml_ng::Mapping;
use smartstring::alias::String;
pub fn use_script( pub fn use_script(
script: String, script: String,
@@ -44,7 +45,7 @@ pub fn use_script(
) )
})?; })?;
let mut out = copy_outputs.borrow_mut(); let mut out = copy_outputs.borrow_mut();
out.push((level, data)); out.push((level.into(), data.into()));
Ok(JsValue::undefined()) Ok(JsValue::undefined())
}, },
), ),
@@ -94,7 +95,7 @@ pub fn use_script(
match res { match res {
Ok(config) => Ok((use_lowercase(config), out.to_vec())), Ok(config) => Ok((use_lowercase(config), out.to_vec())),
Err(err) => { Err(err) => {
out.push(("exception".into(), err.to_string())); out.push(("exception".into(), err.to_string().into()));
Ok((config, out.to_vec())) Ok((config, out.to_vec()))
} }
} }
@@ -121,7 +122,7 @@ fn strip_outer_quotes(s: &str) -> &str {
// 转义单引号和反斜杠用于单引号包裹的JavaScript字符串 // 转义单引号和反斜杠用于单引号包裹的JavaScript字符串
fn escape_js_string_for_single_quote(s: &str) -> String { fn escape_js_string_for_single_quote(s: &str) -> String {
s.replace('\\', "\\\\").replace('\'', "\\'") s.replace('\\', "\\\\").replace('\'', "\\'").into()
} }
#[test] #[test]
@@ -150,7 +151,7 @@ fn test_script() {
"; ";
let config = serde_yaml_ng::from_str(config).expect("Failed to parse test config YAML"); let config = serde_yaml_ng::from_str(config).expect("Failed to parse test config YAML");
let (config, results) = use_script(script.into(), config, "".to_string()) let (config, results) = use_script(script.into(), config, "".into())
.expect("Script execution should succeed in test"); .expect("Script execution should succeed in test");
let _ = serde_yaml_ng::to_string(&config).expect("Failed to serialize config to YAML"); let _ = serde_yaml_ng::to_string(&config).expect("Failed to serialize config to YAML");

View File

@@ -11,6 +11,7 @@ use anyhow::{Result, anyhow};
use chrono::Utc; use chrono::Utc;
use reqwest_dav::list_cmd::ListFile; use reqwest_dav::list_cmd::ListFile;
use serde::Serialize; use serde::Serialize;
use smartstring::alias::String;
use std::{fs, path::PathBuf}; use std::{fs, path::PathBuf};
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
@@ -82,7 +83,7 @@ pub async fn restore_webdav_backup(filename: String) -> Result<()> {
let backup_storage_path = app_home_dir() let backup_storage_path = app_home_dir()
.map_err(|e| anyhow::anyhow!("Failed to get app home dir: {e}"))? .map_err(|e| anyhow::anyhow!("Failed to get app home dir: {e}"))?
.join(&filename); .join(filename.as_str());
backup::WebDavClient::global() backup::WebDavClient::global()
.download(filename, backup_storage_path.clone()) .download(filename, backup_storage_path.clone())
.await .await
@@ -128,7 +129,7 @@ pub async fn create_local_backup() -> Result<()> {
})?; })?;
let backup_dir = local_backup_dir()?; let backup_dir = local_backup_dir()?;
let target_path = backup_dir.join(&file_name); let target_path = backup_dir.join(file_name.as_str());
if let Err(err) = move_file(temp_file_path.clone(), target_path.clone()) { if let Err(err) = move_file(temp_file_path.clone(), target_path.clone()) {
logging!( logging!(
@@ -196,9 +197,9 @@ pub fn list_local_backup() -> Result<Vec<LocalBackupFile>> {
.map(|time| chrono::DateTime::<Utc>::from(time).to_rfc3339()) .map(|time| chrono::DateTime::<Utc>::from(time).to_rfc3339())
.unwrap_or_default(); .unwrap_or_default();
backups.push(LocalBackupFile { backups.push(LocalBackupFile {
filename: file_name.to_string(), filename: file_name.into(),
path: path.to_string_lossy().to_string(), path: path.to_string_lossy().into(),
last_modified, last_modified: last_modified.into(),
content_length: metadata.len(), content_length: metadata.len(),
}); });
} }
@@ -210,7 +211,7 @@ pub fn list_local_backup() -> Result<Vec<LocalBackupFile>> {
/// Delete local backup /// Delete local backup
pub async fn delete_local_backup(filename: String) -> Result<()> { pub async fn delete_local_backup(filename: String) -> Result<()> {
let backup_dir = local_backup_dir()?; let backup_dir = local_backup_dir()?;
let target_path = backup_dir.join(&filename); let target_path = backup_dir.join(filename.as_str());
if !target_path.exists() { if !target_path.exists() {
logging!( logging!(
warn, warn,
@@ -227,7 +228,7 @@ pub async fn delete_local_backup(filename: String) -> Result<()> {
/// Restore local backup /// Restore local backup
pub async fn restore_local_backup(filename: String) -> Result<()> { pub async fn restore_local_backup(filename: String) -> Result<()> {
let backup_dir = local_backup_dir()?; let backup_dir = local_backup_dir()?;
let target_path = backup_dir.join(&filename); let target_path = backup_dir.join(filename.as_str());
if !target_path.exists() { if !target_path.exists() {
return Err(anyhow!("Backup file not found: {}", filename)); return Err(anyhow!("Backup file not found: {}", filename));
} }
@@ -259,12 +260,12 @@ pub async fn restore_local_backup(filename: String) -> Result<()> {
/// Export local backup file to user selected destination /// Export local backup file to user selected destination
pub fn export_local_backup(filename: String, destination: String) -> Result<()> { pub fn export_local_backup(filename: String, destination: String) -> Result<()> {
let backup_dir = local_backup_dir()?; let backup_dir = local_backup_dir()?;
let source_path = backup_dir.join(&filename); let source_path = backup_dir.join(filename.as_str());
if !source_path.exists() { if !source_path.exists() {
return Err(anyhow!("Backup file not found: {}", filename)); return Err(anyhow!("Backup file not found: {}", filename));
} }
let dest_path = PathBuf::from(destination); let dest_path = PathBuf::from(destination.as_str());
if let Some(parent) = dest_path.parent() { if let Some(parent) = dest_path.parent() {
fs::create_dir_all(parent)?; fs::create_dir_all(parent)?;
} }

View File

@@ -6,6 +6,7 @@ use crate::{
utils::{self, logging::Type, resolve}, utils::{self, logging::Type, resolve},
}; };
use serde_yaml_ng::{Mapping, Value}; use serde_yaml_ng::{Mapping, Value};
use smartstring::alias::String;
/// Restart the Clash core /// Restart the Clash core
pub async fn restart_clash_core() { pub async fn restart_clash_core() {
@@ -58,7 +59,7 @@ fn after_change_clash_mode() {
/// Change Clash mode (rule/global/direct/script) /// Change Clash mode (rule/global/direct/script)
pub async fn change_clash_mode(mode: String) { pub async fn change_clash_mode(mode: String) {
let mut mapping = Mapping::new(); let mut mapping = Mapping::new();
mapping.insert(Value::from("mode"), mode.clone().into()); mapping.insert(Value::from("mode"), Value::from(mode.as_str()));
// Convert YAML mapping to JSON Value // Convert YAML mapping to JSON Value
let json_value = serde_json::json!({ let json_value = serde_json::json!({
"mode": mode "mode": mode
@@ -112,7 +113,7 @@ pub async fn test_delay(url: String) -> anyhow::Result<u32> {
ProxyType::None ProxyType::None
}; };
let user_agent = Some("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0".to_string()); let user_agent = Some("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0".into());
let start = Instant::now(); let start = Instant::now();

View File

@@ -6,6 +6,7 @@ use crate::{
utils::logging::Type, utils::logging::Type,
}; };
use anyhow::{Result, bail}; use anyhow::{Result, bail};
use smartstring::alias::String;
/// Toggle proxy profile /// Toggle proxy profile
pub async fn toggle_proxy_profile(profile_index: String) { pub async fn toggle_proxy_profile(profile_index: String) {

View File

@@ -66,7 +66,7 @@ pub async fn toggle_tun_mode(not_save_file: Option<bool>) {
pub async fn copy_clash_env() { pub async fn copy_clash_env() {
// 从环境变量获取IP地址如果没有则从配置中获取 proxy_host默认为 127.0.0.1 // 从环境变量获取IP地址如果没有则从配置中获取 proxy_host默认为 127.0.0.1
let clash_verge_rev_ip = match env::var("CLASH_VERGE_REV_IP") { let clash_verge_rev_ip = match env::var("CLASH_VERGE_REV_IP") {
Ok(ip) => ip, Ok(ip) => ip.into(),
Err(_) => Config::verge() Err(_) => Config::verge()
.await .await
.latest_ref() .latest_ref()

View File

@@ -86,7 +86,7 @@ mod app_init {
let url = event.urls().first().map(|u| u.to_string()); let url = event.urls().first().map(|u| u.to_string());
if let Some(url) = url { if let Some(url) = url {
AsyncHandler::spawn(|| async { AsyncHandler::spawn(|| async {
if let Err(e) = resolve::resolve_scheme(url).await { if let Err(e) = resolve::resolve_scheme(url.into()).await {
logging!(error, Type::Setup, "Failed to resolve scheme: {}", e); logging!(error, Type::Setup, "Failed to resolve scheme: {}", e);
} }
}); });

View File

@@ -305,7 +305,7 @@ async fn setup_light_weight_timer() -> Result<()> {
interval_minutes: once_by_minutes, interval_minutes: once_by_minutes,
last_run: chrono::Local::now().timestamp(), last_run: chrono::Local::now().timestamp(),
}; };
timer_map.insert(LIGHT_WEIGHT_TASK_UID.to_string(), timer_task); timer_map.insert(LIGHT_WEIGHT_TASK_UID.into(), timer_task);
} }
logging!( logging!(

View File

@@ -541,7 +541,7 @@ pub async fn startup_script() -> Result<()> {
)); ));
}; };
let script_dir = PathBuf::from(&script_path); let script_dir = PathBuf::from(script_path.as_str());
if !script_dir.exists() { if !script_dir.exists() {
return Err(anyhow::anyhow!("script not found: {}", script_path)); return Err(anyhow::anyhow!("script not found: {}", script_path));
} }
@@ -553,7 +553,7 @@ pub async fn startup_script() -> Result<()> {
.shell() .shell()
.command(shell_type) .command(shell_type)
.current_dir(working_dir) .current_dir(working_dir)
.args(&[script_path]) .args([script_path.as_str()])
.output() .output()
.await?; .await?;

View File

@@ -98,21 +98,10 @@ macro_rules! wrap_err {
// Case 1: Future<Result<T, E>> // Case 1: Future<Result<T, E>>
($stat:expr, async) => {{ ($stat:expr, async) => {{
match $stat.await { match $stat.await {
Ok(a) => Ok(a), Ok(a) => Ok::<_, ::anyhow::Error>(a),
Err(err) => { Err(err) => {
log::error!(target: "app", "{}", err); log::error!(target: "app", "{}", err);
Err(err.to_string()) Err(::anyhow::Error::msg(err.to_string()))
}
}
}};
// Case 2: Result<T, E>
($stat:expr) => {{
match $stat {
Ok(a) => Ok(a),
Err(err) => {
log::error!(target: "app", "{}", err);
Err(err.to_string())
} }
} }
}}; }};

View File

@@ -1,3 +1,4 @@
use crate::config::Config;
use anyhow::Result; use anyhow::Result;
use base64::{Engine as _, engine::general_purpose}; use base64::{Engine as _, engine::general_purpose};
use isahc::prelude::*; use isahc::prelude::*;
@@ -9,14 +10,13 @@ use isahc::{
header::{HeaderMap, HeaderValue, USER_AGENT}, header::{HeaderMap, HeaderValue, USER_AGENT},
}, },
}; };
use smartstring::alias::String;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use sysproxy::Sysproxy; use sysproxy::Sysproxy;
use tauri::Url; use tauri::Url;
use tokio::sync::Mutex; use tokio::sync::Mutex;
use tokio::time::timeout; use tokio::time::timeout;
use crate::config::Config;
#[derive(Debug)] #[derive(Debug)]
pub struct HttpResponse { pub struct HttpResponse {
status: StatusCode, status: StatusCode,
@@ -80,7 +80,7 @@ impl NetworkManager {
async fn record_connection_error(&self, error: &str) { async fn record_connection_error(&self, error: &str) {
let mut last_error = self.last_connection_error.lock().await; let mut last_error = self.last_connection_error.lock().await;
*last_error = Some((Instant::now(), error.to_string())); *last_error = Some((Instant::now(), error.into()));
let mut count = self.connection_error_count.lock().await; let mut count = self.connection_error_count.lock().await;
*count += 1; *count += 1;
@@ -181,8 +181,9 @@ impl NetworkManager {
headers.insert( headers.insert(
USER_AGENT, USER_AGENT,
HeaderValue::from_str( HeaderValue::from_str(
&user_agent &user_agent.unwrap_or_else(|| {
.unwrap_or_else(|| format!("clash-verge/v{}", env!("CARGO_PKG_VERSION"))), format!("clash-verge/v{}", env!("CARGO_PKG_VERSION")).into()
}),
)?, )?,
); );
@@ -240,7 +241,7 @@ impl NetworkManager {
let status = response.status(); let status = response.status();
let headers = response.headers().clone(); let headers = response.headers().clone();
let body = response.text().await?; let body = response.text().await?;
Ok::<_, anyhow::Error>(HttpResponse::new(status, headers, body)) Ok::<_, anyhow::Error>(HttpResponse::new(status, headers, body.into()))
}) })
.await .await
{ {

View File

@@ -1,4 +1,5 @@
use anyhow::Result; use anyhow::Result;
use smartstring::alias::String;
use crate::{ use crate::{
config::Config, config::Config,

View File

@@ -1,8 +1,9 @@
use anyhow::{Result, bail}; use anyhow::{Result, bail};
use percent_encoding::percent_decode_str; use percent_encoding::percent_decode_str;
use smartstring::alias::String;
use tauri::Url; use tauri::Url;
use crate::{config::PrfItem, core::handle, logging, utils::logging::Type, wrap_err}; use crate::{config::PrfItem, core::handle, logging, logging_error, utils::logging::Type};
pub(super) async fn resolve_scheme(param: String) -> Result<()> { pub(super) async fn resolve_scheme(param: String) -> Result<()> {
log::info!(target:"app", "received deep link: {param}"); log::info!(target:"app", "received deep link: {param}");
@@ -27,7 +28,7 @@ pub(super) async fn resolve_scheme(param: String) -> Result<()> {
let name = link_parsed let name = link_parsed
.query_pairs() .query_pairs()
.find(|(key, _)| key == "name") .find(|(key, _)| key == "name")
.map(|(_, value)| value.into_owned()); .map(|(_, value)| value.into());
let url_param = if let Some(query) = link_parsed.query() { let url_param = if let Some(query) = link_parsed.query() {
let prefix = "url="; let prefix = "url=";
@@ -58,7 +59,11 @@ pub(super) async fn resolve_scheme(param: String) -> Result<()> {
} }
}; };
let result = crate::config::profiles::profiles_append_item_safe(item).await; let result = crate::config::profiles::profiles_append_item_safe(item).await;
let _ = wrap_err!(result); logging_error!(
Type::Config,
"failed to import subscription url: {:?}",
result
);
handle::Handle::notice_message("import_sub_url::ok", uid); handle::Handle::notice_message("import_sub_url::ok", uid);
} }
Err(e) => { Err(e) => {

View File

@@ -26,11 +26,11 @@ pub async fn build_new_window() -> Result<WebviewWindow, String> {
.latest_ref() .latest_ref()
.start_page .start_page
.clone() .clone()
.unwrap_or("/".to_string()); .unwrap_or("/".into());
match tauri::WebviewWindowBuilder::new( match tauri::WebviewWindowBuilder::new(
app_handle, app_handle,
"main", /* the unique window label */ "main", /* the unique window label */
tauri::WebviewUrl::App(start_page.into()), tauri::WebviewUrl::App(start_page.as_str().into()),
) )
.title("Clash Verge") .title("Clash Verge")
.center() .center()

View File

@@ -1,5 +1,3 @@
use std::time::Duration;
use super::resolve; use super::resolve;
use crate::{ use crate::{
config::{Config, DEFAULT_PAC, IVerge}, config::{Config, DEFAULT_PAC, IVerge},
@@ -13,6 +11,8 @@ use once_cell::sync::OnceCell;
use parking_lot::Mutex; use parking_lot::Mutex;
use port_scanner::local_port_available; use port_scanner::local_port_available;
use reqwest::ClientBuilder; use reqwest::ClientBuilder;
use smartstring::alias::String;
use std::time::Duration;
use tokio::sync::oneshot; use tokio::sync::oneshot;
use warp::Filter; use warp::Filter;
@@ -31,7 +31,7 @@ pub async fn check_singleton() -> Result<()> {
let client = ClientBuilder::new() let client = ClientBuilder::new()
.timeout(Duration::from_millis(500)) .timeout(Duration::from_millis(500))
.build()?; .build()?;
let argvs: Vec<String> = std::env::args().collect(); let argvs: Vec<std::string::String> = std::env::args().collect();
if argvs.len() > 1 { if argvs.len() > 1 {
#[cfg(not(target_os = "macos"))] #[cfg(not(target_os = "macos"))]
{ {
@@ -75,8 +75,8 @@ pub fn embed_server() {
} else { } else {
logging!(error, Type::Window, "轻量模式退出失败,无法恢复应用窗口"); logging!(error, Type::Window, "轻量模式退出失败,无法恢复应用窗口");
}; };
Ok::<_, warp::Rejection>(warp::reply::with_status::<String>( Ok::<_, warp::Rejection>(warp::reply::with_status::<std::string::String>(
"ok".into(), "ok".to_string(),
warp::http::StatusCode::OK, warp::http::StatusCode::OK,
)) ))
}); });
@@ -111,7 +111,10 @@ pub fn embed_server() {
tokio::task::spawn_local(async move { tokio::task::spawn_local(async move {
logging_error!(Type::Setup, resolve::resolve_scheme(param).await); logging_error!(Type::Setup, resolve::resolve_scheme(param).await);
}); });
warp::reply::with_status::<String>("ok".into(), warp::http::StatusCode::OK) warp::reply::with_status::<std::string::String>(
"ok".to_string(),
warp::http::StatusCode::OK,
)
}); });
let commands = visible.or(scheme).or(pac); let commands = visible.or(scheme).or(pac);