//! Process-global mutable state. use parking_lot::{Mutex, RwLock}; use std::collections::{HashMap, HashSet}; use std::path::PathBuf; use std::sync::Arc; use crate::cli_usage::CliUsage; use crate::jsonl::UsageEvent; use crate::settings::Settings; use crate::watch::WatcherHandle; /// Per-file parse cache. We resume reading from `byte_offset` on the next /// poll, so we never re-parse already-seen lines. pub struct FileCache { pub mtime_ns: i128, pub size: u64, pub byte_offset: u64, pub messages: Vec, } pub struct AppState { pub roots: RwLock>, pub files: RwLock>, pub seen_ids: RwLock>, pub settings: RwLock, /// Latest /usage result. Refreshed periodically by a background task, /// or on-demand via the `refresh_cli_usage` command. pub cli_usage: RwLock>, /// Boxed so we can keep the watcher alive across the whole app lifetime /// without polluting Tauri's setup hook. pub watcher: Mutex>, } pub type SharedState = Arc; impl AppState { pub fn new(settings: Settings) -> SharedState { Arc::new(Self { roots: RwLock::new(Vec::new()), files: RwLock::new(HashMap::new()), seen_ids: RwLock::new(HashSet::new()), settings: RwLock::new(settings), cli_usage: RwLock::new(None), watcher: Mutex::new(None), }) } pub fn set_watcher(&self, w: WatcherHandle) { *self.watcher.lock() = Some(w); } /// Snapshot all events across all cached files in one allocation. /// Caller is responsible for sorting if needed. pub fn collect_events(&self) -> Vec { let files = self.files.read(); let mut out = Vec::with_capacity(files.values().map(|f| f.messages.len()).sum()); for f in files.values() { out.extend(f.messages.iter().cloned()); } out } }