feat(clippy): cognitive-complexity rule (#5215)

* feat(config): enhance configuration initialization and validation process

* refactor(profile): streamline profile update logic and enhance error handling

* refactor(config): simplify profile item checks and streamline update flag processing

* refactor(disney_plus): add cognitive complexity allowance for check_disney_plus function

* refactor(enhance): restructure configuration and profile item handling for improved clarity and maintainability

* refactor(tray): add cognitive complexity allowance for create_tray_menu function

* refactor(config): add cognitive complexity allowance for patch_config function

* refactor(profiles): simplify item removal logic by introducing take_item_file_by_uid helper function

* refactor(profile): add new validation logic for profile configuration syntax

* refactor(profiles): improve formatting and readability of take_item_file_by_uid function

* refactor(cargo): change cognitive complexity level from warn to deny

* refactor(cargo): ensure cognitive complexity is denied in Cargo.toml

* refactor(i18n): clean up imports and improve code readability
refactor(proxy): simplify system proxy toggle logic
refactor(service): remove unnecessary `as_str()` conversion in error handling
refactor(tray): modularize tray menu creation for better maintainability

* refactor(tray): update menu item text handling to use references for improved performance
This commit is contained in:
Tunglies
2025-10-27 20:55:51 +08:00
committed by GitHub
parent 6df1e137f3
commit c736796380
14 changed files with 1130 additions and 984 deletions

View File

@@ -1,8 +1,10 @@
use once_cell::sync::OnceCell;
use tauri::Emitter;
use tauri::tray::TrayIconBuilder;
use tauri_plugin_mihomo::models::Proxies;
#[cfg(target_os = "macos")]
pub mod speed_rate;
use crate::config::PrfSelected;
use crate::module::lightweight;
use crate::process::AsyncHandler;
use crate::utils::window_manager::WindowManager;
@@ -34,6 +36,56 @@ use tauri::{
// TODO: 是否需要将可变菜单抽离存储起来,后续直接更新对应菜单实例,无需重新创建菜单(待考虑)
type ProxyMenuItem = (Option<Submenu<Wry>>, Vec<Box<dyn IsMenuItem<Wry>>>);
struct MenuTexts {
dashboard: String,
rule_mode: String,
global_mode: String,
direct_mode: String,
profiles: String,
proxies: String,
system_proxy: String,
tun_mode: String,
close_all_connections: String,
lightweight_mode: String,
copy_env: String,
conf_dir: String,
core_dir: String,
logs_dir: String,
open_dir: String,
restart_clash: String,
restart_app: String,
verge_version: String,
more: String,
exit: String,
}
async fn fetch_menu_texts() -> MenuTexts {
MenuTexts {
dashboard: t("Dashboard").await,
rule_mode: t("Rule Mode").await,
global_mode: t("Global Mode").await,
direct_mode: t("Direct Mode").await,
profiles: t("Profiles").await,
proxies: t("Proxies").await,
system_proxy: t("System Proxy").await,
tun_mode: t("TUN Mode").await,
close_all_connections: t("Close All Connections").await,
lightweight_mode: t("LightWeight Mode").await,
copy_env: t("Copy Env").await,
conf_dir: t("Conf Dir").await,
core_dir: t("Core Dir").await,
logs_dir: t("Logs Dir").await,
open_dir: t("Open Dir").await,
restart_clash: t("Restart Clash Core").await,
restart_app: t("Restart App").await,
verge_version: t("Verge Version").await,
more: t("More").await,
exit: t("Exit").await,
}
}
#[derive(Clone)]
struct TrayState {}
@@ -606,70 +658,8 @@ impl Tray {
}
}
async fn create_tray_menu(
app_handle: &AppHandle,
mode: Option<&str>,
system_proxy_enabled: bool,
tun_mode_enabled: bool,
profile_uid_and_name: Vec<(String, String)>,
is_lightweight_mode: bool,
) -> Result<tauri::menu::Menu<Wry>> {
let mode = mode.unwrap_or("");
// 获取当前配置文件的选中代理组信息
let current_profile_selected = {
let profiles_config = Config::profiles().await;
let profiles_ref = profiles_config.latest_ref();
profiles_ref
.get_current()
.and_then(|uid| profiles_ref.get_item(&uid).ok())
.and_then(|profile| profile.selected.clone())
.unwrap_or_default()
};
let proxy_nodes_data = handle::Handle::mihomo().await.get_proxies().await;
let runtime_proxy_groups_order = cmd::get_runtime_config()
.await
.map_err(|e| {
logging!(
error,
Type::Cmd,
"Failed to fetch runtime proxy groups for tray menu: {e}"
);
})
.ok()
.flatten()
.map(|config| {
config
.get("proxy-groups")
.and_then(|groups| groups.as_sequence())
.map(|groups| {
groups
.iter()
.filter_map(|group| group.get("name"))
.filter_map(|name| name.as_str())
.map(|name| name.into())
.collect::<Vec<String>>()
})
.unwrap_or_default()
});
let proxy_group_order_map = runtime_proxy_groups_order.as_ref().map(|group_names| {
group_names
.iter()
.enumerate()
.map(|(index, name)| (name.clone(), index))
.collect::<HashMap<String, usize>>()
});
let verge_settings = Config::verge().await.latest_ref().clone();
let show_proxy_groups_inline = verge_settings.tray_inline_proxy_groups.unwrap_or(false);
let version = env!("CARGO_PKG_VERSION");
let hotkeys = verge_settings
.hotkeys
fn create_hotkeys(hotkeys: &Option<Vec<String>>) -> HashMap<String, String> {
hotkeys
.as_ref()
.map(|h| {
h.iter()
@@ -689,35 +679,45 @@ async fn create_tray_menu(
})
.collect::<std::collections::HashMap<String, String>>()
})
.unwrap_or_default();
.unwrap_or_default()
}
let profile_menu_items: Vec<CheckMenuItem<Wry>> = {
let futures = profile_uid_and_name
.iter()
.map(|(profile_uid, profile_name)| {
let app_handle = app_handle.clone();
let profile_uid = profile_uid.clone();
let profile_name = profile_name.clone();
async move {
let is_current_profile = Config::profiles()
.await
.data_mut()
.is_current_profile_index(profile_uid.clone());
CheckMenuItem::with_id(
&app_handle,
format!("profiles_{profile_uid}"),
t(&profile_name).await,
true,
is_current_profile,
None::<&str>,
)
}
});
let results = join_all(futures).await;
results.into_iter().collect::<Result<Vec<_>, _>>()?
};
async fn create_profile_menu_item(
app_handle: &AppHandle,
profile_uid_and_name: Vec<(String, String)>,
) -> Result<Vec<CheckMenuItem<Wry>>> {
let futures = profile_uid_and_name
.iter()
.map(|(profile_uid, profile_name)| {
let app_handle = app_handle.clone();
let profile_uid = profile_uid.clone();
let profile_name = profile_name.clone();
async move {
let is_current_profile = Config::profiles()
.await
.latest_ref()
.is_current_profile_index(profile_uid.clone());
CheckMenuItem::with_id(
&app_handle,
format!("profiles_{profile_uid}"),
t(&profile_name).await,
true,
is_current_profile,
None::<&str>,
)
}
});
let results = join_all(futures).await;
Ok(results.into_iter().collect::<Result<Vec<_>, _>>()?)
}
// 代理组子菜单
fn create_subcreate_proxy_menu_item(
app_handle: &AppHandle,
proxy_mode: &str,
current_profile_selected: &[PrfSelected],
proxy_group_order_map: Option<HashMap<String, usize>>,
proxy_nodes_data: Result<Proxies>,
) -> Result<Vec<Submenu<Wry>>> {
let proxy_submenus: Vec<Submenu<Wry>> = {
let mut submenus: Vec<(String, usize, Submenu<Wry>)> = Vec::new();
@@ -725,7 +725,7 @@ async fn create_tray_menu(
if let Ok(proxy_nodes_data) = proxy_nodes_data {
for (group_name, group_data) in proxy_nodes_data.proxies.iter() {
// Filter groups based on mode
let should_show = match mode {
let should_show = match proxy_mode {
"global" => group_name == "GLOBAL",
_ => group_name != "GLOBAL",
} &&
@@ -781,7 +781,7 @@ async fn create_tray_menu(
}
// Determine if group is active
let is_group_active = match mode {
let is_group_active = match proxy_mode {
"global" => group_name == "GLOBAL" && !now_proxy.is_empty(),
"direct" => false,
_ => {
@@ -837,28 +837,117 @@ async fn create_tray_menu(
.map(|(_, _, submenu)| submenu)
.collect()
};
Ok(proxy_submenus)
}
fn create_proxy_menu_item(
app_handle: &AppHandle,
show_proxy_groups_inline: bool,
proxy_submenus: Vec<Submenu<Wry>>,
proxies_text: &String,
) -> Result<ProxyMenuItem> {
// 创建代理主菜单
let (proxies_submenu, inline_proxy_items) = if show_proxy_groups_inline {
(
None,
proxy_submenus
.into_iter()
.map(|submenu| Box::new(submenu) as Box<dyn IsMenuItem<Wry>>)
.collect(),
)
} else if !proxy_submenus.is_empty() {
let proxy_submenu_refs: Vec<&dyn IsMenuItem<Wry>> = proxy_submenus
.iter()
.map(|submenu| submenu as &dyn IsMenuItem<Wry>)
.collect();
(
Some(Submenu::with_id_and_items(
app_handle,
"proxies",
proxies_text,
true,
&proxy_submenu_refs,
)?),
Vec::new(),
)
} else {
(None, Vec::new())
};
Ok((proxies_submenu, inline_proxy_items))
}
async fn create_tray_menu(
app_handle: &AppHandle,
mode: Option<&str>,
system_proxy_enabled: bool,
tun_mode_enabled: bool,
profile_uid_and_name: Vec<(String, String)>,
is_lightweight_mode: bool,
) -> Result<tauri::menu::Menu<Wry>> {
let current_proxy_mode = mode.unwrap_or("");
// 获取当前配置文件的选中代理组信息
let current_profile_selected = {
let profiles_config = Config::profiles().await;
let profiles_ref = profiles_config.latest_ref();
profiles_ref
.get_current()
.and_then(|uid| profiles_ref.get_item(&uid).ok())
.and_then(|profile| profile.selected.clone())
.unwrap_or_default()
};
let proxy_nodes_data = handle::Handle::mihomo().await.get_proxies().await;
let runtime_proxy_groups_order = cmd::get_runtime_config()
.await
.map_err(|e| {
logging!(
error,
Type::Cmd,
"Failed to fetch runtime proxy groups for tray menu: {e}"
);
})
.ok()
.flatten()
.map(|config| {
config
.get("proxy-groups")
.and_then(|groups| groups.as_sequence())
.map(|groups| {
groups
.iter()
.filter_map(|group| group.get("name"))
.filter_map(|name| name.as_str())
.map(|name| name.into())
.collect::<Vec<String>>()
})
.unwrap_or_default()
});
let proxy_group_order_map: Option<
HashMap<smartstring::SmartString<smartstring::LazyCompact>, usize>,
> = runtime_proxy_groups_order.as_ref().map(|group_names| {
group_names
.iter()
.enumerate()
.map(|(index, name)| (name.clone(), index))
.collect::<HashMap<String, usize>>()
});
let verge_settings = Config::verge().await.latest_ref().clone();
let show_proxy_groups_inline = verge_settings.tray_inline_proxy_groups.unwrap_or(false);
let version = env!("CARGO_PKG_VERSION");
let hotkeys = create_hotkeys(&verge_settings.hotkeys);
let profile_menu_items: Vec<CheckMenuItem<Wry>> =
create_profile_menu_item(app_handle, profile_uid_and_name).await?;
// Pre-fetch all localized strings
let dashboard_text = t("Dashboard").await;
let rule_mode_text = t("Rule Mode").await;
let global_mode_text = t("Global Mode").await;
let direct_mode_text = t("Direct Mode").await;
let profiles_text = t("Profiles").await;
let proxies_text = t("Proxies").await;
let system_proxy_text = t("System Proxy").await;
let tun_mode_text = t("TUN Mode").await;
let close_all_connections_text = t("Close All Connections").await;
let lightweight_mode_text = t("LightWeight Mode").await;
let copy_env_text = t("Copy Env").await;
let conf_dir_text = t("Conf Dir").await;
let core_dir_text = t("Core Dir").await;
let logs_dir_text = t("Logs Dir").await;
let open_dir_text = t("Open Dir").await;
let restart_clash_text = t("Restart Clash Core").await;
let restart_app_text = t("Restart App").await;
let verge_version_text = t("Verge Version").await;
let more_text = t("More").await;
let exit_text = t("Exit").await;
let texts = &fetch_menu_texts().await;
// Convert to references only when needed
let profile_menu_items_refs: Vec<&dyn IsMenuItem<Wry>> = profile_menu_items
@@ -869,7 +958,7 @@ async fn create_tray_menu(
let open_window = &MenuItem::with_id(
app_handle,
"open_window",
dashboard_text,
&texts.dashboard,
true,
hotkeys.get("open_or_close_dashboard").map(|s| s.as_str()),
)?;
@@ -877,72 +966,57 @@ async fn create_tray_menu(
let rule_mode = &CheckMenuItem::with_id(
app_handle,
"rule_mode",
rule_mode_text,
&texts.rule_mode,
true,
mode == "rule",
current_proxy_mode == "rule",
hotkeys.get("clash_mode_rule").map(|s| s.as_str()),
)?;
let global_mode = &CheckMenuItem::with_id(
app_handle,
"global_mode",
global_mode_text,
&texts.global_mode,
true,
mode == "global",
current_proxy_mode == "global",
hotkeys.get("clash_mode_global").map(|s| s.as_str()),
)?;
let direct_mode = &CheckMenuItem::with_id(
app_handle,
"direct_mode",
direct_mode_text,
&texts.direct_mode,
true,
mode == "direct",
current_proxy_mode == "direct",
hotkeys.get("clash_mode_direct").map(|s| s.as_str()),
)?;
let profiles = &Submenu::with_id_and_items(
app_handle,
"profiles",
profiles_text,
&texts.profiles,
true,
&profile_menu_items_refs,
)?;
// 创建代理主菜单
let (proxies_submenu, inline_proxy_items): (Option<Submenu<Wry>>, Vec<&dyn IsMenuItem<Wry>>) =
if show_proxy_groups_inline {
(
None,
proxy_submenus
.iter()
.map(|submenu| submenu as &dyn IsMenuItem<Wry>)
.collect(),
)
} else if !proxy_submenus.is_empty() {
let proxy_submenu_refs: Vec<&dyn IsMenuItem<Wry>> = proxy_submenus
.iter()
.map(|submenu| submenu as &dyn IsMenuItem<Wry>)
.collect();
let proxy_sub_menus = create_subcreate_proxy_menu_item(
app_handle,
current_proxy_mode,
&current_profile_selected,
proxy_group_order_map,
proxy_nodes_data.map_err(anyhow::Error::from),
)?;
(
Some(Submenu::with_id_and_items(
app_handle,
"proxies",
proxies_text,
true,
&proxy_submenu_refs,
)?),
Vec::new(),
)
} else {
(None, Vec::new())
};
let (proxies_menu, inline_proxy_items) = create_proxy_menu_item(
app_handle,
show_proxy_groups_inline,
proxy_sub_menus,
&texts.proxies,
)?;
let system_proxy = &CheckMenuItem::with_id(
app_handle,
"system_proxy",
system_proxy_text,
&texts.system_proxy,
true,
system_proxy_enabled,
hotkeys.get("toggle_system_proxy").map(|s| s.as_str()),
@@ -951,7 +1025,7 @@ async fn create_tray_menu(
let tun_mode = &CheckMenuItem::with_id(
app_handle,
"tun_mode",
tun_mode_text,
&texts.tun_mode,
true,
tun_mode_enabled,
hotkeys.get("toggle_tun_mode").map(|s| s.as_str()),
@@ -960,7 +1034,7 @@ async fn create_tray_menu(
let close_all_connections = &MenuItem::with_id(
app_handle,
"close_all_connections",
close_all_connections_text,
&texts.close_all_connections,
true,
None::<&str>,
)?;
@@ -968,18 +1042,18 @@ async fn create_tray_menu(
let lighteweight_mode = &CheckMenuItem::with_id(
app_handle,
"entry_lightweight_mode",
lightweight_mode_text,
&texts.lightweight_mode,
true,
is_lightweight_mode,
hotkeys.get("entry_lightweight_mode").map(|s| s.as_str()),
)?;
let copy_env = &MenuItem::with_id(app_handle, "copy_env", copy_env_text, true, None::<&str>)?;
let copy_env = &MenuItem::with_id(app_handle, "copy_env", &texts.copy_env, true, None::<&str>)?;
let open_app_dir = &MenuItem::with_id(
app_handle,
"open_app_dir",
conf_dir_text,
&texts.conf_dir,
true,
None::<&str>,
)?;
@@ -987,7 +1061,7 @@ async fn create_tray_menu(
let open_core_dir = &MenuItem::with_id(
app_handle,
"open_core_dir",
core_dir_text,
&texts.core_dir,
true,
None::<&str>,
)?;
@@ -995,7 +1069,7 @@ async fn create_tray_menu(
let open_logs_dir = &MenuItem::with_id(
app_handle,
"open_logs_dir",
logs_dir_text,
&texts.logs_dir,
true,
None::<&str>,
)?;
@@ -1003,7 +1077,7 @@ async fn create_tray_menu(
let open_dir = &Submenu::with_id_and_items(
app_handle,
"open_dir",
open_dir_text,
&texts.open_dir,
true,
&[open_app_dir, open_core_dir, open_logs_dir],
)?;
@@ -1011,7 +1085,7 @@ async fn create_tray_menu(
let restart_clash = &MenuItem::with_id(
app_handle,
"restart_clash",
restart_clash_text,
&texts.restart_clash,
true,
None::<&str>,
)?;
@@ -1019,7 +1093,7 @@ async fn create_tray_menu(
let restart_app = &MenuItem::with_id(
app_handle,
"restart_app",
restart_app_text,
&texts.restart_app,
true,
None::<&str>,
)?;
@@ -1027,7 +1101,7 @@ async fn create_tray_menu(
let app_version = &MenuItem::with_id(
app_handle,
"app_version",
format!("{} {version}", verge_version_text),
format!("{} {version}", &texts.verge_version),
true,
None::<&str>,
)?;
@@ -1035,7 +1109,7 @@ async fn create_tray_menu(
let more = &Submenu::with_id_and_items(
app_handle,
"more",
more_text,
&texts.more,
true,
&[
close_all_connections,
@@ -1045,7 +1119,13 @@ async fn create_tray_menu(
],
)?;
let quit = &MenuItem::with_id(app_handle, "quit", exit_text, true, Some("CmdOrControl+Q"))?;
let quit = &MenuItem::with_id(
app_handle,
"quit",
&texts.exit,
true,
Some("CmdOrControl+Q"),
)?;
let separator = &PredefinedMenuItem::separator(app_handle)?;
@@ -1063,9 +1143,9 @@ async fn create_tray_menu(
// 如果有代理节点,添加代理节点菜单
if show_proxy_groups_inline {
if !inline_proxy_items.is_empty() {
menu_items.extend_from_slice(&inline_proxy_items);
menu_items.extend(inline_proxy_items.iter().map(|item| item.as_ref()));
}
} else if let Some(ref proxies_menu) = proxies_submenu {
} else if let Some(ref proxies_menu) = proxies_menu {
menu_items.push(proxies_menu);
}