Skip to main content

fret_runtime/
window_command_enabled.rs

1use std::collections::HashMap;
2
3use fret_core::AppWindowId;
4
5use crate::CommandId;
6
7/// Window-scoped per-command enabled/disabled overrides published by the app layer.
8///
9/// This is a data-only integration seam used by cross-surface command gating (menus, command
10/// palette, shortcuts) without depending on UI-kit or app-specific model types.
11///
12/// Semantics:
13/// - `None`: no override; fall back to `when` evaluation and other gating.
14/// - `Some(true)`: force enabled (still may be disabled by other gating).
15/// - `Some(false)`: force disabled.
16#[derive(Debug, Default)]
17pub struct WindowCommandEnabledService {
18    by_window: HashMap<AppWindowId, HashMap<CommandId, bool>>,
19}
20
21impl WindowCommandEnabledService {
22    pub fn snapshot(&self, window: AppWindowId) -> Option<&HashMap<CommandId, bool>> {
23        self.by_window.get(&window)
24    }
25
26    pub fn enabled(&self, window: AppWindowId, command: &CommandId) -> Option<bool> {
27        self.by_window
28            .get(&window)
29            .and_then(|m| m.get(command).copied())
30    }
31
32    pub fn set_snapshot(&mut self, window: AppWindowId, enabled: HashMap<CommandId, bool>) {
33        self.by_window.insert(window, enabled);
34    }
35
36    pub fn set_enabled(&mut self, window: AppWindowId, command: CommandId, enabled: bool) {
37        self.by_window
38            .entry(window)
39            .or_default()
40            .insert(command, enabled);
41    }
42
43    pub fn clear_command(&mut self, window: AppWindowId, command: &CommandId) {
44        let Some(map) = self.by_window.get_mut(&window) else {
45            return;
46        };
47        map.remove(command);
48        if map.is_empty() {
49            self.by_window.remove(&window);
50        }
51    }
52
53    pub fn remove_window(&mut self, window: AppWindowId) {
54        self.by_window.remove(&window);
55    }
56}
57
58#[cfg(test)]
59mod tests {
60    use super::*;
61
62    #[test]
63    fn enabled_default_is_none() {
64        let svc = WindowCommandEnabledService::default();
65        assert_eq!(
66            svc.enabled(AppWindowId::default(), &CommandId::from("app.preferences")),
67            None
68        );
69    }
70
71    #[test]
72    fn set_and_clear_command() {
73        let mut svc = WindowCommandEnabledService::default();
74        let window = AppWindowId::default();
75        let cmd = CommandId::from("app.preferences");
76
77        svc.set_enabled(window, cmd.clone(), false);
78        assert_eq!(svc.enabled(window, &cmd), Some(false));
79
80        svc.set_enabled(window, cmd.clone(), true);
81        assert_eq!(svc.enabled(window, &cmd), Some(true));
82
83        svc.clear_command(window, &cmd);
84        assert_eq!(svc.enabled(window, &cmd), None);
85        assert!(svc.snapshot(window).is_none());
86    }
87}