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