refactor: import profile

This commit is contained in:
GyDi 2021-12-14 22:33:42 +08:00
parent b5bb39ef3d
commit eb3daf7b3e
4 changed files with 85 additions and 70 deletions

View File

@ -1,7 +1,10 @@
use crate::{ use crate::{
config::{read_profiles, save_profiles, ProfileItem},
events::{emit::ClashInfoPayload, state::ClashInfoState}, events::{emit::ClashInfoPayload, state::ClashInfoState},
utils::{clash, import}, utils::{app_home_dir, clash, fetch::fetch_profile},
}; };
use std::fs::File;
use std::io::Write;
use tauri::{api::process::kill_children, AppHandle, State}; use tauri::{api::process::kill_children, AppHandle, State};
#[tauri::command] #[tauri::command]
@ -14,14 +17,6 @@ pub fn restart_sidebar(app_handle: AppHandle, clash_info: State<'_, ClashInfoSta
} }
} }
#[tauri::command]
pub async fn import_profile(url: String) -> Result<String, String> {
match import::import_profile(&url).await {
Ok(_) => Ok(String::from("success")),
Err(_) => Err(String::from("error")),
}
}
#[tauri::command] #[tauri::command]
pub fn get_clash_info(clash_info: State<'_, ClashInfoState>) -> Option<ClashInfoPayload> { pub fn get_clash_info(clash_info: State<'_, ClashInfoState>) -> Option<ClashInfoPayload> {
match clash_info.0.lock() { match clash_info.0.lock() {
@ -29,3 +24,44 @@ pub fn get_clash_info(clash_info: State<'_, ClashInfoState>) -> Option<ClashInfo
_ => None, _ => None,
} }
} }
/// Import the Profile from url and
/// save to the `profiles.yaml` file
#[tauri::command]
pub async fn import_profile(url: String) -> Result<String, String> {
let result = match fetch_profile(&url).await {
Some(r) => r,
None => {
log::error!("failed to fetch profile from `{}`", url);
return Err(format!("failed"));
}
};
let path = app_home_dir().join("profiles").join(&result.file);
File::create(path)
.unwrap()
.write(result.data.as_bytes())
.unwrap();
// update profiles.yaml
let mut profiles = read_profiles();
let mut items = match profiles.items {
Some(p) => p,
None => vec![],
};
let profile = ProfileItem {
name: Some(result.name),
file: Some(result.file),
mode: Some(format!("rule")),
url: Some(url),
selected: Some(vec![]), // Todo: parse the selected list
extra: Some(result.extra),
};
items.push(profile);
profiles.items = Some(items);
save_profiles(&profiles);
Ok(format!("success"))
}

View File

@ -39,3 +39,12 @@ pub struct ProfileExtra {
pub total: u64, pub total: u64,
pub expire: u64, pub expire: u64,
} }
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
/// the result from url
pub struct ProfileResponse {
pub name: String,
pub file: String,
pub data: String,
pub extra: ProfileExtra,
}

View File

@ -1,9 +1,4 @@
extern crate reqwest; use crate::config::{ProfileExtra, ProfileResponse};
use crate::config::{read_profiles, save_profiles, ProfileExtra, ProfileItem};
use crate::utils::app_home_dir;
use std::fs::File;
use std::io::Write;
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
/// parse the string /// parse the string
@ -21,12 +16,13 @@ fn parse_string<'a>(target: &'a str, key: &'a str) -> Option<&'a str> {
} }
} }
/// Todo: log /// fetch and parse the profile
/// Import the Profile from url pub async fn fetch_profile(url: &str) -> Option<ProfileResponse> {
/// save to the `verge.yaml` file let resp = match reqwest::get(url).await {
pub async fn import_profile(profile_url: &str) -> Result<(), reqwest::Error> { Ok(res) => res,
let resp = reqwest::get(profile_url).await?; Err(_) => return None,
let header = resp.headers().clone(); };
let header = resp.headers();
// parse the Subscription Userinfo // parse the Subscription Userinfo
let extra = { let extra = {
@ -56,60 +52,34 @@ pub async fn import_profile(profile_url: &str) -> Result<(), reqwest::Error> {
} }
}; };
// parse the file name // parse the `name` and `file`
let file_name = { let (name, file) = {
let file_name = header.get("Content-Disposition").unwrap().to_str().unwrap(); let now = SystemTime::now()
let file_name = parse_string(file_name, "filename=");
match file_name {
Some(f) => f.to_string(),
None => {
let cur_time = SystemTime::now()
.duration_since(UNIX_EPOCH) .duration_since(UNIX_EPOCH)
.unwrap() .unwrap()
.as_secs(); .as_secs();
format!("{}.yaml", cur_time) let file = format!("{}.yaml", now);
} let name = header.get("Content-Disposition").unwrap().to_str().unwrap();
let name = parse_string(name, "filename=");
match name {
Some(f) => (f.to_string(), file),
None => (file.clone(), file),
} }
}; };
// save file // get the data
let file_data = resp.text_with_charset("utf-8").await?; let data = match resp.text_with_charset("utf-8").await {
let file_path = app_home_dir().join("profiles").join(&file_name); Ok(d) => d,
File::create(file_path) Err(_) => return None,
.unwrap()
.write(file_data.as_bytes())
.unwrap();
// update profiles.yaml
let mut profiles = read_profiles();
let mut items = match profiles.items {
Some(p) => p,
None => vec![],
}; };
let profile = ProfileItem { Some(ProfileResponse {
name: Some(file_name.clone()), file,
file: Some(file_name.clone()), name,
mode: Some(String::from("rule")), data,
url: Some(String::from(profile_url)), extra,
selected: Some(vec![]), })
extra: Some(extra),
};
let target_index = items
.iter()
.position(|x| x.name.is_some() && x.name.as_ref().unwrap().as_str() == file_name.as_str());
match target_index {
Some(idx) => items[idx] = profile,
None => items.push(profile),
};
profiles.items = Some(items);
save_profiles(&profiles);
Ok(())
} }
#[test] #[test]

View File

@ -2,6 +2,6 @@ mod dirs;
pub use self::dirs::*; pub use self::dirs::*;
pub mod clash; pub mod clash;
pub mod import; pub mod fetch;
pub mod init; pub mod init;
pub mod sysopt; pub mod sysopt;