Skip to main content

rmux_core/hooks/
deferred.rs

1use rmux_proto::{HookName, ScopeSelector, WindowTarget};
2
3use super::rules::{hook_class, root_for_hook};
4use super::types::HookClass;
5use super::{HookDispatch, HookStore};
6
7impl HookStore {
8    /// Dispatches against a pre-mutation snapshot and commits only the
9    /// resulting one-shot consumption to this live store.
10    ///
11    /// Reusing the same snapshot for an ordered event batch preserves normal
12    /// fallback semantics and prevents a global one-shot from being copied to
13    /// more than one deferred event.
14    pub fn dispatch_deferred_from(
15        &mut self,
16        snapshot: &mut Self,
17        scope: &ScopeSelector,
18        hook: HookName,
19    ) -> Vec<HookDispatch> {
20        let (resolved_scope, one_shot_indices) = snapshot.resolved_dispatch_binding(scope, hook);
21        let dispatches = snapshot.dispatch(scope, hook);
22        if dispatches.is_empty() {
23            return dispatches;
24        }
25        for index in one_shot_indices {
26            let _ = self.unset(resolved_scope.clone(), hook, Some(index));
27        }
28        dispatches
29    }
30
31    pub(super) fn resolved_dispatch_binding(
32        &self,
33        scope: &ScopeSelector,
34        hook: HookName,
35    ) -> (ScopeSelector, Vec<u32>) {
36        let local_scope = match hook_class(hook) {
37            HookClass::Session => match scope {
38                ScopeSelector::Session(session_name) => {
39                    Some(ScopeSelector::Session(session_name.clone()))
40                }
41                ScopeSelector::Window(target) => {
42                    Some(ScopeSelector::Session(target.session_name().clone()))
43                }
44                ScopeSelector::Pane(target) => {
45                    Some(ScopeSelector::Session(target.session_name().clone()))
46                }
47                ScopeSelector::Global => None,
48            },
49            HookClass::Window => match scope {
50                ScopeSelector::Window(target) => Some(ScopeSelector::Window(target.clone())),
51                ScopeSelector::Pane(target) => Some(ScopeSelector::Window(
52                    WindowTarget::with_window(target.session_name().clone(), target.window_index()),
53                )),
54                ScopeSelector::Global | ScopeSelector::Session(_) => None,
55            },
56            HookClass::Pane => match scope {
57                ScopeSelector::Pane(target) => Some(ScopeSelector::Pane(target.clone())),
58                ScopeSelector::Global | ScopeSelector::Session(_) | ScopeSelector::Window(_) => {
59                    None
60                }
61            },
62        };
63
64        if let Some(local_scope) = local_scope {
65            let indices = self.one_shot_indices_for_exact_scope(&local_scope, hook);
66            let has_binding = match &local_scope {
67                ScopeSelector::Session(session_name) => self
68                    .sessions
69                    .get(session_name)
70                    .is_some_and(|bindings| bindings.command(hook).is_some()),
71                ScopeSelector::Window(target) => self.window_command(target, hook).is_some(),
72                ScopeSelector::Pane(target) => self.pane_command(target, hook).is_some(),
73                ScopeSelector::Global => false,
74            };
75            if has_binding {
76                return (local_scope, indices);
77            }
78        }
79
80        let global_scope = ScopeSelector::Global;
81        let indices = self.one_shot_indices_for_exact_scope(&global_scope, hook);
82        (global_scope, indices)
83    }
84
85    fn one_shot_indices_for_exact_scope(&self, scope: &ScopeSelector, hook: HookName) -> Vec<u32> {
86        match scope {
87            ScopeSelector::Global => self
88                .global_bindings(root_for_hook(hook))
89                .one_shot_indices(hook),
90            ScopeSelector::Session(session_name) => self
91                .sessions
92                .get(session_name)
93                .map_or_else(Vec::new, |bindings| bindings.one_shot_indices(hook)),
94            ScopeSelector::Window(target) => self
95                .windows
96                .get(target)
97                .or_else(|| {
98                    self.window_aliases
99                        .get(target)
100                        .and_then(|window_id| self.windows_by_id.get(window_id))
101                })
102                .map_or_else(Vec::new, |bindings| bindings.one_shot_indices(hook)),
103            ScopeSelector::Pane(target) => self
104                .panes
105                .get(target)
106                .or_else(|| {
107                    self.pane_aliases
108                        .get(target)
109                        .and_then(|(_, pane_id)| self.panes_by_id.get(pane_id))
110                })
111                .map_or_else(Vec::new, |bindings| bindings.one_shot_indices(hook)),
112        }
113    }
114}