Skip to main content

rmux_proto/
request.rs

1//! Detached request contracts.
2
3use serde::{Deserialize, Serialize};
4use std::path::PathBuf;
5
6use crate::{
7    ControlModeRequest, HandshakeRequest, PaneTarget, PaneTargetRef, SdkWaitId, SdkWaitOwnerId,
8    SessionName, Target, WindowTarget,
9};
10
11#[path = "request/compat.rs"]
12mod compat;
13
14#[path = "request/show.rs"]
15mod show;
16pub use show::ShowHooksRequest;
17pub use show::{ShowEnvironmentRequest, ShowOptionsRequest};
18
19#[path = "request/layout.rs"]
20mod layout;
21pub use layout::{
22    NextLayoutRequest, PreviousLayoutRequest, SelectCustomLayoutRequest, SelectLayoutRequest,
23    SelectLayoutTarget, SelectOldLayoutRequest, SpreadLayoutRequest,
24};
25
26#[path = "request/pane.rs"]
27mod pane;
28pub use pane::{
29    BreakPaneRequest, DisplayPanesRequest, JoinPaneRequest, KillPaneRequest, LastPaneRequest,
30    MovePaneRequest, PaneBroadcastInputRequest, PaneInputRequest, PaneKillRequest,
31    PaneOutputCursorRequest, PaneOutputSubscriptionStart, PaneResizeRequest, PaneRespawnRequest,
32    PaneSelectRequest, PaneSnapshotRefRequest, PaneSnapshotRequest, PaneSplitSize, PipePaneRequest,
33    ResizePaneRequest, RespawnPaneRequest, SelectPaneAdjacentRequest, SelectPaneDirection,
34    SelectPaneMarkRequest, SelectPaneRequest, SendKeysExtRequest, SendKeysRequest,
35    SplitWindowExtRequest, SplitWindowRequest, SplitWindowTarget, SubscribePaneOutputRefRequest,
36    SubscribePaneOutputRequest, SwapPaneDirection, SwapPaneRequest, UnsubscribePaneOutputRequest,
37};
38
39#[path = "request/window.rs"]
40mod window;
41pub use window::{
42    KillWindowRequest, LastWindowRequest, LinkWindowRequest, ListWindowsRequest, MoveWindowRequest,
43    MoveWindowTarget, NewWindowRequest, NextWindowRequest, PreviousWindowRequest,
44    RenameWindowRequest, ResizeWindowAdjustment, ResizeWindowRequest, RespawnWindowRequest,
45    RotateWindowDirection, RotateWindowRequest, SelectWindowRequest, SwapWindowRequest,
46};
47
48#[path = "request/target.rs"]
49mod target;
50pub use target::{ResolveTargetRequest, ResolveTargetType};
51
52#[path = "request/session.rs"]
53mod session;
54pub use session::{
55    CreateSessionLeaseRequest, HasSessionRequest, KillSessionRequest, ListSessionsRequest,
56    NewSessionExtRequest, NewSessionRequest, ReleaseSessionLeaseRequest, RenameSessionRequest,
57    RenewSessionLeaseRequest,
58};
59
60#[path = "request/server.rs"]
61mod server;
62pub use server::{
63    DaemonStatusRequest, KillServerRequest, LockClientRequest, LockServerRequest,
64    LockSessionRequest, ServerAccessRequest, ShutdownIfIdleRequest,
65};
66
67#[path = "request/client.rs"]
68mod client;
69pub use client::{
70    AttachSessionExt2Request, AttachSessionExtRequest, AttachSessionRequest,
71    DetachClientExtRequest, DetachClientRequest, ListClientsRequest, RefreshClientRequest,
72    SuspendClientRequest, SwitchClientExt2Request, SwitchClientExt3Request, SwitchClientExtRequest,
73    SwitchClientRequest,
74};
75
76#[path = "request/keys.rs"]
77mod keys;
78pub use keys::{
79    BindKeyRequest, ClockModeRequest, CopyModeRequest, ListKeysRequest, SendPrefixRequest,
80    UnbindKeyRequest,
81};
82
83#[path = "request/options.rs"]
84mod options;
85pub use options::{
86    SetEnvironmentMode, SetEnvironmentRequest, SetHookMutationRequest, SetHookRequest,
87    SetOptionByNameRequest, SetOptionRequest,
88};
89
90#[path = "request/buffer.rs"]
91mod buffer;
92pub use buffer::{
93    CapturePaneRequest, ClearHistoryRequest, DeleteBufferRequest, ListBuffersRequest,
94    LoadBufferRequest, PasteBufferRequest, SaveBufferRequest, SetBufferRequest, ShowBufferRequest,
95};
96
97/// All detached public command and internal RPC requests supported by the wire
98/// protocol.
99#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
100pub enum Request {
101    /// `new-session`
102    NewSession(NewSessionRequest),
103    /// `has-session`
104    HasSession(HasSessionRequest),
105    /// `kill-session`
106    KillSession(KillSessionRequest),
107    /// `new-window`
108    NewWindow(NewWindowRequest),
109    /// `kill-window`
110    KillWindow(KillWindowRequest),
111    /// `select-window`
112    SelectWindow(SelectWindowRequest),
113    /// `rename-window`
114    RenameWindow(RenameWindowRequest),
115    /// `next-window`
116    NextWindow(NextWindowRequest),
117    /// `previous-window`
118    PreviousWindow(PreviousWindowRequest),
119    /// `last-window`
120    LastWindow(LastWindowRequest),
121    /// `list-windows`
122    ListWindows(ListWindowsRequest),
123    /// `move-window`
124    MoveWindow(MoveWindowRequest),
125    /// `swap-window`
126    SwapWindow(SwapWindowRequest),
127    /// `rotate-window`
128    RotateWindow(RotateWindowRequest),
129    /// `split-window`
130    SplitWindow(SplitWindowRequest),
131    /// `swap-pane`
132    SwapPane(SwapPaneRequest),
133    /// `last-pane`
134    LastPane(LastPaneRequest),
135    /// `join-pane`
136    JoinPane(JoinPaneRequest),
137    /// `break-pane`
138    BreakPane(BreakPaneRequest),
139    /// `kill-pane`
140    KillPane(KillPaneRequest),
141    /// `select-layout`
142    SelectLayout(SelectLayoutRequest),
143    /// `resize-pane`
144    ResizePane(ResizePaneRequest),
145    /// `display-panes`
146    DisplayPanes(DisplayPanesRequest),
147    /// `select-pane`
148    SelectPane(SelectPaneRequest),
149    /// `select-pane -U/-D/-L/-R`
150    SelectPaneAdjacent(SelectPaneAdjacentRequest),
151    /// `send-keys`
152    SendKeys(SendKeysRequest),
153    /// `attach-session`
154    AttachSession(AttachSessionRequest),
155    /// `switch-client`
156    SwitchClient(SwitchClientRequest),
157    /// `detach-client`
158    DetachClient(DetachClientRequest),
159    /// `set-option`
160    SetOption(SetOptionRequest),
161    /// `set-environment`
162    SetEnvironment(SetEnvironmentRequest),
163    /// `set-hook`
164    SetHook(SetHookRequest),
165    /// `next-layout`
166    NextLayout(NextLayoutRequest),
167    /// `previous-layout`
168    PreviousLayout(PreviousLayoutRequest),
169    /// `show-options`
170    ShowOptions(ShowOptionsRequest),
171    /// `show-environment`
172    ShowEnvironment(ShowEnvironmentRequest),
173    /// `set-buffer`
174    SetBuffer(SetBufferRequest),
175    /// `show-buffer`
176    ShowBuffer(ShowBufferRequest),
177    /// `paste-buffer`
178    PasteBuffer(PasteBufferRequest),
179    /// `list-buffers`
180    ListBuffers(ListBuffersRequest),
181    /// `delete-buffer`
182    DeleteBuffer(DeleteBufferRequest),
183    /// `load-buffer`
184    LoadBuffer(LoadBufferRequest),
185    /// `save-buffer`
186    SaveBuffer(SaveBufferRequest),
187    /// `capture-pane`
188    CapturePane(CapturePaneRequest),
189    /// `display-message`
190    DisplayMessage(DisplayMessageRequest),
191    /// `run-shell`
192    RunShell(RunShellRequest),
193    /// `if-shell`
194    IfShell(IfShellRequest),
195    /// `wait-for`
196    WaitFor(WaitForRequest),
197    /// `rename-session`
198    RenameSession(RenameSessionRequest),
199    /// `list-sessions`
200    ListSessions(ListSessionsRequest),
201    /// `list-panes`
202    ListPanes(ListPanesRequest),
203    /// `source-file`
204    SourceFile(SourceFileRequest),
205    /// `set-option` using an open string-based option name.
206    SetOptionByName(SetOptionByNameRequest),
207    /// Extended `set-hook` mutation semantics.
208    SetHookMutation(SetHookMutationRequest),
209    /// `show-hooks`
210    ShowHooks(ShowHooksRequest),
211    /// Extended `send-keys` semantics including key-table dispatch and format expansion.
212    SendKeysExt(SendKeysExtRequest),
213    /// Extended `switch-client` semantics including `-T key-table`.
214    SwitchClientExt(SwitchClientExtRequest),
215    /// `bind-key`
216    BindKey(BindKeyRequest),
217    /// `unbind-key`
218    UnbindKey(UnbindKeyRequest),
219    /// `list-keys`
220    ListKeys(ListKeysRequest),
221    /// `send-prefix`
222    SendPrefix(SendPrefixRequest),
223    /// `clear-history`
224    ClearHistory(ClearHistoryRequest),
225    /// `copy-mode`
226    CopyMode(CopyModeRequest),
227    /// Internal detached upgrade into tmux-compatible control mode.
228    ControlMode(ControlModeRequest),
229    /// `clock-mode`
230    ClockMode(ClockModeRequest),
231    /// `show-messages`
232    ShowMessages(ShowMessagesRequest),
233    /// Extended `new-session` semantics including grouped sessions and attach-if-exists.
234    NewSessionExt(NewSessionExtRequest),
235    /// Extended `attach-session` semantics including client flags and detach-others.
236    AttachSessionExt(AttachSessionExtRequest),
237    /// Extended `switch-client` semantics including `-l`, `-n`, `-p`, and readonly toggles.
238    SwitchClientExt2(SwitchClientExt2Request),
239    /// `select-layout` with a tmux custom layout string.
240    SelectCustomLayout(SelectCustomLayoutRequest),
241    /// `select-layout -o`
242    SelectOldLayout(SelectOldLayoutRequest),
243    /// `select-layout -E`
244    SpreadLayout(SpreadLayoutRequest),
245    /// `kill-server`
246    KillServer(KillServerRequest),
247    /// `lock-server`
248    LockServer(LockServerRequest),
249    /// `lock-session`
250    LockSession(LockSessionRequest),
251    /// `lock-client`
252    LockClient(LockClientRequest),
253    /// `server-access`
254    ServerAccess(ServerAccessRequest),
255    /// `refresh-client`
256    RefreshClient(RefreshClientRequest),
257    /// `list-clients`
258    ListClients(ListClientsRequest),
259    /// `suspend-client`
260    SuspendClient(SuspendClientRequest),
261    /// Extended `detach-client` semantics including `-a`, `-s`, `-P`, and `-E`.
262    DetachClientExt(DetachClientExtRequest),
263    /// Further-extended `attach-session` semantics including `-c working-directory`.
264    AttachSessionExt2(AttachSessionExt2Request),
265    /// Further-extended `switch-client` semantics including `-c target-client` and `-Z`.
266    SwitchClientExt3(SwitchClientExt3Request),
267    /// `resize-window`
268    ResizeWindow(ResizeWindowRequest),
269    /// `respawn-window`
270    RespawnWindow(RespawnWindowRequest),
271    /// `move-pane`
272    MovePane(MovePaneRequest),
273    /// `pipe-pane`
274    PipePane(PipePaneRequest),
275    /// `respawn-pane`
276    RespawnPane(RespawnPaneRequest),
277    /// `link-window`
278    LinkWindow(LinkWindowRequest),
279    /// `unlink-window`
280    UnlinkWindow(UnlinkWindowRequest),
281    /// `select-pane -m` / `select-pane -M`
282    SelectPaneMark(SelectPaneMarkRequest),
283    /// Internal detached target resolution for tmux-style raw target text.
284    ResolveTarget(ResolveTargetRequest),
285    /// Extended `split-window` semantics including an explicit shell command.
286    SplitWindowExt(SplitWindowExtRequest),
287    /// Internal SDK/daemon version and capability negotiation.
288    Handshake(HandshakeRequest),
289    /// Internal daemon-backed structured pane snapshot endpoint.
290    PaneSnapshot(PaneSnapshotRequest),
291    /// Internal daemon-backed pane output subscription endpoint.
292    SubscribePaneOutput(SubscribePaneOutputRequest),
293    /// Internal daemon-backed pane output unsubscription endpoint.
294    UnsubscribePaneOutput(UnsubscribePaneOutputRequest),
295    /// Internal daemon-backed pane output cursor polling endpoint.
296    PaneOutputCursor(PaneOutputCursorRequest),
297    /// Internal daemon-backed SDK byte wait endpoint.
298    SdkWaitForOutput(SdkWaitForOutputRequest),
299    /// Internal daemon-backed SDK wait cancellation endpoint.
300    CancelSdkWait(CancelSdkWaitRequest),
301    /// SDK pane input endpoint with stable pane-id targeting.
302    PaneInput(PaneInputRequest),
303    /// SDK pane resize endpoint with stable pane-id targeting.
304    PaneResize(PaneResizeRequest),
305    /// SDK pane kill endpoint with stable pane-id targeting.
306    PaneKill(PaneKillRequest),
307    /// SDK pane respawn endpoint with stable pane-id targeting.
308    PaneRespawn(PaneRespawnRequest),
309    /// SDK pane snapshot endpoint with stable pane-id targeting.
310    PaneSnapshotRef(PaneSnapshotRefRequest),
311    /// SDK pane select/title endpoint with stable pane-id targeting.
312    PaneSelect(PaneSelectRequest),
313    /// SDK pane input broadcast endpoint with stable pane-id targeting.
314    PaneBroadcastInput(PaneBroadcastInputRequest),
315    /// SDK app-owned session lease create endpoint.
316    CreateSessionLease(CreateSessionLeaseRequest),
317    /// SDK app-owned session lease renewal endpoint.
318    RenewSessionLease(RenewSessionLeaseRequest),
319    /// SDK app-owned session lease release endpoint.
320    ReleaseSessionLease(ReleaseSessionLeaseRequest),
321    /// Internal daemon-backed pane output subscription endpoint with stable pane-id targeting.
322    SubscribePaneOutputRef(SubscribePaneOutputRefRequest),
323    /// Internal daemon-backed SDK byte wait endpoint with stable pane-id targeting.
324    SdkWaitForOutputRef(SdkWaitForOutputRefRequest),
325    /// Internal daemon version and activity status endpoint.
326    DaemonStatus(DaemonStatusRequest),
327    /// Internal idle-only shutdown endpoint used by seamless upgrades.
328    ShutdownIfIdle(ShutdownIfIdleRequest),
329}
330
331impl Request {
332    /// Returns the stable routing name for the request variant.
333    #[must_use]
334    pub const fn command_name(&self) -> &'static str {
335        match self {
336            Self::NewSession(_) => "new-session",
337            Self::HasSession(_) => "has-session",
338            Self::KillSession(_) => "kill-session",
339            Self::NewWindow(_) => "new-window",
340            Self::KillWindow(_) => "kill-window",
341            Self::SelectWindow(_) => "select-window",
342            Self::RenameWindow(_) => "rename-window",
343            Self::NextWindow(_) => "next-window",
344            Self::PreviousWindow(_) => "previous-window",
345            Self::LastWindow(_) => "last-window",
346            Self::ListWindows(_) => "list-windows",
347            Self::LinkWindow(_) => "link-window",
348            Self::MoveWindow(_) => "move-window",
349            Self::SwapWindow(_) => "swap-window",
350            Self::RotateWindow(_) => "rotate-window",
351            Self::SplitWindow(_) | Self::SplitWindowExt(_) => "split-window",
352            Self::SwapPane(_) => "swap-pane",
353            Self::LastPane(_) => "last-pane",
354            Self::JoinPane(_) => "join-pane",
355            Self::BreakPane(_) => "break-pane",
356            Self::KillPane(_) => "kill-pane",
357            Self::SelectLayout(_) => "select-layout",
358            Self::ResizePane(_) => "resize-pane",
359            Self::DisplayPanes(_) => "display-panes",
360            Self::SelectPane(_) | Self::SelectPaneAdjacent(_) | Self::SelectPaneMark(_) => {
361                "select-pane"
362            }
363            Self::SendKeys(_) => "send-keys",
364            Self::AttachSession(_) => "attach-session",
365            Self::SwitchClient(_) => "switch-client",
366            Self::DetachClient(_) => "detach-client",
367            Self::SetOption(_) => "set-option",
368            Self::SetEnvironment(_) => "set-environment",
369            Self::SetHook(_) => "set-hook",
370            Self::NextLayout(_) => "next-layout",
371            Self::PreviousLayout(_) => "previous-layout",
372            Self::ShowOptions(_) => "show-options",
373            Self::ShowEnvironment(_) => "show-environment",
374            Self::SetBuffer(_) => "set-buffer",
375            Self::ShowBuffer(_) => "show-buffer",
376            Self::PasteBuffer(_) => "paste-buffer",
377            Self::ListBuffers(_) => "list-buffers",
378            Self::DeleteBuffer(_) => "delete-buffer",
379            Self::LoadBuffer(_) => "load-buffer",
380            Self::SaveBuffer(_) => "save-buffer",
381            Self::CapturePane(_) => "capture-pane",
382            Self::PaneSnapshot(_) => "pane-snapshot",
383            Self::SubscribePaneOutput(_) | Self::SubscribePaneOutputRef(_) => {
384                "subscribe-pane-output"
385            }
386            Self::UnsubscribePaneOutput(_) => "unsubscribe-pane-output",
387            Self::PaneOutputCursor(_) => "pane-output-cursor",
388            Self::SdkWaitForOutput(_) | Self::SdkWaitForOutputRef(_) => "sdk-wait-output",
389            Self::CancelSdkWait(_) => "cancel-sdk-wait",
390            Self::PaneInput(_) => "send-keys",
391            Self::PaneBroadcastInput(_) => "send-keys",
392            Self::CreateSessionLease(_) => "create-session-lease",
393            Self::RenewSessionLease(_) => "renew-session-lease",
394            Self::ReleaseSessionLease(_) => "release-session-lease",
395            Self::PaneResize(_) => "resize-pane",
396            Self::PaneKill(_) => "kill-pane",
397            Self::PaneRespawn(_) => "respawn-pane",
398            Self::PaneSnapshotRef(_) => "pane-snapshot",
399            Self::PaneSelect(_) => "select-pane",
400            Self::DisplayMessage(_) => "display-message",
401            Self::ResolveTarget(_) => "resolve-target",
402            Self::RunShell(_) => "run-shell",
403            Self::IfShell(_) => "if-shell",
404            Self::WaitFor(_) => "wait-for",
405            Self::RenameSession(_) => "rename-session",
406            Self::ListSessions(_) => "list-sessions",
407            Self::ListPanes(_) => "list-panes",
408            Self::SourceFile(_) => "source-file",
409            Self::UnlinkWindow(_) => "unlink-window",
410            Self::SetOptionByName(_) => "set-option",
411            Self::SetHookMutation(_) => "set-hook",
412            Self::ShowHooks(_) => "show-hooks",
413            Self::SendKeysExt(_) => "send-keys",
414            Self::SwitchClientExt(_) => "switch-client",
415            Self::BindKey(_) => "bind-key",
416            Self::UnbindKey(_) => "unbind-key",
417            Self::ListKeys(_) => "list-keys",
418            Self::SendPrefix(_) => "send-prefix",
419            Self::ClearHistory(_) => "clear-history",
420            Self::CopyMode(_) => "copy-mode",
421            Self::ControlMode(_) => "control-mode",
422            Self::ClockMode(_) => "clock-mode",
423            Self::ShowMessages(_) => "show-messages",
424            Self::NewSessionExt(_) => "new-session",
425            Self::AttachSessionExt(_) => "attach-session",
426            Self::SwitchClientExt2(_) => "switch-client",
427            Self::SelectCustomLayout(_) => "select-layout",
428            Self::SelectOldLayout(_) => "select-layout",
429            Self::SpreadLayout(_) => "select-layout",
430            Self::ResizeWindow(_) => "resize-window",
431            Self::RespawnWindow(_) => "respawn-window",
432            Self::MovePane(_) => "move-pane",
433            Self::PipePane(_) => "pipe-pane",
434            Self::RespawnPane(_) => "respawn-pane",
435            Self::KillServer(_) => "kill-server",
436            Self::LockServer(_) => "lock-server",
437            Self::LockSession(_) => "lock-session",
438            Self::LockClient(_) => "lock-client",
439            Self::ServerAccess(_) => "server-access",
440            Self::RefreshClient(_) => "refresh-client",
441            Self::ListClients(_) => "list-clients",
442            Self::SuspendClient(_) => "suspend-client",
443            Self::DetachClientExt(_) => "detach-client",
444            Self::AttachSessionExt2(_) => "attach-session",
445            Self::SwitchClientExt3(_) => "switch-client",
446            Self::Handshake(_) => "handshake",
447            Self::DaemonStatus(_) => "daemon-status",
448            Self::ShutdownIfIdle(_) => "shutdown-if-idle",
449        }
450    }
451}
452
453/// Request payload for `display-message`.
454#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
455pub struct DisplayMessageRequest {
456    /// The optional exact session, window, or pane target used as format context.
457    pub target: Option<Target>,
458    /// Whether to print the expanded message to stdout instead of displaying it.
459    pub print: bool,
460    /// The optional format string. When omitted, the tmux-compatible default is used.
461    pub message: Option<String>,
462}
463
464/// Request payload for `show-messages`.
465#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
466pub struct ShowMessagesRequest {
467    /// Whether to print the server job summary.
468    pub jobs: bool,
469    /// Whether to print terminal information.
470    pub terminals: bool,
471    /// The optional target client filter used by terminal and job summaries.
472    pub target_client: Option<String>,
473}
474
475/// Request payload for `run-shell`.
476#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
477pub struct RunShellRequest {
478    /// The server-local shell command passed to `sh -c`.
479    pub command: String,
480    /// Whether the command should run fire-and-forget without output capture.
481    pub background: bool,
482    /// Whether the command should be executed as tmux commands instead of `sh -c`.
483    #[serde(default)]
484    pub as_commands: bool,
485    /// Whether stderr should be captured alongside stdout.
486    #[serde(default)]
487    pub show_stderr: bool,
488    /// Optional delay, in seconds, before the command runs.
489    #[serde(default)]
490    pub delay_seconds: Option<RunShellDelaySeconds>,
491    /// Optional explicit working directory.
492    #[serde(default)]
493    pub start_directory: Option<PathBuf>,
494    /// Optional explicit target pane used for format and session context.
495    #[serde(default)]
496    pub target: Option<PaneTarget>,
497}
498
499/// Losslessly serializable `run-shell -d` seconds value with stable `Eq`.
500#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
501pub struct RunShellDelaySeconds(pub f64);
502
503impl RunShellDelaySeconds {
504    /// Returns the raw seconds value.
505    #[must_use]
506    pub const fn as_secs_f64(self) -> f64 {
507        self.0
508    }
509}
510
511impl PartialEq for RunShellDelaySeconds {
512    fn eq(&self, other: &Self) -> bool {
513        self.0.to_bits() == other.0.to_bits()
514    }
515}
516
517impl Eq for RunShellDelaySeconds {}
518
519/// Request payload for `if-shell`.
520#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
521pub struct IfShellRequest {
522    /// The condition string expanded through the shared formatter before evaluation.
523    pub condition: String,
524    /// Whether to evaluate the expanded condition with format truthiness instead of `sh -c`.
525    pub format_mode: bool,
526    /// The nested RMUX command string dispatched when the condition is true.
527    pub then_command: String,
528    /// The optional nested RMUX command string dispatched when the condition is false.
529    pub else_command: Option<String>,
530    /// Optional exact target used as shared-format context.
531    pub target: Option<Target>,
532    /// The caller working directory used to resolve nested relative file paths.
533    pub caller_cwd: Option<PathBuf>,
534    /// Whether the condition should be evaluated asynchronously.
535    #[serde(default)]
536    pub background: bool,
537}
538
539/// Request payload for `source-file`.
540#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
541pub struct SourceFileRequest {
542    /// The path arguments to expand, glob, parse, and optionally execute.
543    pub paths: Vec<String>,
544    /// Whether missing files and glob misses should be suppressed.
545    pub quiet: bool,
546    /// Whether to parse only without executing parsed commands.
547    pub parse_only: bool,
548    /// Whether parsed commands should be printed to stdout.
549    pub verbose: bool,
550    /// Whether each path argument should be format-expanded before globbing.
551    pub expand_paths: bool,
552    /// Optional pane target used as the `-F` format context.
553    pub target: Option<PaneTarget>,
554    /// The caller working directory used to resolve relative paths.
555    pub caller_cwd: Option<PathBuf>,
556    /// Content read from client stdin for `source-file -`.
557    pub stdin: Option<String>,
558}
559
560/// Request payload for `unlink-window`.
561#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
562pub struct UnlinkWindowRequest {
563    /// The window slot to remove.
564    pub target: WindowTarget,
565    /// Whether removing the final link is allowed (`-k`).
566    #[serde(default)]
567    pub kill_if_last: bool,
568}
569
570/// The supported `wait-for` operation modes.
571#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
572pub enum WaitForMode {
573    /// Wait for the next signal on the named channel.
574    Wait,
575    /// Signal all current plain waiters on the named channel.
576    Signal,
577    /// Acquire the named server-local lock, waiting in FIFO order when held.
578    Lock,
579    /// Release the named server-local lock.
580    Unlock,
581}
582
583/// Request payload for `wait-for`.
584#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
585pub struct WaitForRequest {
586    /// The server-local wait channel name.
587    pub channel: String,
588    /// The selected wait operation.
589    pub mode: WaitForMode,
590}
591
592/// Request payload for a daemon-backed SDK byte wait.
593///
594/// This is intentionally distinct from tmux-compatible [`WaitForRequest`].
595/// SDK waits are pane-output waits with typed IDs used only for cancellation
596/// and teardown bookkeeping; they never signal or lock tmux `wait-for`
597/// channels.
598#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
599pub struct SdkWaitForOutputRequest {
600    /// Opaque SDK transport owner for this wait.
601    pub owner_id: SdkWaitOwnerId,
602    /// Wait ID allocated by the SDK under `owner_id`.
603    pub wait_id: SdkWaitId,
604    /// Pane whose raw output stream is observed.
605    pub target: PaneTarget,
606    /// Raw byte sequence to search for in pane output.
607    pub bytes: Vec<u8>,
608    /// Cursor position used when arming the wait.
609    pub start: PaneOutputSubscriptionStart,
610}
611
612/// Request payload for a daemon-backed SDK byte wait by slot or stable id.
613#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
614pub struct SdkWaitForOutputRefRequest {
615    /// Opaque SDK transport owner for this wait.
616    pub owner_id: SdkWaitOwnerId,
617    /// Wait ID allocated by the SDK under `owner_id`.
618    pub wait_id: SdkWaitId,
619    /// Pane whose raw output stream is observed.
620    pub target: PaneTargetRef,
621    /// Raw byte sequence to search for in pane output.
622    pub bytes: Vec<u8>,
623    /// Cursor position used when arming the wait.
624    pub start: PaneOutputSubscriptionStart,
625}
626
627/// Request payload for best-effort cancellation of a daemon-backed SDK wait.
628#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
629pub struct CancelSdkWaitRequest {
630    /// Opaque SDK transport owner for the wait being cancelled.
631    pub owner_id: SdkWaitOwnerId,
632    /// Wait ID allocated by the SDK under `owner_id`.
633    pub wait_id: SdkWaitId,
634}
635
636/// Request payload for `list-panes`.
637#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
638pub struct ListPanesRequest {
639    /// The exact target session name.
640    pub target: SessionName,
641    /// Optional exact target window index.
642    #[serde(default)]
643    pub target_window_index: Option<u32>,
644    /// An optional server-side format template.
645    pub format: Option<String>,
646}
647
648#[cfg(test)]
649#[path = "request/compat_tests.rs"]
650mod compat_tests;
651
652#[cfg(test)]
653mod tests {
654    use super::*;
655    use crate::{
656        OptionScopeSelector, PaneTarget, ScopeSelector, SelectPaneDirection, SetOptionMode,
657        SplitDirection,
658    };
659
660    fn alpha() -> SessionName {
661        SessionName::new("alpha").expect("valid session")
662    }
663
664    fn pane() -> PaneTarget {
665        PaneTarget::new(alpha(), 0)
666    }
667
668    #[test]
669    fn request_command_names_cover_extended_aliases_and_internal_tags() {
670        assert_eq!(
671            Request::NewSessionExt(NewSessionExtRequest {
672                session_name: None,
673                working_directory: None,
674                detached: true,
675                size: None,
676                environment: None,
677                group_target: None,
678                attach_if_exists: false,
679                detach_other_clients: false,
680                kill_other_clients: false,
681                flags: None,
682                window_name: None,
683                print_session_info: false,
684                print_format: None,
685                command: None,
686                process_command: None,
687                client_environment: None,
688            })
689            .command_name(),
690            "new-session"
691        );
692        assert_eq!(
693            Request::SplitWindowExt(SplitWindowExtRequest {
694                target: SplitWindowTarget::Pane(pane()),
695                direction: SplitDirection::Vertical,
696                before: false,
697                environment: None,
698                command: None,
699                process_command: None,
700                start_directory: None,
701                keep_alive_on_exit: None,
702            })
703            .command_name(),
704            "split-window"
705        );
706        assert_eq!(
707            Request::SelectPaneAdjacent(SelectPaneAdjacentRequest {
708                target: pane(),
709                direction: SelectPaneDirection::Right,
710            })
711            .command_name(),
712            "select-pane"
713        );
714        assert_eq!(
715            Request::SelectPaneMark(SelectPaneMarkRequest {
716                target: pane(),
717                clear: false,
718                title: None,
719            })
720            .command_name(),
721            "select-pane"
722        );
723        assert_eq!(
724            Request::SetOptionByName(SetOptionByNameRequest {
725                scope: OptionScopeSelector::ServerGlobal,
726                name: "status".to_owned(),
727                value: Some("on".to_owned()),
728                mode: SetOptionMode::Replace,
729                only_if_unset: false,
730                unset: false,
731                unset_pane_overrides: false,
732            })
733            .command_name(),
734            "set-option"
735        );
736        assert_eq!(
737            Request::SetHookMutation(SetHookMutationRequest {
738                scope: ScopeSelector::Global,
739                hook: crate::HookName::AfterNewSession,
740                command: None,
741                lifecycle: crate::HookLifecycle::Persistent,
742                append: false,
743                unset: true,
744                run_immediately: false,
745                index: None,
746            })
747            .command_name(),
748            "set-hook"
749        );
750        assert_eq!(
751            Request::AttachSessionExt2(AttachSessionExt2Request {
752                target: Some(alpha()),
753                target_spec: None,
754                detach_other_clients: false,
755                kill_other_clients: false,
756                read_only: false,
757                skip_environment_update: false,
758                flags: None,
759                working_directory: None,
760                client_terminal: crate::ClientTerminalContext::default(),
761                client_size: None,
762            })
763            .command_name(),
764            "attach-session"
765        );
766        assert_eq!(
767            Request::SwitchClientExt3(SwitchClientExt3Request {
768                target_client: None,
769                target: Some("alpha:0.0".to_owned()),
770                key_table: None,
771                last_session: false,
772                next_session: false,
773                previous_session: false,
774                toggle_read_only: false,
775                sort_order: None,
776                skip_environment_update: false,
777                zoom: false,
778            })
779            .command_name(),
780            "switch-client"
781        );
782        assert_eq!(
783            Request::ResolveTarget(ResolveTargetRequest {
784                target: Some("alpha:0.0".to_owned()),
785                target_type: ResolveTargetType::Pane,
786                window_index: false,
787                prefer_unattached: false,
788            })
789            .command_name(),
790            "resolve-target"
791        );
792        assert_eq!(
793            Request::Handshake(HandshakeRequest::current()).command_name(),
794            "handshake"
795        );
796        assert_eq!(
797            Request::SdkWaitForOutput(SdkWaitForOutputRequest {
798                owner_id: SdkWaitOwnerId::new(1),
799                wait_id: SdkWaitId::new(1),
800                target: pane(),
801                bytes: b"ready".to_vec(),
802                start: PaneOutputSubscriptionStart::Now,
803            })
804            .command_name(),
805            "sdk-wait-output"
806        );
807        assert_eq!(
808            Request::CancelSdkWait(CancelSdkWaitRequest {
809                owner_id: SdkWaitOwnerId::new(1),
810                wait_id: SdkWaitId::new(1),
811            })
812            .command_name(),
813            "cancel-sdk-wait"
814        );
815        assert_eq!(
816            Request::WaitFor(WaitForRequest {
817                channel: "ready".to_owned(),
818                mode: WaitForMode::Wait,
819            })
820            .command_name(),
821            "wait-for"
822        );
823    }
824}