Skip to main content

rmux_core/
lifecycle.rs

1use rmux_proto::{HookName, PaneTarget, ScopeSelector, SessionName, Target, WindowTarget};
2
3/// A typed server lifecycle event that may dispatch a registered hook.
4#[derive(Debug, Clone, PartialEq, Eq)]
5pub enum LifecycleEvent {
6    /// A client attached to a session.
7    ClientAttached {
8        /// The session associated with the event.
9        session_name: SessionName,
10        /// The best available rmux client identifier.
11        client_name: Option<String>,
12    },
13    /// A client detached from a session.
14    ClientDetached {
15        /// The session associated with the event.
16        session_name: SessionName,
17        /// The best available rmux client identifier.
18        client_name: Option<String>,
19    },
20    /// A client switched to another session.
21    ClientSessionChanged {
22        /// The session associated with the event.
23        session_name: SessionName,
24        /// The best available rmux client identifier.
25        client_name: Option<String>,
26    },
27    /// A session was created.
28    SessionCreated {
29        /// The session associated with the event.
30        session_name: SessionName,
31    },
32    /// A session was closed.
33    SessionClosed {
34        /// The session associated with the event.
35        session_name: SessionName,
36        /// The removed session ID captured at notification time, when known.
37        session_id: Option<u32>,
38    },
39    /// A session was renamed.
40    SessionRenamed {
41        /// The session associated with the event.
42        session_name: SessionName,
43    },
44    /// A session's active window changed.
45    SessionWindowChanged {
46        /// The session associated with the event.
47        session_name: SessionName,
48    },
49    /// A window was linked into a session.
50    WindowLinked {
51        /// The session associated with the event.
52        session_name: SessionName,
53        /// The linked window, when it is known at the call site.
54        target: Option<WindowTarget>,
55    },
56    /// A window was unlinked from a session.
57    WindowUnlinked {
58        /// The session associated with the event.
59        session_name: SessionName,
60        /// The unlinked window, when it is known at the call site.
61        target: Option<WindowTarget>,
62        /// The removed window ID captured at notification time, when known.
63        window_id: Option<u32>,
64        /// The removed window name captured at notification time, when known.
65        window_name: Option<String>,
66    },
67    /// A window was renamed.
68    WindowRenamed {
69        /// The targeted window.
70        target: WindowTarget,
71    },
72    /// A window layout changed.
73    WindowLayoutChanged {
74        /// The targeted window.
75        target: WindowTarget,
76    },
77    /// A window size changed.
78    WindowResized {
79        /// The targeted window.
80        target: WindowTarget,
81    },
82    /// A window's active pane changed.
83    WindowPaneChanged {
84        /// The targeted window.
85        target: WindowTarget,
86    },
87    /// A bell alert fired for a window.
88    AlertBell {
89        /// The targeted window.
90        target: WindowTarget,
91    },
92    /// An activity alert fired for a window.
93    AlertActivity {
94        /// The targeted window.
95        target: WindowTarget,
96    },
97    /// A silence alert fired for a window.
98    AlertSilence {
99        /// The targeted window.
100        target: WindowTarget,
101    },
102    /// A pane exited or was removed from service.
103    PaneExited {
104        /// The targeted pane.
105        target: PaneTarget,
106        /// The removed pane ID captured at notification time, when known.
107        pane_id: Option<u32>,
108        /// The removed window ID captured at notification time, when known.
109        window_id: Option<u32>,
110        /// The removed window name captured at notification time, when known.
111        window_name: Option<String>,
112    },
113    /// A pane died and was kept by `remain-on-exit`.
114    PaneDied {
115        /// The targeted pane.
116        target: PaneTarget,
117        /// The dead pane ID captured at notification time, when known.
118        pane_id: Option<u32>,
119        /// The dead pane's window ID captured at notification time, when known.
120        window_id: Option<u32>,
121        /// The dead pane's window name captured at notification time, when known.
122        window_name: Option<String>,
123    },
124    /// A pane mode or display overlay state changed.
125    PaneModeChanged {
126        /// The targeted pane.
127        target: PaneTarget,
128    },
129    /// A paste buffer was created or replaced.
130    PasteBufferChanged {
131        /// The affected paste buffer name.
132        buffer_name: String,
133    },
134    /// A paste buffer was deleted or evicted.
135    PasteBufferDeleted {
136        /// The affected paste buffer name.
137        buffer_name: String,
138    },
139    /// A `select-window`-family command completed successfully.
140    AfterSelectWindow {
141        /// The selected window.
142        target: WindowTarget,
143    },
144    /// A `select-pane`-family command completed successfully.
145    AfterSelectPane {
146        /// The selected pane.
147        target: PaneTarget,
148    },
149    /// A `send-keys` command completed successfully.
150    AfterSendKeys {
151        /// The targeted pane.
152        target: PaneTarget,
153    },
154    /// A `set-option` command completed successfully.
155    AfterSetOption {
156        /// The session associated with the option scope, when one exists.
157        session_name: Option<SessionName>,
158    },
159}
160
161impl LifecycleEvent {
162    /// Returns the hook name corresponding to this lifecycle event.
163    #[must_use]
164    pub const fn hook_name(&self) -> HookName {
165        match self {
166            Self::ClientAttached { .. } => HookName::ClientAttached,
167            Self::ClientDetached { .. } => HookName::ClientDetached,
168            Self::ClientSessionChanged { .. } => HookName::ClientSessionChanged,
169            Self::SessionCreated { .. } => HookName::SessionCreated,
170            Self::SessionClosed { .. } => HookName::SessionClosed,
171            Self::SessionRenamed { .. } => HookName::SessionRenamed,
172            Self::SessionWindowChanged { .. } => HookName::SessionWindowChanged,
173            Self::WindowLinked { .. } => HookName::WindowLinked,
174            Self::WindowUnlinked { .. } => HookName::WindowUnlinked,
175            Self::WindowRenamed { .. } => HookName::WindowRenamed,
176            Self::WindowLayoutChanged { .. } => HookName::WindowLayoutChanged,
177            Self::WindowResized { .. } => HookName::WindowResized,
178            Self::WindowPaneChanged { .. } => HookName::WindowPaneChanged,
179            Self::AlertBell { .. } => HookName::AlertBell,
180            Self::AlertActivity { .. } => HookName::AlertActivity,
181            Self::AlertSilence { .. } => HookName::AlertSilence,
182            Self::PaneExited { .. } => HookName::PaneExited,
183            Self::PaneDied { .. } => HookName::PaneDied,
184            Self::PaneModeChanged { .. } => HookName::PaneModeChanged,
185            Self::PasteBufferChanged { .. } => HookName::PasteBufferChanged,
186            Self::PasteBufferDeleted { .. } => HookName::PasteBufferDeleted,
187            Self::AfterSelectWindow { .. } => HookName::AfterSelectWindow,
188            Self::AfterSelectPane { .. } => HookName::AfterSelectPane,
189            Self::AfterSendKeys { .. } => HookName::AfterSendKeys,
190            Self::AfterSetOption { .. } => HookName::AfterSetOption,
191        }
192    }
193
194    /// Returns the event scope used for hook resolution.
195    #[must_use]
196    pub fn scope(&self) -> ScopeSelector {
197        match self {
198            Self::ClientAttached { session_name, .. }
199            | Self::ClientDetached { session_name, .. }
200            | Self::ClientSessionChanged { session_name, .. }
201            | Self::SessionCreated { session_name }
202            | Self::SessionClosed { session_name, .. }
203            | Self::SessionRenamed { session_name }
204            | Self::SessionWindowChanged { session_name }
205            | Self::WindowLinked { session_name, .. }
206            | Self::WindowUnlinked { session_name, .. } => {
207                ScopeSelector::Session(session_name.clone())
208            }
209            Self::WindowRenamed { target }
210            | Self::WindowLayoutChanged { target }
211            | Self::WindowResized { target }
212            | Self::WindowPaneChanged { target }
213            | Self::AlertBell { target }
214            | Self::AlertActivity { target }
215            | Self::AlertSilence { target }
216            | Self::AfterSelectWindow { target } => ScopeSelector::Window(target.clone()),
217            Self::PaneExited { target, .. }
218            | Self::PaneDied { target, .. }
219            | Self::PaneModeChanged { target }
220            | Self::AfterSelectPane { target }
221            | Self::AfterSendKeys { target } => ScopeSelector::Pane(target.clone()),
222            Self::PasteBufferChanged { .. } | Self::PasteBufferDeleted { .. } => {
223                ScopeSelector::Global
224            }
225            Self::AfterSetOption {
226                session_name: Some(session_name),
227            } => ScopeSelector::Session(session_name.clone()),
228            Self::AfterSetOption { session_name: None } => ScopeSelector::Global,
229        }
230    }
231
232    /// Returns the session associated with this lifecycle event, when one exists.
233    #[must_use]
234    pub fn session_name(&self) -> Option<&SessionName> {
235        match self {
236            Self::ClientAttached { session_name, .. }
237            | Self::ClientDetached { session_name, .. }
238            | Self::ClientSessionChanged { session_name, .. }
239            | Self::SessionCreated { session_name }
240            | Self::SessionClosed { session_name, .. }
241            | Self::SessionRenamed { session_name }
242            | Self::SessionWindowChanged { session_name }
243            | Self::WindowLinked { session_name, .. }
244            | Self::WindowUnlinked { session_name, .. } => Some(session_name),
245            Self::WindowRenamed { target }
246            | Self::WindowLayoutChanged { target }
247            | Self::WindowResized { target }
248            | Self::WindowPaneChanged { target }
249            | Self::AlertBell { target }
250            | Self::AlertActivity { target }
251            | Self::AlertSilence { target }
252            | Self::AfterSelectWindow { target } => Some(target.session_name()),
253            Self::PaneExited { target, .. }
254            | Self::PaneDied { target, .. }
255            | Self::PaneModeChanged { target }
256            | Self::AfterSelectPane { target }
257            | Self::AfterSendKeys { target } => Some(target.session_name()),
258            Self::AfterSetOption { session_name } => session_name.as_ref(),
259            Self::PasteBufferChanged { .. } | Self::PasteBufferDeleted { .. } => None,
260        }
261    }
262
263    /// Returns the event client identifier when one is available.
264    #[must_use]
265    pub fn client_name(&self) -> Option<&str> {
266        match self {
267            Self::ClientAttached { client_name, .. }
268            | Self::ClientDetached { client_name, .. }
269            | Self::ClientSessionChanged { client_name, .. } => client_name.as_deref(),
270            _ => None,
271        }
272    }
273
274    /// Returns the event window target when one is available.
275    #[must_use]
276    pub fn window_target(&self) -> Option<WindowTarget> {
277        match self {
278            Self::WindowLinked { target, .. } | Self::WindowUnlinked { target, .. } => {
279                target.clone()
280            }
281            Self::WindowRenamed { target }
282            | Self::WindowLayoutChanged { target }
283            | Self::WindowResized { target }
284            | Self::WindowPaneChanged { target }
285            | Self::AlertBell { target }
286            | Self::AlertActivity { target }
287            | Self::AlertSilence { target }
288            | Self::AfterSelectWindow { target } => Some(target.clone()),
289            Self::PaneExited { target, .. }
290            | Self::PaneDied { target, .. }
291            | Self::PaneModeChanged { target }
292            | Self::AfterSelectPane { target }
293            | Self::AfterSendKeys { target } => Some(WindowTarget::with_window(
294                target.session_name().clone(),
295                target.window_index(),
296            )),
297            _ => None,
298        }
299    }
300
301    /// Returns the event pane target when one is available.
302    #[must_use]
303    pub fn pane_target(&self) -> Option<&PaneTarget> {
304        match self {
305            Self::PaneExited { target, .. }
306            | Self::PaneDied { target, .. }
307            | Self::PaneModeChanged { target }
308            | Self::AfterSelectPane { target }
309            | Self::AfterSendKeys { target } => Some(target),
310            _ => None,
311        }
312    }
313
314    /// Returns the event session ID captured at notification time, when one exists.
315    #[must_use]
316    pub const fn session_id(&self) -> Option<u32> {
317        match self {
318            Self::SessionClosed { session_id, .. } => *session_id,
319            _ => None,
320        }
321    }
322
323    /// Returns the event window ID captured at notification time, when one exists.
324    #[must_use]
325    pub const fn window_id(&self) -> Option<u32> {
326        match self {
327            Self::WindowUnlinked { window_id, .. }
328            | Self::PaneExited { window_id, .. }
329            | Self::PaneDied { window_id, .. } => *window_id,
330            _ => None,
331        }
332    }
333
334    /// Returns the event window name captured at notification time, when one exists.
335    #[must_use]
336    pub fn window_name_snapshot(&self) -> Option<&str> {
337        match self {
338            Self::WindowUnlinked { window_name, .. }
339            | Self::PaneExited { window_name, .. }
340            | Self::PaneDied { window_name, .. } => window_name.as_deref(),
341            _ => None,
342        }
343    }
344
345    /// Returns the event pane ID captured at notification time, when one exists.
346    #[must_use]
347    pub const fn pane_id(&self) -> Option<u32> {
348        match self {
349            Self::PaneExited { pane_id, .. } | Self::PaneDied { pane_id, .. } => *pane_id,
350            _ => None,
351        }
352    }
353
354    /// Returns the paste buffer name when one is available.
355    #[must_use]
356    pub fn buffer_name(&self) -> Option<&str> {
357        match self {
358            Self::PasteBufferChanged { buffer_name } | Self::PasteBufferDeleted { buffer_name } => {
359                Some(buffer_name)
360            }
361            _ => None,
362        }
363    }
364
365    /// Returns the best available current target for hook execution.
366    #[must_use]
367    pub fn current_target(&self) -> Option<Target> {
368        match self {
369            Self::WindowLinked {
370                session_name,
371                target: Some(target),
372            }
373            | Self::WindowUnlinked {
374                session_name,
375                target: Some(target),
376                ..
377            } => Some(Target::Window(WindowTarget::with_window(
378                session_name.clone(),
379                target.window_index(),
380            ))),
381            Self::WindowLinked { session_name, .. }
382            | Self::WindowUnlinked { session_name, .. }
383            | Self::ClientAttached { session_name, .. }
384            | Self::ClientDetached { session_name, .. }
385            | Self::ClientSessionChanged { session_name, .. }
386            | Self::SessionCreated { session_name }
387            | Self::SessionClosed { session_name, .. }
388            | Self::SessionRenamed { session_name }
389            | Self::SessionWindowChanged { session_name } => {
390                Some(Target::Session(session_name.clone()))
391            }
392            Self::WindowRenamed { target }
393            | Self::WindowLayoutChanged { target }
394            | Self::WindowResized { target }
395            | Self::WindowPaneChanged { target }
396            | Self::AlertBell { target }
397            | Self::AlertActivity { target }
398            | Self::AlertSilence { target }
399            | Self::AfterSelectWindow { target } => Some(Target::Window(target.clone())),
400            Self::PaneExited { target, .. }
401            | Self::PaneDied { target, .. }
402            | Self::PaneModeChanged { target }
403            | Self::AfterSelectPane { target }
404            | Self::AfterSendKeys { target } => Some(Target::Pane(target.clone())),
405            Self::AfterSetOption {
406                session_name: Some(session_name),
407            } => Some(Target::Session(session_name.clone())),
408            Self::AfterSetOption { session_name: None }
409            | Self::PasteBufferChanged { .. }
410            | Self::PasteBufferDeleted { .. } => None,
411        }
412    }
413}