feat: supports cron update profiles

This commit is contained in:
GyDi 2022-04-20 20:37:16 +08:00 committed by GitHub
parent 5f7a1fa5cd
commit 4de944b41e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 190 additions and 49 deletions

View File

@ -58,38 +58,7 @@ pub async fn update_profile(
option: Option<PrfOption>,
core: State<'_, Core>,
) -> CmdResult {
let (url, opt) = {
// must release the lock here
let profiles = core.profiles.lock();
let item = wrap_err!(profiles.get_item(&index))?;
// check the profile type
if let Some(typ) = item.itype.as_ref() {
if *typ != "remote" {
ret_err!(format!("could not update the `{typ}` profile"));
}
}
if item.url.is_none() {
ret_err!("failed to get the item url");
}
(item.url.clone().unwrap(), item.option.clone())
};
let fetch_opt = PrfOption::merge(opt, option);
let item = wrap_err!(PrfItem::from_url(&url, None, None, fetch_opt).await)?;
let mut profiles = core.profiles.lock();
wrap_err!(profiles.update_item(index.clone(), item))?;
// reactivate the profile
if Some(index) == profiles.get_current() {
drop(profiles);
log_if_err!(core.activate_enhanced(false));
}
Ok(())
wrap_err!(Core::update_profile_item(core.inner().clone(), index, option).await)
}
/// change the current profile
@ -213,9 +182,7 @@ pub fn patch_clash_config(payload: Mapping, core: State<'_, Core>) -> CmdResult
#[tauri::command]
pub fn get_verge_config(core: State<'_, Core>) -> CmdResult<Verge> {
let verge = core.verge.lock();
let config = verge.clone();
Ok(config)
Ok(verge.clone())
}
/// patch the verge config

View File

@ -1,6 +1,7 @@
use self::notice::Notice;
use self::service::Service;
use self::sysopt::Sysopt;
use self::timer::Timer;
use crate::core::enhance::PrfEnhancedResult;
use crate::log_if_err;
use crate::utils::{dirs, help};
@ -39,6 +40,8 @@ pub struct Core {
pub sysopt: Arc<Mutex<Sysopt>>,
pub timer: Arc<Mutex<Timer>>,
pub window: Arc<Mutex<Option<Window>>>,
}
@ -55,6 +58,7 @@ impl Core {
profiles: Arc::new(Mutex::new(profiles)),
service: Arc::new(Mutex::new(service)),
sysopt: Arc::new(Mutex::new(Sysopt::new())),
timer: Arc::new(Mutex::new(Timer::new())),
window: Arc::new(Mutex::new(None)),
}
}
@ -98,6 +102,11 @@ impl Core {
sleep(Duration::from_secs(2)).await;
log_if_err!(core.activate_enhanced(true));
});
// timer initialize
let mut timer = self.timer.lock();
timer.set_core(self.clone());
log_if_err!(timer.refresh());
}
/// save the window instance
@ -324,3 +333,51 @@ impl Core {
Ok(())
}
}
impl Core {
/// Static function
/// update profile item
pub async fn update_profile_item(
core: Core,
uid: String,
option: Option<PrfOption>,
) -> Result<()> {
let (url, opt) = {
let profiles = core.profiles.lock();
let item = profiles.get_item(&uid)?;
if let Some(typ) = item.itype.as_ref() {
// maybe only valid for `local` profile
if *typ != "remote" {
// reactivate the config
if Some(uid) == profiles.get_current() {
drop(profiles);
return core.activate_enhanced(false);
}
return Ok(());
}
}
if item.url.is_none() {
bail!("failed to get the profile item url");
}
(item.url.clone().unwrap(), item.option.clone())
};
let merged_opt = PrfOption::merge(opt, option);
let item = PrfItem::from_url(&url, None, None, merged_opt).await?;
let mut profiles = core.profiles.lock();
profiles.update_item(uid.clone(), item)?;
// reactivate the profile
if Some(uid) == profiles.get_current() {
drop(profiles);
core.activate_enhanced(false)?;
}
Ok(())
}
}

View File

@ -102,17 +102,6 @@ impl PrfOption {
return one;
}
pub fn diff_update_interval(one: Option<&Self>, other: Option<&Self>) -> bool {
if one.is_some() && other.is_some() {
let one = one.unwrap();
let other = other.unwrap();
return one.update_interval == other.update_interval;
}
return false;
}
}
impl Default for PrfItem {

View File

@ -100,6 +100,11 @@ impl Profiles {
self.valid = valid;
}
/// get items ref
pub fn get_items(&self) -> Option<&Vec<PrfItem>> {
self.items.as_ref()
}
/// find the item by the uid
pub fn get_item(&self, uid: &String) -> Result<&PrfItem> {
if self.items.is_some() {

View File

@ -1,12 +1,19 @@
use delay_timer::prelude::{DelayTimer, DelayTimerBuilder, Task, TaskBuilder};
use super::Core;
use crate::log_if_err;
use anyhow::{bail, Context, Result};
use delay_timer::prelude::{DelayTimer, DelayTimerBuilder, TaskBuilder};
use std::collections::HashMap;
type TaskID = u64;
pub struct Timer {
delay_timer: DelayTimer,
timer_map: HashMap<String, u64>,
timer_map: HashMap<String, (TaskID, u64)>,
timer_count: u64,
timer_count: TaskID,
core: Option<Core>,
}
impl Timer {
@ -15,6 +22,122 @@ impl Timer {
delay_timer: DelayTimerBuilder::default().build(),
timer_map: HashMap::new(),
timer_count: 1,
core: None,
}
}
pub fn set_core(&mut self, core: Core) {
self.core = Some(core);
}
/// Correctly update all cron tasks
pub fn refresh(&mut self) -> Result<()> {
if self.core.is_none() {
bail!("unhandle error for core is none");
}
let diff_map = self.gen_diff();
for (uid, diff) in diff_map.into_iter() {
match diff {
DiffFlag::Del(tid) => {
log_if_err!(self.delay_timer.remove_task(tid));
}
DiffFlag::Add(tid, val) => {
log_if_err!(self.add_task(uid, tid, val));
}
DiffFlag::Mod(tid, val) => {
log_if_err!(self.delay_timer.remove_task(tid));
log_if_err!(self.add_task(uid, tid, val));
}
}
}
Ok(())
}
/// generate a uid -> update_interval map
fn gen_map(&self) -> HashMap<String, u64> {
let profiles = self.core.as_ref().unwrap().profiles.lock();
let mut new_map = HashMap::new();
if let Some(items) = profiles.get_items() {
for item in items.iter() {
if item.option.is_some() {
let option = item.option.as_ref().unwrap();
let interval = option.update_interval.unwrap_or(0);
if interval > 0 {
new_map.insert(item.uid.clone().unwrap(), interval);
}
}
}
}
new_map
}
/// generate the diff map for refresh
fn gen_diff(&mut self) -> HashMap<String, DiffFlag> {
let mut diff_map = HashMap::new();
let new_map = self.gen_map();
let cur_map = &self.timer_map;
cur_map.iter().for_each(|(uid, (tid, val))| {
let new_val = new_map.get(uid).unwrap_or(&0);
if *new_val == 0 {
diff_map.insert(uid.clone(), DiffFlag::Del(*tid));
} else if new_val != val {
diff_map.insert(uid.clone(), DiffFlag::Mod(*tid, *new_val));
}
});
let mut count = self.timer_count;
new_map.iter().for_each(|(uid, val)| {
if cur_map.get(uid).is_none() {
diff_map.insert(uid.clone(), DiffFlag::Add(count, *val));
count += 1;
}
});
self.timer_count = count;
diff_map
}
/// add a cron task
fn add_task(&self, uid: String, tid: TaskID, minutes: u64) -> Result<()> {
let core = self.core.clone().unwrap();
let task = TaskBuilder::default()
.set_task_id(tid)
.set_frequency_repeated_by_minutes(minutes)
// .set_frequency_repeated_by_seconds(minutes) // for test
.spawn_async_routine(move || Self::async_task(core.clone(), uid.clone()))
.context("failed to create timer task")?;
self
.delay_timer
.add_task(task)
.context("failed to add timer task")?;
Ok(())
}
/// the task runner
async fn async_task(core: Core, uid: String) {
log::info!("running timer task `{uid}`");
log_if_err!(Core::update_profile_item(core, uid, None).await);
}
}
enum DiffFlag {
Del(TaskID),
Add(TaskID, u64),
Mod(TaskID, u64),
}