Skip to main content

fret_runtime/
runner_window_lifecycle_diagnostics.rs

1use std::collections::HashSet;
2
3use fret_core::{
4    AppWindowId,
5    time::{SystemTime, UNIX_EPOCH},
6};
7
8/// Diagnostics store for runner window lifecycle events (open/close).
9///
10/// Diagnostics scripts use window-count predicates (e.g. `known_window_count_is`) as a stable
11/// signal that OS windows have been created/closed. Counting windows via input-context snapshots
12/// is opportunistic (it depends on input dispatch), so the runner maintains a source-of-truth set
13/// of open windows for deterministic scripted runs.
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
15pub struct RunnerWindowLifecycleSnapshot {
16    pub open_window_count: u32,
17    pub window_open_events: u64,
18    pub window_close_events: u64,
19    pub last_window_open_unix_ms: Option<u64>,
20    pub last_window_close_unix_ms: Option<u64>,
21}
22
23#[derive(Debug, Default)]
24pub struct RunnerWindowLifecycleDiagnosticsStore {
25    open_windows: HashSet<AppWindowId>,
26    snapshot: RunnerWindowLifecycleSnapshot,
27}
28
29impl RunnerWindowLifecycleDiagnosticsStore {
30    pub fn snapshot(&self) -> RunnerWindowLifecycleSnapshot {
31        self.snapshot
32    }
33
34    pub fn record_window_open(&mut self, window: AppWindowId) {
35        let inserted = self.open_windows.insert(window);
36        if inserted {
37            self.snapshot.window_open_events = self.snapshot.window_open_events.saturating_add(1);
38            self.snapshot.last_window_open_unix_ms = Some(unix_ms_now());
39            self.snapshot.open_window_count = self.open_windows.len().min(u32::MAX as usize) as u32;
40        }
41    }
42
43    pub fn record_window_close(&mut self, window: AppWindowId) {
44        let removed = self.open_windows.remove(&window);
45        if removed {
46            self.snapshot.window_close_events = self.snapshot.window_close_events.saturating_add(1);
47            self.snapshot.last_window_close_unix_ms = Some(unix_ms_now());
48            self.snapshot.open_window_count = self.open_windows.len().min(u32::MAX as usize) as u32;
49        }
50    }
51}
52
53fn unix_ms_now() -> u64 {
54    SystemTime::now()
55        .duration_since(UNIX_EPOCH)
56        .map(|d| d.as_millis() as u64)
57        .unwrap_or(0)
58}