refactor: replace unwrap_or with unwrap_or_else for improved error handling (#5163)

In Rust, the `or` and `or_else` methods have distinct behavioral differences. The `or` method always eagerly evaluates its argument and executes any associated function calls. This can lead to unnecessary performance costs—especially in expensive operations like string processing or file handling—and may even trigger unintended side effects.

In contrast, `or_else` evaluates its closure lazily, only when necessary. Introducing a Clippy lint to disallow `or` sacrifices a bit of code simplicity but ensures predictable behavior and enforces lazy evaluation for better performance.
This commit is contained in:
Tunglies
2025-10-22 17:33:55 +08:00
committed by GitHub
parent a05ea64bcd
commit 2d2167e048
18 changed files with 70 additions and 52 deletions

View File

@@ -221,3 +221,5 @@ needless_raw_string_hashes = "deny" # Too many in existing code
#pedantic = { level = "allow", priority = -1 }
#nursery = { level = "allow", priority = -1 }
#restriction = { level = "allow", priority = -1 }
or_fun_call = "deny"

View File

@@ -1,6 +1,6 @@
use super::CmdResult;
use crate::{cmd::StringifyErr, config::*, core::CoreManager, log_err};
use anyhow::Context;
use anyhow::{Context, anyhow};
use serde_yaml_ng::Mapping;
use smartstring::alias::String;
use std::collections::HashMap;
@@ -19,7 +19,7 @@ pub async fn get_runtime_yaml() -> CmdResult<String> {
let config = runtime.config.as_ref();
config
.ok_or(anyhow::anyhow!("failed to parse config to yaml file"))
.ok_or_else(|| anyhow!("failed to parse config to yaml file"))
.and_then(|config| {
serde_yaml_ng::to_string(config)
.context("failed to convert config to yaml")
@@ -48,7 +48,7 @@ pub async fn get_runtime_proxy_chain_config(proxy_chain_exit_node: String) -> Cm
let config = runtime
.config
.as_ref()
.ok_or(anyhow::anyhow!("failed to parse config to yaml file"))
.ok_or_else(|| anyhow!("failed to parse config to yaml file"))
.stringify_err()?;
if let Some(serde_yaml_ng::Value::Sequence(proxies)) = config.get("proxies") {

View File

@@ -287,7 +287,7 @@ impl IClashTemp {
}
None => None,
})
.unwrap_or("127.0.0.1:9097".into())
.unwrap_or_else(|| "127.0.0.1:9097".into())
}
pub fn guard_external_controller(config: &Mapping) -> String {

View File

@@ -140,7 +140,7 @@ impl Config {
.latest_ref()
.config
.as_ref()
.ok_or(anyhow!("failed to get runtime config"))?
.ok_or_else(|| anyhow!("failed to get runtime config"))?
.clone();
drop(runtime); // 显式释放锁

View File

@@ -164,8 +164,8 @@ impl PrfItem {
PrfItem::from_url(url, name, desc, item.option).await
}
"local" => {
let name = item.name.unwrap_or("Local File".into());
let desc = item.desc.unwrap_or("".into());
let name = item.name.unwrap_or_else(|| "Local File".into());
let desc = item.desc.unwrap_or_else(|| "".into());
PrfItem::from_local(name, desc, file_data, item.option).await
}
typ => bail!("invalid profile item type \"{typ}\""),
@@ -235,7 +235,7 @@ impl PrfItem {
}),
home: None,
updated: Some(chrono::Local::now().timestamp() as usize),
file_data: Some(file_data.unwrap_or(tmpl::ITEM_LOCAL.into())),
file_data: Some(file_data.unwrap_or_else(|| tmpl::ITEM_LOCAL.into())),
})
}
@@ -331,7 +331,8 @@ impl PrfItem {
}
}
None => Some(
crate::utils::help::get_last_part_and_decode(url).unwrap_or("Remote File".into()),
crate::utils::help::get_last_part_and_decode(url)
.unwrap_or_else(|| "Remote File".into()),
),
};
let update_interval = match update_interval {
@@ -355,7 +356,7 @@ impl PrfItem {
let uid = help::get_uid("R").into();
let file = format!("{uid}.yaml").into();
let name = name.unwrap_or(filename.unwrap_or("Remote File".into()).into());
let name = name.unwrap_or_else(|| filename.unwrap_or_else(|| "Remote File".into()).into());
let data = resp.text_with_charset()?;
// process the charset "UTF-8 with BOM"

View File

@@ -241,8 +241,11 @@ impl IProfiles {
// move the field value after save
if let Some(file_data) = item.file_data.take() {
let file = each.file.take();
let file = file
.unwrap_or(item.file.take().unwrap_or(format!("{}.yaml", &uid).into()));
let file = file.unwrap_or_else(|| {
item.file
.take()
.unwrap_or_else(|| format!("{}.yaml", &uid).into())
});
// the file must exists
each.file = Some(file.clone());

View File

@@ -32,11 +32,11 @@ impl IRuntime {
let patch_tun = patch.get("tun");
if patch_tun.is_some() {
let tun = config.get("tun");
let mut tun: Mapping = tun.map_or(Mapping::new(), |val| {
val.as_mapping().cloned().unwrap_or(Mapping::new())
let mut tun: Mapping = tun.map_or_else(Mapping::new, |val| {
val.as_mapping().cloned().unwrap_or_else(Mapping::new)
});
let patch_tun = patch_tun.map_or(Mapping::new(), |val| {
val.as_mapping().cloned().unwrap_or(Mapping::new())
let patch_tun = patch_tun.map_or_else(Mapping::new, |val| {
val.as_mapping().cloned().unwrap_or_else(Mapping::new)
});
use_keys(&patch_tun).into_iter().for_each(|key| {
if let Some(value) = patch_tun.get(key.as_str()) {

View File

@@ -330,7 +330,7 @@ async fn check_service_version() -> Result<String> {
return Err(anyhow::anyhow!(err_msg));
}
let version = response.data.unwrap_or("unknown".into());
let version = response.data.unwrap_or_else(|| "unknown".into());
Ok(version)
};

View File

@@ -84,7 +84,10 @@ impl TrayState {
}
#[cfg(target_os = "macos")]
{
let tray_icon_colorful = verge.tray_icon.unwrap_or("monochrome".into());
let tray_icon_colorful = verge
.tray_icon
.clone()
.unwrap_or_else(|| "monochrome".into());
if tray_icon_colorful == "monochrome" {
(
false,
@@ -118,7 +121,10 @@ impl TrayState {
}
#[cfg(target_os = "macos")]
{
let tray_icon_colorful = verge.tray_icon.clone().unwrap_or("monochrome".into());
let tray_icon_colorful = verge
.tray_icon
.clone()
.unwrap_or_else(|| "monochrome".into());
if tray_icon_colorful == "monochrome" {
(
false,
@@ -152,7 +158,10 @@ impl TrayState {
}
#[cfg(target_os = "macos")]
{
let tray_icon_colorful = verge.tray_icon.clone().unwrap_or("monochrome".into());
let tray_icon_colorful = verge
.tray_icon
.clone()
.unwrap_or_else(|| "monochrome".into());
if tray_icon_colorful == "monochrome" {
(
false,
@@ -219,7 +228,7 @@ impl Tray {
let app_handle = handle::Handle::app_handle();
let tray_event = { Config::verge().await.latest_ref().tray_event.clone() };
let tray_event = tray_event.unwrap_or("main_window".into());
let tray_event = tray_event.unwrap_or_else(|| "main_window".into());
let tray = app_handle
.tray_by_id("main")
.ok_or_else(|| anyhow::anyhow!("Failed to get main tray"))?;
@@ -350,7 +359,10 @@ impl Tray {
(false, false) => TrayState::get_common_tray_icon().await,
};
let colorful = verge.tray_icon.clone().unwrap_or("monochrome".into());
let colorful = verge
.tray_icon
.clone()
.unwrap_or_else(|| "monochrome".into());
let is_colorful = colorful == "colorful";
let _ = tray.set_icon(Some(tauri::image::Image::from_bytes(&icon_bytes)?));
@@ -448,9 +460,10 @@ impl Tray {
let profile_text = t("Profile").await;
let v = env!("CARGO_PKG_VERSION");
let reassembled_version = v.split_once('+').map_or(v.into(), |(main, rest)| {
format!("{main}+{}", rest.split('.').next().unwrap_or(""))
});
let reassembled_version = v.split_once('+').map_or_else(
|| v.into(),
|(main, rest)| format!("{main}+{}", rest.split('.').next().unwrap_or("")),
);
let tooltip = format!(
"Clash Verge {}\n{}: {}\n{}: {}\n{}: {}",
@@ -505,7 +518,7 @@ impl Tray {
#[cfg(any(target_os = "macos", target_os = "windows"))]
let show_menu_on_left_click = {
let tray_event = { Config::verge().await.latest_ref().tray_event.clone() };
let tray_event: String = tray_event.unwrap_or("main_window".into());
let tray_event: String = tray_event.unwrap_or_else(|| "main_window".into());
tray_event.as_str() == "tray_menu"
};
@@ -526,7 +539,7 @@ impl Tray {
tray.on_tray_icon_event(|_app_handle, event| {
AsyncHandler::spawn(|| async move {
let tray_event = { Config::verge().await.latest_ref().tray_event.clone() };
let tray_event: String = tray_event.unwrap_or("main_window".into());
let tray_event: String = tray_event.unwrap_or_else(|| "main_window".into());
log::debug!(target: "app", "tray event: {tray_event:?}");
if let TrayIconEvent::Click {

View File

@@ -73,7 +73,7 @@ impl AsyncChainItemFrom for Option<ChainItem> {
async fn from_async(item: &PrfItem) -> Option<ChainItem> {
let itype = item.itype.as_ref()?.as_str();
let file = item.file.clone()?;
let uid = item.uid.clone().unwrap_or("".into());
let uid = item.uid.clone().unwrap_or_else(|| "".into());
let path = dirs::app_profiles_dir().ok()?.join(file.as_str());
if !path.exists() {

View File

@@ -296,10 +296,10 @@ pub async fn enhance() -> (Mapping, Vec<String>, HashMap<String, ResultLog>) {
// 合并默认的config
for (key, value) in clash_config.into_iter() {
if key.as_str() == Some("tun") {
let mut tun = config.get_mut("tun").map_or(Mapping::new(), |val| {
val.as_mapping().cloned().unwrap_or(Mapping::new())
let mut tun = config.get_mut("tun").map_or_else(Mapping::new, |val| {
val.as_mapping().cloned().unwrap_or_else(Mapping::new)
});
let patch_tun = value.as_mapping().cloned().unwrap_or(Mapping::new());
let patch_tun = value.as_mapping().cloned().unwrap_or_else(Mapping::new);
for (key, value) in patch_tun.into_iter() {
tun.insert(key, value);
}

View File

@@ -24,8 +24,8 @@ macro_rules! append {
pub fn use_tun(mut config: Mapping, enable: bool) -> Mapping {
let tun_key = Value::from("tun");
let tun_val = config.get(&tun_key);
let mut tun_val = tun_val.map_or(Mapping::new(), |val| {
val.as_mapping().cloned().unwrap_or(Mapping::new())
let mut tun_val = tun_val.map_or_else(Mapping::new, |val| {
val.as_mapping().cloned().unwrap_or_else(Mapping::new)
});
if enable {
@@ -52,8 +52,8 @@ pub fn use_tun(mut config: Mapping, enable: bool) -> Mapping {
// 读取DNS配置
let dns_key = Value::from("dns");
let dns_val = config.get(&dns_key);
let mut dns_val = dns_val.map_or(Mapping::new(), |val| {
val.as_mapping().cloned().unwrap_or(Mapping::new())
let mut dns_val = dns_val.map_or_else(Mapping::new, |val| {
val.as_mapping().cloned().unwrap_or_else(Mapping::new)
});
let ipv6_key = Value::from("ipv6");
let ipv6_val = config

View File

@@ -33,9 +33,9 @@ impl Debug for PlatformSpecification {
impl PlatformSpecification {
pub fn new() -> Self {
let system_name = System::name().unwrap_or("Null".into());
let system_version = System::long_os_version().unwrap_or("Null".into());
let system_kernel_version = System::kernel_version().unwrap_or("Null".into());
let system_name = System::name().unwrap_or_else(|| "Null".into());
let system_version = System::long_os_version().unwrap_or_else(|| "Null".into());
let system_kernel_version = System::kernel_version().unwrap_or_else(|| "Null".into());
let system_arch = System::cpu_arch();
let handler = handle::Handle::app_handle();

View File

@@ -47,7 +47,7 @@ pub fn app_home_dir() -> Result<PathBuf> {
let app_exe = dunce::canonicalize(app_exe)?;
let app_dir = app_exe
.parent()
.ok_or(anyhow::anyhow!("failed to get the portable app dir"))?;
.ok_or_else(|| anyhow::anyhow!("failed to get the portable app dir"))?;
return Ok(PathBuf::from(app_dir).join(".config").join(APP_ID));
}
@@ -170,7 +170,7 @@ pub fn path_to_str(path: &PathBuf) -> Result<&str> {
let path_str = path
.as_os_str()
.to_str()
.ok_or(anyhow::anyhow!("failed to get path from {:?}", path))?;
.ok_or_else(|| anyhow::anyhow!("failed to get path from {:?}", path))?;
Ok(path_str)
}

View File

@@ -34,10 +34,9 @@ pub async fn read_mapping(path: &PathBuf) -> Result<Mapping> {
Ok(val
.as_mapping()
.ok_or(anyhow!(
"failed to transform to yaml mapping \"{}\"",
path.display()
))?
.ok_or_else(|| {
anyhow!("failed to transform to yaml mapping \"{}\"", path.display())
})?
.to_owned())
}
Err(err) => {

View File

@@ -155,9 +155,9 @@ pub async fn delete_log() -> Result<()> {
let month = u32::from_str(sa[1])?;
let day = u32::from_str(sa[2])?;
let time = chrono::NaiveDate::from_ymd_opt(year, month, day)
.ok_or(anyhow::anyhow!("invalid time str"))?
.ok_or_else(|| anyhow::anyhow!("invalid time str"))?
.and_hms_opt(0, 0, 0)
.ok_or(anyhow::anyhow!("invalid time str"))?;
.ok_or_else(|| anyhow::anyhow!("invalid time str"))?;
Ok(time)
};
@@ -171,7 +171,7 @@ pub async fn delete_log() -> Result<()> {
let file_time = Local
.from_local_datetime(&created_time)
.single()
.ok_or(anyhow::anyhow!("invalid local datetime"))?;
.ok_or_else(|| anyhow::anyhow!("invalid local datetime"))?;
let duration = now.signed_duration_since(file_time);
if duration.num_days() > day {
@@ -523,7 +523,7 @@ pub async fn startup_script() -> Result<()> {
let script_path = {
let verge = Config::verge().await;
let verge = verge.latest_ref();
verge.startup_script.clone().unwrap_or("".into())
verge.startup_script.clone().unwrap_or_else(|| "".into())
};
if script_path.is_empty() {
@@ -547,7 +547,7 @@ pub async fn startup_script() -> Result<()> {
}
let parent_dir = script_dir.parent();
let working_dir = parent_dir.unwrap_or(script_dir.as_ref());
let working_dir = parent_dir.unwrap_or_else(|| script_dir.as_ref());
app_handle
.shell()

View File

@@ -26,7 +26,7 @@ pub async fn build_new_window() -> Result<WebviewWindow, String> {
.latest_ref()
.start_page
.clone()
.unwrap_or("/".into());
.unwrap_or_else(|| "/".into());
match tauri::WebviewWindowBuilder::new(
app_handle,
"main", /* the unique window label */

View File

@@ -88,12 +88,12 @@ pub fn embed_server() {
.latest_ref()
.pac_file_content
.clone()
.unwrap_or(DEFAULT_PAC.into());
.unwrap_or_else(|| DEFAULT_PAC.into());
let pac_port = verge_config
.latest_ref()
.verge_mixed_port
.unwrap_or(clash_config.latest_ref().get_mixed_port());
.unwrap_or_else(|| clash_config.latest_ref().get_mixed_port());
let pac = warp::path!("commands" / "pac").map(move || {
let processed_content = pac_content.replace("%mixed-port%", &format!("{pac_port}"));