Skip to main content

rmux_core/hooks/
rules.rs

1use rmux_proto::{HookName, RmuxError, ScopeSelector, SessionName, Target, WindowTarget};
2
3use super::types::{HookClass, HookGlobalRoot};
4
5/// Validates that a hook may be stored at the requested scope.
6pub fn validate_hook_scope(hook: HookName, scope: &ScopeSelector) -> Result<(), RmuxError> {
7    let _ = hook_class(hook);
8    let _ = scope;
9    Ok(())
10}
11
12/// Validates that rmux ships the requested hook and that it may be stored at
13/// the requested scope.
14pub fn validate_hook_registration(hook: HookName, scope: &ScopeSelector) -> Result<(), RmuxError> {
15    if !hook_is_supported_for_registration(hook) {
16        return Err(RmuxError::Message(format!(
17            "{} is not supported: rmux does not dispatch this hook",
18            hook_name(hook)
19        )));
20    }
21
22    validate_hook_scope(hook, scope)
23}
24
25/// Resolves the measured natural hook storage scope after target-pane lookup.
26#[must_use]
27pub fn hook_natural_scope_for_target(hook: HookName, target: Target) -> ScopeSelector {
28    match target {
29        Target::Session(session_name) => ScopeSelector::Session(session_name),
30        Target::Window(target) => match hook_class(hook) {
31            HookClass::Session => ScopeSelector::Session(target.session_name().clone()),
32            HookClass::Window | HookClass::Pane => ScopeSelector::Window(target),
33        },
34        Target::Pane(target) => match hook_class(hook) {
35            HookClass::Session => ScopeSelector::Session(target.session_name().clone()),
36            HookClass::Window | HookClass::Pane => ScopeSelector::Window(
37                WindowTarget::with_window(target.session_name().clone(), target.window_index()),
38            ),
39        },
40    }
41}
42
43/// Normalizes a scope selected explicitly with `-w` or `-p` for the hook.
44#[must_use]
45pub fn hook_explicit_scope_for_target(hook: HookName, target: Target) -> ScopeSelector {
46    match target {
47        Target::Session(session_name) => ScopeSelector::Session(session_name),
48        Target::Window(target) => match hook_class(hook) {
49            HookClass::Session => ScopeSelector::Session(target.session_name().clone()),
50            HookClass::Window | HookClass::Pane => ScopeSelector::Window(target),
51        },
52        Target::Pane(target) => match hook_class(hook) {
53            HookClass::Session => ScopeSelector::Session(target.session_name().clone()),
54            HookClass::Window => ScopeSelector::Window(WindowTarget::with_window(
55                target.session_name().clone(),
56                target.window_index(),
57            )),
58            HookClass::Pane => ScopeSelector::Pane(target),
59        },
60    }
61}
62
63/// Resolves tmux's natural `-t <session>` storage scope after the session's
64/// current window and pane have been looked up by the server.
65#[must_use]
66pub fn hook_natural_scope_for_session_target(
67    hook: HookName,
68    session_name: SessionName,
69    window_index: u32,
70    _pane_index: u32,
71) -> ScopeSelector {
72    match hook_class(hook) {
73        HookClass::Session => ScopeSelector::Session(session_name),
74        HookClass::Window | HookClass::Pane => {
75            ScopeSelector::Window(WindowTarget::with_window(session_name, window_index))
76        }
77    }
78}
79
80pub(super) const fn hook_inventory() -> [HookName; 70] {
81    [
82        HookName::AfterBindKey,
83        HookName::AfterCapturePane,
84        HookName::AfterCopyMode,
85        HookName::AfterDisplayMessage,
86        HookName::AfterDisplayPanes,
87        HookName::AfterKillPane,
88        HookName::AfterListBuffers,
89        HookName::AfterListClients,
90        HookName::AfterListKeys,
91        HookName::AfterListPanes,
92        HookName::AfterListSessions,
93        HookName::AfterListWindows,
94        HookName::AfterLoadBuffer,
95        HookName::AfterLockServer,
96        HookName::AfterNewSession,
97        HookName::AfterNewWindow,
98        HookName::AfterPasteBuffer,
99        HookName::AfterPipePane,
100        HookName::AfterQueue,
101        HookName::AfterRefreshClient,
102        HookName::AfterRenameSession,
103        HookName::AfterRenameWindow,
104        HookName::AfterResizePane,
105        HookName::AfterResizeWindow,
106        HookName::AfterSaveBuffer,
107        HookName::AfterSelectLayout,
108        HookName::AfterSelectPane,
109        HookName::AfterSelectWindow,
110        HookName::AfterSendKeys,
111        HookName::AfterSetBuffer,
112        HookName::AfterSetEnvironment,
113        HookName::AfterSetHook,
114        HookName::AfterSetOption,
115        HookName::AfterShowEnvironment,
116        HookName::AfterShowMessages,
117        HookName::AfterShowOptions,
118        HookName::AfterSplitWindow,
119        HookName::AfterUnbindKey,
120        HookName::AlertActivity,
121        HookName::AlertBell,
122        HookName::AlertSilence,
123        HookName::ClientActive,
124        HookName::ClientAttached,
125        HookName::ClientDetached,
126        HookName::ClientFocusIn,
127        HookName::ClientFocusOut,
128        HookName::ClientResized,
129        HookName::ClientSessionChanged,
130        HookName::ClientLightTheme,
131        HookName::ClientDarkTheme,
132        HookName::CommandError,
133        HookName::PaneDied,
134        HookName::PaneExited,
135        HookName::PaneFocusIn,
136        HookName::PaneFocusOut,
137        HookName::PaneModeChanged,
138        HookName::PaneSetClipboard,
139        HookName::PaneTitleChanged,
140        HookName::SessionClosed,
141        HookName::SessionCreated,
142        HookName::SessionRenamed,
143        HookName::SessionWindowChanged,
144        HookName::WindowLayoutChanged,
145        HookName::WindowLinked,
146        HookName::WindowPaneChanged,
147        HookName::WindowRenamed,
148        HookName::WindowResized,
149        HookName::WindowUnlinked,
150        HookName::PasteBufferChanged,
151        HookName::PasteBufferDeleted,
152    ]
153}
154
155pub(super) const fn hook_class(hook: HookName) -> HookClass {
156    match hook {
157        HookName::WindowLayoutChanged
158        | HookName::WindowPaneChanged
159        | HookName::WindowRenamed
160        | HookName::WindowResized => HookClass::Window,
161        HookName::PaneDied
162        | HookName::PaneExited
163        | HookName::PaneFocusIn
164        | HookName::PaneFocusOut
165        | HookName::PaneModeChanged
166        | HookName::PaneSetClipboard
167        | HookName::PaneTitleChanged => HookClass::Pane,
168        HookName::AfterBindKey
169        | HookName::AfterCapturePane
170        | HookName::AfterCopyMode
171        | HookName::AfterDisplayMessage
172        | HookName::AfterDisplayPanes
173        | HookName::AfterKillPane
174        | HookName::AfterListBuffers
175        | HookName::AfterListClients
176        | HookName::AfterListKeys
177        | HookName::AfterListPanes
178        | HookName::AfterListSessions
179        | HookName::AfterListWindows
180        | HookName::AfterLoadBuffer
181        | HookName::AfterLockServer
182        | HookName::AfterNewSession
183        | HookName::AfterNewWindow
184        | HookName::AfterPasteBuffer
185        | HookName::AfterPipePane
186        | HookName::AfterQueue
187        | HookName::AfterRefreshClient
188        | HookName::AfterRenameSession
189        | HookName::AfterRenameWindow
190        | HookName::AfterResizePane
191        | HookName::AfterResizeWindow
192        | HookName::AfterSaveBuffer
193        | HookName::AfterSelectLayout
194        | HookName::AfterSelectPane
195        | HookName::AfterSelectWindow
196        | HookName::AfterSendKeys
197        | HookName::AfterSetBuffer
198        | HookName::AfterSetEnvironment
199        | HookName::AfterSetHook
200        | HookName::AfterSetOption
201        | HookName::AfterShowEnvironment
202        | HookName::AfterShowMessages
203        | HookName::AfterShowOptions
204        | HookName::AfterSplitWindow
205        | HookName::AfterUnbindKey
206        | HookName::AlertActivity
207        | HookName::AlertBell
208        | HookName::AlertSilence
209        | HookName::ClientActive
210        | HookName::ClientAttached
211        | HookName::ClientDetached
212        | HookName::ClientFocusIn
213        | HookName::ClientFocusOut
214        | HookName::ClientResized
215        | HookName::ClientSessionChanged
216        | HookName::ClientLightTheme
217        | HookName::ClientDarkTheme
218        | HookName::CommandError
219        | HookName::SessionCreated
220        | HookName::SessionClosed
221        | HookName::SessionRenamed
222        | HookName::SessionWindowChanged
223        | HookName::WindowLinked
224        | HookName::WindowUnlinked
225        | HookName::PasteBufferChanged
226        | HookName::PasteBufferDeleted => HookClass::Session,
227    }
228}
229
230pub(super) const fn root_for_hook(hook: HookName) -> HookGlobalRoot {
231    match hook_class(hook) {
232        HookClass::Session => HookGlobalRoot::Session,
233        HookClass::Window | HookClass::Pane => HookGlobalRoot::Window,
234    }
235}
236
237/// Returns the global hook root where tmux stores a hook.
238#[must_use]
239pub const fn hook_global_root(hook: HookName) -> HookGlobalRoot {
240    root_for_hook(hook)
241}
242
243pub(super) const fn hook_is_visible_in_show_hooks(hook: HookName) -> bool {
244    !matches!(
245        hook,
246        HookName::PasteBufferChanged | HookName::PasteBufferDeleted
247    )
248}
249
250const fn hook_is_supported_for_registration(hook: HookName) -> bool {
251    !matches!(
252        hook,
253        HookName::PasteBufferChanged | HookName::PasteBufferDeleted
254    )
255}
256
257const fn hook_name(hook: HookName) -> &'static str {
258    hook.as_str()
259}