mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-01-29 08:45:41 +08:00
refactor: use anyhow to handle error
This commit is contained in:
@@ -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<CommandChild>,
|
||||
}
|
||||
|
||||
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::<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
|
||||
/// 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::<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
|
||||
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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String>,
|
||||
|
||||
/// profile description
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub desc: Option<String>,
|
||||
|
||||
/// profile file
|
||||
pub file: Option<String>,
|
||||
|
||||
/// current mode
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub mode: Option<String>,
|
||||
|
||||
/// source url
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub url: Option<String>,
|
||||
|
||||
/// selected infomation
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub selected: Option<Vec<ProfileSelected>>,
|
||||
|
||||
/// user info
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub extra: Option<ProfileExtra>,
|
||||
|
||||
/// updated time
|
||||
pub updated: Option<usize>,
|
||||
}
|
||||
@@ -63,29 +75,26 @@ pub struct ProfileResponse {
|
||||
pub extra: Option<ProfileExtra>,
|
||||
}
|
||||
|
||||
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::<Profiles>(dirs::app_home_dir().join(PROFILE_YAML))
|
||||
config::read_yaml::<Profiles>(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::<Self>(dirs::app_home_dir().join(PROFILE_YAML));
|
||||
pub fn sync_file(&mut self) -> Result<()> {
|
||||
let data = config::read_yaml::<Self>(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<bool, String> {
|
||||
pub fn delete_item(&mut self, index: usize) -> Result<bool> {
|
||||
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(())
|
||||
}
|
||||
|
||||
@@ -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<u64>,
|
||||
}
|
||||
|
||||
static VERGE_CONFIG: &str = "verge.yaml";
|
||||
|
||||
impl VergeConfig {
|
||||
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
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user