use std::{collections::HashMap, mem, ops::Deref, sync::atomic::{AtomicU64, Ordering}, time::UNIX_EPOCH}; use anyhow::Result; use parking_lot::RwLock; use tokio::{fs::{self, File, OpenOptions}, io::{AsyncBufReadExt, AsyncWriteExt, BufReader, BufWriter}}; use yazi_boot::BOOT; use yazi_shared::{timestamp_us, RoCell}; use crate::{body::Body, CLIENTS}; pub static STATE: RoCell = RoCell::new(); #[derive(Default)] pub struct State { inner: RwLock>>, last: AtomicU64, } impl Deref for State { type Target = RwLock>>; fn deref(&self) -> &Self::Target { &self.inner } } impl State { pub fn set(&self, kind: &str, severity: u16, body: &str) -> bool { let Some(inner) = &mut *self.inner.write() else { return false }; let key = format!("{}_{severity}_{kind}", Body::tab(kind, body)); if body == "null" { return inner .remove(&key) .map(|_| self.last.store(timestamp_us(), Ordering::Relaxed)) .is_some(); } let value = format!("{kind},0,{severity},{body}\n"); if inner.get(&key).is_some_and(|s| *s == value) { return false; } inner.insert(key, value); self.last.store(timestamp_us(), Ordering::Relaxed); true } pub async fn load_or_create(&self) { if self.load().await.is_err() { self.inner.write().replace(Default::default()); self.last.store(timestamp_us(), Ordering::Relaxed); } } pub async fn drain(&self) -> Result<()> { let Some(inner) = self.inner.write().take() else { return Ok(()) }; if self.skip().await.unwrap_or(false) { return Ok(()); } let mut buf = BufWriter::new( OpenOptions::new() .write(true) .create(true) .truncate(true) .open(BOOT.state_dir.join(".dds")) .await?, ); let mut state = inner.into_iter().collect::>(); state.sort_unstable_by(|(a, _), (b, _)| a.cmp(b)); for (_, v) in state { buf.write_all(v.as_bytes()).await?; buf.write_u8(b'\n').await?; } buf.flush().await?; Ok(()) } async fn load(&self) -> Result<()> { let mut buf = BufReader::new(File::open(BOOT.state_dir.join(".dds")).await?); let mut line = String::new(); let mut inner = HashMap::new(); while buf.read_line(&mut line).await? > 0 { let mut parts = line.splitn(4, ','); let Some(kind) = parts.next() else { continue }; let Some(_) = parts.next() else { continue }; let Some(severity) = parts.next().and_then(|s| s.parse::().ok()) else { continue }; let Some(body) = parts.next() else { continue }; inner.insert(format!("{}_{severity}_{kind}", Body::tab(kind, body)), mem::take(&mut line)); } let clients = CLIENTS.read(); for payload in inner.values() { clients.values().for_each(|c| _ = c.tx.send(payload.clone())); } self.inner.write().replace(inner); self.last.store(timestamp_us(), Ordering::Relaxed); Ok(()) } async fn skip(&self) -> Result { let meta = fs::symlink_metadata(BOOT.state_dir.join(".dds")).await?; let modified = meta.modified()?.duration_since(UNIX_EPOCH)?.as_micros(); Ok(modified >= self.last.load(Ordering::Relaxed) as u128) } }