Skip to main content

fret_runtime/
window_command_action_availability.rs

1use std::collections::HashMap;
2use std::sync::Arc;
3
4use fret_core::AppWindowId;
5
6use crate::CommandId;
7
8/// Window-scoped per-command "action availability" snapshots published by the UI layer.
9///
10/// This is a data-only integration seam used by cross-surface command gating (menus, command
11/// palette, shortcut help) without depending on `fret-ui` internals.
12///
13/// Semantics:
14/// - `None`: no availability information is published for this window/command (treat as unknown).
15/// - `Some(true)`: command is available along the current dispatch path.
16/// - `Some(false)`: command is unavailable (blocked or not reachable) along the current dispatch path.
17#[derive(Debug, Default)]
18pub struct WindowCommandActionAvailabilityService {
19    by_window: HashMap<AppWindowId, Arc<HashMap<CommandId, bool>>>,
20}
21
22impl WindowCommandActionAvailabilityService {
23    pub fn snapshot(&self, window: AppWindowId) -> Option<&HashMap<CommandId, bool>> {
24        self.by_window.get(&window).map(|m| m.as_ref())
25    }
26
27    pub fn snapshot_arc(&self, window: AppWindowId) -> Option<Arc<HashMap<CommandId, bool>>> {
28        self.by_window.get(&window).cloned()
29    }
30
31    pub fn available(&self, window: AppWindowId, command: &CommandId) -> Option<bool> {
32        self.by_window
33            .get(&window)
34            .and_then(|m| m.get(command).copied())
35    }
36
37    pub fn set_snapshot(&mut self, window: AppWindowId, availability: HashMap<CommandId, bool>) {
38        self.by_window.insert(window, Arc::new(availability));
39    }
40
41    pub fn remove_window(&mut self, window: AppWindowId) {
42        self.by_window.remove(&window);
43    }
44}
45
46#[cfg(test)]
47mod tests {
48    use super::*;
49
50    #[test]
51    fn available_default_is_none() {
52        let svc = WindowCommandActionAvailabilityService::default();
53        assert_eq!(
54            svc.available(AppWindowId::default(), &CommandId::from("app.preferences")),
55            None
56        );
57    }
58
59    #[test]
60    fn set_snapshot_publishes_values() {
61        let mut svc = WindowCommandActionAvailabilityService::default();
62        let window = AppWindowId::default();
63
64        let mut snapshot: HashMap<CommandId, bool> = HashMap::new();
65        snapshot.insert(CommandId::from("x"), true);
66        snapshot.insert(CommandId::from("y"), false);
67
68        svc.set_snapshot(window, snapshot);
69
70        assert_eq!(svc.available(window, &CommandId::from("x")), Some(true));
71        assert_eq!(svc.available(window, &CommandId::from("y")), Some(false));
72        assert_eq!(svc.available(window, &CommandId::from("z")), None);
73    }
74}