From e017d4d9d4469d24a5929437d45be4eb992668c9 Mon Sep 17 00:00:00 2001 From: Tunglies <77394545+Tunglies@users.noreply.github.com> Date: Tue, 27 Oct 2026 06:47:53 +0800 Subject: [PATCH] refactor: replace smartstring with tristr across the codebase Co-authored-by: GLM-5.3 --- Cargo.lock | 22 +++---- Cargo.toml | 3 +- src-tauri/Cargo.toml | 2 +- src-tauri/src/cmd/app.rs | 25 ++++---- src-tauri/src/cmd/backup.rs | 22 +++---- src-tauri/src/cmd/clash.rs | 32 +++++----- src-tauri/src/cmd/media_unlock_checker/mod.rs | 7 ++- src-tauri/src/cmd/mod.rs | 22 ++----- src-tauri/src/cmd/network.rs | 19 ++++-- src-tauri/src/cmd/profile.rs | 52 +++++++--------- src-tauri/src/cmd/proxy.rs | 11 ++-- src-tauri/src/cmd/runtime.rs | 17 ++--- src-tauri/src/cmd/save_profile.rs | 17 +++-- src-tauri/src/cmd/service.rs | 7 ++- src-tauri/src/cmd/system.rs | 1 + src-tauri/src/cmd/webdav.rs | 22 +++---- src-tauri/src/config/clash.rs | 29 +++++---- src-tauri/src/config/config.rs | 6 +- src-tauri/src/config/dns.rs | 3 +- src-tauri/src/config/encrypt.rs | 8 ++- src-tauri/src/config/port.rs | 5 +- src-tauri/src/config/prfitem.rs | 38 +++--------- src-tauri/src/config/profiles.rs | 5 +- src-tauri/src/config/runtime.rs | 2 +- src-tauri/src/config/verge.rs | 40 +----------- src-tauri/src/core/auto_backup.rs | 7 ++- src-tauri/src/core/backup.rs | 2 +- src-tauri/src/core/hotkey.rs | 2 +- src-tauri/src/core/listener.rs | 23 ++++--- src-tauri/src/core/manager/config.rs | 4 +- src-tauri/src/core/manager/lifecycle.rs | 14 ++--- src-tauri/src/core/manager/state.rs | 19 +++--- src-tauri/src/core/notify/handle.rs | 9 +-- src-tauri/src/core/notify/notification.rs | 22 +++---- src-tauri/src/core/owner_identity.rs | 18 +++--- src-tauri/src/core/proxy_control.rs | 24 +++---- src-tauri/src/core/proxy_view.rs | 37 ++++++----- src-tauri/src/core/runstate/env.rs | 15 ++--- src-tauri/src/core/runstate/health.rs | 1 + src-tauri/src/core/runstate/mod.rs | 10 +-- src-tauri/src/core/runstate/probe.rs | 28 +++++---- src-tauri/src/core/runtime_bundle.rs | 32 +++++----- src-tauri/src/core/service.rs | 62 +++++++++++-------- src-tauri/src/core/sysopt.rs | 6 +- src-tauri/src/core/timer.rs | 4 +- src-tauri/src/core/tray/mod.rs | 2 +- src-tauri/src/core/updater.rs | 10 +-- src-tauri/src/core/validate.rs | 12 +--- src-tauri/src/enhance/chain.rs | 2 +- src-tauri/src/enhance/field.rs | 2 +- src-tauri/src/enhance/mod.rs | 2 +- src-tauri/src/enhance/script.rs | 26 ++++---- src-tauri/src/enhance/seq.rs | 19 +++--- src-tauri/src/feat/backup.rs | 5 +- src-tauri/src/feat/clash.rs | 2 +- src-tauri/src/feat/core_upgrade.rs | 23 ++++--- src-tauri/src/feat/dns.rs | 7 +-- src-tauri/src/feat/icon.rs | 19 +++--- src-tauri/src/feat/profile.rs | 4 +- src-tauri/src/ipc.rs | 11 ++-- src-tauri/src/utils/help.rs | 32 +++++----- src-tauri/src/utils/linux/mime.rs | 29 ++++----- src-tauri/src/utils/network.rs | 12 ++-- src-tauri/src/utils/resolve/scheme.rs | 14 ++--- src-tauri/src/utils/schtasks.rs | 29 +++++---- src-tauri/src/utils/server.rs | 3 +- src-tauri/src/utils/yaml_emitter.rs | 10 +-- 67 files changed, 497 insertions(+), 534 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 384c6330f..2d813dc55 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1139,7 +1139,6 @@ dependencies = [ "serde_json", "serde_yaml_ng", "sha2 0.11.1", - "smartstring", "socket2", "specta", "specta-typescript", @@ -1169,6 +1168,7 @@ dependencies = [ "tokio-stream", "tokio-util", "tracing", + "tristr", "warp", "webpki-roots", "windows 0.62.2", @@ -6720,18 +6720,6 @@ dependencies = [ "thiserror 2.0.20", ] -[[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]] name = "socket2" version = "0.6.5" @@ -8505,6 +8493,14 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "tristr" +version = "0.1.0" +dependencies = [ + "serde", + "specta", +] + [[package]] name = "try-lock" version = "0.2.5" diff --git a/Cargo.toml b/Cargo.toml index 8f52bdadb..1769a0cbe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -85,9 +85,8 @@ reqwest = { version = "0.13.5", features = [ ] } rust_iso3166 = "0.2.0" -smartstring = { version = "1.0.1" } - serde = { version = "1.0.229" } +tristr = { path = "../tristr", default-features = false, features = ["serde", "specta"] } serde_json = { version = "1.0.154" } serde_yaml_ng = { version = "0.10.0" } bitflags = { version = "2.13.2" } diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 824c83107..9e1be1175 100755 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -52,9 +52,9 @@ anyhow = { workspace = true } tokio = { workspace = true } log = { workspace = true } serde = { workspace = true, features = ["derive"] } +tristr = { workspace = true } serde_json = { workspace = true } serde_yaml_ng = { workspace = true } -smartstring = { workspace = true, features = ["serde"] } bitflags = { workspace = true } socket2 = { workspace = true } warp = { version = "0.4.3", features = ["server"] } diff --git a/src-tauri/src/cmd/app.rs b/src-tauri/src/cmd/app.rs index e043dbc4e..c648bfecf 100644 --- a/src-tauri/src/cmd/app.rs +++ b/src-tauri/src/cmd/app.rs @@ -5,8 +5,8 @@ use crate::{ feat, utils::dirs, }; -use smartstring::alias::String; use tauri::{AppHandle, Manager as _, ipc::Channel}; +use tristr::alias::String; #[tauri::command] #[specta::specta] @@ -59,10 +59,10 @@ pub async fn restart_app() -> CmdResult<()> { #[specta::specta] pub async fn install_update( app_handle: AppHandle, - version: std::string::String, + version: String, on_event: Channel, ) -> CmdResult { - let version: String = version.into(); + let version: String = version; SilentUpdater::global() .install_update(&app_handle, &version, |event| { let _ = on_event.send(event); @@ -79,25 +79,22 @@ pub fn cancel_update_download() { #[tauri::command] #[specta::specta] -pub fn get_app_dir() -> CmdResult { +pub fn get_app_dir() -> CmdResult { let app_home_dir = dirs::app_home_dir().stringify_err()?.to_string_lossy().into(); Ok(app_home_dir) } #[tauri::command] #[specta::specta] -pub async fn download_icon_cache( - url: std::string::String, - name: std::string::String, -) -> CmdResult { - let url: String = url.into(); - let name: String = name.into(); - feat::download_icon_cache(url, name).await.map(Into::into) +pub async fn download_icon_cache(url: String, name: String) -> CmdResult { + let url: String = url; + let name: String = name; + feat::download_icon_cache(url, name).await } #[tauri::command] #[specta::specta] -pub async fn copy_icon_file(path: std::string::String, icon_info: feat::IconInfo) -> CmdResult { - let path: String = path.into(); - feat::copy_icon_file(path, icon_info).await.map(Into::into) +pub async fn copy_icon_file(path: String, icon_info: feat::IconInfo) -> CmdResult { + let path: String = path; + feat::copy_icon_file(path, icon_info).await } diff --git a/src-tauri/src/cmd/backup.rs b/src-tauri/src/cmd/backup.rs index 349c869b5..242db6f9e 100644 --- a/src-tauri/src/cmd/backup.rs +++ b/src-tauri/src/cmd/backup.rs @@ -1,7 +1,7 @@ use super::CmdResult; use crate::{cmd::StringifyErr as _, feat}; use feat::LocalBackupFile; -use smartstring::alias::String; +use tristr::alias::String; #[tauri::command] #[specta::specta] @@ -17,15 +17,15 @@ pub async fn list_local_backup() -> CmdResult> { #[tauri::command] #[specta::specta] -pub async fn delete_local_backup(filename: std::string::String) -> CmdResult<()> { - let filename: String = filename.into(); +pub async fn delete_local_backup(filename: String) -> CmdResult<()> { + let filename: String = filename; feat::delete_local_backup(filename).await.stringify_err() } #[tauri::command] #[specta::specta] -pub async fn restore_local_backup(filename: std::string::String) -> CmdResult<()> { - let filename: String = filename.into(); +pub async fn restore_local_backup(filename: String) -> CmdResult<()> { + let filename: String = filename; feat::restore_local_backup(filename) .await .map_err(|error| super::proxy_aware_error(&error)) @@ -33,15 +33,15 @@ pub async fn restore_local_backup(filename: std::string::String) -> CmdResult<() #[tauri::command] #[specta::specta] -pub async fn import_local_backup(source: std::string::String) -> CmdResult { - let source: String = source.into(); - feat::import_local_backup(source).await.map(Into::into).stringify_err() +pub async fn import_local_backup(source: String) -> CmdResult { + let source: String = source; + feat::import_local_backup(source).await.stringify_err() } #[tauri::command] #[specta::specta] -pub async fn export_local_backup(filename: std::string::String, destination: std::string::String) -> CmdResult<()> { - let filename: String = filename.into(); - let destination: String = destination.into(); +pub async fn export_local_backup(filename: String, destination: String) -> CmdResult<()> { + let filename: String = filename; + let destination: String = destination; feat::export_local_backup(filename, destination).await.stringify_err() } diff --git a/src-tauri/src/cmd/clash.rs b/src-tauri/src/cmd/clash.rs index 6fb9602ad..d55b30b35 100644 --- a/src-tauri/src/cmd/clash.rs +++ b/src-tauri/src/cmd/clash.rs @@ -12,8 +12,8 @@ use crate::{ }, }; use clash_verge_logging::{Type, logging, logging_error}; -use smartstring::alias::String; use tokio::fs; +use tristr::alias::String; #[tauri::command] #[specta::specta] @@ -37,8 +37,8 @@ pub async fn patch_clash_config(payload: crate::ipc::YamlMapping) -> CmdResult { #[tauri::command] #[specta::specta] -pub async fn patch_clash_mode(payload: std::string::String) -> CmdResult { - let payload: String = payload.into(); +pub async fn patch_clash_mode(payload: String) -> CmdResult { + let payload: String = payload; feat::change_clash_mode(payload) .await .with_error_code("CLASH_MODE_UPDATE_FAILED") @@ -47,14 +47,14 @@ pub async fn patch_clash_mode(payload: std::string::String) -> CmdResult { /// Reads the saved mode without depending on strict mihomo `/configs` deserialization. #[tauri::command] #[specta::specta] -pub async fn get_clash_mode() -> CmdResult> { +pub async fn get_clash_mode() -> CmdResult> { Ok(Config::clash().await.data_arc().get_mode()) } #[tauri::command] #[specta::specta] -pub async fn change_clash_core(clash_core: std::string::String) -> CmdResult> { - let clash_core: String = clash_core.into(); +pub async fn change_clash_core(clash_core: String) -> CmdResult> { + let clash_core: String = clash_core; logging!(info, Type::Config, "changing core to {clash_core}"); match CoreManager::global().change_core(&clash_core).await { @@ -112,8 +112,8 @@ pub async fn upgrade_clash_core(force: bool) -> CmdResult CmdResult { - let url: String = url.into(); +pub async fn test_delay(url: String) -> CmdResult { + let url: String = url; let result = match feat::test_delay(url).await { Ok(delay) => delay, Err(e) => { @@ -148,12 +148,12 @@ pub fn take_dns_override_notice() -> bool { #[tauri::command] #[specta::specta] pub async fn set_dns_override( - profile_uid: std::string::String, + profile_uid: String, enabled: bool, - confirmation: Option, + confirmation: Option, ) -> CmdResult { - let profile_uid: String = profile_uid.into(); - let confirmation = confirmation.map(String::from); + let profile_uid: String = profile_uid; + let confirmation = confirmation; feat::set_dns_override(profile_uid, enabled, confirmation) .await .map_err(|error| proxy_aware_coded_error(&error, "DNS_OVERRIDE_UPDATE_FAILED")) @@ -212,14 +212,14 @@ pub async fn apply_dns_config(apply: bool) -> CmdResult { #[tauri::command] #[specta::specta] -pub async fn get_dns_config_content() -> CmdResult> { +pub async fn get_dns_config_content() -> CmdResult> { use crate::utils::dirs; use tokio::fs; let dns_path = dirs::app_home_dir().stringify_err()?.join(constants::files::DNS_CONFIG); match fs::read_to_string(&dns_path).await { - Ok(content) => Ok(Some(content)), + Ok(content) => Ok(Some(content.into())), Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(None), Err(error) => Err(CommandFailure::plain(error)), } @@ -244,7 +244,7 @@ pub async fn validate_dns_config() -> CmdResult { #[tauri::command] #[specta::specta] -pub async fn get_clash_logs() -> CmdResult> { +pub async fn get_clash_logs() -> CmdResult> { let logs = CoreManager::global().get_clash_logs().await.unwrap_or_default(); - Ok(logs) + Ok(logs.into_iter().collect()) } diff --git a/src-tauri/src/cmd/media_unlock_checker/mod.rs b/src-tauri/src/cmd/media_unlock_checker/mod.rs index e5aa71560..ead08c8c2 100644 --- a/src-tauri/src/cmd/media_unlock_checker/mod.rs +++ b/src-tauri/src/cmd/media_unlock_checker/mod.rs @@ -2,6 +2,7 @@ pub use clash_verge_media_unlock::UnlockItem; use reqwest::{Client, Proxy}; use std::time::Duration; use tauri::{command, ipc::Channel}; +use tristr::alias::String; #[command] #[specta::specta] @@ -23,7 +24,7 @@ async fn create_client() -> Result { .cookie_store(true) .proxy(proxy) .build() - .map_err(|error| format!("创建HTTP客户端失败: {error}")) + .map_err(|error| format!("创建HTTP客户端失败: {error}").into()) } #[command] @@ -40,5 +41,7 @@ pub async fn check_media_unlock(on_complete: Channel) -> Result Result { let client = create_client().await?; - clash_verge_media_unlock::check_media_unlock_item(&client, &name).await + clash_verge_media_unlock::check_media_unlock_item(&client, &name) + .await + .map_err(Into::into) } diff --git a/src-tauri/src/cmd/mod.rs b/src-tauri/src/cmd/mod.rs index 1bb2d3041..e45a060f6 100644 --- a/src-tauri/src/cmd/mod.rs +++ b/src-tauri/src/cmd/mod.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use smartstring::alias::String; +use tristr::alias::String; pub type CmdResult = Result; @@ -8,9 +8,9 @@ pub type CmdResult = Result; pub struct CommandFailure { /// Stable frontend identifier, when classified. #[serde(skip_serializing_if = "Option::is_none")] - pub code: Option, + pub code: Option, /// Human-readable diagnostic detail. - pub detail: std::string::String, + pub detail: String, /// Requested operation when known by the command. #[serde(skip_serializing_if = "Option::is_none")] pub operation: Option, @@ -20,8 +20,8 @@ impl CommandFailure { /// A failure with a stable code. pub fn coded(code: &str, detail: impl std::fmt::Display) -> Self { Self { - code: Some(code.to_owned()), - detail: detail.to_string(), + code: Some(code.into()), + detail: detail.to_string().into(), operation: None, } } @@ -30,7 +30,7 @@ impl CommandFailure { pub fn plain(detail: impl std::fmt::Display) -> Self { Self { code: None, - detail: detail.to_string(), + detail: detail.to_string().into(), operation: None, } } @@ -50,16 +50,6 @@ impl From<&str> for CommandFailure { } } -impl From for CommandFailure { - fn from(detail: std::string::String) -> Self { - Self { - code: None, - detail, - operation: None, - } - } -} - impl From for CommandFailure { fn from(detail: String) -> Self { Self::plain(detail) diff --git a/src-tauri/src/cmd/network.rs b/src-tauri/src/cmd/network.rs index 8d4aeed96..3179f0061 100644 --- a/src-tauri/src/cmd/network.rs +++ b/src-tauri/src/cmd/network.rs @@ -5,6 +5,7 @@ use crate::core::{proxy_control, sysopt::Sysopt}; use gethostname::gethostname; use sysproxy::{Autoproxy, Sysproxy}; use tauri_plugin_clash_verge_sysinfo; +use tristr::alias::String; #[tauri::command] #[specta::specta] @@ -24,8 +25,8 @@ pub async fn get_sys_proxy() -> CmdResult { Ok(SystemProxy { enable, - server: format!("{host}:{port}"), - bypass, + server: format!("{host}:{port}").into(), + bypass: bypass.into(), }) } @@ -39,7 +40,10 @@ pub async fn get_auto_proxy() -> CmdResult { }; let Autoproxy { enable, url } = auto_proxy; - Ok(AutoProxy { enable, url }) + Ok(AutoProxy { + enable, + url: url.into(), + }) } #[tauri::command] @@ -52,10 +56,10 @@ pub fn get_embedded_server_port() -> CmdResult { #[specta::specta] pub fn get_system_hostname() -> String { match gethostname().into_string() { - Ok(name) => name, + Ok(name) => name.into(), Err(os_string) => { let fallback = format!("{os_string:?}"); - fallback.trim_matches('"').to_string() + fallback.trim_matches('"').into() } } } @@ -64,6 +68,9 @@ pub fn get_system_hostname() -> String { #[specta::specta] pub fn get_network_interfaces() -> Vec { tauri_plugin_clash_verge_sysinfo::list_network_interfaces() + .into_iter() + .map(Into::into) + .collect() } #[tauri::command] @@ -77,7 +84,7 @@ pub fn get_network_interfaces_info() -> CmdResult std::string::String { +fn profile_import_error(err: &anyhow::Error) -> String { if let Some(cause) = err.chain().find(|cause| cause.to_string().contains("TLS 1.0/1.1")) { - return cause.to_string(); + return cause.to_string().into(); } - format!("导入订阅失败: {err:#}") + format!("导入订阅失败: {err:#}").into() } #[tauri::command] @@ -68,7 +68,7 @@ pub async fn enhance_profiles() -> CmdResult { #[tauri::command] #[tracing::instrument(skip_all, level = "info", fields(url = %help::mask_url(&url), uid = tracing::field::Empty))] #[specta::specta] -pub async fn import_profile(url: std::string::String, option: Option) -> CmdResult { +pub async fn import_profile(url: String, option: Option) -> CmdResult { let item = &mut match PrfItem::from_url(&url, None, None, option.as_ref()).await { Ok(it) => it, Err(e) => { @@ -98,9 +98,9 @@ pub async fn import_profile(url: std::string::String, option: Option) #[tauri::command] #[specta::specta] -pub async fn reorder_profile(active_id: std::string::String, over_id: std::string::String) -> CmdResult { - let active_id: String = active_id.into(); - let over_id: String = over_id.into(); +pub async fn reorder_profile(active_id: String, over_id: String) -> CmdResult { + let active_id: String = active_id; + let over_id: String = over_id; match profiles_reorder_safe(&active_id, &over_id).await { Ok(_) => { logging!(info, Type::Cmd, "重新排序配置文件: {} -> {}", active_id, over_id); @@ -121,9 +121,9 @@ pub async fn reorder_profile(active_id: std::string::String, over_id: std::strin #[tauri::command] #[specta::specta] -pub async fn create_profile(item: ProfileCreate, file_data: Option) -> CmdResult { +pub async fn create_profile(item: ProfileCreate, file_data: Option) -> CmdResult { let item: PrfItem = item.into(); - let file_data = file_data.map(String::from); + let file_data = file_data; match profiles_append_item_with_filedata_safe(&item, file_data).await { Ok(_) => { profiles_save_file_safe() @@ -138,8 +138,8 @@ pub async fn create_profile(item: ProfileCreate, file_data: Option) -> CmdResult { - let index: String = index.into(); +pub async fn update_profile(index: String, option: Option) -> CmdResult { + let index: String = index; match feat::update_profile(&index, option.as_ref(), true).await { Ok(_) => Ok(()), Err(e) => { @@ -151,8 +151,8 @@ pub async fn update_profile(index: std::string::String, option: Option CmdResult { - let index: String = index.into(); +pub async fn delete_profile(index: String) -> CmdResult { + let index: String = index; let profile_write_guard = PROFILE_WRITE_LOCK.lock().await; let profiles = Config::profiles().await; @@ -325,7 +325,6 @@ async fn perform_config_update( #[derive(Default, serde::Deserialize, specta::Type)] #[serde(default)] pub struct ProfileSelection { - #[specta(type = Option)] pub current: Option, } @@ -333,13 +332,9 @@ pub struct ProfileSelection { #[serde(default)] pub struct ProfileCreate { #[serde(rename = "type")] - #[specta(type = Option)] pub itype: Option, - #[specta(type = Option)] pub name: Option, - #[specta(type = Option)] pub desc: Option, - #[specta(type = Option)] pub url: Option, pub option: Option, } @@ -361,15 +356,10 @@ impl From for PrfItem { #[serde(default)] pub struct ProfilePatch { #[serde(rename = "type")] - #[specta(type = Option)] pub itype: Option, - #[specta(type = Option)] pub name: Option, - #[specta(type = Option)] pub desc: Option, - #[specta(type = Option)] pub file: Option, - #[specta(type = Option)] pub url: Option, pub selected: Option>, pub extra: Option, @@ -436,9 +426,9 @@ pub async fn patch_profiles_config_by_profile_index(profile_index: String) -> Cm #[tauri::command] #[specta::specta] -pub async fn patch_profile(index: std::string::String, profile: ProfilePatch) -> CmdResult { +pub async fn patch_profile(index: String, profile: ProfilePatch) -> CmdResult { let profile: PrfItem = profile.into(); - let index: String = index.into(); + let index: String = index; let profiles = Config::profiles().await; let should_refresh_timer = if let Ok(old_profile) = profiles.latest_arc().get_item(&index) && let Some(new_option) = profile.option.as_ref() @@ -479,7 +469,7 @@ pub async fn patch_profile(index: std::string::String, profile: ProfilePatch) -> #[tauri::command] #[specta::specta] -pub async fn view_profile(index: std::string::String) -> CmdResult { +pub async fn view_profile(index: String) -> CmdResult { let profiles = Config::profiles().await; let profiles_ref = profiles.latest_arc(); let file = profiles_ref @@ -504,7 +494,7 @@ pub async fn view_profile(index: std::string::String) -> CmdResult { #[tauri::command] #[specta::specta] -pub async fn read_profile_file(index: std::string::String) -> CmdResult { +pub async fn read_profile_file(index: String) -> CmdResult { let item = { let profiles = Config::profiles().await; let profiles_ref = profiles.latest_arc(); @@ -524,7 +514,7 @@ pub async fn read_profile_file(index: std::string::String) -> CmdResult {} - Ok(false) => return Ok(std::string::String::new()), + Ok(false) => return Ok(String::new()), Err(err) => { return Err(coded_error( "PROFILE_READ_FAILED", @@ -535,12 +525,12 @@ pub async fn read_profile_file(index: std::string::String) -> CmdResult CmdResult> { +pub async fn get_next_update_time(uid: String) -> CmdResult> { let timer = Timer::global(); let next_time = timer.get_next_update_time(&uid).await; Ok(next_time) diff --git a/src-tauri/src/cmd/proxy.rs b/src-tauri/src/cmd/proxy.rs index aa5521833..17ed824de 100644 --- a/src-tauri/src/cmd/proxy.rs +++ b/src-tauri/src/cmd/proxy.rs @@ -15,6 +15,7 @@ use std::{ collections::{BTreeMap, HashSet}, sync::atomic::{AtomicBool, Ordering}, }; +use tristr::alias::String; /// Merges one selection pair into fresh backend state to avoid stale-list overwrites. #[tauri::command] @@ -48,7 +49,7 @@ fn runtime_group_order(config: Option<&Mapping>) -> Vec { .filter_map(|name| name.as_str()) .filter(|name| !name.is_empty() && *name != "GLOBAL") .filter(|name| seen.insert(*name)) - .map(str::to_owned) + .map(String::from) .collect() } @@ -63,7 +64,7 @@ fn runtime_group_scopes(config: Option<&Mapping>) -> BTreeMap>(); all_providers.sort(); let flag = |group: &Mapping, key: &str| group.get(key).and_then(|value| value.as_bool()) == Some(true); @@ -75,7 +76,7 @@ fn runtime_group_scopes(config: Option<&Mapping>) -> BTreeMap) -> BTreeMap CmdResult> #[tauri::command] #[specta::specta] -pub async fn get_runtime_yaml() -> CmdResult { +pub async fn get_runtime_yaml() -> CmdResult { let runtime = Config::runtime().await; let runtime = runtime.latest_arc(); @@ -36,22 +36,17 @@ pub async fn get_runtime_logs() -> CmdResult { #[derive(serde::Serialize, specta::Type)] #[serde(transparent)] -pub struct RuntimeLogs( - #[specta(type = HashMap>)] - HashMap>, -); +pub struct RuntimeLogs(HashMap>); #[tauri::command] #[specta::specta] -pub fn take_discarded_keys_notice() -> Option { - crate::enhance::take_discarded_keys_notice().map(Into::into) +pub fn take_discarded_keys_notice() -> Option { + crate::enhance::take_discarded_keys_notice() } #[tauri::command] #[specta::specta] -pub async fn get_runtime_proxy_chain_config( - proxy_chain_exit_node: std::string::String, -) -> CmdResult { +pub async fn get_runtime_proxy_chain_config(proxy_chain_exit_node: String) -> CmdResult { let runtime = Config::runtime().await; let runtime = runtime.latest_arc(); diff --git a/src-tauri/src/cmd/save_profile.rs b/src-tauri/src/cmd/save_profile.rs index 984af1100..f6219826d 100644 --- a/src-tauri/src/cmd/save_profile.rs +++ b/src-tauri/src/cmd/save_profile.rs @@ -1,4 +1,4 @@ -use super::CmdResult; +use super::{CmdResult, CommandFailure}; use crate::{ cmd::StringifyErr as _, cmd::validate::{ValidationNoticeTarget, handle_validation_notice}, @@ -11,18 +11,15 @@ use crate::{ utils::dirs, }; use clash_verge_logging::{Type, logging, logging_error}; -use smartstring::alias::String; use tokio::fs; +use tristr::alias::String; /// 保存profiles的配置 #[tauri::command] #[specta::specta] -pub async fn save_profile_file( - index: std::string::String, - file_data: Option, -) -> CmdResult { - let index: String = index.into(); - let file_data = file_data.map(String::from); +pub async fn save_profile_file(index: String, file_data: Option) -> CmdResult { + let index: String = index; + let file_data = file_data; let file_data = match file_data { Some(d) => d, None => return Ok(ValidationOutcome::Valid), @@ -153,7 +150,7 @@ async fn handle_saved_profile_file( Err(e) => { logging!(error, Type::Config, "[cmd配置save] 验证过程发生错误: {e:#}"); restore_original(file_path, original_content, original_existed).await?; - return Err(e.to_string().into()); + return Err(CommandFailure::plain(e)); } } @@ -181,7 +178,7 @@ async fn handle_saved_profile_file( Err(err) => { logging!(error, Type::Config, "[cmd配置save] 运行时配置应用错误: {err:#}"); restore_original(file_path, original_content, original_existed).await?; - Err(err.to_string().into()) + Err(CommandFailure::plain(err)) } } } diff --git a/src-tauri/src/cmd/service.rs b/src-tauri/src/cmd/service.rs index 592df0417..3360758af 100644 --- a/src-tauri/src/cmd/service.rs +++ b/src-tauri/src/cmd/service.rs @@ -7,6 +7,7 @@ use crate::{ service::{SERVICE_MANAGER, ServiceStatus, request_runtime_provider_sync}, }, }; +use tristr::alias::String; #[derive(Debug, PartialEq, Eq, serde::Serialize, specta::Type)] #[serde(tag = "status", rename_all = "camelCase")] @@ -74,7 +75,7 @@ where if reasons.is_empty() { return Err(proxy_aware_coded_error(&error, error_code)); } - let reason = reasons.join("\n"); + let reason: String = reasons.join("\n").into(); fallback(reason.clone()) .await .map_err(|error| proxy_aware_coded_error(&error, "SERVICE_SIDECAR_FAILED"))?; @@ -179,7 +180,7 @@ mod tests { let error = match availability { Some(availability) => anyhow::Error::new(InstallationVerificationError { status: InstallationStatus { - service_sha256: String::new(), + service_sha256: std::string::String::new(), protocol: ProtocolInfo::current(), cores: vec![CoreInspection { name: "verge-mihomo.exe".into(), @@ -203,7 +204,7 @@ mod tests { assert_eq!( result, Ok(ServiceInstallOutcome::Sidecar { - reason: format!("verge-mihomo.exe: {reason}") + reason: format!("verge-mihomo.exe: {reason}").into() }) ); } else { diff --git a/src-tauri/src/cmd/system.rs b/src-tauri/src/cmd/system.rs index e3f263cb9..646a1d4b4 100644 --- a/src-tauri/src/cmd/system.rs +++ b/src-tauri/src/cmd/system.rs @@ -2,6 +2,7 @@ use crate::core::{ notification::{self, PendingFailure}, runstate::{RUN_STATE, RunStateView}, }; +use tristr::alias::String; /// Returns one coherent core/service snapshot instead of independently refreshed state. #[tauri::command] diff --git a/src-tauri/src/cmd/webdav.rs b/src-tauri/src/cmd/webdav.rs index dad1de657..b071193a3 100644 --- a/src-tauri/src/cmd/webdav.rs +++ b/src-tauri/src/cmd/webdav.rs @@ -4,18 +4,14 @@ use crate::{ config::{Config, IVerge}, core, feat, }; -use smartstring::alias::String; +use tristr::alias::String; #[tauri::command] #[specta::specta] -pub async fn save_webdav_config( - url: std::string::String, - username: std::string::String, - password: std::string::String, -) -> CmdResult<()> { - let url: String = url.into(); - let username: String = username.into(); - let password: String = password.into(); +pub async fn save_webdav_config(url: String, username: String, password: String) -> CmdResult<()> { + let url: String = url; + let username: String = username; + let password: String = password; let patch = IVerge { webdav_url: Some(url), webdav_username: Some(username), @@ -48,15 +44,15 @@ pub async fn list_webdav_backup() -> CmdResult> { #[tauri::command] #[specta::specta] -pub async fn delete_webdav_backup(filename: std::string::String) -> CmdResult<()> { - let filename: String = filename.into(); +pub async fn delete_webdav_backup(filename: String) -> CmdResult<()> { + let filename: String = filename; feat::delete_webdav_backup(filename).await.stringify_err() } #[tauri::command] #[specta::specta] -pub async fn restore_webdav_backup(filename: std::string::String) -> CmdResult<()> { - let filename: String = filename.into(); +pub async fn restore_webdav_backup(filename: String) -> CmdResult<()> { + let filename: String = filename; feat::restore_webdav_backup(filename) .await .map_err(|error| super::proxy_aware_error(&error)) diff --git a/src-tauri/src/config/clash.rs b/src-tauri/src/config/clash.rs index 106c55d9f..d8b2164dc 100644 --- a/src-tauri/src/config/clash.rs +++ b/src-tauri/src/config/clash.rs @@ -10,6 +10,7 @@ use std::{ net::{IpAddr, Ipv4Addr, SocketAddr}, str::FromStr as _, }; +use tristr::alias::String; #[derive(Default, Debug, Clone)] pub struct IClashTemp(pub Mapping); @@ -84,12 +85,12 @@ impl IClashTemp { #[cfg(unix)] map.insert( "external-controller-unix".into(), - Self::guard_external_controller_ipc().into(), + Self::guard_external_controller_ipc().as_str().into(), ); #[cfg(windows)] map.insert( "external-controller-pipe".into(), - Self::guard_external_controller_ipc().into(), + Self::guard_external_controller_ipc().as_str().into(), ); map.insert("tun".into(), tun_config.into()); cors_map.insert("allow-private-network".into(), true.into()); @@ -134,12 +135,18 @@ impl IClashTemp { config.insert("mixed-port".into(), mixed_port.into()); config.insert("socks-port".into(), socks_port.into()); config.insert("port".into(), port.into()); - config.insert("external-controller".into(), ctrl.into()); + config.insert("external-controller".into(), ctrl.as_str().into()); #[cfg(unix)] - config.insert("external-controller-unix".into(), external_controller_unix.into()); + config.insert( + "external-controller-unix".into(), + external_controller_unix.as_str().into(), + ); #[cfg(windows)] - config.insert("external-controller-pipe".into(), external_controller_pipe.into()); + config.insert( + "external-controller-pipe".into(), + external_controller_pipe.as_str().into(), + ); config } @@ -166,9 +173,9 @@ impl IClashTemp { port: Self::guard_port(config), server: Self::guard_client_ctrl(config), secret: config.get("secret").and_then(|value| match value { - Value::String(val_str) => Some(val_str.clone()), - Value::Bool(val_bool) => Some(val_bool.to_string()), - Value::Number(val_num) => Some(val_num.to_string()), + Value::String(val_str) => Some(val_str.clone().into()), + Value::Bool(val_bool) => Some(val_bool.to_string().into()), + Value::Number(val_num) => Some(val_num.to_string().into()), _ => None, }), } @@ -180,7 +187,7 @@ impl IClashTemp { /// 的严格 `BaseConfig` 反序列化,因此即使核心返回的字段与插件结构体不匹配也能取到。 pub fn get_mode(&self) -> Option { self.0.get("mode").and_then(|value| match value { - Value::String(val_str) => Some(val_str.clone()), + Value::String(val_str) => Some(val_str.clone().into()), _ => None, }) } @@ -276,7 +283,7 @@ impl IClashTemp { false => Cow::Borrowed(val_str), }; - SocketAddr::from_str(&val).ok().map(|s| s.to_string()) + SocketAddr::from_str(&val).ok().map(|s| s.to_string().into()) } None => None, }) @@ -290,7 +297,7 @@ impl IClashTemp { if socket.ip().is_unspecified() { socket.set_ip(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))); } - socket.to_string() + socket.to_string().into() } Err(_) => "127.0.0.1:9097".into(), } diff --git a/src-tauri/src/config/config.rs b/src-tauri/src/config/config.rs index 59e6f921d..f9191a784 100644 --- a/src-tauri/src/config/config.rs +++ b/src-tauri/src/config/config.rs @@ -20,10 +20,10 @@ use backon::{ExponentialBuilder, Retryable as _}; use clash_verge_draft::Draft; use clash_verge_logging::{Type, logging, logging_error}; use serde_yaml_ng::{Mapping, Value}; -use smartstring::alias::String; use std::{collections::HashSet, path::PathBuf}; use tokio::sync::{Mutex, MutexGuard, OnceCell}; use tokio::time::sleep; +use tristr::alias::String; pub(crate) struct Config { clash_config: Draft, @@ -205,7 +205,7 @@ impl Config { Self::write_runtime_file(&yaml).await } - pub(crate) async fn runtime_config_yaml() -> Result { + pub(crate) async fn runtime_config_yaml() -> Result { let runtime = Self::runtime().await; AsyncHandler::spawn_blocking(move || { let runtime_lastest = runtime.latest_arc(); @@ -216,7 +216,7 @@ impl Config { .or_else(|| runtime_data.config.as_ref()) .ok_or_else(|| anyhow!("failed to generate runtime config, might need to restart application"))?; let yaml_str = serde_yaml_ng::to_string(config)?; - Ok(format!("# Generated by Clash Verge\n\n{}", yaml_str)) + Ok(format!("# Generated by Clash Verge\n\n{}", yaml_str).into()) }) .await .map_err(|join| anyhow!("runtime serialization task failed: {join}"))? diff --git a/src-tauri/src/config/dns.rs b/src-tauri/src/config/dns.rs index 86727ab76..7c3b50781 100644 --- a/src-tauri/src/config/dns.rs +++ b/src-tauri/src/config/dns.rs @@ -4,8 +4,8 @@ use clash_verge_draft::DraftTransaction; use serde::{Deserialize, Serialize}; use serde_yaml_ng::{Mapping, Value}; use sha2::{Digest as _, Sha256}; -use smartstring::alias::String; use std::sync::atomic::{AtomicBool, Ordering}; +use tristr::alias::String; use super::{Config, IVerge}; use crate::core::handle::Handle; @@ -110,7 +110,6 @@ impl DnsOverrideState { pub struct ProfileDnsSettings { pub enabled: bool, #[serde(default, skip_serializing_if = "Option::is_none")] - #[specta(type = Option)] pub confirmation: Option, } diff --git a/src-tauri/src/config/encrypt.rs b/src-tauri/src/config/encrypt.rs index cd2717207..2b85ced41 100644 --- a/src-tauri/src/config/encrypt.rs +++ b/src-tauri/src/config/encrypt.rs @@ -7,6 +7,7 @@ use base64::{Engine as _, engine::general_purpose::STANDARD}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::cell::Cell; use std::future::Future; +use tristr::alias::String; const NONCE_LENGTH: usize = 12; @@ -28,7 +29,7 @@ pub fn encrypt_data(data: &str) -> Result> { let mut combined = nonce.to_vec(); combined.extend(ciphertext); - Ok(STANDARD.encode(combined)) + Ok(STANDARD.encode(combined).into()) } pub fn decrypt_data(encrypted: &str) -> Result> { @@ -46,7 +47,10 @@ pub fn decrypt_data(encrypted: &str) -> Result(value: &T, serializer: S) -> Result diff --git a/src-tauri/src/config/port.rs b/src-tauri/src/config/port.rs index c445fd5be..aca577440 100644 --- a/src-tauri/src/config/port.rs +++ b/src-tauri/src/config/port.rs @@ -18,6 +18,7 @@ use clash_verge_logging::{Type, logging}; use parking_lot::Mutex; use serde_yaml_ng::Value; use std::{collections::HashSet, net::SocketAddr, str::FromStr as _}; +use tristr::alias::String; #[derive(Clone, Copy)] struct MixedPortFallback { @@ -141,8 +142,8 @@ impl Config { } pub(crate) fn block_startup_core(error: &anyhow::Error) { - *STARTUP_CORE_BLOCK_REASON.lock() = Some(error.to_string()); - report_fallback_error(error.to_string()); + *STARTUP_CORE_BLOCK_REASON.lock() = Some(error.to_string().into()); + report_fallback_error(error.to_string().into()); } pub(crate) fn startup_core_block_reason() -> Option { diff --git a/src-tauri/src/config/prfitem.rs b/src-tauri/src/config/prfitem.rs index da8728d21..b06584fc6 100644 --- a/src-tauri/src/config/prfitem.rs +++ b/src-tauri/src/config/prfitem.rs @@ -9,9 +9,9 @@ use crate::{ use anyhow::{Context as _, Result, bail}; use serde::{Deserialize, Serialize}; use serde_yaml_ng::Mapping; -use smartstring::alias::String; use std::time::Duration; use tokio::fs; +use tristr::alias::String; // TODO, use other re-export use reqwest_dav::re_exports::url::form_urlencoded; use tauri::Url; @@ -30,24 +30,18 @@ pub(super) fn normalize_profile_home_url(raw: &str) -> Option { #[derive(Debug, Clone, Deserialize, Serialize, Default, specta::Type)] #[serde(default)] pub struct PrfItem { - #[specta(type = Option)] pub uid: Option, /// enum value: remote | local | script | merge #[serde(rename = "type")] - #[specta(type = Option)] pub itype: Option, - #[specta(type = Option)] pub name: Option, - #[specta(type = Option)] pub file: Option, #[serde(skip_serializing_if = "Option::is_none")] - #[specta(type = Option)] pub desc: Option, #[serde(skip_serializing_if = "Option::is_none")] - #[specta(type = Option)] pub url: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -62,20 +56,16 @@ pub struct PrfItem { pub option: Option, #[serde(skip_serializing_if = "Option::is_none")] - #[specta(type = Option)] pub home: Option, #[serde(skip)] - #[specta(type = Option)] pub file_data: Option, } #[derive(Default, Debug, Clone, Deserialize, Serialize, PartialEq, Eq, specta::Type)] #[serde(default)] pub struct PrfSelected { - #[specta(type = Option)] pub name: Option, - #[specta(type = Option)] pub now: Option, } @@ -91,7 +81,6 @@ pub struct PrfExtra { #[serde(default)] pub struct PrfOption { #[serde(skip_serializing_if = "Option::is_none")] - #[specta(type = Option)] pub user_agent: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -114,15 +103,10 @@ pub struct PrfOption { #[serde(skip_serializing_if = "Option::is_none")] pub allow_auto_update: Option, - #[specta(type = Option)] pub merge: Option, - #[specta(type = Option)] pub script: Option, - #[specta(type = Option)] pub rules: Option, - #[specta(type = Option)] pub proxies: Option, - #[specta(type = Option)] pub groups: Option, } @@ -187,7 +171,7 @@ impl PrfItem { file_data: Option, option: Option<&PrfOption>, ) -> Result { - let uid = help::get_uid("L").into(); + let uid = help::get_uid("L"); let file = format!("{uid}.yaml").into(); let opt_ref = option.as_ref(); let update_interval = opt_ref.and_then(|o| o.update_interval); @@ -360,11 +344,11 @@ impl PrfItem { .and_then(|value| value.to_str().ok()) .and_then(normalize_profile_home_url); - let uid = help::get_uid("R").into(); + let uid = help::get_uid("R"); let file = format!("{uid}.yaml").into(); let name = name .map(|s| s.to_owned()) - .unwrap_or_else(|| filename.map(|s| s.into()).unwrap_or_else(|| "Remote File".into())); + .unwrap_or_else(|| filename.unwrap_or_else(|| "Remote File".into())); let data = resp.text(); let data = data.trim_start_matches('\u{feff}'); @@ -430,7 +414,7 @@ impl PrfItem { let (id, template) = if let Some(uid) = uid { (uid, tmpl::ITEM_MERGE.into()) } else { - (help::get_uid("m").into(), tmpl::ITEM_MERGE_EMPTY.into()) + (help::get_uid("m"), tmpl::ITEM_MERGE_EMPTY.into()) }; let file = format!("{id}.yaml").into(); @@ -445,11 +429,7 @@ impl PrfItem { } pub(super) fn from_script(uid: Option) -> Self { - let id = if let Some(uid) = uid { - uid - } else { - help::get_uid("s").into() - }; + let id = if let Some(uid) = uid { uid } else { help::get_uid("s") }; let file = format!("{id}.js").into(); // js ext Self { uid: Some(id), @@ -462,7 +442,7 @@ impl PrfItem { } fn from_rules() -> Self { - let uid = help::get_uid("r").into(); + let uid = help::get_uid("r"); let file = format!("{uid}.yaml").into(); // yaml ext Self { @@ -476,7 +456,7 @@ impl PrfItem { } fn from_proxies() -> Self { - let uid = help::get_uid("p").into(); + let uid = help::get_uid("p"); let file = format!("{uid}.yaml").into(); // yaml ext Self { @@ -490,7 +470,7 @@ impl PrfItem { } fn from_groups() -> Self { - let uid = help::get_uid("g").into(); + let uid = help::get_uid("g"); let file = format!("{uid}.yaml").into(); // yaml ext Self { diff --git a/src-tauri/src/config/profiles.rs b/src-tauri/src/config/profiles.rs index 4b149b716..f87ca3442 100644 --- a/src-tauri/src/config/profiles.rs +++ b/src-tauri/src/config/profiles.rs @@ -17,7 +17,6 @@ use clash_verge_logging::{Type, logging}; use parking_lot::Mutex; use serde::{Deserialize, Serialize}; use serde_yaml_ng::Mapping; -use smartstring::alias::String; use std::num::NonZeroUsize; use std::{ collections::{HashMap, HashSet}, @@ -30,6 +29,7 @@ use std::{ }; use tauri_plugin_mihomo::models::{Proxies, ProxyType}; use tokio::{fs, task::JoinHandle}; +use tristr::alias::String; pub(crate) static PROFILE_WRITE_LOCK: tokio::sync::Mutex<()> = tokio::sync::Mutex::const_new(()); @@ -51,7 +51,6 @@ const SELECTED_NODES_FIRST_PASS_BUDGET: Duration = Duration::from_secs(3); #[derive(Default, Debug, Clone, Deserialize, Serialize, specta::Type)] #[serde(default)] pub struct IProfiles { - #[specta(type = Option)] pub current: Option, pub items: Option>, @@ -115,7 +114,7 @@ impl IProfiles { let mut home_changed = false; for item in items.iter_mut() { if item.uid.is_none() { - item.uid = Some(help::get_uid("d").into()); + item.uid = Some(help::get_uid("d")); } if item.itype.as_deref() == Some("remote") { item.option diff --git a/src-tauri/src/config/runtime.rs b/src-tauri/src/config/runtime.rs index 105dc8b46..0fbcb9811 100644 --- a/src-tauri/src/config/runtime.rs +++ b/src-tauri/src/config/runtime.rs @@ -1,6 +1,6 @@ use serde_yaml_ng::{Mapping, Value}; -use smartstring::alias::String; use std::collections::{HashMap, HashSet}; +use tristr::alias::String; use crate::enhance::field::use_keys; diff --git a/src-tauri/src/config/verge.rs b/src-tauri/src/config/verge.rs index 45b0e18ec..3b469a1a9 100644 --- a/src-tauri/src/config/verge.rs +++ b/src-tauri/src/config/verge.rs @@ -8,33 +8,26 @@ use anyhow::Result; use clash_verge_logging::{Type, logging}; use log::LevelFilter; use serde::{Deserialize, Serialize}; -use smartstring::alias::String; +use tristr::alias::String; /// ### `verge.yaml` schema #[derive(Default, Debug, Clone, Deserialize, Serialize, specta::Type)] #[serde(default)] pub struct IVerge { /// silent | error | warn | info | debug | trace - #[specta(type = Option)] pub app_log_level: Option, /// Maximum app log size in KiB. pub app_log_max_size: Option, pub app_log_max_count: Option, - #[specta(type = Option)] pub language: Option, /// `light` or `dark` or `system` - #[specta(type = Option)] pub theme_mode: Option, - #[specta(type = Option)] pub tray_event: Option, - #[specta(type = Option)] pub env_type: Option, - #[specta(type = Option)] pub start_page: Option, - #[specta(type = Option)] pub startup_script: Option, pub traffic_graph: Option, @@ -52,23 +45,18 @@ pub struct IVerge { #[cfg(target_os = "macos")] #[serde(skip_serializing_if = "Option::is_none")] - #[specta(type = Option)] pub tray_icon: Option, #[serde(skip_serializing_if = "Option::is_none")] - #[specta(type = Option)] pub menu_icon: Option, #[serde(skip_serializing_if = "Option::is_none")] - #[specta(type = Option>)] pub menu_order: Option>, #[serde(skip_serializing_if = "Option::is_none")] - #[specta(type = Option)] pub proxy_group_tools_position: Option, #[serde(skip_serializing_if = "Option::is_none")] - #[specta(type = Option)] pub notice_position: Option, pub collapse_navbar: Option, @@ -93,32 +81,25 @@ pub struct IVerge { pub enable_dns_settings: Option, #[serde(default, skip_serializing_if = "std::collections::BTreeMap::is_empty")] - #[specta(type = std::collections::BTreeMap)] pub profile_dns_settings: std::collections::BTreeMap, pub use_default_bypass: Option, - #[specta(type = Option)] pub system_proxy_bypass: Option, pub proxy_guard_duration: Option, pub proxy_auto_config: Option, - #[specta(type = Option)] pub pac_file_content: Option, - #[specta(type = Option)] pub proxy_host: Option, pub theme_setting: Option, - #[specta(type = Option>)] pub web_ui_list: Option>, #[serde(skip_serializing_if = "Option::is_none")] - #[specta(type = Option)] pub clash_core: Option, /// format: {func},{key} #[serde(skip_serializing_if = "Option::is_none")] - #[specta(type = Option>)] pub hotkeys: Option>, pub enable_global_hotkey: Option, @@ -129,7 +110,6 @@ pub struct IVerge { pub auto_close_connection: Option, pub auto_check_update: Option, - #[specta(type = Option)] pub default_latency_test: Option, pub default_latency_timeout: Option, @@ -184,7 +164,6 @@ pub struct IVerge { skip_serializing_if = "Option::is_none", default )] - #[specta(type = Option)] pub webdav_url: Option, #[serde( @@ -193,7 +172,6 @@ pub struct IVerge { skip_serializing_if = "Option::is_none", default )] - #[specta(type = Option)] pub webdav_username: Option, #[serde( @@ -202,14 +180,12 @@ pub struct IVerge { skip_serializing_if = "Option::is_none", default )] - #[specta(type = Option)] pub webdav_password: Option, #[cfg(target_os = "macos")] pub enable_tray_speed: Option, #[serde(skip_serializing_if = "Option::is_none")] - #[specta(type = Option)] pub tray_proxy_groups_display_mode: Option, pub tray_inline_outbound_modes: Option, @@ -229,38 +205,24 @@ pub struct IVerge { #[derive(Default, Debug, Clone, Deserialize, Serialize, specta::Type)] #[serde(default)] pub struct IVergeTestItem { - #[specta(type = Option)] pub uid: Option, - #[specta(type = Option)] pub name: Option, - #[specta(type = Option)] pub icon: Option, - #[specta(type = Option)] pub url: Option, } #[derive(Default, Debug, Clone, Deserialize, Serialize, specta::Type)] #[serde(default)] pub struct IVergeTheme { - #[specta(type = Option)] pub primary_color: Option, - #[specta(type = Option)] pub secondary_color: Option, - #[specta(type = Option)] pub primary_text: Option, - #[specta(type = Option)] pub secondary_text: Option, - #[specta(type = Option)] pub info_color: Option, - #[specta(type = Option)] pub error_color: Option, - #[specta(type = Option)] pub warning_color: Option, - #[specta(type = Option)] pub success_color: Option, - #[specta(type = Option)] pub font_family: Option, - #[specta(type = Option)] pub css_injection: Option, } diff --git a/src-tauri/src/core/auto_backup.rs b/src-tauri/src/core/auto_backup.rs index c475cb711..92de8bc1f 100644 --- a/src-tauri/src/core/auto_backup.rs +++ b/src-tauri/src/core/auto_backup.rs @@ -20,6 +20,7 @@ use tokio::{ fs, sync::{Mutex, watch}, }; +use tristr::alias::String; const DEFAULT_INTERVAL_HOURS: u64 = 24; const MIN_INTERVAL_HOURS: u64 = 1; @@ -211,7 +212,7 @@ impl AutoBackupManager { return Ok(()); } - let file_name = create_local_backup_with_namer(|name| append_auto_suffix(name, trigger.slug()).into()).await?; + let file_name = create_local_backup_with_namer(|name| append_auto_suffix(name, trigger.slug())).await?; self.last_backup.store(Local::now().timestamp(), Ordering::Release); if let Err(err) = cleanup_auto_backups().await { @@ -239,8 +240,8 @@ impl AutoBackupManager { fn append_auto_suffix(file_name: &str, slug: &str) -> String { match file_name.rsplit_once('.') { - Some((stem, ext)) => format!("{stem}{AUTO_MARKER}{slug}.{ext}"), - None => format!("{file_name}{AUTO_MARKER}{slug}"), + Some((stem, ext)) => format!("{stem}{AUTO_MARKER}{slug}.{ext}").into(), + None => format!("{file_name}{AUTO_MARKER}{slug}").into(), } } diff --git a/src-tauri/src/core/backup.rs b/src-tauri/src/core/backup.rs index 481fdcb83..d0d40ca67 100644 --- a/src-tauri/src/core/backup.rs +++ b/src-tauri/src/core/backup.rs @@ -5,7 +5,6 @@ use arc_swap::{ArcSwap, ArcSwapOption}; use backon::{ConstantBuilder, Retryable as _}; use clash_verge_logging::{Type, logging}; use reqwest_dav::list_cmd::{ListEntity, ListFile, ListMultiStatus}; -use smartstring::alias::String; use std::{ collections::HashMap, env::{consts::OS, temp_dir}, @@ -15,6 +14,7 @@ use std::{ time::Duration, }; use tokio::{fs, time::timeout}; +use tristr::alias::String; use zip::write::SimpleFileOptions; const APP_VERSION: &str = env!("CARGO_PKG_VERSION"); diff --git a/src-tauri/src/core/hotkey.rs b/src-tauri/src/core/hotkey.rs index 75027d176..10df185ac 100755 --- a/src-tauri/src/core/hotkey.rs +++ b/src-tauri/src/core/hotkey.rs @@ -7,7 +7,6 @@ use crate::{config::Config, core::handle, core::lightweight::entry_lightweight_m use anyhow::{Result, bail}; use arc_swap::ArcSwap; use clash_verge_logging::{Type, logging}; -use smartstring::alias::String; use std::{ collections::HashMap, fmt, @@ -18,6 +17,7 @@ use std::{ }, }; use tauri_plugin_global_shortcut::{Code, GlobalShortcutExt as _, ShortcutState}; +use tristr::alias::String; /// Enum representing all available hotkey functions #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] diff --git a/src-tauri/src/core/listener.rs b/src-tauri/src/core/listener.rs index 8fb97c82e..6dfe456f4 100644 --- a/src-tauri/src/core/listener.rs +++ b/src-tauri/src/core/listener.rs @@ -10,6 +10,7 @@ use std::{ net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}, str::FromStr as _, }; +use tristr::alias::String; /// The config key of the Mixed Port, named once so callers stop spelling it out. pub(crate) const MIXED_PORT_KEY: &str = "mixed-port"; @@ -163,7 +164,7 @@ pub(crate) fn probe_listener(request: &ListenerProbe) -> ListenerProbeOutcome { Ok(address) => address, Err(error) => { return ListenerProbeOutcome::Invalid { - message: error.to_string(), + message: error.to_string().into(), }; } }; @@ -201,7 +202,7 @@ pub(crate) fn probe_proxy_port_change( Ok(claims) => claims, Err(error) => { return ListenerProbeOutcome::Invalid { - message: format!("current proxy listener configuration is invalid: {error:#}"), + message: format!("current proxy listener configuration is invalid: {error:#}").into(), }; } } @@ -212,7 +213,7 @@ pub(crate) fn probe_proxy_port_change( Ok(claims) => claims, Err(error) => { return ListenerProbeOutcome::Invalid { - message: format!("candidate proxy listener configuration is invalid: {error:#}"), + message: format!("candidate proxy listener configuration is invalid: {error:#}").into(), }; } }; @@ -401,7 +402,8 @@ fn probe_claims(claims: &[BindClaim]) -> ListenerProbeOutcome { claim.socket_addr(), transport_name(claim.transport), error - ), + ) + .into(), }; } Err(error) => { @@ -412,7 +414,8 @@ fn probe_claims(claims: &[BindClaim]) -> ListenerProbeOutcome { claim.socket_addr(), transport_name(claim.transport), error - ), + ) + .into(), }; } } @@ -739,7 +742,7 @@ mod tests { let port = listener.local_addr()?.port(); assert_eq!( probe_listener(&ListenerProbe { - address: format!("0.0.0.0:{port}"), + address: format!("0.0.0.0:{port}").into(), transports: vec![ListenerTransport::Tcp], }), ListenerProbeOutcome::Conflict { @@ -755,7 +758,7 @@ mod tests { let listener = TcpListener::bind((Ipv4Addr::LOCALHOST, 0))?; let port = listener.local_addr()?.port(); let outcome = probe_listener(&ListenerProbe { - address: format!("127.0.0.1:{port}"), + address: format!("127.0.0.1:{port}").into(), transports: vec![ListenerTransport::Tcp], }); assert_eq!( @@ -773,7 +776,7 @@ mod tests { let listener = TcpListener::bind((Ipv4Addr::UNSPECIFIED, 0))?; let port = listener.local_addr()?.port(); let outcome = probe_listener(&ListenerProbe { - address: format!("127.0.0.1:{port}"), + address: format!("127.0.0.1:{port}").into(), transports: vec![ListenerTransport::Tcp], }); assert_eq!( @@ -809,7 +812,7 @@ mod tests { assert_eq!( probe_listener(&ListenerProbe { - address: format!("127.0.0.1:{port}"), + address: format!("127.0.0.1:{port}").into(), transports: vec![ListenerTransport::Tcp], }), ListenerProbeOutcome::Available @@ -822,7 +825,7 @@ mod tests { let socket = UdpSocket::bind((Ipv4Addr::LOCALHOST, 0))?; let port = socket.local_addr()?.port(); let outcome = probe_listener(&ListenerProbe { - address: format!("127.0.0.1:{port}"), + address: format!("127.0.0.1:{port}").into(), transports: vec![ListenerTransport::Udp], }); assert_eq!( diff --git a/src-tauri/src/core/manager/config.rs b/src-tauri/src/core/manager/config.rs index 82496fc93..42eb11b97 100644 --- a/src-tauri/src/core/manager/config.rs +++ b/src-tauri/src/core/manager/config.rs @@ -15,7 +15,6 @@ use clash_verge_draft::DraftTransaction; use clash_verge_logging::{Type, logging}; use clash_verge_service_ipc::StageRuntimeOutcome; use scopeguard::defer; -use smartstring::alias::String; use std::{ collections::HashSet, path::PathBuf, @@ -23,6 +22,7 @@ use std::{ time::{Duration, Instant}, }; use tauri_plugin_mihomo::Error as MihomoError; +use tristr::alias::String; /// A staging result separated from the policy that acts on it. #[derive(Debug, Clone, PartialEq, Eq)] @@ -92,7 +92,7 @@ async fn ask_to_stage(path: &std::path::Path) -> StageAttempt { match crate::core::service::stage_runtime_by_service(path).await { Ok(StageRequest::Answered(outcome)) => StageAttempt::Answered(outcome), Ok(StageRequest::Refused { code, message }) => { - let message = message.into(); + let message = message; if StageRequest::is_about_the_bundle(code) { StageAttempt::RefusedTheBundle(message) } else { diff --git a/src-tauri/src/core/manager/lifecycle.rs b/src-tauri/src/core/manager/lifecycle.rs index ad67b6b0a..7dcf34933 100644 --- a/src-tauri/src/core/manager/lifecycle.rs +++ b/src-tauri/src/core/manager/lifecycle.rs @@ -7,10 +7,10 @@ use crate::core::service::{SERVICE_MANAGER, ServiceStatus}; use anyhow::Result; use clash_verge_logging::{Type, logging}; use scopeguard::defer; -use smartstring::alias::String; use std::path::Path; #[cfg(target_os = "windows")] use tauri_plugin_clash_verge_sysinfo::is_current_app_handle_admin; +use tristr::alias::String; #[cfg(any(target_os = "windows", test))] const fn should_wait_for_service(tun_enabled: bool, service_ready: bool, is_admin: bool) -> bool { @@ -194,7 +194,7 @@ async fn run_service_start_with_sidecar_fallback Result<()> where Start: FnOnce() -> StartFuture, @@ -217,7 +217,7 @@ where // A lost start response can hide a running core; the shared check must resolve occupancy. if let Err(probe_error) = confirm_idle().await { let error = error.context(format!("Sidecar fallback was not safe: {probe_error:#}")); - record_unavailable(format!("{error:#}")); + record_unavailable(format!("{error:#}").into()); return Err(error); } logging!( @@ -650,7 +650,7 @@ impl CoreManager { |error| self.start_sidecar_after_service_failure(error), |reason| { crate::core::runstate::RUN_STATE - .observe(crate::core::runstate::ServiceHealth::Unavailable(reason)); + .observe(crate::core::runstate::ServiceHealth::Unavailable(reason.into())); }, ) .await @@ -688,7 +688,7 @@ impl CoreManager { } let reason = match state.health { ServiceHealth::Unavailable(reason) => reason, - ServiceHealth::VersionMismatch => "registered service is unavailable or incompatible".to_owned(), + ServiceHealth::VersionMismatch => "registered service is unavailable or incompatible".into(), _ => return Ok(()), }; if let Err(error) = clash_verge_service_ipc::execution::check_sidecar_available() @@ -712,7 +712,7 @@ impl CoreManager { if Handle::global().is_exiting() { return Err(error.context("application exited before Sidecar fallback")); } - let reason = format!("{error:#}"); + let reason: String = format!("{error:#}").into(); RUN_STATE.allow_sidecar_after_service_refusal(reason.clone())?; logging!(warn, Type::Core, "Starting Sidecar with Service unavailable: {reason}"); let result = self.start_core_by_sidecar().await; @@ -1590,7 +1590,7 @@ mod tests { started.store(true, Ordering::SeqCst); Ok(()) }, - |reason| store.observe(ServiceHealth::Unavailable(reason)), + |reason| store.observe(ServiceHealth::Unavailable(reason.into())), ) .await; diff --git a/src-tauri/src/core/manager/state.rs b/src-tauri/src/core/manager/state.rs index d30549b91..42e76850b 100644 --- a/src-tauri/src/core/manager/state.rs +++ b/src-tauri/src/core/manager/state.rs @@ -16,6 +16,7 @@ use std::num::NonZeroUsize; use std::path::Path; use tauri_plugin_mihomo::MihomoExt as _; use tauri_plugin_shell::ShellExt as _; +use tristr::alias::String; pub(super) fn explain_core_occupancy(error: anyhow::Error) -> anyhow::Error { let detail = error.to_string(); @@ -27,7 +28,7 @@ pub(super) fn explain_core_occupancy(error: anyhow::Error) -> anyhow::Error { } #[cfg(target_os = "windows")] -fn sidecar_config_without_tun(yaml: &str) -> Result { +fn sidecar_config_without_tun(yaml: &str) -> Result { use serde_yaml_ng::{Mapping, Value}; let mut config: Mapping = serde_yaml_ng::from_str(yaml)?; @@ -40,7 +41,7 @@ fn sidecar_config_without_tun(yaml: &str) -> Result { tun.as_mapping_mut() .context("invalid Sidecar TUN configuration")? .insert(Value::from("enable"), Value::Bool(false)); - Ok(serde_yaml_ng::to_string(&config)?) + Ok(serde_yaml_ng::to_string(&config)?.into()) } const SIDECAR_READINESS_ATTEMPTS: usize = 30; @@ -153,7 +154,7 @@ impl CoreManager { pub async fn get_clash_logs(&self) -> Result> { match *self.get_running_mode() { RunningMode::Service => service::get_clash_logs_by_service().await, - RunningMode::Sidecar => Ok(CLASH_LOGGER.get_logs()), + RunningMode::Sidecar => Ok(CLASH_LOGGER.get_logs().into_iter().map(Into::into).collect()), RunningMode::NotRunning => Ok(Vec::new()), } } @@ -257,7 +258,9 @@ impl CoreManager { match event { tauri_plugin_shell::process::CommandEvent::Stdout(line) | tauri_plugin_shell::process::CommandEvent::Stderr(line) => { - let message = String::from_utf8_lossy_owned(line); + // from_utf8_lossy_owned exists only on the std String type; the tristr + // alias shadows the bare name. LogRing stores std Strings. + let message = std::string::String::from_utf8_lossy_owned(line); Logger::global().writer_sidecar_log(Level::Error, &message); CLASH_LOGGER.append_log(message); } @@ -265,12 +268,12 @@ impl CoreManager { let _ = terminated.send(()); let manager = Self::global(); let _ = manager.invalidate_core_readiness_if(core_readiness_generation); - let message = if let Some(code) = term.code { - format!("Process terminated with code: {}", code) + let message: String = if let Some(code) = term.code { + format!("Process terminated with code: {}", code).into() } else if let Some(signal) = term.signal { - format!("Process terminated by signal: {}", signal) + format!("Process terminated by signal: {}", signal).into() } else { - String::from("Process terminated") + "Process terminated".into() }; Logger::global().writer_sidecar_log(Level::Info, &message); CLASH_LOGGER.clear_logs(); diff --git a/src-tauri/src/core/notify/handle.rs b/src-tauri/src/core/notify/handle.rs index 6a05ab138..01165ae5e 100644 --- a/src-tauri/src/core/notify/handle.rs +++ b/src-tauri/src/core/notify/handle.rs @@ -1,12 +1,9 @@ use super::NoticeStatus; use crate::{APP_HANDLE, singleton}; -use smartstring::alias::String; -use std::sync::{ - Arc, - atomic::{AtomicBool, Ordering}, -}; +use std::sync::atomic::{AtomicBool, Ordering}; use tauri::AppHandle; use tauri_plugin_mihomo::{Mihomo, MihomoExt as _}; +use tristr::alias::String; use super::notification::{FrontendEvent, NotificationSystem}; @@ -73,7 +70,7 @@ impl Handle { Self::send_event(FrontendEvent::ProfileUpdateCompleted { uid }); } - pub fn notice(status: NoticeStatus, message: impl Into>) { + pub fn notice(status: NoticeStatus, message: impl Into) { Self::send_event(FrontendEvent::NoticeMessage { status, message: message.into(), diff --git a/src-tauri/src/core/notify/notification.rs b/src-tauri/src/core/notify/notification.rs index be6a01f03..54be518e2 100644 --- a/src-tauri/src/core/notify/notification.rs +++ b/src-tauri/src/core/notify/notification.rs @@ -3,11 +3,10 @@ use clash_verge_logging::{Type, logging}; use parking_lot::Mutex; #[cfg(test)] use serde_json::json; -use smartstring::alias::String; -use std::sync::Arc; use std::sync::LazyLock; use std::{collections::HashMap, future::Future}; use tauri::{AppHandle, Emitter as _, Manager as _, WebviewWindow}; +use tristr::alias::String; #[derive(Debug, Clone)] pub enum FrontendEvent<'a> { @@ -17,7 +16,7 @@ pub enum FrontendEvent<'a> { RefreshProxyConfig, NoticeMessage { status: NoticeStatus, - message: Arc, + message: String, }, ProfileChanged { current_profile_id: &'a String, @@ -77,10 +76,8 @@ impl FailedOperation { #[serde(rename_all = "camelCase")] pub struct PendingFailure { /// Stable code used as the table key. - #[specta(type = std::string::String)] pub code: String, /// Full diagnostic context chain. - #[specta(type = std::string::String)] pub detail: String, pub operation: FailedOperation, /// Monotonic identity for repeated failures under the same code. @@ -247,7 +244,6 @@ static PENDING_FAILURES: LazyLock = LazyLock::new(FailureTable::de #[derive(Debug, Default, Clone, serde::Serialize, specta::Type)] pub struct SidecarFailureSnapshot { pub revision: u64, - #[specta(type = Option)] pub detail: Option, } @@ -391,7 +387,7 @@ fn sample_frontend_events(uid: &String) -> Vec<(FrontendEvent<'_>, &'static str, ( FrontendEvent::NoticeMessage { status: NoticeStatus::Info, - message: Arc::from("ok"), + message: String::from("ok"), }, "verge://notice-message", json!(["info", "ok"]), @@ -456,16 +452,16 @@ frontend_events! { refresh_verge: () = "verge://refresh-verge-config", FrontendEvent::RefreshVerge => (); refresh_profiles: () = "verge://refresh-profiles", FrontendEvent::RefreshProfiles => (); refresh_proxy: () = "verge://refresh-proxy-config", FrontendEvent::RefreshProxyConfig => (); - notice: (NoticeStatus, std::string::String) = "verge://notice-message", FrontendEvent::NoticeMessage { status, ref message } => (status, message.to_string()); - profile_changed: std::string::String = "profile-changed", FrontendEvent::ProfileChanged { current_profile_id } => current_profile_id.to_string(); - timer_updated: std::string::String = "verge://timer-updated", FrontendEvent::TimerUpdated { profile_index } => profile_index.to_string(); - profile_started: ProfileUpdatePayload = "profile-update-started", FrontendEvent::ProfileUpdateStarted { uid } => ProfileUpdatePayload { uid: uid.to_string() }; - profile_completed: ProfileUpdatePayload = "profile-update-completed", FrontendEvent::ProfileUpdateCompleted { uid } => ProfileUpdatePayload { uid: uid.to_string() }; + notice: (NoticeStatus, String) = "verge://notice-message", FrontendEvent::NoticeMessage { status, ref message } => (status, message.clone()); + profile_changed: String = "profile-changed", FrontendEvent::ProfileChanged { current_profile_id } => current_profile_id.clone(); + timer_updated: String = "verge://timer-updated", FrontendEvent::TimerUpdated { profile_index } => profile_index.clone(); + profile_started: ProfileUpdatePayload = "profile-update-started", FrontendEvent::ProfileUpdateStarted { uid } => ProfileUpdatePayload { uid: uid.clone() }; + profile_completed: ProfileUpdatePayload = "profile-update-completed", FrontendEvent::ProfileUpdateCompleted { uid } => ProfileUpdatePayload { uid: uid.clone() }; run_state: crate::core::runstate::RunStateView = "verge://run-state-changed", FrontendEvent::RunStateChanged { state } => state; pending_failures: () = "verge://pending-failures-changed", FrontendEvent::PendingFailuresChanged => (); } #[derive(serde::Serialize, specta::Type)] pub struct ProfileUpdatePayload { - pub uid: std::string::String, + pub uid: String, } diff --git a/src-tauri/src/core/owner_identity.rs b/src-tauri/src/core/owner_identity.rs index b730fabbe..915197c11 100644 --- a/src-tauri/src/core/owner_identity.rs +++ b/src-tauri/src/core/owner_identity.rs @@ -2,6 +2,8 @@ use crate::utils::dirs; use anyhow::{Context as _, Result}; use clash_verge_service_ipc::{OwnerCredentials, OwnerIdentity}; use std::path::Path; +#[cfg(windows)] +use tristr::alias::String; pub(crate) fn current_owner_credentials() -> Result { current_owner_credentials_for_root(&dirs::app_home_dir()?) @@ -39,16 +41,16 @@ pub(crate) fn current_owner_credentials_for_root(app_root: &Path) -> Result Result<(OwnerIdentity, Option)> { +fn windows_owner_credentials(app_data_root: &Path) -> Result<(OwnerIdentity, Option)> { let sid = windows_owner::current_sid()?; let token = windows_owner::load_or_create_token(app_data_root, &sid)?; - Ok((OwnerIdentity::Windows { sid }, Some(token))) + Ok((OwnerIdentity::Windows { sid: sid.into() }, Some(token.into()))) } #[cfg(windows)] fn windows_current_identity() -> Result { Ok(OwnerIdentity::Windows { - sid: windows_owner::current_sid()?, + sid: windows_owner::current_sid()?.into(), }) } @@ -79,7 +81,7 @@ pub(crate) fn repair_app_data_root_owner(app_data_root: &Path) -> Result<()> { #[cfg(windows)] pub(crate) fn current_user_pipe_sddl() -> Result { let sid = windows_owner::current_sid()?; - Ok(format!("D:P(A;;GA;;;{sid})(A;;GA;;;SY)(A;;GA;;;BA)")) + Ok(format!("D:P(A;;GA;;;{sid})(A;;GA;;;SY)(A;;GA;;;BA)").into()) } #[cfg(windows)] @@ -644,17 +646,19 @@ mod windows_owner { } let value = LocalWideString(value); let length = (0..).take_while(|index| unsafe { *value.0.add(*index) } != 0).count(); - String::from_utf16(unsafe { std::slice::from_raw_parts(value.0, length) }) + // from_utf16 exists only on the std String type; the tristr alias shadows the bare name. + std::string::String::from_utf16(unsafe { std::slice::from_raw_parts(value.0, length) }) .context("process SID is not valid UTF-16") + .map(Into::into) } fn encode_token(token: &[u8; TOKEN_BYTES]) -> String { - let mut encoded = String::with_capacity(TOKEN_BYTES * 2); + let mut encoded = std::string::String::with_capacity(TOKEN_BYTES * 2); for byte in token { use std::fmt::Write as _; let _ = write!(encoded, "{byte:02x}"); } - encoded + encoded.into() } fn wide_path(path: &Path) -> Result> { diff --git a/src-tauri/src/core/proxy_control.rs b/src-tauri/src/core/proxy_control.rs index e9f482247..6d728ba70 100644 --- a/src-tauri/src/core/proxy_control.rs +++ b/src-tauri/src/core/proxy_control.rs @@ -24,6 +24,7 @@ use std::{ time::Duration, }; use tokio::{sync::Mutex, time::timeout}; +use tristr::alias::String; const CLEAR_ATTEMPTS: u32 = 3; const CLEAR_RETRY_DELAY: Duration = Duration::from_millis(100); @@ -320,7 +321,8 @@ fn truncate_utf8(value: &mut String, max_bytes: usize) { while !value.is_char_boundary(boundary) { boundary -= 1; } - value.truncate(boundary); + let truncated = value.as_str()[..boundary].into(); + *value = truncated; } fn service_bypass(verge: &IVerge) -> Result { @@ -328,11 +330,11 @@ fn service_bypass(verge: &IVerge) -> Result { ensure!(!custom.contains('\0'), "system proxy bypass contains NUL"); let mut bypass = if custom.is_empty() { - MACOS_DEFAULT_BYPASS.to_owned() + MACOS_DEFAULT_BYPASS.into() } else if verge.use_default_bypass.unwrap_or(true) { - format!("{MACOS_DEFAULT_BYPASS},{custom}") + format!("{MACOS_DEFAULT_BYPASS},{custom}").into() } else { - custom.to_owned() + custom.into() }; truncate_utf8(&mut bypass, MAX_SERVICE_BYPASS_LEN); Ok(bypass) @@ -353,7 +355,7 @@ fn service_proxy_config(verge: &IVerge, mixed_port: u16, pac_port: u16) -> Resul Ok(MacosProxyConfig::Global { host: LOOPBACK_HOST.to_owned(), port: mixed_port, - bypass: service_bypass(verge)?, + bypass: service_bypass(verge)?.into(), }) } } @@ -368,7 +370,7 @@ fn service_apply_result(requested: &MacosProxyConfig, outcome: ProxyApplyOutcome // Direct fallback satisfies a disable request. ProxyApplyOutcome::DirectFallback { .. } if matches!(requested, MacosProxyConfig::Disabled) => Ok(()), ProxyApplyOutcome::DirectFallback { message } => { - Err(SysproxyFailure::DirectFallback { detail: message }.into()) + Err(SysproxyFailure::DirectFallback { detail: message.into() }.into()) } } } @@ -644,7 +646,7 @@ const GUARD_FAILURES_BEFORE_STOPPING: u32 = 3; async fn report_guard_stopped(captured_generation: u64, error: &anyhow::Error) { let stopped = anyhow::Error::new(SysproxyFailure::GuardStopped { - detail: format!("{error:#}"), + detail: format!("{error:#}").into(), }); // Do not publish a late failure from a replaced guard. let recorded = SERVICE_PROXY_OPERATIONS @@ -1106,7 +1108,7 @@ mod tests { let requested = MacosProxyConfig::Global { host: "127.0.0.1".to_owned(), port: 7890, - bypass: String::new(), + bypass: std::string::String::new(), }; let error = service_apply_result( @@ -1146,8 +1148,8 @@ mod tests { let events = Mutex::new(Vec::new()); super::settle_table( effect, - |asked| events.lock().push(format!("retire:{asked:?}")), - |operation, _| events.lock().push(format!("file:{operation:?}")), + |asked| events.lock().push(format!("retire:{asked:?}").into()), + |operation, _| events.lock().push(format!("file:{operation:?}").into()), ); events.into_inner() } @@ -1276,7 +1278,7 @@ mod tests { let combined = rollback_failure( mappable(), anyhow::anyhow!("the service could not set it").context(SysproxyFailure::DirectFallback { - detail: "fell back to direct".to_owned(), + detail: "fell back to direct".into(), }), ); diff --git a/src-tauri/src/core/proxy_view.rs b/src-tauri/src/core/proxy_view.rs index 0fe394280..1efe9f38a 100644 --- a/src-tauri/src/core/proxy_view.rs +++ b/src-tauri/src/core/proxy_view.rs @@ -4,6 +4,7 @@ use serde::Serialize; use tauri_plugin_mihomo::models::{ DelayHistory, Proxies, Proxy, ProxyProvider, ProxyProviders, ProxyType, VehicleType, }; +use tristr::alias::String; pub struct ProxyViewInput { pub runtime_group_order: Vec, @@ -270,7 +271,7 @@ impl ProxyViewBuilder { let global = core_groups .remove("GLOBAL") - .map(|proxy| build_group("GLOBAL".to_owned(), proxy, &resolver)); + .map(|proxy| build_group(String::from("GLOBAL"), proxy, &resolver)); let (groups, order_source) = build_ordered_groups(core_groups, runtime_group_order, &resolver); let direct = core_node_ids.get("DIRECT").cloned(); let standalone = build_standalone(&core_node_ids); @@ -294,6 +295,7 @@ fn partition_core(proxies: Proxies) -> (BTreeMap, BTreeMap, ) -> (Vec, ProviderCandidates) { let providers = providers - .map(|providers| providers.providers.into_iter().collect::>()) + .map(|providers| { + providers + .providers + .into_iter() + .map(|(name, provider)| (String::from(name), provider)) + .collect::>() + }) .unwrap_or_default(); let mut views = Vec::new(); let mut candidates = BTreeMap::new(); @@ -355,9 +363,9 @@ fn build_provider_records( let mut proxy_record_ids = Vec::new(); for (member_index, proxy) in proxies.into_iter().enumerate() { - let record_id = format!("p:{provider_index}:{member_index}"); - let proxy_name = proxy.name.clone(); - let node_name = proxy.name.clone(); + let record_id = String::from(format!("p:{provider_index}:{member_index}")); + let proxy_name = String::from(proxy.name.as_str()); + let node_name = String::from(proxy.name.as_str()); candidates .entry(proxy_name.clone()) .or_insert_with(Vec::new) @@ -380,7 +388,7 @@ fn build_provider_records( views.push(ProxyProviderView { name: provider_name, vehicle_type, - updated_at, + updated_at: updated_at.map(String::from), subscription_info: subscription_info.map(|subscription_info| ProxySubscriptionInfo { upload: subscription_info.upload, download: subscription_info.download, @@ -417,6 +425,7 @@ fn build_group(name: String, proxy: Proxy, resolver: &MemberResolver<'_>) -> Pro .unwrap_or_default() .into_iter() .map(|member| { + let member = String::from(member); let (occurrence, scoped) = seen .entry(member.clone()) .or_insert_with(|| (0, resolver.scoped_candidates(&name, &member))); @@ -429,11 +438,11 @@ fn build_group(name: String, proxy: Proxy, resolver: &MemberResolver<'_>) -> Pro ProxyGroupView { name, proxy_type, - now, - fixed, + now: now.map(String::from), + fixed: fixed.map(String::from), hidden, - icon, - test_url, + icon: icon.map(String::from), + test_url: test_url.map(String::from), history, capabilities: ProxyCapabilities { udp, @@ -467,10 +476,10 @@ fn build_node(record_id: String, name: String, proxy: Proxy, source: ProxyNodeSo name, proxy_type, history, - id, + id: id.map(String::from), hidden, - icon, - test_url, + icon: icon.map(String::from), + test_url: test_url.map(String::from), capabilities: ProxyCapabilities { udp, xudp, diff --git a/src-tauri/src/core/runstate/env.rs b/src-tauri/src/core/runstate/env.rs index bda09ff5a..72e04e17d 100644 --- a/src-tauri/src/core/runstate/env.rs +++ b/src-tauri/src/core/runstate/env.rs @@ -64,7 +64,7 @@ impl RunStateEnv for RealEnv { Ok(ServiceVersionReply { core, code: response.code, - message: response.message, + message: response.message.into(), protocol: response.data, }) } @@ -123,6 +123,7 @@ mod fake { use super::{PendingAction, RunState, RunStateEnv, ServiceVersionReply}; use crate::core::manager::RunningMode; + use tristr::alias::String; /// A fail-closed scripted machine that records outbound effects. #[derive(Debug)] @@ -163,7 +164,7 @@ mod fake { self.with_evidence(true).always_replying(Ok(ServiceVersionReply { core: Some(clash_verge_service_ipc::CoreAvailability::Ready), code: 0, - message: "ok".to_owned(), + message: "ok".into(), protocol: Some(ProtocolInfo::current()), })) } @@ -173,7 +174,7 @@ mod fake { self.with_evidence(true).always_replying(Ok(ServiceVersionReply { core: Some(clash_verge_service_ipc::CoreAvailability::Ready), code: 0, - message: "ok".to_owned(), + message: "ok".into(), protocol: None, })) } @@ -181,12 +182,12 @@ mod fake { #[must_use] pub fn service_unreachable(self) -> Self { self.with_evidence(true) - .always_replying(Err("ipc transport refused".to_owned())) + .always_replying(Err("ipc transport refused".into())) } #[must_use] pub fn evidence_unavailable(mut self) -> Self { - self.evidence = Err("registry probe failed".to_owned()); + self.evidence = Err("registry probe failed".into()); self } @@ -236,7 +237,7 @@ mod fake { #[must_use] pub fn privileged_operations_fail(self, reason: &str) -> Self { - *self.privileged_outcome.lock() = Err(reason.to_owned()); + *self.privileged_outcome.lock() = Err(reason.into()); self } @@ -256,7 +257,7 @@ mod fake { replies .first() .cloned() - .unwrap_or_else(|| Err("no service configured".to_owned())) + .unwrap_or_else(|| Err("no service configured".into())) }; reply.map_err(|error| anyhow!(error)) } diff --git a/src-tauri/src/core/runstate/health.rs b/src-tauri/src/core/runstate/health.rs index 1a9ff685e..1c9423cec 100644 --- a/src-tauri/src/core/runstate/health.rs +++ b/src-tauri/src/core/runstate/health.rs @@ -2,6 +2,7 @@ //! Core lifecycle remains in `CoreManager`. use crate::core::manager::RunningMode; +use tristr::alias::String; #[derive(Debug, Clone, Default, PartialEq, Eq)] pub enum ServiceHealth { diff --git a/src-tauri/src/core/runstate/mod.rs b/src-tauri/src/core/runstate/mod.rs index 9aa2291a5..6557e3bc5 100644 --- a/src-tauri/src/core/runstate/mod.rs +++ b/src-tauri/src/core/runstate/mod.rs @@ -173,7 +173,7 @@ impl RunStateStore { Type::Service, "failed to inspect trusted service evidence: {error:#}" ); - return ServiceHealth::Unavailable(format!("service detection failed: {error:#}")); + return ServiceHealth::Unavailable(format!("service detection failed: {error:#}").into()); } }; @@ -331,7 +331,7 @@ impl RunStateStore { } #[cfg(target_os = "windows")] - pub fn allow_sidecar_after_service_refusal(&self, reason: String) -> Result<()> { + pub fn allow_sidecar_after_service_refusal(&self, reason: tristr::alias::String) -> Result<()> { let mut state = self.service.lock(); if self.operation_running.load(Ordering::Acquire) || state.service.pending.is_some() { bail!("cannot fall back while a service operation is pending"); @@ -496,7 +496,7 @@ mod tests { ServiceVersionReply { core: Some(clash_verge_service_ipc::CoreAvailability::Ready), code: 0, - message: "ok".to_owned(), + message: "ok".into(), protocol: Some(clash_verge_service_ipc::ProtocolInfo::current()), } } @@ -713,7 +713,7 @@ mod tests { for health in [ ServiceHealth::NotInstalled, ServiceHealth::VersionMismatch, - ServiceHealth::Unavailable("boom".to_owned()), + ServiceHealth::Unavailable("boom".into()), ] { let store = with_env(FakeEnv::new()); store.observe(health.clone()); @@ -858,7 +858,7 @@ mod tests { #[tokio::test] async fn awaiting_readiness_retries_a_silent_service_then_accepts_its_reply() { - let store = with_env(FakeEnv::new().replying(vec![Err("ipc path not ready".to_owned()), Ok(ready_reply())])); + let store = with_env(FakeEnv::new().replying(vec![Err("ipc path not ready".into()), Ok(ready_reply())])); let state = store .await_ready(2, Duration::ZERO) diff --git a/src-tauri/src/core/runstate/probe.rs b/src-tauri/src/core/runstate/probe.rs index 93b284708..b933710f2 100644 --- a/src-tauri/src/core/runstate/probe.rs +++ b/src-tauri/src/core/runstate/probe.rs @@ -9,6 +9,7 @@ use clash_verge_service_ipc::{ }; use super::health::ServiceHealth; +use tristr::alias::String; /// What the Service replied to a protocol-version query. #[derive(Debug, Clone, PartialEq, Eq)] @@ -50,7 +51,7 @@ pub fn classify_service_version_reply(reply: &ServiceVersionReply) -> ServiceVer return match &reply.core { Some(clash_verge_service_ipc::CoreAvailability::Ready) => ServiceVersionCheck::Ready, Some(clash_verge_service_ipc::CoreAvailability::Rejected { reason }) => { - ServiceVersionCheck::CoreUnavailable(format!("{CORE_REJECTED_PREFIX}{reason}")) + ServiceVersionCheck::CoreUnavailable(format!("{CORE_REJECTED_PREFIX}{reason}").into()) } _ => ServiceVersionCheck::CoreUnavailable( "the selected approved core is missing or does not match; reinstall the service to repair it".into(), @@ -77,9 +78,12 @@ pub fn classify_service_version_reply(reply: &ServiceVersionReply) -> ServiceVer reply.code, reply.message, client.epoch ) }; - ServiceVersionCheck::NeedsReinstall(format!( - "Service helper version or protocol mismatch: {detail}. Choose Reinstall to uninstall the existing service and install the required version" - )) + ServiceVersionCheck::NeedsReinstall( + format!( + "Service helper version or protocol mismatch: {detail}. Choose Reinstall to uninstall the existing service and install the required version" + ) + .into(), + ) } /// Classify a version reply into the probe outcome used by [`classify_service_health`]. @@ -104,7 +108,7 @@ pub fn classify_service_health( match current { CurrentServiceProbe::Ready => ServiceHealth::Ready, CurrentServiceProbe::VersionMismatch => ServiceHealth::VersionMismatch, - CurrentServiceProbe::Unavailable => ServiceHealth::Unavailable(unavailable_reason.to_owned()), + CurrentServiceProbe::Unavailable => ServiceHealth::Unavailable(unavailable_reason.into()), CurrentServiceProbe::Missing if has_install_marker => ServiceHealth::VersionMismatch, CurrentServiceProbe::Missing => ServiceHealth::NotInstalled, } @@ -119,7 +123,7 @@ mod tests { ServiceVersionReply { core: Some(clash_verge_service_ipc::CoreAvailability::Ready), code: 0, - message: "ok".to_owned(), + message: "ok".into(), protocol: Some(ProtocolInfo::current()), } } @@ -154,7 +158,7 @@ mod tests { let reply = ServiceVersionReply { core: Some(clash_verge_service_ipc::CoreAvailability::Ready), code: 0, - message: "ok".to_owned(), + message: "ok".into(), protocol: None, }; let ServiceVersionCheck::NeedsReinstall(detail) = classify_service_version_reply(&reply) else { @@ -172,7 +176,7 @@ mod tests { let reply = ServiceVersionReply { core: Some(clash_verge_service_ipc::CoreAvailability::Ready), code: 7, - message: "nope".to_owned(), + message: "nope".into(), protocol: Some(ProtocolInfo::current()), }; let ServiceVersionCheck::NeedsReinstall(detail) = classify_service_version_reply(&reply) else { @@ -188,7 +192,7 @@ mod tests { let reply = ServiceVersionReply { core: Some(clash_verge_service_ipc::CoreAvailability::Ready), code: 0, - message: "ok".to_owned(), + message: "ok".into(), protocol: Some(info), }; assert!(matches!( @@ -209,7 +213,7 @@ mod tests { let reply = ServiceVersionReply { core: Some(clash_verge_service_ipc::CoreAvailability::Ready), code: 0, - message: "ok".to_owned(), + message: "ok".into(), protocol: Some(info), }; assert!(matches!( @@ -228,7 +232,7 @@ mod tests { let reply = ServiceVersionReply { core: None, code, - message: "unsupported command".to_owned(), + message: "unsupported command".into(), protocol: protocol.clone(), }; let ServiceVersionCheck::NeedsReinstall(detail) = classify_service_version_reply(&reply) else { @@ -250,7 +254,7 @@ mod tests { fn an_unreachable_service_keeps_the_reason() { assert_eq!( classify_service_health(CurrentServiceProbe::Unavailable, true, "socket refused"), - ServiceHealth::Unavailable("socket refused".to_owned()) + ServiceHealth::Unavailable("socket refused".into()) ); } diff --git a/src-tauri/src/core/runtime_bundle.rs b/src-tauri/src/core/runtime_bundle.rs index 9987d669c..2dbd82205 100644 --- a/src-tauri/src/core/runtime_bundle.rs +++ b/src-tauri/src/core/runtime_bundle.rs @@ -5,6 +5,7 @@ use serde_yaml_ng::{Mapping, Value}; use sha2::{Digest as _, Sha256}; use std::collections::{BTreeMap, HashSet}; use std::path::{Component, Path, PathBuf}; +use tristr::alias::String; pub(crate) const GEO_ASSETS: &[&str] = &[ "Country.mmdb", @@ -64,7 +65,7 @@ struct ProviderPathOwners { pub(crate) fn resolve_provider_path_conflicts(config: &mut Mapping, config_root: &Path) -> Result<()> { let config_root = std::fs::canonicalize(config_root)?; let mut paths = BTreeMap::::new(); - let mut reserved: HashSet = GEO_ASSETS.iter().map(|name| (*name).to_owned()).collect(); + let mut reserved: HashSet = GEO_ASSETS.iter().map(|name| (*name).into()).collect(); for section in ["proxy-providers", "rule-providers"] { let Some(providers) = config.get(section).and_then(Value::as_mapping) else { continue; @@ -89,7 +90,7 @@ pub(crate) fn resolve_provider_path_conflicts(config: &mut Mapping, config_root: match (is_remote, provider.get("url").and_then(Value::as_str)) { (true, Some(url)) => owners .remote - .entry(url.to_owned()) + .entry(url.into()) .or_default() .push((section, name.clone())), _ => owners.local = true, @@ -105,7 +106,7 @@ pub(crate) fn resolve_provider_path_conflicts(config: &mut Mapping, config_root: for (url, providers) in owners.remote { let replacement = allocate_provider_destination(&destination, &url, &mut reserved); for (section, name) in providers { - config[section][&name]["path"] = Value::String(replacement.clone()); + config[section][&name]["path"] = Value::String(replacement.clone().into()); logging!( warn, Type::Config, @@ -131,15 +132,16 @@ fn allocate_provider_destination(destination: &str, url: &str, reserved: &mut Ha let mut suffix = String::new(); let mut sequence = 0; loop { - let candidate = path + let candidate: String = path .with_file_name(format!("cvr-{digest}{suffix}{extension}")) .to_string_lossy() - .replace('\\', "/"); + .replace('\\', "/") + .into(); if reserved.insert(candidate.clone()) { return candidate; } sequence += 1; - suffix = format!("-{sequence}"); + suffix = format!("-{sequence}").into(); } } @@ -167,7 +169,7 @@ fn collect_runtime_parts(mut config: Value, config_root: &Path) -> Result Result { if destination.as_os_str().is_empty() { bail!("local runtime provider destination is empty"); } - Ok(destination.to_string_lossy().replace('\\', "/")) + Ok(destination.to_string_lossy().replace('\\', "/").into()) } #[cfg(test)] @@ -385,12 +387,12 @@ mod tests { for declared in readback { assert!(bundle.remote_providers.contains(&declared.provider)); assert_eq!( - config[declared.section][&declared.name]["path"], + config[declared.section][declared.name.as_str()]["path"], declared.provider.destination ); assert_eq!( - config[declared.section][&declared.name]["url"], - original[declared.section][&declared.name]["url"] + config[declared.section][declared.name.as_str()]["url"], + original[declared.section][declared.name.as_str()]["url"] ); } std::fs::remove_dir_all(root)?; diff --git a/src-tauri/src/core/service.rs b/src-tauri/src/core/service.rs index 7d9242087..709a29100 100644 --- a/src-tauri/src/core/service.rs +++ b/src-tauri/src/core/service.rs @@ -36,6 +36,7 @@ use std::{ sync::atomic::{AtomicBool, AtomicU64, Ordering}, time::{Duration, SystemTime, UNIX_EPOCH}, }; +use tristr::alias::String; static OWNER_MONITOR_GENERATION: AtomicU64 = AtomicU64::new(0); static ACTIVE_SERVICE_SESSION: Mutex> = Mutex::new(None); @@ -62,7 +63,7 @@ pub(crate) fn is_not_auto_started(reason: &str) -> bool { #[cfg(target_os = "windows")] pub(crate) fn notify_service_fallback(reason: &str) { - *PENDING_SERVICE_FALLBACK_NOTICE.lock() = Some(reason.to_owned()); + *PENDING_SERVICE_FALLBACK_NOTICE.lock() = Some(reason.into()); Handle::notice(NoticeStatus::ServiceCoreSidecarFallback, ""); } @@ -91,7 +92,7 @@ fn app_data_owner_command(credentials: &OwnerCredentials) -> Option { return None; }; let path = credentials.app_data_dir.replace('\'', r"'\''"); - Some(format!("sudo chown -R {uid}:{gid} '{path}'")) + Some(format!("sudo chown -R {uid}:{gid} '{path}'").into()) } /// Capabilities of the service session that owns the running Core. @@ -243,22 +244,24 @@ fn macos_service_install_markers() -> Vec { format!( "/Library/LaunchDaemons/{}.plist", clash_verge_service_ipc::MACOS_SERVICE_ID - ), + ) + .into(), format!( "/Library/PrivilegedHelperTools/{}.bundle", clash_verge_service_ipc::MACOS_SERVICE_ID - ), + ) + .into(), #[cfg(not(feature = "verge-dev"))] - "/Library/LaunchDaemons/io.github.clashverge.helper.plist".to_owned(), + "/Library/LaunchDaemons/io.github.clashverge.helper.plist".into(), #[cfg(not(feature = "verge-dev"))] - "/Library/PrivilegedHelperTools/io.github.clashverge.helper".to_owned(), + "/Library/PrivilegedHelperTools/io.github.clashverge.helper".into(), ] } #[cfg(target_os = "macos")] fn macos_service_install_marker_exists() -> std::io::Result { for marker in macos_service_install_markers() { - if path_entry_exists_without_follow(Path::new(&marker))? { + if path_entry_exists_without_follow(Path::new(marker.as_str()))? { return Ok(true); } } @@ -335,7 +338,8 @@ pub(crate) fn trusted_service_evidence() -> Result { output.status ); } - Ok(String::from_utf8_lossy(&output.stdout).trim() != "not-found") + // from_utf8_lossy exists only on the std String type; the tristr alias shadows the bare name. + Ok(std::string::String::from_utf8_lossy(&output.stdout).trim() != "not-found") } #[cfg(target_os = "macos")] @@ -564,12 +568,12 @@ fn service_core_path(clash_core: &str, bin_ext: &str) -> Result { #[cfg(target_os = "macos")] fn escape_osascript_double_quoted_string(value: &str) -> String { - value.replace('\\', "\\\\").replace('"', "\\\"") + value.replace('\\', "\\\\").replace('"', "\\\"").into() } #[cfg(target_os = "macos")] fn shell_single_quote(value: &str) -> String { - format!("'{}'", value.replace('\'', r"'\''")) + format!("'{}'", value.replace('\'', r"'\''")).into() } fn packaged_service_tool_path(file_name: &str, packaged_path: impl FnOnce() -> Result) -> Result { @@ -641,7 +645,7 @@ fn uninstall_service() -> Result<()> { let status = if linux_running_as_root() { StdCommand::new(&uninstall_path).status()? } else { - let result = StdCommand::new(&elevator) + let result = StdCommand::new(elevator.as_str()) .arg("--disable-internal-agent") .arg(&uninstall_path) .status()?; @@ -697,7 +701,7 @@ fn uninstall_service() -> Result<()> { } let uninstall_path = macos_service_tool_path(&uninstall_path)?; - let uninstall_shell: String = uninstall_path.to_string_lossy().into_owned(); + let uninstall_shell: String = uninstall_path.to_string_lossy().into(); // clash_verge_i18n::sync_locale(Config::verge().await.latest_arc().language.as_deref()); @@ -843,7 +847,7 @@ pub(super) async fn stage_runtime_by_service(config_file: &Path) -> Result 0 { return Ok(StageRequest::Refused { code: response.code, - message: response.message, + message: response.message.into(), }); } response @@ -879,7 +883,7 @@ fn record_service_start_refusal( if store.state().mode == crate::core::manager::RunningMode::NotRunning && refusal.code != ServiceErrorCode::ProxyClearFailed as u16 { - store.observe(ServiceHealth::Unavailable(refusal.to_string())); + store.observe(ServiceHealth::Unavailable(refusal.to_string().into())); } refusal.into() } @@ -887,7 +891,7 @@ fn record_service_start_refusal( /// Sidecar stays blocked until a reinstall replaces the residual helper, so ask for repair. pub(super) fn record_residual_service(error: &anyhow::Error) { if let Some(residual) = error.downcast_ref::() { - RUN_STATE.observe(ServiceHealth::Unavailable(residual.to_string())); + RUN_STATE.observe(ServiceHealth::Unavailable(residual.to_string().into())); } } @@ -901,7 +905,7 @@ pub(super) async fn start_with_existing_service(config_file: &Path) -> Result<() let proposed_session_token = generate_service_session_token()?; let request = StartClashRequest { runtime, - proposed_session_token: proposed_session_token.clone(), + proposed_session_token: proposed_session_token.clone().into(), macos_proxy: None, }; @@ -939,8 +943,8 @@ pub(super) async fn start_with_existing_service(config_file: &Path) -> Result<() &RUN_STATE, ServiceStartRefusal { code: response.code, - core_path: request.runtime.core_path, - message: err_msg, + core_path: request.runtime.core_path.into(), + message: err_msg.into(), }, )); } @@ -952,7 +956,7 @@ pub(super) async fn start_with_existing_service(config_file: &Path) -> Result<() *ACTIVE_SERVICE_SESSION.lock() = Some(ActiveServiceSession { proof: OwnerSessionProof { generation: result.session.generation, - token: proposed_session_token, + token: proposed_session_token.into(), }, supports_runtime_staging: capabilities.runtime_staging, supports_runtime_file_read: capabilities.runtime_file_read, @@ -1012,7 +1016,10 @@ pub(super) async fn get_clash_logs_by_service() -> Result> { bail!(err_msg); } - Ok(response.data.unwrap_or_default()) + Ok(response + .data + .map(|logs| logs.into_iter().map(Into::into).collect()) + .unwrap_or_default()) } pub(crate) async fn get_clash_log_snapshot_by_service() -> Result { @@ -1030,7 +1037,8 @@ pub(crate) async fn get_clash_log_snapshot_by_service() -> Result { } let encoded = response.data.context("服务未返回核心日志快照")?; let content = decode_hex(&encoded).context("服务返回了无效的核心日志快照")?; - Ok(String::from_utf8_lossy_owned(content)) + // from_utf8_lossy_owned exists only on the std String type; the tristr alias shadows the bare name. + Ok(std::string::String::from_utf8_lossy_owned(content).into()) } fn decode_hex(encoded: &str) -> Result> { @@ -1344,7 +1352,7 @@ async fn read_chunk( } Ok(Some(match response.data.context("服务未返回运行时文件")? { RuntimeFileOutcome::Absent => (None, 0, None), - RuntimeFileOutcome::Chunk { hex, len, mtime_ns } => (Some(hex), len, mtime_ns), + RuntimeFileOutcome::Chunk { hex, len, mtime_ns } => (Some(hex.into()), len, mtime_ns), })) } @@ -1356,11 +1364,11 @@ async fn provider_load_times() -> Result { let proxies = proxies.map_err(|error| anyhow!("failed to query proxy providers: {error}"))?; let mut loaded = ProviderLoadTimes::new(); for (name, provider) in rules.providers { - loaded.insert(("rule-providers", name), epoch_nanos(&provider.updated_at)); + loaded.insert(("rule-providers", name.into()), epoch_nanos(&provider.updated_at)); } for (name, provider) in proxies.providers { loaded.insert( - ("proxy-providers", name), + ("proxy-providers", name.into()), provider.updated_at.as_deref().and_then(epoch_nanos), ); } @@ -1586,7 +1594,7 @@ const fn owner_recovery_policy(reason: OwnerRecoveryReason, is_macos: bool) -> O fn mark_service_unavailable_after_owner_loss(store: &RunStateStore, reason: OwnerRecoveryReason) { if matches!(reason, OwnerRecoveryReason::TransportFailure) { store.observe(ServiceHealth::Unavailable( - "service control IPC unavailable after sustained transport failure".to_owned(), + "service control IPC unavailable after sustained transport failure".into(), )); } } @@ -1847,7 +1855,7 @@ async fn wait_for_service_ipc() -> Result<()> { } Err(ReadyWaitError::Unreachable(error)) => { tracing::Span::current().record("outcome", "unreachable"); - RUN_STATE.observe(ServiceHealth::Unavailable(format!("{CONTEXT}: {error:#}"))); + RUN_STATE.observe(ServiceHealth::Unavailable(format!("{CONTEXT}: {error:#}").into())); Err(error).context(CONTEXT) } Err(ReadyWaitError::Rejected(error)) => { @@ -2662,7 +2670,7 @@ mod tests { let outcome = super::run_action_restoring_sidecar(&store, was_allowed, async { store.perform(PendingAction::Install).await?; // Simulate `wait_for_service_ipc` recording health before it fails. - store.observe(ServiceHealth::Unavailable("service never answered".to_owned())); + store.observe(ServiceHealth::Unavailable("service never answered".into())); bail!("service IPC did not become available") }) .await; diff --git a/src-tauri/src/core/sysopt.rs b/src-tauri/src/core/sysopt.rs index 9b3d158dc..9d791a44e 100644 --- a/src-tauri/src/core/sysopt.rs +++ b/src-tauri/src/core/sysopt.rs @@ -8,7 +8,6 @@ use anyhow::Result; use clash_verge_logging::{Type, logging}; use parking_lot::RwLock; use scopeguard::defer; -use smartstring::alias::String; use std::{ sync::{ Arc, @@ -18,6 +17,7 @@ use std::{ }; use sysproxy::{Autoproxy, GuardMonitor, GuardType, Sysproxy}; use tokio::sync::Mutex as TokioMutex; +use tristr::alias::String; #[derive(Clone, Copy, Debug, PartialEq, Eq)] enum ProxyApplyStep { @@ -677,8 +677,8 @@ mod tests { (calls.into_inner(), result.is_ok()) } - fn failure_text(result: anyhow::Result<()>) -> std::string::String { - result.map_or_else(|error| format!("{error:#}"), |()| "no failure".to_owned()) + fn failure_text(result: anyhow::Result<()>) -> String { + result.map_or_else(|error| format!("{error:#}").into(), |()| String::from("no failure")) } #[test] diff --git a/src-tauri/src/core/timer.rs b/src-tauri/src/core/timer.rs index c11bea60a..7c8abc733 100644 --- a/src-tauri/src/core/timer.rs +++ b/src-tauri/src/core/timer.rs @@ -8,7 +8,6 @@ use crate::{ use anyhow::Result; use clash_verge_logging::{Type, logging, logging_error}; use parking_lot::{Mutex, RwLock}; -use smartstring::alias::String; use std::{ collections::HashMap, sync::{ @@ -23,6 +22,7 @@ use tokio::{ }; use tokio_stream::StreamExt as _; use tokio_util::time::{DelayQueue, delay_queue::Key}; +use tristr::alias::String; enum TimerCommand { Apply(HashMap), @@ -502,7 +502,6 @@ mod tests { use super::{TaskSchedule, Timer, run_timer_profile_update_transition}; use crate::config::{PrfItem, PrfOption}; use parking_lot::Mutex; - use smartstring::alias::String; use std::collections::HashMap; use std::{ sync::{ @@ -515,6 +514,7 @@ mod tests { use tokio::sync::Barrier; use tokio_stream::StreamExt as _; use tokio_util::time::DelayQueue; + use tristr::alias::String; struct CancellationProbe { cancelled: Arc, diff --git a/src-tauri/src/core/tray/mod.rs b/src-tauri/src/core/tray/mod.rs index ae8c63c2c..cb63764dc 100644 --- a/src-tauri/src/core/tray/mod.rs +++ b/src-tauri/src/core/tray/mod.rs @@ -19,7 +19,6 @@ use tokio::fs; use super::handle; use anyhow::Result; -use smartstring::alias::String; use std::borrow::Cow; use std::collections::HashMap; use std::time::Duration; @@ -27,6 +26,7 @@ use tauri::{ AppHandle, Wry, menu::{CheckMenuItem, IsMenuItem, MenuEvent, MenuItem, PredefinedMenuItem, Submenu}, }; +use tristr::alias::String; mod menu_def; #[cfg(target_os = "macos")] diff --git a/src-tauri/src/core/updater.rs b/src-tauri/src/core/updater.rs index c017582eb..0eda7c0bf 100644 --- a/src-tauri/src/core/updater.rs +++ b/src-tauri/src/core/updater.rs @@ -11,6 +11,7 @@ use std::{ }; use tauri_plugin_updater::{Update, UpdaterExt as _}; use tokio_util::sync::CancellationToken; +use tristr::alias::String; pub struct SilentUpdater { update_ready: AtomicBool, @@ -55,8 +56,8 @@ impl SilentUpdater { .with_context(|| format!("failed to write update cache {}", bin_path.display()))?; let meta = UpdateCacheMeta { - version: version.to_string(), - downloaded_at: Utc::now().to_rfc3339(), + version: version.into(), + downloaded_at: Utc::now().to_rfc3339().into(), }; let meta_path = cache_dir.join("pending_update.json"); std::fs::write(&meta_path, serde_json::to_string_pretty(&meta)?) @@ -133,9 +134,8 @@ fn verify_signature(app_handle: &tauri::AppHandle, bytes: &[u8], signature: &str use base64::Engine as _; let decode = |value: &str| -> Result { - Ok(String::from_utf8( - base64::engine::general_purpose::STANDARD.decode(value)?, - )?) + // from_utf8 exists only on the std String type; the tristr alias shadows the bare name. + Ok(std::string::String::from_utf8(base64::engine::general_purpose::STANDARD.decode(value)?)?.into()) }; let config: tauri_plugin_updater::Config = serde_json::from_value( app_handle diff --git a/src-tauri/src/core/validate.rs b/src-tauri/src/core/validate.rs index b244127d1..4bd24589a 100644 --- a/src-tauri/src/core/validate.rs +++ b/src-tauri/src/core/validate.rs @@ -1,7 +1,6 @@ use anyhow::Result; use scopeguard::defer; use serde::Serialize; -use smartstring::alias::String; use std::{ fmt, hash::{DefaultHasher, Hash as _, Hasher as _}, @@ -10,6 +9,7 @@ use std::{ }; use tauri_plugin_shell::ShellExt as _; use tokio::fs; +use tristr::alias::String; use crate::config::Config; use crate::core::handle; @@ -114,14 +114,8 @@ impl fmt::Display for ValidationSkipReason { #[serde(tag = "status", rename_all = "camelCase")] pub enum ValidationOutcome { Valid, - Invalid { - kind: ValidationErrorKind, - #[specta(type = std::string::String)] - message: String, - }, - Skipped { - reason: ValidationSkipReason, - }, + Invalid { kind: ValidationErrorKind, message: String }, + Skipped { reason: ValidationSkipReason }, Busy, } diff --git a/src-tauri/src/enhance/chain.rs b/src-tauri/src/enhance/chain.rs index 797b02cd7..b14249e7a 100644 --- a/src-tauri/src/enhance/chain.rs +++ b/src-tauri/src/enhance/chain.rs @@ -4,8 +4,8 @@ use crate::{ utils::{dirs, help}, }; use serde_yaml_ng::Mapping; -use smartstring::alias::String; use tokio::fs; +use tristr::alias::String; #[derive(Debug, Clone)] pub struct ChainItem { diff --git a/src-tauri/src/enhance/field.rs b/src-tauri/src/enhance/field.rs index 04e73a786..41b4beb7e 100644 --- a/src-tauri/src/enhance/field.rs +++ b/src-tauri/src/enhance/field.rs @@ -1,5 +1,5 @@ use serde_yaml_ng::{Mapping, Value}; -use smartstring::alias::String; +use tristr::alias::String; pub const HANDLE_FIELDS: [&str; 12] = [ "mode", diff --git a/src-tauri/src/enhance/mod.rs b/src-tauri/src/enhance/mod.rs index ba0ba5708..02f0524d8 100644 --- a/src-tauri/src/enhance/mod.rs +++ b/src-tauri/src/enhance/mod.rs @@ -26,9 +26,9 @@ use anyhow::{Context as _, Result}; use clash_verge_logging::{Type, logging}; use parking_lot::Mutex; use serde_yaml_ng::{Mapping, Value}; -use smartstring::alias::String; use std::collections::{HashMap, HashSet}; use tokio::fs; +use tristr::alias::String; type ResultLog = Vec<(String, String)>; #[derive(Debug)] diff --git a/src-tauri/src/enhance/script.rs b/src-tauri/src/enhance/script.rs index 2aaee1121..46f938a7b 100644 --- a/src-tauri/src/enhance/script.rs +++ b/src-tauri/src/enhance/script.rs @@ -6,8 +6,8 @@ use boa_engine::{Context, JsString, JsValue, Source, js_string, native_function: use clash_verge_logging::{Type, logging}; use parking_lot::Mutex; use serde_yaml_ng::Mapping; -use smartstring::alias::String; use std::sync::Arc; +use tristr::alias::String; const MAX_OUTPUTS: usize = 1000; const MAX_OUTPUT_SIZE: usize = 1024 * 1024; // 1MB @@ -16,7 +16,7 @@ const MAX_LOOP_ITERATIONS: u64 = 10_000_000; const SCRIPT_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(5); type ScriptResult = (Mapping, Vec, Vec<(String, String)>); -type EvalOutcome = (Result, Vec<(String, String)>); +type EvalOutcome = (Result, Vec<(String, String)>); /// Never fails: exceptions are logged and the input config is returned unchanged. pub(super) async fn use_script(script: String, config: Mapping, name: String) -> ScriptResult { @@ -37,9 +37,9 @@ pub(super) async fn use_script(script: String, config: Mapping, name: String) -> let evaluated = AsyncHandler::spawn_blocking(move || eval_script(&script, &json, &name)); let (outcome, mut logs) = match tokio::time::timeout(SCRIPT_TIMEOUT, evaluated).await { Ok(Ok(evaluated)) => evaluated, - Ok(Err(join_err)) => (Err(format!("script task panicked: {join_err}")), vec![]), + Ok(Err(join_err)) => (Err(format!("script task panicked: {join_err}").into()), vec![]), Err(_elapsed) => ( - Err(format!("script execution timed out after {SCRIPT_TIMEOUT:?}")), + Err(format!("script execution timed out after {SCRIPT_TIMEOUT:?}").into()), vec![], ), }; @@ -59,7 +59,7 @@ pub(super) async fn use_script(script: String, config: Mapping, name: String) -> (result, changed_keys, logs) } Err(reason) => { - logs.push(("exception".into(), reason.into())); + logs.push(("exception".into(), reason)); (config, vec![], logs) } } @@ -79,7 +79,7 @@ fn eval_script(script: &str, config_json: &str, name: &String) -> EvalOutcome { let outputs_clone = Arc::clone(&outputs); let total_size_clone = Arc::clone(&total_size); - let bail = |reason: std::string::String| (Err(reason), outputs.lock().to_vec()); + let bail = |reason: String| (Err(reason), outputs.lock().to_vec()); let _ = context.register_global_builtin_callable("__verge_log__".into(), 2, unsafe { NativeFunction::from_closure(move |_: &JsValue, args: &[JsValue], context: &mut Context| { @@ -137,13 +137,13 @@ fn eval_script(script: &str, config_json: &str, name: &String) -> EvalOutcome { JsValue::from(JsString::from(config_json)), Attribute::all(), ) { - return bail(format!("failed to bind config for script: {err}")); + return bail(format!("failed to bind config for script: {err}").into()); } // 仅处理 name 参数中的特殊字符 let safe_name = escape_js_string_for_single_quote(name); if safe_name.len() > 1024 { - return bail("Name parameter too long".to_owned()); + return bail("Name parameter too long".into()); } let code = format!( @@ -157,19 +157,19 @@ fn eval_script(script: &str, config_json: &str, name: &String) -> EvalOutcome { let result = match context.eval(Source::from_bytes(code.as_str())) { Ok(result) if result.is_string() => result, - _ => return bail("main function should return object".to_owned()), + _ => return bail("main function should return object".into()), }; let result = match result.to_string(&mut context) { Ok(result) => result, - Err(e) => return bail(format!("Failed to convert JS result to string: {e}")), + Err(e) => return bail(format!("Failed to convert JS result to string: {e}").into()), }; let result = match result.to_std_string() { Ok(result) => result, - Err(_) => return bail("Failed to convert JS string to std string".to_owned()), + Err(_) => return bail("Failed to convert JS string to std string".into()), }; if result.len() > MAX_JSON_SIZE { - return bail("Script result exceeds maximum allowed size".to_owned()); + return bail("Script result exceeds maximum allowed size".into()); } match parse_json_safely(&result) { @@ -180,7 +180,7 @@ fn eval_script(script: &str, config_json: &str, name: &String) -> EvalOutcome { Type::Config, "Script execution error: {err:#}. Script name: {name}" ); - bail("Script execution failed".to_owned()) + bail("Script execution failed".into()) } } } diff --git a/src-tauri/src/enhance/seq.rs b/src-tauri/src/enhance/seq.rs index 826f2fc47..e09f7b39d 100644 --- a/src-tauri/src/enhance/seq.rs +++ b/src-tauri/src/enhance/seq.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; use serde_yaml_ng::{Mapping, Sequence, Value}; use std::collections::HashSet; +use tristr::alias::String; #[derive(Debug, Clone, Default, Serialize, Deserialize)] pub struct SeqMap { @@ -12,8 +13,8 @@ pub struct SeqMap { fn collect_proxy_names(seq: &Sequence) -> Vec { seq.iter() .filter_map(|item| match item { - Value::Mapping(map) => map.get("name").and_then(Value::as_str).map(str::to_owned), - Value::String(name) => Some(name.to_owned()), + Value::Mapping(map) => map.get("name").and_then(Value::as_str).map(Into::into), + Value::String(name) => Some(name.to_owned().into()), _ => None, }) .collect() @@ -58,10 +59,10 @@ pub fn use_seq(seq: SeqMap, mut config: Mapping, field: &str) -> Mapping { .into_iter() .filter(|item| { if let Value::String(s) = item { - !delete.contains(s) + !delete.iter().any(|deleted| deleted == s) } else if let Value::Mapping(m) = item { if let Some(Value::String(name)) = m.get("name") { - !delete.contains(name) + !delete.iter().any(|deleted| deleted == name) } else { true } @@ -99,7 +100,7 @@ pub fn use_seq(seq: SeqMap, mut config: Mapping, field: &str) -> Mapping { .into_iter() .filter(|p| { if let Value::String(name) = p { - !delete.contains(name) + !delete.iter().any(|deleted| deleted == name) } else { true } @@ -119,12 +120,12 @@ pub fn use_seq(seq: SeqMap, mut config: Mapping, field: &str) -> Mapping { let mut seen_proxy_names = HashSet::new(); for name in &added_proxy_names { if seen_proxy_names.insert(name.clone()) { - merged_proxies.push(Value::String(name.clone())); + merged_proxies.push(Value::String(name.clone().into())); } } for value in base_group_proxies { if let Value::String(name) = &value - && !seen_proxy_names.insert(name.to_owned()) + && !seen_proxy_names.insert(name.to_owned().into()) { continue; } @@ -180,7 +181,7 @@ proxy-groups: let seq = SeqMap { prepend: Sequence::new(), append: Sequence::new(), - delete: vec!["proxy1".to_string()], + delete: vec!["proxy1".into()], }; config = use_seq(seq, config, "proxies"); @@ -317,7 +318,7 @@ proxy-groups: "invalid" let seq = SeqMap { prepend: Sequence::new(), append: Sequence::new(), - delete: vec!["proxy1".to_string()], + delete: vec!["proxy1".into()], }; config = use_seq(seq, config, "proxies"); diff --git a/src-tauri/src/feat/backup.rs b/src-tauri/src/feat/backup.rs index 7c246d36a..b09f0ddfe 100644 --- a/src-tauri/src/feat/backup.rs +++ b/src-tauri/src/feat/backup.rs @@ -12,17 +12,14 @@ use chrono::Utc; use clash_verge_logging::{Type, logging, logging_error}; use reqwest_dav::list_cmd::ListFile; use serde::Serialize; -use smartstring::alias::String; use std::path::PathBuf; use tokio::fs; +use tristr::alias::String; #[derive(Debug, Serialize, specta::Type)] pub struct LocalBackupFile { - #[specta(type = std::string::String)] pub filename: String, - #[specta(type = std::string::String)] pub path: String, - #[specta(type = std::string::String)] pub last_modified: String, pub content_length: u64, } diff --git a/src-tauri/src/feat/clash.rs b/src-tauri/src/feat/clash.rs index 0f90dffc4..d36b072c1 100644 --- a/src-tauri/src/feat/clash.rs +++ b/src-tauri/src/feat/clash.rs @@ -10,8 +10,8 @@ use crate::{ use bytes::BytesMut; use clash_verge_logging::{Type, logging}; use serde_yaml_ng::{Mapping, Value}; -use smartstring::alias::String; use std::sync::{Arc, LazyLock}; +use tristr::alias::String; #[allow(clippy::expect_used)] static TLS_CONFIG: LazyLock> = LazyLock::new(|| { diff --git a/src-tauri/src/feat/core_upgrade.rs b/src-tauri/src/feat/core_upgrade.rs index dfb415aa0..352c3dbfa 100644 --- a/src-tauri/src/feat/core_upgrade.rs +++ b/src-tauri/src/feat/core_upgrade.rs @@ -21,6 +21,7 @@ use std::{ process::Command, sync::atomic::{AtomicU64, Ordering}, }; +use tristr::alias::String; const RELEASE_VERSION_URL: &str = "https://github.com/MetaCubeX/mihomo/releases/latest/download/version.txt"; const ALPHA_BASE_URL: &str = "https://github.com/MetaCubeX/mihomo/releases/download/Prerelease-Alpha"; @@ -50,7 +51,7 @@ pub async fn upgrade_core(force: bool) -> Result { // A core already broken by the in-place updater cannot report a version; upgrading is the repair. let installed = read_core_version(&target).unwrap_or_else(|error| { logging!(warn, Type::Core, "core upgrade: unreadable installed core: {error:#}"); - std::string::String::new() + String::new() }); let (proxy, latest) = resolve_latest_version(alpha).await?; @@ -172,14 +173,15 @@ fn managed_core_path(core: &str) -> Result { } /// Pins the package to the resolved version, so a release moving on mid-upgrade cannot 404. -fn package_url(alpha: bool, version: &str) -> Result { +fn package_url(alpha: bool, version: &str) -> Result { let asset = asset_base_name(alpha)?; let extension = if cfg!(windows) { "zip" } else { "gz" }; Ok(if alpha { format!("{ALPHA_BASE_URL}/{asset}-{version}.{extension}") } else { format!("{RELEASE_DOWNLOAD_URL}/{version}/{asset}-{version}.{extension}") - }) + } + .into()) } /// Mirrors the asset map in `scripts/prebuild.mjs` so an upgrade keeps the build variant @@ -218,11 +220,11 @@ fn asset_base_name(alpha: bool) -> Result<&'static str> { /// Returns the proxy that reached GitHub so the package download reuses it. #[tracing::instrument(skip_all, level = "debug", fields(alpha))] -async fn resolve_latest_version(alpha: bool) -> Result<(ProxyType, std::string::String)> { +async fn resolve_latest_version(alpha: bool) -> Result<(ProxyType, String)> { let url = if alpha { - format!("{ALPHA_BASE_URL}/version.txt") + String::from(format!("{ALPHA_BASE_URL}/version.txt")) } else { - RELEASE_VERSION_URL.to_owned() + String::from(RELEASE_VERSION_URL) }; let mut last_error = None; @@ -234,7 +236,7 @@ async fn resolve_latest_version(alpha: bool) -> Result<(ProxyType, std::string:: match attempt { Ok(response) if response.status().is_success() => { // An error page answered with 200 must not become a version, nor reach the URL. - let version = response.text().trim().to_owned(); + let version = String::from(response.text().trim()); if !is_usable_version(&version) { logging!( warn, @@ -446,7 +448,7 @@ fn new_command(program: impl AsRef) -> Command { } /// Parses the version out of `Mihomo Meta v1.19.30 darwin arm64 with go1.22.12 ...`. -fn read_core_version(path: &Path) -> Result { +fn read_core_version(path: &Path) -> Result { let output = new_command(path) .arg("-v") .output() @@ -456,11 +458,12 @@ fn read_core_version(path: &Path) -> Result { bail!("{} -v exited with {}", path.display(), output.status); } - let stdout = String::from_utf8_lossy(&output.stdout); + // from_utf8_lossy exists only on the std String type; the tristr alias shadows the bare name. + let stdout = std::string::String::from_utf8_lossy(&output.stdout); stdout .split_whitespace() .nth(2) - .map(str::to_owned) + .map(String::from) .with_context(|| format!("unexpected version output from {}: {stdout}", path.display())) } diff --git a/src-tauri/src/feat/dns.rs b/src-tauri/src/feat/dns.rs index a897b1b59..0bb2dd624 100644 --- a/src-tauri/src/feat/dns.rs +++ b/src-tauri/src/feat/dns.rs @@ -2,7 +2,7 @@ use crate::core::notify::{Refresh, announce}; use anyhow::{Result, ensure}; use clash_verge_draft::DraftTransaction; use serde::Serialize; -use smartstring::alias::String; +use tristr::alias::String; use crate::{ config::{ @@ -19,10 +19,7 @@ use crate::{ #[serde(tag = "status", rename_all = "snake_case")] pub enum DnsOverrideOutcome { Applied, - ConfirmationRequired { - #[specta(type = std::string::String)] - source: String, - }, + ConfirmationRequired { source: String }, } pub async fn set_dns_override( diff --git a/src-tauri/src/feat/icon.rs b/src-tauri/src/feat/icon.rs index 2ac973002..3d67c0530 100644 --- a/src-tauri/src/feat/icon.rs +++ b/src-tauri/src/feat/icon.rs @@ -1,20 +1,17 @@ use crate::{ - cmd::{CmdResult, StringifyErr as _}, + cmd::{CmdResult, CommandFailure, StringifyErr as _}, utils::dirs::{self, PathBufExec as _}, }; use clash_verge_logging::{Type, logging}; -use smartstring::alias::String; use std::path::{Component, Path, PathBuf}; use tokio::fs; use tokio::io::AsyncWriteExt as _; +use tristr::alias::String; #[derive(Debug, serde::Serialize, serde::Deserialize, specta::Type)] pub struct IconInfo { - #[specta(type = std::string::String)] name: String, - #[specta(type = std::string::String)] previous_t: String, - #[specta(type = std::string::String)] current_t: String, } @@ -43,7 +40,7 @@ fn ensure_icon_cache_target(icon_cache_dir: &Path, file_name: &str) -> CmdResult Ok(icon_path) } -fn normalized_text_prefix(content: &[u8]) -> std::string::String { +fn normalized_text_prefix(content: &[u8]) -> String { let content = content.strip_prefix(&[0xEF, 0xBB, 0xBF]).unwrap_or(content); let start = content .iter() @@ -51,7 +48,8 @@ fn normalized_text_prefix(content: &[u8]) -> std::string::String { .unwrap_or(content.len()); let end = content.len().min(start.saturating_add(2048)); let prefix = &content[start..end]; - std::string::String::from_utf8_lossy(prefix).to_ascii_lowercase() + // from_utf8_lossy exists only on the std String type; the tristr alias shadows the bare name. + std::string::String::from_utf8_lossy(prefix).to_ascii_lowercase().into() } fn looks_like_html(content: &[u8]) -> bool { @@ -95,7 +93,10 @@ pub async fn download_icon_cache(url: String, name: String) -> CmdResult if !is_supported_icon_content(&content) { let _ = temp_path.remove_if_exists().await; - return Err(format!("Downloaded content is not a valid image: {}", url.as_str()).into()); + return Err(CommandFailure::plain(format!( + "Downloaded content is not a valid image: {}", + url.as_str() + ))); } { @@ -164,7 +165,7 @@ pub async fn copy_icon_file(path: String, icon_info: IconInfo) -> CmdResult Ok(dest_path.to_string_lossy().into()), - Err(err) => Err(err.to_string().into()), + Err(err) => Err(CommandFailure::plain(err)), } } else { Err("file not found".into()) diff --git a/src-tauri/src/feat/profile.rs b/src-tauri/src/feat/profile.rs index 56faeb01d..c4eb8bc98 100644 --- a/src-tauri/src/feat/profile.rs +++ b/src-tauri/src/feat/profile.rs @@ -8,7 +8,7 @@ use crate::{ }; use anyhow::{Result, bail}; use clash_verge_logging::{Type, logging, logging_error}; -use smartstring::alias::String; +use tristr::alias::String; /// Toggle proxy profile pub async fn toggle_proxy_profile(profile_index: String) { @@ -231,7 +231,7 @@ pub async fn update_profile(uid: &String, option: Option<&PrfOption>, is_mannual }; let message = mask_err(&message); let message = if profile_persisted { - format!("订阅已保存,但未能确认已应用到内核: {message}") + format!("订阅已保存,但未能确认已应用到内核: {message}").into() } else { message }; diff --git a/src-tauri/src/ipc.rs b/src-tauri/src/ipc.rs index 334ef1ccb..03a444ef2 100644 --- a/src-tauri/src/ipc.rs +++ b/src-tauri/src/ipc.rs @@ -1,4 +1,5 @@ use crate::cmd; +use tristr::alias::String; pub fn builder() -> tauri_specta::Builder { tauri_specta::Builder::new() @@ -121,7 +122,7 @@ pub struct WebDavFile { impl From for WebDavFile { fn from(file: reqwest_dav::list_cmd::ListFile) -> Self { Self { - href: file.href, + href: file.href.into(), last_modified: file.last_modified, } } @@ -148,20 +149,20 @@ pub struct IpAddress { impl From for NetworkInterfaceView { fn from(interface: network_interface::NetworkInterface) -> Self { Self { - name: interface.name, + name: interface.name.into(), addr: interface .addr .into_iter() .map(|address| match address { network_interface::Addr::V4(address) => InterfaceAddress::V4(IpAddress { - ip: address.ip.to_string(), + ip: address.ip.to_string().into(), }), network_interface::Addr::V6(address) => InterfaceAddress::V6(IpAddress { - ip: address.ip.to_string(), + ip: address.ip.to_string().into(), }), }) .collect(), - mac_addr: interface.mac_addr, + mac_addr: interface.mac_addr.map(Into::into), } } } diff --git a/src-tauri/src/utils/help.rs b/src-tauri/src/utils/help.rs index acd95e4c2..8043a9079 100644 --- a/src-tauri/src/utils/help.rs +++ b/src-tauri/src/utils/help.rs @@ -11,6 +11,7 @@ use std::{ str::FromStr, }; use tokio::io::AsyncWriteExt as _; +use tristr::alias::String; pub async fn read_yaml(path: &Path) -> Result { if !tokio::fs::try_exists(path).await.unwrap_or(false) { @@ -34,12 +35,13 @@ pub async fn read_mapping(path: &Path) -> Result { let bytes = tokio::fs::read(path) .await .with_context(|| format!("failed to read the file \"{}\"", path.display()))?; - let yaml_str = - String::from_utf8(bytes).with_context(|| format!("failed to read the file \"{}\"", path.display()))?; + // from_utf8 exists only on the std String type; the tristr alias shadows the bare name. + let yaml_str = std::string::String::from_utf8(bytes) + .with_context(|| format!("failed to read the file \"{}\"", path.display()))?; let label = path.to_path_buf(); // Parsing a multi-megabyte profile is pure CPU; keep it off the async worker. - AsyncHandler::spawn_blocking(move || parse_mapping(yaml_str, label)) + AsyncHandler::spawn_blocking(move || parse_mapping(yaml_str.into(), label)) .await .map_err(|join| anyhow!("yaml parse task failed: {join}"))? } @@ -139,7 +141,7 @@ const ALPHABET: [char; 62] = [ pub fn get_uid(prefix: &str) -> String { let id = nanoid!(11, &ALPHABET); - format!("{prefix}{id}") + format!("{prefix}{id}").into() } pub fn parse_str(target: &str, key: &str) -> Option { @@ -171,7 +173,7 @@ pub fn mask_url(url: &str) -> String { let scheme_and_host = &path_part[..host_end]; let path = &path_part[host_end..]; // starts with '/' or empty - let mut result = scheme_and_host.to_owned(); + let mut result: String = scheme_and_host.into(); if !path.is_empty() { let masked: Vec<&str> = path @@ -186,8 +188,8 @@ pub fn mask_url(url: &str) -> String { let masked_query: Vec = query .split('&') .map(|param| match param.find('=') { - Some(eq) => format!("{}=***", ¶m[..eq]), - None => param.to_owned(), + Some(eq) => format!("{}=***", ¶m[..eq]).into(), + None => param.into(), }) .collect(); result.push_str(&masked_query.join("&")); @@ -198,7 +200,8 @@ pub fn mask_url(url: &str) -> String { /// Masks every HTTP(S) URL embedded in a log string while preserving surrounding text. pub fn mask_err(err: &str) -> String { - let mut result = String::with_capacity(err.len()); + // with_capacity exists only on the std String type; the tristr alias shadows the bare name. + let mut result = std::string::String::with_capacity(err.len()); let mut remaining = err; loop { @@ -224,7 +227,7 @@ pub fn mask_err(err: &str) -> String { remaining = &remaining[url_end..]; } - result + result.into() } pub fn get_last_part_and_decode(url: &str) -> Option { @@ -235,7 +238,8 @@ pub fn get_last_part_and_decode(url: &str) -> Option { Some( percent_encoding::percent_decode_str(last_segment) .decode_utf8_lossy() - .to_string(), + .to_string() + .into(), ) } @@ -277,15 +281,15 @@ pub fn linux_elevator() -> String { Ok(output) => { if !output.stdout.is_empty() { if let Ok(path) = std::str::from_utf8(&output.stdout) { - path.trim().to_string() + path.trim().into() } else { - "sudo".to_string() + "sudo".into() } } else { - "sudo".to_string() + "sudo".into() } } - Err(_) => "sudo".to_string(), + Err(_) => "sudo".into(), } } diff --git a/src-tauri/src/utils/linux/mime.rs b/src-tauri/src/utils/linux/mime.rs index bc98aa52e..95b3bccfc 100644 --- a/src-tauri/src/utils/linux/mime.rs +++ b/src-tauri/src/utils/linux/mime.rs @@ -11,6 +11,7 @@ use std::collections::HashMap; use std::env; use std::fs; use std::path::PathBuf; +use tristr::alias::String; pub fn ensure_mimeapps_entries(desktop_file: &str, schemes: &[&str]) -> Result<()> { let Some(path) = mimeapps_list_path() else { @@ -46,19 +47,19 @@ pub fn ensure_mimeapps_entries(desktop_file: &str, schemes: &[&str]) -> Result<( if trimmed.eq_ignore_ascii_case("[Default Applications]") { default_present = true; current_section = Some(SectionKind::DefaultApplications); - output_lines.push("[Default Applications]".to_string()); + output_lines.push("[Default Applications]".into()); continue; } else if trimmed.eq_ignore_ascii_case("[Added Associations]") { current_section = Some(SectionKind::AddedAssociations); - output_lines.push("[Added Associations]".to_string()); + output_lines.push("[Added Associations]".into()); continue; } } if current_section.is_some() { - section_buffer.push(line.to_string()); + section_buffer.push(line.into()); } else { - output_lines.push(line.to_string()); + output_lines.push(line.into()); } } @@ -78,9 +79,9 @@ pub fn ensure_mimeapps_entries(desktop_file: &str, schemes: &[&str]) -> Result<( if output_lines.last().is_some_and(|line| !line.is_empty()) { output_lines.push(String::new()); } - output_lines.push("[Default Applications]".to_string()); + output_lines.push("[Default Applications]".into()); for &scheme in schemes { - output_lines.push(format!("x-scheme-handler/{scheme}={desktop_file};")); + output_lines.push(format!("x-scheme-handler/{scheme}={desktop_file};").into()); } } @@ -171,7 +172,7 @@ fn flush_section( .split(';') .filter_map(|value| { let trimmed = value.trim(); - (!trimmed.is_empty()).then(|| trimmed.to_string()) + (!trimmed.is_empty()).then(|| trimmed.into()) }) .collect(); @@ -187,7 +188,7 @@ fn flush_section( .split(';') .filter_map(|value| { let trimmed = value.trim(); - (!trimmed.is_empty()).then(|| trimmed.to_string()) + (!trimmed.is_empty()).then(|| trimmed.into()) }) .collect(); @@ -203,10 +204,10 @@ fn flush_section( merged_values.insert(0, moved); } } else { - merged_values.insert(0, desktop_file.to_string()); + merged_values.insert(0, desktop_file.into()); } - let mut merged_line = format!("{existing_prefix}x-scheme-handler/{scheme}="); + let mut merged_line: String = format!("{existing_prefix}x-scheme-handler/{scheme}=").into(); merged_line.push_str(&merged_values.join(";")); merged_line.push(';'); @@ -222,16 +223,16 @@ fn flush_section( if let Some(pos) = values.iter().position(|value| value == desktop_file) { if pos != 0 { values.remove(pos); - values.insert(0, desktop_file.to_string()); + values.insert(0, desktop_file.into()); *changed = true; } } else { - values.insert(0, desktop_file.to_string()); + values.insert(0, desktop_file.into()); *changed = true; } let prefix = line.chars().take_while(|c| c.is_whitespace()).collect::(); - let mut new_line = format!("{prefix}x-scheme-handler/{scheme}="); + let mut new_line: String = format!("{prefix}x-scheme-handler/{scheme}=").into(); new_line.push_str(&values.join(";")); new_line.push(';'); @@ -253,7 +254,7 @@ fn flush_section( if ensure_all { for &scheme in schemes { if !seen.contains_key(scheme) { - processed.push(format!("x-scheme-handler/{scheme}={desktop_file};")); + processed.push(format!("x-scheme-handler/{scheme}={desktop_file};").into()); *changed = true; } } diff --git a/src-tauri/src/utils/network.rs b/src-tauri/src/utils/network.rs index d325fecbd..80ad228f8 100644 --- a/src-tauri/src/utils/network.rs +++ b/src-tauri/src/utils/network.rs @@ -5,10 +5,10 @@ use reqwest::{ Client, Proxy, StatusCode, header::{HeaderMap, HeaderValue, USER_AGENT}, }; -use smartstring::alias::String; use std::{sync::Arc, time::Duration}; use sysproxy::Sysproxy; use tauri::Url; +use tristr::alias::String; #[derive(Debug)] pub struct HttpResponse { @@ -57,7 +57,7 @@ impl NetworkManager { fn build_client( &self, - proxy_url: Option, + proxy_url: Option, default_headers: HeaderMap, accept_invalid_certs: bool, timeout_secs: Option, @@ -76,7 +76,7 @@ impl NetworkManager { // 设置代理 if let Some(proxy_str) = proxy_url { - let proxy = Proxy::all(proxy_str)?; + let proxy = Proxy::all(proxy_str.as_str())?; builder = builder.proxy(proxy); } else { builder = builder.no_proxy(); @@ -226,15 +226,15 @@ impl NetworkManager { accept_invalid_certs: bool, tls_root_mode: TlsRootMode, ) -> Result { - let proxy_url: Option = match proxy_type { + let proxy_url: Option = match proxy_type { ProxyType::None => None, ProxyType::Localhost => { let port = MixedPort::desired().await; - Some(format!("http://127.0.0.1:{port}")) + Some(format!("http://127.0.0.1:{port}").into()) } ProxyType::System => { if let Ok(p @ Sysproxy { enable: true, .. }) = Sysproxy::get_system_proxy() { - Some(format!("http://{}:{}", p.host, p.port)) + Some(format!("http://{}:{}", p.host, p.port).into()) } else { None } diff --git a/src-tauri/src/utils/resolve/scheme.rs b/src-tauri/src/utils/resolve/scheme.rs index 66c4212fe..f19205bc8 100644 --- a/src-tauri/src/utils/resolve/scheme.rs +++ b/src-tauri/src/utils/resolve/scheme.rs @@ -1,8 +1,8 @@ use crate::core::notify::NoticeStatus; use anyhow::Result; use percent_encoding::percent_decode_str; -use smartstring::alias::String; use tauri::Url; +use tristr::alias::String; use crate::{ config::{Config, PrfItem, profiles}, @@ -39,7 +39,7 @@ pub(super) async fn resolve_scheme(param: &str) -> Result<()> { Ok(()) } -fn extract_subscription_info(link_parsed: &Url) -> Option<(std::string::String, Option)> { +fn extract_subscription_info(link_parsed: &Url) -> Option<(String, Option)> { if !matches!(link_parsed.scheme(), "clash" | "clash-verge") { return None; } @@ -52,7 +52,7 @@ fn extract_subscription_info(link_parsed: &Url) -> Option<(std::string::String, Some((url, name)) } -fn extract_subscription_url(link_parsed: &Url) -> Option { +fn extract_subscription_url(link_parsed: &Url) -> Option { let query = link_parsed.query()?; let prefix = "url="; let pos = query.find(prefix)?; @@ -60,15 +60,15 @@ fn extract_subscription_url(link_parsed: &Url) -> Option { Some(decode_subscription_url(raw_url)) } -fn decode_subscription_url(raw_url: &str) -> std::string::String { +fn decode_subscription_url(raw_url: &str) -> String { // Avoid double-decoding nested subscription URLs; decode only when needed. if Url::parse(raw_url).is_ok() { - return raw_url.to_string(); + return String::from(raw_url); } - let mut candidate = raw_url.to_string(); + let mut candidate = String::from(raw_url); for _ in 0..2 { - let next = percent_decode_str(&candidate).decode_utf8_lossy().to_string(); + let next = String::from(percent_decode_str(&candidate).decode_utf8_lossy()); if next == candidate { break; } diff --git a/src-tauri/src/utils/schtasks.rs b/src-tauri/src/utils/schtasks.rs index 9f457a729..bc4c3db21 100644 --- a/src-tauri/src/utils/schtasks.rs +++ b/src-tauri/src/utils/schtasks.rs @@ -5,6 +5,7 @@ use std::fs; use std::os::windows::process::CommandExt as _; use std::path::{Path, PathBuf}; use std::process::{Command, Output}; +use tristr::alias::String; use windows::Win32::Globalization::{GetACP, GetOEMCP, MULTI_BYTE_TO_WIDE_CHAR_FLAGS, MultiByteToWideChar}; const CREATE_NO_WINDOW: u32 = 0x08000000; @@ -72,11 +73,11 @@ fn get_task_user_id() -> Result { if let Some(domain) = domain { let domain = domain.trim(); if !domain.is_empty() { - return Ok(format!("{domain}\\{username}")); + return Ok(format!("{domain}\\{username}").into()); } } - Ok(username.to_string()) + Ok(username.into()) } fn get_startup_dir() -> Result { @@ -118,7 +119,8 @@ fn task_xml_path(mode: TaskMode) -> Result { } fn xml_escape(value: &str) -> String { - let mut escaped = String::with_capacity(value.len()); + // Per-char pushes must stay on the std String: TriStr growth re-materializes per push. + let mut escaped = std::string::String::with_capacity(value.len()); for ch in value.chars() { match ch { '&' => escaped.push_str("&"), @@ -129,7 +131,7 @@ fn xml_escape(value: &str) -> String { _ => escaped.push(ch), } } - escaped + escaped.into() } fn build_task_xml(mode: TaskMode) -> Result { @@ -182,7 +184,8 @@ fn build_task_xml(mode: TaskMode) -> Result { user_id, mode.xml_run_level(), exe_path - )) + ) + .into()) } fn encode_utf16le_with_bom(content: &str) -> Vec { @@ -226,12 +229,13 @@ fn decode_with_code_page(bytes: &[u8], code_page: u32) -> Option { } wide.truncate(written as usize); - Some(String::from_utf16_lossy(&wide)) + // from_utf16_lossy exists only on the std String type; the tristr alias shadows the bare name. + Some(std::string::String::from_utf16_lossy(&wide).into()) } fn decode_console_output(bytes: &[u8]) -> String { if let Ok(text) = std::str::from_utf8(bytes) { - return text.to_string(); + return text.into(); } let oem = unsafe { GetOEMCP() }; @@ -244,7 +248,8 @@ fn decode_console_output(bytes: &[u8]) -> String { return text; } - String::from_utf8_lossy(bytes).to_string() + // from_utf8_lossy exists only on the std String type; the tristr alias shadows the bare name. + std::string::String::from_utf8_lossy(bytes).into() } fn output_message(output: &Output) -> String { @@ -254,10 +259,10 @@ fn output_message(output: &Output) -> String { let stderr = stderr.trim(); match (stdout.is_empty(), stderr.is_empty()) { - (true, true) => "unknown error".to_string(), - (false, true) => stdout.to_string(), - (true, false) => stderr.to_string(), - (false, false) => format!("{stdout} | {stderr}"), + (true, true) => "unknown error".into(), + (false, true) => stdout.into(), + (true, false) => stderr.into(), + (false, false) => format!("{stdout} | {stderr}").into(), } } diff --git a/src-tauri/src/utils/server.rs b/src-tauri/src/utils/server.rs index 42d097a86..1e3ddc46b 100644 --- a/src-tauri/src/utils/server.rs +++ b/src-tauri/src/utils/server.rs @@ -16,6 +16,7 @@ use std::sync::OnceLock; use std::sync::atomic::{AtomicBool, Ordering}; use std::time::Duration; use tokio::sync::oneshot; +use tristr::alias::String; use warp::Filter as _; const INSTANCE_TOKEN_HEADER: &str = "x-instance-token"; @@ -141,7 +142,7 @@ async fn notify_existing_instance(record: &InstanceRecord) -> bool { let request = client.get(format!("http://127.0.0.1:{}/commands/visible", record.port)); request - .header(INSTANCE_TOKEN_HEADER, &record.token) + .header(INSTANCE_TOKEN_HEADER, record.token.as_str()) .send() .await .is_ok_and(|response| response.status().is_success()) diff --git a/src-tauri/src/utils/yaml_emitter.rs b/src-tauri/src/utils/yaml_emitter.rs index 9cf287dc1..50f874064 100644 --- a/src-tauri/src/utils/yaml_emitter.rs +++ b/src-tauri/src/utils/yaml_emitter.rs @@ -1,5 +1,6 @@ use serde::Serialize; use serde_yaml_ng::Result; +use tristr::alias::String; pub fn to_mihomo_config_string(data: &T) -> Result { let yaml = serde_yaml_ng::to_string(data)?; @@ -7,7 +8,8 @@ pub fn to_mihomo_config_string(data: &T) -> Result { } fn quote_fake_ip_filter_wildcards(yaml: &str) -> String { - let mut result = String::with_capacity(yaml.len()); + // with_capacity exists only on the std String type; the tristr alias shadows the bare name. + let mut result = std::string::String::with_capacity(yaml.len()); let mut in_fake_ip_filter = false; let mut fake_ip_filter_indent = 0; @@ -39,7 +41,7 @@ fn quote_fake_ip_filter_wildcards(yaml: &str) -> String { result.push('\n'); } - result + result.into() } fn leading_spaces(line: &str) -> usize { @@ -50,12 +52,12 @@ fn quote_wildcard_item(item: &str) -> String { if !item.starts_with('\"') && !item.starts_with('\'') && contains_wildcard(item) { quote_string(item) } else { - item.to_string() + item.into() } } fn quote_string(s: &str) -> String { - "\'".to_string() + s.replace('\'', "''").as_str() + "\'" + ("\'".to_string() + s.replace('\'', "''").as_str() + "\'").into() } fn contains_wildcard(value: &str) -> bool {