diff --git a/src-tauri/src/cmds.rs b/src-tauri/src/cmds.rs index 2bef9e9..9d4b6b3 100644 --- a/src-tauri/src/cmds.rs +++ b/src-tauri/src/cmds.rs @@ -58,38 +58,7 @@ pub async fn update_profile( option: Option, 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 { let verge = core.verge.lock(); - let config = verge.clone(); - - Ok(config) + Ok(verge.clone()) } /// patch the verge config diff --git a/src-tauri/src/core/mod.rs b/src-tauri/src/core/mod.rs index 3867b2d..37daa47 100644 --- a/src-tauri/src/core/mod.rs +++ b/src-tauri/src/core/mod.rs @@ -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>, + pub timer: Arc>, + pub window: Arc>>, } @@ -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, + ) -> 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(()) + } +} diff --git a/src-tauri/src/core/prfitem.rs b/src-tauri/src/core/prfitem.rs index 48f5a6c..333b5bd 100644 --- a/src-tauri/src/core/prfitem.rs +++ b/src-tauri/src/core/prfitem.rs @@ -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 { diff --git a/src-tauri/src/core/profiles.rs b/src-tauri/src/core/profiles.rs index fe00c50..60f1a9a 100644 --- a/src-tauri/src/core/profiles.rs +++ b/src-tauri/src/core/profiles.rs @@ -100,6 +100,11 @@ impl Profiles { self.valid = valid; } + /// get items ref + pub fn get_items(&self) -> Option<&Vec> { + self.items.as_ref() + } + /// find the item by the uid pub fn get_item(&self, uid: &String) -> Result<&PrfItem> { if self.items.is_some() { diff --git a/src-tauri/src/core/timer.rs b/src-tauri/src/core/timer.rs index 2e94a6f..e857e9d 100644 --- a/src-tauri/src/core/timer.rs +++ b/src-tauri/src/core/timer.rs @@ -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, + timer_map: HashMap, - timer_count: u64, + timer_count: TaskID, + + core: Option, } 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 { + 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 { + 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), }