mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-11-04 14:22:11 +08:00
refactor(core): derive refresh announcements from transaction commit
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
use super::{handle::Handle, notification::FrontendEvent};
|
||||
use anyhow::Result;
|
||||
use std::{cell::RefCell, future::Future};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum Refresh {
|
||||
Clash,
|
||||
Verge,
|
||||
Profiles,
|
||||
Proxies,
|
||||
}
|
||||
|
||||
tokio::task_local! {
|
||||
static PENDING: RefCell<Vec<Refresh>>;
|
||||
}
|
||||
|
||||
pub fn announce(refresh: Refresh) {
|
||||
if PENDING.try_with(|pending| pending.borrow_mut().push(refresh)).is_err() {
|
||||
emit(refresh);
|
||||
}
|
||||
}
|
||||
|
||||
fn emit(refresh: Refresh) {
|
||||
Handle::send_event(match refresh {
|
||||
Refresh::Clash => FrontendEvent::RefreshClash,
|
||||
Refresh::Verge => FrontendEvent::RefreshVerge,
|
||||
Refresh::Profiles => FrontendEvent::RefreshProfiles,
|
||||
Refresh::Proxies => FrontendEvent::RefreshProxyConfig,
|
||||
});
|
||||
}
|
||||
|
||||
async fn collect_committed<T: Send>(operation: impl Future<Output = Result<T>> + Send) -> Result<(T, Vec<Refresh>)> {
|
||||
PENDING
|
||||
.scope(RefCell::new(Vec::new()), async {
|
||||
let value = operation.await?;
|
||||
let refreshes = PENDING.with(|pending| pending.take());
|
||||
Ok((value, refreshes))
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn after_commit<T: Send>(operation: impl Future<Output = Result<T>> + Send) -> Result<T> {
|
||||
let (value, refreshes) = collect_committed(operation).await?;
|
||||
// Nested transactions hand their notifications to the outer transaction.
|
||||
for refresh in refreshes {
|
||||
announce(refresh);
|
||||
}
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn only_successful_transactions_publish_refreshes() -> Result<()> {
|
||||
let (_, refreshes) = collect_committed(async {
|
||||
announce(Refresh::Clash);
|
||||
after_commit(async {
|
||||
announce(Refresh::Verge);
|
||||
Ok(())
|
||||
})
|
||||
.await?;
|
||||
let failed = after_commit(async {
|
||||
announce(Refresh::Profiles);
|
||||
Err::<(), _>(anyhow::anyhow!("persistence failed"))
|
||||
})
|
||||
.await;
|
||||
assert!(failed.is_err());
|
||||
announce(Refresh::Clash);
|
||||
Ok(())
|
||||
})
|
||||
.await?;
|
||||
assert_eq!(refreshes, [Refresh::Clash, Refresh::Verge, Refresh::Clash]);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -40,19 +40,11 @@ impl Handle {
|
||||
}
|
||||
|
||||
pub fn refresh_clash() {
|
||||
Self::send_event(FrontendEvent::RefreshClash);
|
||||
super::announce(super::Refresh::Clash);
|
||||
}
|
||||
|
||||
pub fn refresh_verge() {
|
||||
Self::send_event(FrontendEvent::RefreshVerge);
|
||||
}
|
||||
|
||||
pub fn refresh_profiles() {
|
||||
Self::send_event(FrontendEvent::RefreshProfiles);
|
||||
}
|
||||
|
||||
pub fn refresh_proxy_config() {
|
||||
Self::send_event(FrontendEvent::RefreshProxyConfig);
|
||||
super::announce(super::Refresh::Verge);
|
||||
}
|
||||
|
||||
/// Push a Run State snapshot to the frontend.
|
||||
@@ -103,7 +95,7 @@ impl Handle {
|
||||
self.is_exiting.load(Ordering::Acquire)
|
||||
}
|
||||
|
||||
fn send_event(event: FrontendEvent) {
|
||||
pub(super) fn send_event(event: FrontendEvent) {
|
||||
let handle = Self::global();
|
||||
if handle.is_exiting() {
|
||||
return;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
mod announce;
|
||||
pub use announce::{Refresh, after_commit, announce};
|
||||
pub mod desktop;
|
||||
pub mod handle;
|
||||
pub mod notification;
|
||||
|
||||
Reference in New Issue
Block a user