diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 36f83e8..111805a 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -454,6 +454,7 @@ dependencies = [ name = "clash-verge" version = "0.1.0" dependencies = [ + "anyhow", "auto-launch", "chrono", "dirs", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 97c828a..861b30e 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -13,6 +13,7 @@ build = "build.rs" tauri-build = { version = "1.0.0-rc.3", features = [] } [dependencies] +anyhow = "1.0" dirs = "4.0.0" dunce = "1.0.2" chrono = "0.4.19" diff --git a/src-tauri/src/cmds.rs b/src-tauri/src/cmds.rs index 2a7c87c..8eb1609 100644 --- a/src-tauri/src/cmds.rs +++ b/src-tauri/src/cmds.rs @@ -1,29 +1,46 @@ use crate::{ core::{ClashInfo, ProfileItem, Profiles, VergeConfig}, states::{ClashState, ProfilesState, VergeState}, - utils::{dirs::app_home_dir, fetch::fetch_profile, sysopt::SysProxyConfig}, + utils::{dirs, fetch::fetch_profile, sysopt::SysProxyConfig}, }; +use anyhow::Result; use serde_yaml::Mapping; use std::{path::PathBuf, process::Command}; use tauri::{api, State}; +/// wrap the anyhow error +/// transform the error to String +macro_rules! wrap_err { + ($stat: expr) => { + match $stat { + Ok(a) => Ok(a), + Err(err) => { + log::error!("{}", err.to_string()); + Err(format!("{}", err.to_string())) + } + } + }; +} + +/// return the string literal error +macro_rules! ret_err { + ($str: literal) => { + return Err($str.into()) + }; +} + /// get all profiles from `profiles.yaml` -/// do not acquire the lock of ProfileLock #[tauri::command] pub fn get_profiles(profiles_state: State<'_, ProfilesState>) -> Result { - match profiles_state.0.lock() { - Ok(profiles) => Ok(profiles.clone()), - Err(_) => Err("failed to get profiles lock".into()), - } + let profiles = profiles_state.0.lock().unwrap(); + Ok(profiles.clone()) } /// synchronize data irregularly #[tauri::command] pub fn sync_profiles(profiles_state: State<'_, ProfilesState>) -> Result<(), String> { - match profiles_state.0.lock() { - Ok(mut profiles) => profiles.sync_file(), - Err(_) => Err("failed to get profiles lock".into()), - } + let mut profiles = profiles_state.0.lock().unwrap(); + wrap_err!(profiles.sync_file()) } /// import the profile from url @@ -36,7 +53,7 @@ pub async fn import_profile( ) -> Result<(), String> { let result = fetch_profile(&url, with_proxy).await?; let mut profiles = profiles_state.0.lock().unwrap(); - profiles.import_from_url(url, result) + wrap_err!(profiles.import_from_url(url, result)) } /// new a profile @@ -49,7 +66,7 @@ pub async fn new_profile( profiles_state: State<'_, ProfilesState>, ) -> Result<(), String> { let mut profiles = profiles_state.0.lock().unwrap(); - profiles.append_item(name, desc)?; + wrap_err!(profiles.append_item(name, desc))?; Ok(()) } @@ -66,34 +83,34 @@ pub async fn update_profile( Ok(mut profile) => { let items = profile.items.take().unwrap_or(vec![]); if index >= items.len() { - return Err("the index out of bound".into()); + ret_err!("the index out of bound"); } let url = match &items[index].url { Some(u) => u.clone(), - None => return Err("failed to update profile for `invalid url`".into()), + None => ret_err!("failed to update profile for `invalid url`"), }; profile.items = Some(items); url } - Err(_) => return Err("failed to get profiles lock".into()), + Err(_) => ret_err!("failed to get profiles lock"), }; let result = fetch_profile(&url, with_proxy).await?; match profiles_state.0.lock() { Ok(mut profiles) => { - profiles.update_item(index, result)?; + wrap_err!(profiles.update_item(index, result))?; // reactivate the profile let current = profiles.current.clone().unwrap_or(0); if current == index { let clash = clash_state.0.lock().unwrap(); - profiles.activate(&clash) + wrap_err!(profiles.activate(&clash)) } else { Ok(()) } } - Err(_) => Err("failed to get profiles lock".into()), + Err(_) => ret_err!("failed to get profiles lock"), } } @@ -105,14 +122,10 @@ pub fn select_profile( profiles_state: State<'_, ProfilesState>, ) -> Result<(), String> { let mut profiles = profiles_state.0.lock().unwrap(); + wrap_err!(profiles.put_current(index))?; - match profiles.put_current(index) { - Ok(()) => { - let clash = clash_state.0.lock().unwrap(); - profiles.activate(&clash) - } - Err(err) => Err(err), - } + let clash = clash_state.0.lock().unwrap(); + wrap_err!(profiles.activate(&clash)) } /// delete profile item @@ -123,16 +136,13 @@ pub fn delete_profile( profiles_state: State<'_, ProfilesState>, ) -> Result<(), String> { let mut profiles = profiles_state.0.lock().unwrap(); - match profiles.delete_item(index) { - Ok(change) => match change { - true => { - let clash = clash_state.0.lock().unwrap(); - profiles.activate(&clash) - } - false => Ok(()), - }, - Err(err) => Err(err), + + if wrap_err!(profiles.delete_item(index))? { + let clash = clash_state.0.lock().unwrap(); + wrap_err!(profiles.activate(&clash))?; } + + Ok(()) } /// patch the profile config @@ -142,10 +152,8 @@ pub fn patch_profile( profile: ProfileItem, profiles_state: State<'_, ProfilesState>, ) -> Result<(), String> { - match profiles_state.0.lock() { - Ok(mut profiles) => profiles.patch_item(index, profile), - Err(_) => Err("can not get profiles lock".into()), - } + let mut profiles = profiles_state.0.lock().unwrap(); + wrap_err!(profiles.patch_item(index, profile)) } /// run vscode command to edit the profile @@ -156,19 +164,34 @@ pub fn view_profile(index: usize, profiles_state: State<'_, ProfilesState>) -> R if index >= items.len() { profiles.items = Some(items); - return Err("the index out of bound".into()); + ret_err!("the index out of bound"); } let file = items[index].file.clone().unwrap_or("".into()); profiles.items = Some(items); - let path = app_home_dir().join("profiles").join(file); + let path = dirs::app_profiles_dir().join(file); if !path.exists() { - return Err("the file not found".into()); + ret_err!("the file not found"); } // use vscode first if let Ok(code) = which::which("code") { + #[cfg(target_os = "windows")] + { + use std::os::windows::process::CommandExt; + + return match Command::new(code) + .creation_flags(0x08000000) + .arg(path) + .spawn() + { + Ok(_) => Ok(()), + Err(_) => Err("failed to open file by VScode".into()), + }; + } + + #[cfg(not(target_os = "windows"))] return match Command::new(code).arg(path).spawn() { Ok(_) => Ok(()), Err(_) => Err("failed to open file by VScode".into()), @@ -187,23 +210,15 @@ pub fn restart_sidecar( let mut clash = clash_state.0.lock().unwrap(); let mut profiles = profiles_state.0.lock().unwrap(); - match clash.restart_sidecar(&mut profiles) { - Ok(_) => Ok(()), - Err(err) => { - log::error!("{}", err); - Err(err) - } - } + wrap_err!(clash.restart_sidecar(&mut profiles)) } /// get the clash core info from the state /// the caller can also get the infomation by clash's api #[tauri::command] pub fn get_clash_info(clash_state: State<'_, ClashState>) -> Result { - match clash_state.0.lock() { - Ok(clash) => Ok(clash.info.clone()), - Err(_) => Err("failed to get clash lock".into()), - } + let clash = clash_state.0.lock().unwrap(); + Ok(clash.info.clone()) } /// update the clash core config @@ -219,26 +234,21 @@ pub fn patch_clash_config( let mut clash = clash_state.0.lock().unwrap(); let mut verge = verge_state.0.lock().unwrap(); let mut profiles = profiles_state.0.lock().unwrap(); - clash.patch_config(payload, &mut verge, &mut profiles) + wrap_err!(clash.patch_config(payload, &mut verge, &mut profiles)) } /// get the system proxy #[tauri::command] pub fn get_sys_proxy() -> Result { - match SysProxyConfig::get_sys() { - Ok(value) => Ok(value), - Err(err) => Err(err.to_string()), - } + wrap_err!(SysProxyConfig::get_sys()) } /// get the current proxy config /// which may not the same as system proxy #[tauri::command] pub fn get_cur_proxy(verge_state: State<'_, VergeState>) -> Result, String> { - match verge_state.0.lock() { - Ok(verge) => Ok(verge.cur_sysproxy.clone()), - Err(_) => Err("failed to get verge lock".into()), - } + let verge = verge_state.0.lock().unwrap(); + Ok(verge.cur_sysproxy.clone()) } /// get the verge config @@ -266,16 +276,16 @@ pub fn patch_verge_config( let tun_mode = payload.enable_tun_mode.clone(); let mut verge = verge_state.0.lock().unwrap(); - verge.patch_config(payload)?; + wrap_err!(verge.patch_config(payload))?; // change tun mode if tun_mode.is_some() { let mut clash = clash_state.0.lock().unwrap(); let profiles = profiles_state.0.lock().unwrap(); - clash.tun_mode(tun_mode.unwrap())?; + wrap_err!(clash.tun_mode(tun_mode.unwrap()))?; clash.update_config(); - profiles.activate(&clash)?; + wrap_err!(profiles.activate(&clash))?; } Ok(()) @@ -290,14 +300,14 @@ pub fn kill_sidecars() { /// open app config dir #[tauri::command] pub fn open_app_dir() -> Result<(), String> { - let app_dir = app_home_dir(); + let app_dir = dirs::app_home_dir(); open_path_cmd(app_dir, "failed to open app dir") } /// open logs dir #[tauri::command] pub fn open_logs_dir() -> Result<(), String> { - let log_dir = app_home_dir().join("logs"); + let log_dir = dirs::app_logs_dir(); open_path_cmd(log_dir, "failed to open logs dir") } diff --git a/src-tauri/src/core/clash.rs b/src-tauri/src/core/clash.rs index 80ea316..d6a1d8c 100644 --- a/src-tauri/src/core/clash.rs +++ b/src-tauri/src/core/clash.rs @@ -1,5 +1,6 @@ use super::{Profiles, Verge}; use crate::utils::{config, dirs}; +use anyhow::{bail, Result}; use serde::{Deserialize, Serialize}; use serde_yaml::{Mapping, Value}; use tauri::api::process::{Command, CommandChild, CommandEvent}; @@ -31,12 +32,9 @@ pub struct Clash { pub sidecar: Option, } -static CLASH_CONFIG: &str = "config.yaml"; - -// todo: be able to change config field impl Clash { pub fn new() -> Clash { - let config = Clash::get_config(); + let config = Clash::read_config(); let info = Clash::get_info(&config); Clash { @@ -46,6 +44,20 @@ impl Clash { } } + /// get clash config + fn read_config() -> Mapping { + config::read_yaml::(dirs::clash_path()) + } + + /// save the clash config + fn save_config(&self) -> Result<()> { + config::save_yaml( + dirs::clash_path(), + &self.config, + Some("# Default Config For Clash Core\n\n"), + ) + } + /// parse the clash's config.yaml /// get some information fn get_info(clash_config: &Mapping) -> ClashInfo { @@ -100,7 +112,7 @@ impl Clash { } /// run clash sidecar - pub fn run_sidecar(&mut self) -> Result<(), String> { + pub fn run_sidecar(&mut self) -> Result<()> { let app_dir = dirs::app_home_dir(); let app_dir = app_dir.as_os_str().to_str().unwrap(); @@ -121,25 +133,23 @@ impl Clash { }); Ok(()) } - Err(err) => Err(err.to_string()), + Err(err) => bail!(err.to_string()), }, - Err(err) => Err(err.to_string()), + Err(err) => bail!(err.to_string()), } } /// drop clash sidecar - pub fn drop_sidecar(&mut self) -> Result<(), String> { + pub fn drop_sidecar(&mut self) -> Result<()> { if let Some(sidecar) = self.sidecar.take() { - if let Err(err) = sidecar.kill() { - return Err(format!("failed to drop clash for \"{}\"", err)); - } + sidecar.kill()?; } Ok(()) } /// restart clash sidecar /// should reactivate profile after restart - pub fn restart_sidecar(&mut self, profiles: &mut Profiles) -> Result<(), String> { + pub fn restart_sidecar(&mut self, profiles: &mut Profiles) -> Result<()> { self.update_config(); self.drop_sidecar()?; self.run_sidecar()?; @@ -148,31 +158,17 @@ impl Clash { /// update the clash info pub fn update_config(&mut self) { - self.config = Clash::get_config(); + self.config = Clash::read_config(); self.info = Clash::get_info(&self.config); } - /// get clash config - fn get_config() -> Mapping { - config::read_yaml::(dirs::app_home_dir().join(CLASH_CONFIG)) - } - - /// save the clash config - fn save_config(&self) -> Result<(), String> { - config::save_yaml( - dirs::app_home_dir().join(CLASH_CONFIG), - &self.config, - Some("# Default Config For Clash Core\n\n"), - ) - } - /// patch update the clash config pub fn patch_config( &mut self, patch: Mapping, verge: &mut Verge, profiles: &mut Profiles, - ) -> Result<(), String> { + ) -> Result<()> { for (key, value) in patch.iter() { let value = value.clone(); let key_str = key.as_str().clone().unwrap_or(""); @@ -206,7 +202,7 @@ impl Clash { /// enable tun mode /// only revise the config and restart the - pub fn tun_mode(&mut self, enable: bool) -> Result<(), String> { + pub fn tun_mode(&mut self, enable: bool) -> Result<()> { let tun_key = Value::String("tun".into()); let tun_val = self.config.get(&tun_key); @@ -256,7 +252,7 @@ impl Default for Clash { impl Drop for Clash { fn drop(&mut self) { if let Err(err) = self.drop_sidecar() { - log::error!("{}", err); + log::error!("{err}"); } } } diff --git a/src-tauri/src/core/profiles.rs b/src-tauri/src/core/profiles.rs index 551deeb..4fdae29 100644 --- a/src-tauri/src/core/profiles.rs +++ b/src-tauri/src/core/profiles.rs @@ -1,10 +1,10 @@ use super::{Clash, ClashInfo}; use crate::utils::{config, dirs, tmpl}; +use anyhow::{bail, Result}; use reqwest::header::HeaderMap; use serde::{Deserialize, Serialize}; use serde_yaml::{Mapping, Value}; use std::collections::HashMap; -use std::env::temp_dir; use std::fs::{remove_file, File}; use std::io::Write; use std::path::PathBuf; @@ -24,18 +24,30 @@ pub struct Profiles { pub struct ProfileItem { /// profile name pub name: Option, + /// profile description + #[serde(skip_serializing_if = "Option::is_none")] pub desc: Option, + /// profile file pub file: Option, + /// current mode + #[serde(skip_serializing_if = "Option::is_none")] pub mode: Option, + /// source url + #[serde(skip_serializing_if = "Option::is_none")] pub url: Option, + /// selected infomation + #[serde(skip_serializing_if = "Option::is_none")] pub selected: Option>, + /// user info + #[serde(skip_serializing_if = "Option::is_none")] pub extra: Option, + /// updated time pub updated: Option, } @@ -63,29 +75,26 @@ pub struct ProfileResponse { pub extra: Option, } -static PROFILE_YAML: &str = "profiles.yaml"; -static PROFILE_TEMP: &str = "clash-verge-runtime.yaml"; - impl Profiles { /// read the config from the file pub fn read_file() -> Self { - config::read_yaml::(dirs::app_home_dir().join(PROFILE_YAML)) + config::read_yaml::(dirs::profiles_path()) } /// save the config to the file - pub fn save_file(&self) -> Result<(), String> { + pub fn save_file(&self) -> Result<()> { config::save_yaml( - dirs::app_home_dir().join(PROFILE_YAML), + dirs::profiles_path(), self, Some("# Profiles Config for Clash Verge\n\n"), ) } /// sync the config between file and memory - pub fn sync_file(&mut self) -> Result<(), String> { - let data = config::read_yaml::(dirs::app_home_dir().join(PROFILE_YAML)); + pub fn sync_file(&mut self) -> Result<()> { + let data = config::read_yaml::(dirs::profiles_path()); if data.current.is_none() { - Err("failed to read profiles.yaml".into()) + bail!("failed to read profiles.yaml") } else { self.current = data.current; self.items = data.items; @@ -95,9 +104,9 @@ impl Profiles { /// import the new profile from the url /// and update the config file - pub fn import_from_url(&mut self, url: String, result: ProfileResponse) -> Result<(), String> { + pub fn import_from_url(&mut self, url: String, result: ProfileResponse) -> Result<()> { // save the profile file - let path = dirs::app_home_dir().join("profiles").join(&result.file); + let path = dirs::app_profiles_dir().join(&result.file); let file_data = result.data.as_bytes(); File::create(path).unwrap().write(file_data).unwrap(); @@ -130,11 +139,11 @@ impl Profiles { } /// set the current and save to file - pub fn put_current(&mut self, index: usize) -> Result<(), String> { + pub fn put_current(&mut self, index: usize) -> Result<()> { let items = self.items.take().unwrap_or(vec![]); if index >= items.len() { - return Err("the index out of bound".into()); + bail!("the index out of bound"); } self.items = Some(items); @@ -144,7 +153,7 @@ impl Profiles { /// append new item /// return the new item's index - pub fn append_item(&mut self, name: String, desc: String) -> Result<(usize, PathBuf), String> { + pub fn append_item(&mut self, name: String, desc: String) -> Result<(usize, PathBuf)> { let mut items = self.items.take().unwrap_or(vec![]); // create a new profile file @@ -153,7 +162,7 @@ impl Profiles { .unwrap() .as_secs(); let file = format!("{}.yaml", now); - let path = dirs::app_home_dir().join("profiles").join(&file); + let path = dirs::app_profiles_dir().join(&file); match File::create(&path).unwrap().write(tmpl::ITEM_CONFIG) { Ok(_) => { @@ -172,14 +181,14 @@ impl Profiles { self.items = Some(items); Ok((index, path)) } - Err(_) => Err("failed to create file".into()), + Err(_) => bail!("failed to create file"), } } /// update the target profile /// and save to config file /// only support the url item - pub fn update_item(&mut self, index: usize, result: ProfileResponse) -> Result<(), String> { + pub fn update_item(&mut self, index: usize, result: ProfileResponse) -> Result<()> { let mut items = self.items.take().unwrap_or(vec![]); let now = SystemTime::now() @@ -189,7 +198,7 @@ impl Profiles { // update file let file_path = &items[index].file.as_ref().unwrap(); - let file_path = dirs::app_home_dir().join("profiles").join(file_path); + let file_path = dirs::app_profiles_dir().join(file_path); let file_data = result.data.as_bytes(); File::create(file_path).unwrap().write(file_data).unwrap(); @@ -202,10 +211,10 @@ impl Profiles { } /// patch item - pub fn patch_item(&mut self, index: usize, profile: ProfileItem) -> Result<(), String> { + pub fn patch_item(&mut self, index: usize, profile: ProfileItem) -> Result<()> { let mut items = self.items.take().unwrap_or(vec![]); if index >= items.len() { - return Err("index out of bound".into()); + bail!("index out of range"); } if profile.name.is_some() { @@ -232,19 +241,19 @@ impl Profiles { } /// delete the item - pub fn delete_item(&mut self, index: usize) -> Result { + pub fn delete_item(&mut self, index: usize) -> Result { let mut current = self.current.clone().unwrap_or(0); let mut items = self.items.clone().unwrap_or(vec![]); if index >= items.len() { - return Err("index out of bound".into()); + bail!("index out of range"); } let mut rm_item = items.remove(index); // delete the file if let Some(file) = rm_item.file.take() { - let file_path = dirs::app_home_dir().join("profiles").join(file); + let file_path = dirs::app_profiles_dir().join(file); if file_path.exists() { if let Err(err) = remove_file(file_path) { @@ -272,33 +281,34 @@ impl Profiles { } /// activate current profile - pub fn activate(&self, clash: &Clash) -> Result<(), String> { + pub fn activate(&self, clash: &Clash) -> Result<()> { let current = self.current.unwrap_or(0); match self.items.clone() { Some(items) => { if current >= items.len() { - return Err("the index out of bound".into()); + bail!("the index out of bound"); } let profile = items[current].clone(); let clash_config = clash.config.clone(); let clash_info = clash.info.clone(); + tauri::async_runtime::spawn(async move { let mut count = 5; // retry times - let mut err = String::from(""); + let mut err = None; while count > 0 { match activate_profile(&profile, &clash_config, &clash_info).await { Ok(_) => return, - Err(e) => err = e, + Err(e) => err = Some(e), } count -= 1; } - log::error!("failed to activate for `{}`", err); + log::error!("failed to activate for `{}`", err.unwrap()); }); Ok(()) } - None => Err("empty profiles".into()), + None => bail!("empty profiles"), } } } @@ -308,23 +318,23 @@ pub async fn activate_profile( profile_item: &ProfileItem, clash_config: &Mapping, clash_info: &ClashInfo, -) -> Result<(), String> { +) -> Result<()> { // temp profile's path - let temp_path = temp_dir().join(PROFILE_TEMP); + let temp_path = dirs::profiles_temp_path(); // generate temp profile { let file_name = match profile_item.file.clone() { Some(file_name) => file_name, - None => return Err("profile item should have `file` field".into()), + None => bail!("profile item should have `file` field"), }; - let file_path = dirs::app_home_dir().join("profiles").join(file_name); + let file_path = dirs::app_profiles_dir().join(file_name); if !file_path.exists() { - return Err(format!( + bail!( "profile `{}` not exists", file_path.as_os_str().to_str().unwrap() - )); + ); } // begin to generate the new profile config @@ -372,12 +382,13 @@ pub async fn activate_profile( let mut data = HashMap::new(); data.insert("path", temp_path.as_os_str().to_str().unwrap()); - let client = match reqwest::ClientBuilder::new().no_proxy().build() { - Ok(c) => c, - Err(_) => return Err("failed to create http::put".into()), - }; - match client.put(server).headers(headers).json(&data).send().await { - Ok(_) => Ok(()), - Err(err) => Err(format!("request failed `{}`", err.to_string())), - } + let client = reqwest::ClientBuilder::new().no_proxy().build()?; + + client + .put(server) + .headers(headers) + .json(&data) + .send() + .await?; + Ok(()) } diff --git a/src-tauri/src/core/verge.rs b/src-tauri/src/core/verge.rs index 349480a..67f85c4 100644 --- a/src-tauri/src/core/verge.rs +++ b/src-tauri/src/core/verge.rs @@ -2,6 +2,7 @@ use crate::{ core::Clash, utils::{config, dirs, sysopt::SysProxyConfig}, }; +use anyhow::{bail, Result}; use auto_launch::{AutoLaunch, AutoLaunchBuilder}; use serde::{Deserialize, Serialize}; use std::sync::Arc; @@ -39,17 +40,15 @@ pub struct VergeConfig { pub proxy_guard_duration: Option, } -static VERGE_CONFIG: &str = "verge.yaml"; - impl VergeConfig { pub fn new() -> Self { - config::read_yaml::(dirs::app_home_dir().join(VERGE_CONFIG)) + config::read_yaml::(dirs::verge_path()) } /// Save Verge App Config - pub fn save_file(&self) -> Result<(), String> { + pub fn save_file(&self) -> Result<()> { config::save_yaml( - dirs::app_home_dir().join(VERGE_CONFIG), + dirs::verge_path(), self, Some("# The Config for Clash Verge App\n\n"), ) @@ -140,30 +139,28 @@ impl Verge { } /// sync the startup when run the app - pub fn sync_launch(&self) -> Result<(), String> { + pub fn sync_launch(&self) -> Result<()> { let enable = self.config.enable_auto_launch.clone().unwrap_or(false); if !enable { return Ok(()); } if self.auto_launch.is_none() { - return Err("should init the auto launch first".into()); + bail!("should init the auto launch first"); } let auto_launch = self.auto_launch.clone().unwrap(); let is_enabled = auto_launch.is_enabled().unwrap_or(false); if !is_enabled { - if let Err(_) = auto_launch.enable() { - return Err("failed to enable auto-launch".into()); - } + auto_launch.enable()?; } Ok(()) } /// update the startup - fn update_launch(&mut self, enable: bool) -> Result<(), String> { + fn update_launch(&mut self, enable: bool) -> Result<()> { let conf_enable = self.config.enable_auto_launch.clone().unwrap_or(false); if enable == conf_enable { @@ -172,24 +169,18 @@ impl Verge { let auto_launch = self.auto_launch.clone().unwrap(); - let result = match enable { - true => auto_launch.enable(), - false => auto_launch.disable(), + match enable { + true => auto_launch.enable()?, + false => auto_launch.disable()?, }; - match result { - Ok(_) => Ok(()), - Err(err) => { - log::error!("{err}"); - Err("failed to set system startup info".into()) - } - } + Ok(()) } /// patch verge config /// There should be only one update at a time here /// so call the save_file at the end is savely - pub fn patch_config(&mut self, patch: VergeConfig) -> Result<(), String> { + pub fn patch_config(&mut self, patch: VergeConfig) -> Result<()> { // only change it if patch.theme_mode.is_some() { self.config.theme_mode = patch.theme_mode; @@ -218,7 +209,7 @@ impl Verge { self.cur_sysproxy = Some(sysproxy); log::error!("failed to set system proxy"); - return Err("failed to set system proxy".into()); + bail!("failed to set system proxy"); } self.cur_sysproxy = Some(sysproxy); } @@ -237,7 +228,7 @@ impl Verge { self.cur_sysproxy = Some(sysproxy); log::error!("failed to set system proxy"); - return Err("failed to set system proxy".into()); + bail!("failed to set system proxy"); } } diff --git a/src-tauri/src/utils/config.rs b/src-tauri/src/utils/config.rs index 04a33cc..7b7a357 100644 --- a/src-tauri/src/utils/config.rs +++ b/src-tauri/src/utils/config.rs @@ -1,3 +1,4 @@ +use anyhow::{Context, Result}; use serde::{de::DeserializeOwned, Serialize}; use std::{fs, path::PathBuf}; @@ -7,26 +8,16 @@ pub fn read_yaml(path: PathBuf) -> T { serde_yaml::from_str::(&yaml_str).unwrap_or(T::default()) } -/// - save the data to the file -/// - can set `prefix` string to add some comments -pub fn save_yaml( - path: PathBuf, - data: &T, - prefix: Option<&str>, -) -> Result<(), String> { - match serde_yaml::to_string(data) { - Ok(data_str) => { - let yaml_str = match prefix { - Some(prefix) => format!("{}{}", prefix, data_str), - None => data_str, - }; +/// save the data to the file +/// can set `prefix` string to add some comments +pub fn save_yaml(path: PathBuf, data: &T, prefix: Option<&str>) -> Result<()> { + let data_str = serde_yaml::to_string(data)?; - let path_str = path.as_os_str().to_string_lossy().to_string(); - match fs::write(path, yaml_str.as_bytes()) { - Ok(_) => Ok(()), - Err(_) => Err(format!("can not save file `{}`", path_str)), - } - } - Err(_) => Err("can not convert the data to yaml".into()), - } + let yaml_str = match prefix { + Some(prefix) => format!("{prefix}{data_str}"), + None => data_str, + }; + + let path_str = path.as_os_str().to_string_lossy().to_string(); + fs::write(path, yaml_str.as_bytes()).context(format!("failed to save file \"{path_str}\"")) } diff --git a/src-tauri/src/utils/dirs.rs b/src-tauri/src/utils/dirs.rs index 8b159b4..985939c 100644 --- a/src-tauri/src/utils/dirs.rs +++ b/src-tauri/src/utils/dirs.rs @@ -1,3 +1,4 @@ +use std::env::temp_dir; use std::path::{Path, PathBuf}; use tauri::{ api::path::{home_dir, resource_dir}, @@ -18,3 +19,34 @@ pub fn app_resources_dir(package_info: &PackageInfo) -> PathBuf { .unwrap() .join("resources") } + +/// profiles dir +pub fn app_profiles_dir() -> PathBuf { + app_home_dir().join("profiles") +} + +/// logs dir +pub fn app_logs_dir() -> PathBuf { + app_home_dir().join("logs") +} + +static CLASH_CONFIG: &str = "config.yaml"; +static VERGE_CONFIG: &str = "verge.yaml"; +static PROFILE_YAML: &str = "profiles.yaml"; +static PROFILE_TEMP: &str = "clash-verge-runtime.yaml"; + +pub fn clash_path() -> PathBuf { + app_home_dir().join(CLASH_CONFIG) +} + +pub fn verge_path() -> PathBuf { + app_home_dir().join(VERGE_CONFIG) +} + +pub fn profiles_path() -> PathBuf { + app_home_dir().join(PROFILE_YAML) +} + +pub fn profiles_temp_path() -> PathBuf { + temp_dir().join(PROFILE_TEMP) +} diff --git a/src-tauri/src/utils/init.rs b/src-tauri/src/utils/init.rs index ce5ed6c..0fc16fd 100644 --- a/src-tauri/src/utils/init.rs +++ b/src-tauri/src/utils/init.rs @@ -1,5 +1,3 @@ -extern crate serde_yaml; - use crate::utils::{dirs, tmpl}; use chrono::Local; use log::LevelFilter; @@ -7,7 +5,7 @@ use log4rs::append::console::ConsoleAppender; use log4rs::append::file::FileAppender; use log4rs::config::{Appender, Config, Root}; use log4rs::encode::pattern::PatternEncoder; -use std::fs::{self, File}; +use std::fs; use std::io::Write; use std::path::PathBuf; use tauri::PackageInfo; @@ -48,13 +46,13 @@ fn init_config(app_dir: &PathBuf) -> std::io::Result<()> { let profile_path = app_dir.join("profiles.yaml"); if !clash_path.exists() { - File::create(clash_path)?.write(tmpl::CLASH_CONFIG)?; + fs::File::create(clash_path)?.write(tmpl::CLASH_CONFIG)?; } if !verge_path.exists() { - File::create(verge_path)?.write(tmpl::VERGE_CONFIG)?; + fs::File::create(verge_path)?.write(tmpl::VERGE_CONFIG)?; } if !profile_path.exists() { - File::create(profile_path)?.write(tmpl::PROFILES_CONFIG)?; + fs::File::create(profile_path)?.write(tmpl::PROFILES_CONFIG)?; } Ok(()) } @@ -63,8 +61,8 @@ fn init_config(app_dir: &PathBuf) -> std::io::Result<()> { pub fn init_app(package_info: &PackageInfo) { // create app dir let app_dir = dirs::app_home_dir(); - let log_dir = app_dir.join("logs"); - let profiles_dir = app_dir.join("profiles"); + let log_dir = dirs::app_logs_dir(); + let profiles_dir = dirs::app_profiles_dir(); let res_dir = dirs::app_resources_dir(package_info);