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/display.rs"]
15mod display;
16pub use display::{DisplayMessageExtRequest, DisplayMessageRequest};
17
18#[path = "request/show.rs"]
19mod show;
20pub use show::ShowHooksRequest;
21pub use show::{ShowEnvironmentRequest, ShowOptionsRequest};
22
23#[path = "request/layout.rs"]
24mod layout;
25pub use layout::{
26    NextLayoutRequest, PreviousLayoutRequest, SelectCustomLayoutRequest, SelectLayoutRequest,
27    SelectLayoutTarget, SelectOldLayoutRequest, SpreadLayoutRequest,
28};
29
30#[path = "request/pane.rs"]
31mod pane;
32pub use pane::{
33    BreakPaneRequest, DisplayPanesRequest, JoinPaneRequest, KillPaneRequest, LastPaneRequest,
34    MovePaneRequest, PaneBroadcastInputRequest, PaneForegroundStateRequest, PaneInputRequest,
35    PaneKillRequest, PaneOptionGetRequest, PaneOptionSetRequest, PaneOutputCursorRequest,
36    PaneOutputSubscriptionStart, PaneResizeRequest, PaneRespawnRequest, PaneSelectRequest,
37    PaneSnapshotRefRequest, PaneSnapshotRequest, PaneSplitSize, PaneStateCursorRequest,
38    PipePaneRequest, ResizePaneRequest, ResizePaneTargetActionRequest, RespawnPaneRequest,
39    SelectPaneAdjacentRequest, SelectPaneDirection, SelectPaneMarkRequest, SelectPaneRequest,
40    SendKeysExt2Request, SendKeysExtRequest, SendKeysRequest, SplitWindowExtRequest,
41    SplitWindowIdentityRequest, SplitWindowRequest, SplitWindowTarget,
42    SplitWindowTargetActionRequest, SubscribePaneOutputRefRequest, SubscribePaneOutputRequest,
43    SubscribePaneStateRequest, SwapPaneDirection, SwapPaneRequest, UnsubscribePaneOutputRequest,
44    UnsubscribePaneStateRequest,
45};
46
47#[path = "request/window.rs"]
48mod window;
49pub use window::{
50    KillWindowRequest, LastWindowRequest, LinkWindowRequest, ListWindowsRequest, MoveWindowRequest,
51    MoveWindowTarget, NewWindowRequest, NextWindowRequest, PreviousWindowRequest,
52    RenameWindowRequest, ResizeWindowAdjustment, ResizeWindowRequest, RespawnWindowRequest,
53    RotateWindowDirection, RotateWindowRequest, SelectWindowRequest, SwapWindowRequest,
54};
55
56#[path = "request/target.rs"]
57mod target;
58pub use target::{ResolveTargetRequest, ResolveTargetType};
59
60#[path = "request/session.rs"]
61mod session;
62pub use session::{
63    CreateSessionLeaseRequest, HasSessionRequest, KillSessionRequest, ListSessionsRequest,
64    NewSessionExtRequest, NewSessionRequest, ReleaseSessionLeaseRequest, RenameSessionRequest,
65    RenewSessionLeaseRequest,
66};
67
68#[path = "request/server.rs"]
69mod server;
70pub use server::{
71    DaemonStatusRequest, KillServerRequest, LockClientRequest, LockServerRequest,
72    LockSessionRequest, ServerAccessRequest, ShutdownIfIdleRequest,
73};
74
75#[path = "request/client.rs"]
76mod client;
77pub use client::{
78    AttachSessionExt2Request, AttachSessionExt3Request, AttachSessionExtRequest,
79    AttachSessionRequest, DetachClientExtRequest, DetachClientRequest, ListClientsRequest,
80    RefreshClientRequest, SuspendClientRequest, SwitchClientExt2Request, SwitchClientExt3Request,
81    SwitchClientExtRequest, SwitchClientRequest,
82};
83
84#[path = "request/keys.rs"]
85mod keys;
86pub use keys::{
87    BindKeyRequest, ClockModeRequest, CopyModeRequest, ListKeysRequest, SendPrefixRequest,
88    UnbindKeyRequest,
89};
90
91#[path = "request/options.rs"]
92mod options;
93pub use options::{
94    SetEnvironmentMode, SetEnvironmentRequest, SetHookMutationRequest, SetHookRequest,
95    SetOptionByNameRequest, SetOptionRequest,
96};
97
98#[path = "request/buffer.rs"]
99mod buffer;
100pub use buffer::{
101    CapturePaneRequest, CapturePaneTargetActionRequest, ClearHistoryRequest, DeleteBufferRequest,
102    ListBuffersRequest, LoadBufferRequest, PasteBufferRequest, SaveBufferRequest, SetBufferRequest,
103    ShowBufferRequest,
104};
105
106#[path = "request/web.rs"]
107mod web;
108pub use web::{
109    CreateWebShareRequest, ListWebSharesRequest, LookupWebShareRequest, StopAllWebSharesRequest,
110    StopWebShareRequest, WebShareConfigRequest, WebShareRequest, WebShareScope, WebShareUrlOptions,
111    WebTerminalPalette, WebTerminalTheme,
112};
113
114/// All detached public command and internal RPC requests supported by the wire
115/// protocol.
116#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
117pub enum Request {
118    /// `new-session`
119    NewSession(NewSessionRequest),
120    /// `has-session`
121    HasSession(HasSessionRequest),
122    /// `kill-session`
123    KillSession(KillSessionRequest),
124    /// `new-window`
125    NewWindow(Box<NewWindowRequest>),
126    /// `kill-window`
127    KillWindow(KillWindowRequest),
128    /// `select-window`
129    SelectWindow(SelectWindowRequest),
130    /// `rename-window`
131    RenameWindow(RenameWindowRequest),
132    /// `next-window`
133    NextWindow(NextWindowRequest),
134    /// `previous-window`
135    PreviousWindow(PreviousWindowRequest),
136    /// `last-window`
137    LastWindow(LastWindowRequest),
138    /// `list-windows`
139    ListWindows(Box<ListWindowsRequest>),
140    /// `move-window`
141    MoveWindow(MoveWindowRequest),
142    /// `swap-window`
143    SwapWindow(SwapWindowRequest),
144    /// `rotate-window`
145    RotateWindow(RotateWindowRequest),
146    /// `split-window`
147    SplitWindow(SplitWindowRequest),
148    /// `swap-pane`
149    SwapPane(SwapPaneRequest),
150    /// `last-pane`
151    LastPane(LastPaneRequest),
152    /// `join-pane`
153    JoinPane(JoinPaneRequest),
154    /// `break-pane`
155    BreakPane(Box<BreakPaneRequest>),
156    /// `kill-pane`
157    KillPane(KillPaneRequest),
158    /// `select-layout`
159    SelectLayout(SelectLayoutRequest),
160    /// `resize-pane`
161    ResizePane(ResizePaneRequest),
162    /// `display-panes`
163    DisplayPanes(Box<DisplayPanesRequest>),
164    /// `select-pane`
165    SelectPane(Box<SelectPaneRequest>),
166    /// `select-pane -U/-D/-L/-R`
167    SelectPaneAdjacent(SelectPaneAdjacentRequest),
168    /// `send-keys`
169    SendKeys(SendKeysRequest),
170    /// `attach-session`
171    AttachSession(AttachSessionRequest),
172    /// `switch-client`
173    SwitchClient(SwitchClientRequest),
174    /// `detach-client`
175    DetachClient(DetachClientRequest),
176    /// `set-option`
177    SetOption(SetOptionRequest),
178    /// `set-environment`
179    SetEnvironment(Box<SetEnvironmentRequest>),
180    /// `set-hook`
181    SetHook(SetHookRequest),
182    /// `next-layout`
183    NextLayout(NextLayoutRequest),
184    /// `previous-layout`
185    PreviousLayout(PreviousLayoutRequest),
186    /// `show-options`
187    ShowOptions(ShowOptionsRequest),
188    /// `show-environment`
189    ShowEnvironment(ShowEnvironmentRequest),
190    /// `set-buffer`
191    SetBuffer(Box<SetBufferRequest>),
192    /// `show-buffer`
193    ShowBuffer(ShowBufferRequest),
194    /// `paste-buffer`
195    PasteBuffer(Box<PasteBufferRequest>),
196    /// `list-buffers`
197    ListBuffers(ListBuffersRequest),
198    /// `delete-buffer`
199    DeleteBuffer(DeleteBufferRequest),
200    /// `load-buffer`
201    LoadBuffer(Box<LoadBufferRequest>),
202    /// `save-buffer`
203    SaveBuffer(SaveBufferRequest),
204    /// `capture-pane`
205    CapturePane(Box<CapturePaneRequest>),
206    /// `display-message`
207    DisplayMessage(DisplayMessageRequest),
208    /// `run-shell`
209    RunShell(Box<RunShellRequest>),
210    /// `if-shell`
211    IfShell(Box<IfShellRequest>),
212    /// `wait-for`
213    WaitFor(WaitForRequest),
214    /// `rename-session`
215    RenameSession(RenameSessionRequest),
216    /// `list-sessions`
217    ListSessions(ListSessionsRequest),
218    /// `list-panes`
219    ListPanes(Box<ListPanesRequest>),
220    /// `source-file`
221    SourceFile(Box<SourceFileRequest>),
222    /// `set-option` using an open string-based option name.
223    SetOptionByName(Box<SetOptionByNameRequest>),
224    /// Extended `set-hook` mutation semantics.
225    SetHookMutation(SetHookMutationRequest),
226    /// `show-hooks`
227    ShowHooks(ShowHooksRequest),
228    /// Extended `send-keys` semantics including key-table dispatch and format expansion.
229    SendKeysExt(SendKeysExtRequest),
230    /// Extended `switch-client` semantics including `-T key-table`.
231    SwitchClientExt(SwitchClientExtRequest),
232    /// `bind-key`
233    BindKey(Box<BindKeyRequest>),
234    /// `unbind-key`
235    UnbindKey(UnbindKeyRequest),
236    /// `list-keys`
237    ListKeys(Box<ListKeysRequest>),
238    /// `send-prefix`
239    SendPrefix(SendPrefixRequest),
240    /// `clear-history`
241    ClearHistory(ClearHistoryRequest),
242    /// `copy-mode`
243    CopyMode(CopyModeRequest),
244    /// Internal detached upgrade into tmux-compatible control mode.
245    ControlMode(ControlModeRequest),
246    /// `clock-mode`
247    ClockMode(ClockModeRequest),
248    /// `show-messages`
249    ShowMessages(ShowMessagesRequest),
250    /// Extended `new-session` semantics including grouped sessions and attach-if-exists.
251    NewSessionExt(Box<NewSessionExtRequest>),
252    /// Extended `attach-session` semantics including client flags and detach-others.
253    AttachSessionExt(AttachSessionExtRequest),
254    /// Extended `switch-client` semantics including `-l`, `-n`, `-p`, and readonly toggles.
255    SwitchClientExt2(Box<SwitchClientExt2Request>),
256    /// `select-layout` with a tmux custom layout string.
257    SelectCustomLayout(SelectCustomLayoutRequest),
258    /// `select-layout -o`
259    SelectOldLayout(SelectOldLayoutRequest),
260    /// `select-layout -E`
261    SpreadLayout(SpreadLayoutRequest),
262    /// `kill-server`
263    KillServer(KillServerRequest),
264    /// `lock-server`
265    LockServer(LockServerRequest),
266    /// `lock-session`
267    LockSession(LockSessionRequest),
268    /// `lock-client`
269    LockClient(LockClientRequest),
270    /// `server-access`
271    ServerAccess(ServerAccessRequest),
272    /// `refresh-client`
273    RefreshClient(Box<RefreshClientRequest>),
274    /// `list-clients`
275    ListClients(Box<ListClientsRequest>),
276    /// `suspend-client`
277    SuspendClient(SuspendClientRequest),
278    /// Extended `detach-client` semantics including `-a`, `-s`, `-P`, and `-E`.
279    DetachClientExt(DetachClientExtRequest),
280    /// Further-extended `attach-session` semantics including `-c working-directory`.
281    AttachSessionExt2(Box<AttachSessionExt2Request>),
282    /// Further-extended `switch-client` semantics including `-c target-client` and `-Z`.
283    SwitchClientExt3(Box<SwitchClientExt3Request>),
284    /// `resize-window`
285    ResizeWindow(ResizeWindowRequest),
286    /// `respawn-window`
287    RespawnWindow(Box<RespawnWindowRequest>),
288    /// `move-pane`
289    MovePane(MovePaneRequest),
290    /// `pipe-pane`
291    PipePane(PipePaneRequest),
292    /// `respawn-pane`
293    RespawnPane(Box<RespawnPaneRequest>),
294    /// `link-window`
295    LinkWindow(LinkWindowRequest),
296    /// `unlink-window`
297    UnlinkWindow(UnlinkWindowRequest),
298    /// `select-pane -m` / `select-pane -M`
299    SelectPaneMark(SelectPaneMarkRequest),
300    /// Internal detached target resolution for tmux-style raw target text.
301    ResolveTarget(ResolveTargetRequest),
302    /// Extended `split-window` semantics including an explicit shell command.
303    SplitWindowExt(Box<SplitWindowExtRequest>),
304    /// Internal SDK/daemon version and capability negotiation.
305    Handshake(HandshakeRequest),
306    /// Internal daemon-backed structured pane snapshot endpoint.
307    PaneSnapshot(PaneSnapshotRequest),
308    /// Internal daemon-backed pane output subscription endpoint.
309    SubscribePaneOutput(SubscribePaneOutputRequest),
310    /// Internal daemon-backed pane output unsubscription endpoint.
311    UnsubscribePaneOutput(UnsubscribePaneOutputRequest),
312    /// Internal daemon-backed pane output cursor polling endpoint.
313    PaneOutputCursor(PaneOutputCursorRequest),
314    /// Internal daemon-backed SDK byte wait endpoint.
315    SdkWaitForOutput(SdkWaitForOutputRequest),
316    /// Internal daemon-backed SDK wait cancellation endpoint.
317    CancelSdkWait(CancelSdkWaitRequest),
318    /// SDK pane input endpoint with stable pane-id targeting.
319    PaneInput(PaneInputRequest),
320    /// SDK pane resize endpoint with stable pane-id targeting.
321    PaneResize(PaneResizeRequest),
322    /// SDK pane kill endpoint with stable pane-id targeting.
323    PaneKill(PaneKillRequest),
324    /// SDK pane respawn endpoint with stable pane-id targeting.
325    PaneRespawn(Box<PaneRespawnRequest>),
326    /// SDK pane snapshot endpoint with stable pane-id targeting.
327    PaneSnapshotRef(PaneSnapshotRefRequest),
328    /// SDK pane select/title endpoint with stable pane-id targeting.
329    PaneSelect(PaneSelectRequest),
330    /// SDK pane input broadcast endpoint with stable pane-id targeting.
331    PaneBroadcastInput(PaneBroadcastInputRequest),
332    /// SDK app-owned session lease create endpoint.
333    CreateSessionLease(CreateSessionLeaseRequest),
334    /// SDK app-owned session lease renewal endpoint.
335    RenewSessionLease(RenewSessionLeaseRequest),
336    /// SDK app-owned session lease release endpoint.
337    ReleaseSessionLease(ReleaseSessionLeaseRequest),
338    /// Internal daemon-backed pane output subscription endpoint with stable pane-id targeting.
339    SubscribePaneOutputRef(SubscribePaneOutputRefRequest),
340    /// Internal daemon-backed SDK byte wait endpoint with stable pane-id targeting.
341    SdkWaitForOutputRef(SdkWaitForOutputRefRequest),
342    /// Internal daemon version and activity status endpoint.
343    DaemonStatus(DaemonStatusRequest),
344    /// Internal idle-only shutdown endpoint used by seamless upgrades.
345    ShutdownIfIdle(ShutdownIfIdleRequest),
346    /// Browser-visible pane sharing command family.
347    WebShare(Box<WebShareRequest>),
348    /// `display-message` extension with target-client context.
349    DisplayMessageExt(Box<DisplayMessageExtRequest>),
350    /// `send-keys` extension with target-client context.
351    SendKeysExt2(Box<SendKeysExt2Request>),
352    /// Attach-session extension with attach-stream client capabilities.
353    AttachSessionExt3(Box<AttachSessionExt3Request>),
354    /// `split-window` with raw target text resolved server-side.
355    SplitWindowTargetAction(Box<SplitWindowTargetActionRequest>),
356    /// `resize-pane` with raw target text resolved server-side.
357    ResizePaneTargetAction(ResizePaneTargetActionRequest),
358    /// `capture-pane` with raw target text resolved server-side.
359    CapturePaneTargetAction(Box<CapturePaneTargetActionRequest>),
360    /// SDK pane option mutation endpoint with stable pane-id targeting.
361    PaneOptionSet(PaneOptionSetRequest),
362    /// SDK pane option lookup endpoint with stable pane-id targeting.
363    PaneOptionGet(PaneOptionGetRequest),
364    /// SDK pane-state subscription endpoint with stable pane-id targeting.
365    SubscribePaneState(SubscribePaneStateRequest),
366    /// SDK pane-state cursor endpoint.
367    PaneStateCursor(PaneStateCursorRequest),
368    /// SDK pane-state unsubscription endpoint.
369    UnsubscribePaneState(UnsubscribePaneStateRequest),
370    /// SDK pane foreground-state endpoint with stable pane-id targeting.
371    PaneForegroundState(PaneForegroundStateRequest),
372    /// SDK split endpoint returning visible and stable pane identity atomically.
373    SplitWindowIdentity(Box<SplitWindowIdentityRequest>),
374}
375
376impl Request {
377    /// Returns the stable routing name for the request variant.
378    #[must_use]
379    pub const fn command_name(&self) -> &'static str {
380        match self {
381            Self::NewSession(_) => "new-session",
382            Self::HasSession(_) => "has-session",
383            Self::KillSession(_) => "kill-session",
384            Self::NewWindow(_) => "new-window",
385            Self::KillWindow(_) => "kill-window",
386            Self::SelectWindow(_) => "select-window",
387            Self::RenameWindow(_) => "rename-window",
388            Self::NextWindow(_) => "next-window",
389            Self::PreviousWindow(_) => "previous-window",
390            Self::LastWindow(_) => "last-window",
391            Self::ListWindows(_) => "list-windows",
392            Self::LinkWindow(_) => "link-window",
393            Self::MoveWindow(_) => "move-window",
394            Self::SwapWindow(_) => "swap-window",
395            Self::RotateWindow(_) => "rotate-window",
396            Self::SplitWindow(_)
397            | Self::SplitWindowExt(_)
398            | Self::SplitWindowTargetAction(_)
399            | Self::SplitWindowIdentity(_) => "split-window",
400            Self::SwapPane(_) => "swap-pane",
401            Self::LastPane(_) => "last-pane",
402            Self::JoinPane(_) => "join-pane",
403            Self::BreakPane(_) => "break-pane",
404            Self::KillPane(_) => "kill-pane",
405            Self::SelectLayout(_) => "select-layout",
406            Self::ResizePane(_) => "resize-pane",
407            Self::DisplayPanes(_) => "display-panes",
408            Self::SelectPane(_) | Self::SelectPaneAdjacent(_) | Self::SelectPaneMark(_) => {
409                "select-pane"
410            }
411            Self::SendKeys(_) | Self::SendKeysExt2(_) => "send-keys",
412            Self::AttachSession(_) => "attach-session",
413            Self::SwitchClient(_) => "switch-client",
414            Self::DetachClient(_) => "detach-client",
415            Self::SetOption(_) => "set-option",
416            Self::SetEnvironment(_) => "set-environment",
417            Self::SetHook(_) => "set-hook",
418            Self::NextLayout(_) => "next-layout",
419            Self::PreviousLayout(_) => "previous-layout",
420            Self::ShowOptions(_) => "show-options",
421            Self::ShowEnvironment(_) => "show-environment",
422            Self::SetBuffer(_) => "set-buffer",
423            Self::ShowBuffer(_) => "show-buffer",
424            Self::PasteBuffer(_) => "paste-buffer",
425            Self::ListBuffers(_) => "list-buffers",
426            Self::DeleteBuffer(_) => "delete-buffer",
427            Self::LoadBuffer(_) => "load-buffer",
428            Self::SaveBuffer(_) => "save-buffer",
429            Self::CapturePane(_) | Self::CapturePaneTargetAction(_) => "capture-pane",
430            Self::PaneSnapshot(_) => "pane-snapshot",
431            Self::SubscribePaneOutput(_) | Self::SubscribePaneOutputRef(_) => {
432                "subscribe-pane-output"
433            }
434            Self::UnsubscribePaneOutput(_) => "unsubscribe-pane-output",
435            Self::PaneOutputCursor(_) => "pane-output-cursor",
436            Self::SdkWaitForOutput(_) | Self::SdkWaitForOutputRef(_) => "sdk-wait-output",
437            Self::CancelSdkWait(_) => "cancel-sdk-wait",
438            Self::PaneInput(_) => "send-keys",
439            Self::PaneBroadcastInput(_) => "send-keys",
440            Self::CreateSessionLease(_) => "create-session-lease",
441            Self::RenewSessionLease(_) => "renew-session-lease",
442            Self::ReleaseSessionLease(_) => "release-session-lease",
443            Self::PaneResize(_) | Self::ResizePaneTargetAction(_) => "resize-pane",
444            Self::PaneKill(_) => "kill-pane",
445            Self::PaneRespawn(_) => "respawn-pane",
446            Self::PaneSnapshotRef(_) => "pane-snapshot",
447            Self::PaneSelect(_) => "select-pane",
448            Self::PaneOptionSet(_) => "pane-option-set",
449            Self::PaneOptionGet(_) => "pane-option-get",
450            Self::SubscribePaneState(_) => "subscribe-pane-state",
451            Self::PaneStateCursor(_) => "pane-state-cursor",
452            Self::UnsubscribePaneState(_) => "unsubscribe-pane-state",
453            Self::PaneForegroundState(_) => "pane-foreground-state",
454            Self::DisplayMessage(_) | Self::DisplayMessageExt(_) => "display-message",
455            Self::ResolveTarget(_) => "resolve-target",
456            Self::RunShell(_) => "run-shell",
457            Self::IfShell(_) => "if-shell",
458            Self::WaitFor(_) => "wait-for",
459            Self::RenameSession(_) => "rename-session",
460            Self::ListSessions(_) => "list-sessions",
461            Self::ListPanes(_) => "list-panes",
462            Self::SourceFile(_) => "source-file",
463            Self::UnlinkWindow(_) => "unlink-window",
464            Self::SetOptionByName(_) => "set-option",
465            Self::SetHookMutation(_) => "set-hook",
466            Self::ShowHooks(_) => "show-hooks",
467            Self::SendKeysExt(_) => "send-keys",
468            Self::SwitchClientExt(_) => "switch-client",
469            Self::BindKey(_) => "bind-key",
470            Self::UnbindKey(_) => "unbind-key",
471            Self::ListKeys(_) => "list-keys",
472            Self::SendPrefix(_) => "send-prefix",
473            Self::ClearHistory(_) => "clear-history",
474            Self::CopyMode(_) => "copy-mode",
475            Self::ControlMode(_) => "control-mode",
476            Self::ClockMode(_) => "clock-mode",
477            Self::ShowMessages(_) => "show-messages",
478            Self::NewSessionExt(_) => "new-session",
479            Self::AttachSessionExt(_) => "attach-session",
480            Self::SwitchClientExt2(_) => "switch-client",
481            Self::SelectCustomLayout(_) => "select-layout",
482            Self::SelectOldLayout(_) => "select-layout",
483            Self::SpreadLayout(_) => "select-layout",
484            Self::ResizeWindow(_) => "resize-window",
485            Self::RespawnWindow(_) => "respawn-window",
486            Self::MovePane(_) => "move-pane",
487            Self::PipePane(_) => "pipe-pane",
488            Self::RespawnPane(_) => "respawn-pane",
489            Self::KillServer(_) => "kill-server",
490            Self::LockServer(_) => "lock-server",
491            Self::LockSession(_) => "lock-session",
492            Self::LockClient(_) => "lock-client",
493            Self::ServerAccess(_) => "server-access",
494            Self::RefreshClient(_) => "refresh-client",
495            Self::ListClients(_) => "list-clients",
496            Self::SuspendClient(_) => "suspend-client",
497            Self::DetachClientExt(_) => "detach-client",
498            Self::AttachSessionExt2(_) | Self::AttachSessionExt3(_) => "attach-session",
499            Self::SwitchClientExt3(_) => "switch-client",
500            Self::Handshake(_) => "handshake",
501            Self::DaemonStatus(_) => "daemon-status",
502            Self::ShutdownIfIdle(_) => "shutdown-if-idle",
503            Self::WebShare(_) => "web-share",
504        }
505    }
506}
507
508/// Request payload for `show-messages`.
509#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
510pub struct ShowMessagesRequest {
511    /// Whether to print the server job summary.
512    pub jobs: bool,
513    /// Whether to print terminal information.
514    pub terminals: bool,
515    /// The optional target client filter used by terminal and job summaries.
516    pub target_client: Option<String>,
517}
518
519/// Request payload for `run-shell`.
520#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
521pub struct RunShellRequest {
522    /// The server-local shell command passed to `sh -c`.
523    pub command: String,
524    /// Literal positional format arguments exposed as `#{1}`, `#{2}`, and so on.
525    #[serde(default)]
526    pub arguments: Vec<String>,
527    /// Whether the command should run fire-and-forget without output capture.
528    pub background: bool,
529    /// Whether the command should be executed as tmux commands instead of `sh -c`.
530    #[serde(default)]
531    pub as_commands: bool,
532    /// Whether stderr should be captured alongside stdout.
533    #[serde(default)]
534    pub show_stderr: bool,
535    /// Optional delay, in seconds, before the command runs.
536    #[serde(default)]
537    pub delay_seconds: Option<RunShellDelaySeconds>,
538    /// Optional explicit working directory.
539    #[serde(default)]
540    pub start_directory: Option<PathBuf>,
541    /// Optional explicit target pane used for format and session context.
542    #[serde(default)]
543    pub target: Option<PaneTarget>,
544    /// Internal source-file recursion depth inherited by queued run-shell.
545    #[serde(default)]
546    pub source_depth: Option<usize>,
547}
548
549/// Losslessly serializable `run-shell -d` seconds value with stable `Eq`.
550#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
551pub struct RunShellDelaySeconds(pub f64);
552
553impl RunShellDelaySeconds {
554    /// Returns the raw seconds value.
555    #[must_use]
556    pub const fn as_secs_f64(self) -> f64 {
557        self.0
558    }
559}
560
561impl PartialEq for RunShellDelaySeconds {
562    fn eq(&self, other: &Self) -> bool {
563        self.0.to_bits() == other.0.to_bits()
564    }
565}
566
567impl Eq for RunShellDelaySeconds {}
568
569/// Request payload for `if-shell`.
570#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
571pub struct IfShellRequest {
572    /// The condition string expanded through the shared formatter before evaluation.
573    pub condition: String,
574    /// Whether to evaluate the expanded condition with format truthiness instead of `sh -c`.
575    pub format_mode: bool,
576    /// The nested RMUX command string dispatched when the condition is true.
577    pub then_command: String,
578    /// The optional nested RMUX command string dispatched when the condition is false.
579    pub else_command: Option<String>,
580    /// Optional exact target used as shared-format context.
581    pub target: Option<Target>,
582    /// The caller working directory used to resolve nested relative file paths.
583    pub caller_cwd: Option<PathBuf>,
584    /// Whether the condition should be evaluated asynchronously.
585    #[serde(default)]
586    pub background: bool,
587}
588
589/// Request payload for `source-file`.
590#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
591pub struct SourceFileRequest {
592    /// The path arguments to expand, glob, parse, and optionally execute.
593    pub paths: Vec<String>,
594    /// Whether missing files and glob misses should be suppressed.
595    pub quiet: bool,
596    /// Whether to parse only without executing parsed commands.
597    pub parse_only: bool,
598    /// Whether parsed commands should be printed to stdout.
599    pub verbose: bool,
600    /// Whether each path argument should be format-expanded before globbing.
601    pub expand_paths: bool,
602    /// Optional pane target used as the `-F` format context.
603    pub target: Option<PaneTarget>,
604    /// The caller working directory used to resolve relative paths.
605    pub caller_cwd: Option<PathBuf>,
606    /// Content read from client stdin for `source-file -`.
607    pub stdin: Option<String>,
608}
609
610/// Request payload for `unlink-window`.
611#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
612pub struct UnlinkWindowRequest {
613    /// The window slot to remove.
614    pub target: WindowTarget,
615    /// Whether removing the final link is allowed (`-k`).
616    #[serde(default)]
617    pub kill_if_last: bool,
618}
619
620/// The supported `wait-for` operation modes.
621#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
622pub enum WaitForMode {
623    /// Wait for the next signal on the named channel.
624    Wait,
625    /// Signal all current plain waiters on the named channel.
626    Signal,
627    /// Acquire the named server-local lock, waiting in FIFO order when held.
628    Lock,
629    /// Release the named server-local lock.
630    Unlock,
631}
632
633/// Request payload for `wait-for`.
634#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
635pub struct WaitForRequest {
636    /// The server-local wait channel name.
637    pub channel: String,
638    /// The selected wait operation.
639    pub mode: WaitForMode,
640}
641
642/// Request payload for a daemon-backed SDK byte wait.
643///
644/// This is intentionally distinct from tmux-compatible [`WaitForRequest`].
645/// SDK waits are pane-output waits with typed IDs used only for cancellation
646/// and teardown bookkeeping; they never signal or lock tmux `wait-for`
647/// channels.
648#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
649pub struct SdkWaitForOutputRequest {
650    /// Opaque SDK transport owner for this wait.
651    pub owner_id: SdkWaitOwnerId,
652    /// Wait ID allocated by the SDK under `owner_id`.
653    pub wait_id: SdkWaitId,
654    /// Pane whose raw output stream is observed.
655    pub target: PaneTarget,
656    /// Raw byte sequence to search for in pane output.
657    pub bytes: Vec<u8>,
658    /// Cursor position used when arming the wait.
659    pub start: PaneOutputSubscriptionStart,
660}
661
662/// Request payload for a daemon-backed SDK byte wait by slot or stable id.
663#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
664pub struct SdkWaitForOutputRefRequest {
665    /// Opaque SDK transport owner for this wait.
666    pub owner_id: SdkWaitOwnerId,
667    /// Wait ID allocated by the SDK under `owner_id`.
668    pub wait_id: SdkWaitId,
669    /// Pane whose raw output stream is observed.
670    pub target: PaneTargetRef,
671    /// Raw byte sequence to search for in pane output.
672    pub bytes: Vec<u8>,
673    /// Cursor position used when arming the wait.
674    pub start: PaneOutputSubscriptionStart,
675}
676
677/// Request payload for best-effort cancellation of a daemon-backed SDK wait.
678#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
679pub struct CancelSdkWaitRequest {
680    /// Opaque SDK transport owner for the wait being cancelled.
681    pub owner_id: SdkWaitOwnerId,
682    /// Wait ID allocated by the SDK under `owner_id`.
683    pub wait_id: SdkWaitId,
684}
685
686/// Request payload for `list-panes`.
687#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
688pub struct ListPanesRequest {
689    /// The exact target session name.
690    pub target: SessionName,
691    /// Optional exact target window index.
692    #[serde(default)]
693    pub target_window_index: Option<u32>,
694    /// An optional server-side format template.
695    pub format: Option<String>,
696    /// Optional filter expression.
697    #[serde(default)]
698    pub filter: Option<String>,
699    /// Optional sort-order token.
700    #[serde(default)]
701    pub sort_order: Option<String>,
702    /// Whether the selected sort order should be reversed.
703    #[serde(default)]
704    pub reversed: bool,
705}
706
707#[cfg(test)]
708#[path = "request/compat_tests.rs"]
709mod compat_tests;
710
711#[cfg(test)]
712mod tests {
713    use super::*;
714    use crate::{
715        OptionScopeSelector, PaneTarget, ScopeSelector, SelectPaneDirection, SetOptionMode,
716        SplitDirection, WebShareScope,
717    };
718    use serde::Serialize;
719    use std::mem::size_of;
720
721    fn alpha() -> SessionName {
722        SessionName::new("alpha").expect("valid session")
723    }
724
725    fn pane() -> PaneTarget {
726        PaneTarget::new(alpha(), 0)
727    }
728
729    #[test]
730    fn request_command_names_cover_extended_aliases_and_internal_tags() {
731        assert_eq!(
732            Request::NewSessionExt(Box::new(NewSessionExtRequest {
733                session_name: None,
734                working_directory: None,
735                detached: true,
736                size: None,
737                environment: None,
738                group_target: None,
739                attach_if_exists: false,
740                detach_other_clients: false,
741                kill_other_clients: false,
742                flags: None,
743                window_name: None,
744                print_session_info: false,
745                print_format: None,
746                command: None,
747                process_command: None,
748                client_environment: None,
749                skip_environment_update: false,
750            }))
751            .command_name(),
752            "new-session"
753        );
754        assert_eq!(
755            Request::SplitWindowExt(Box::new(SplitWindowExtRequest {
756                target: SplitWindowTarget::Pane(pane()),
757                direction: SplitDirection::Vertical,
758                before: false,
759                environment: None,
760                command: None,
761                process_command: None,
762                start_directory: None,
763                keep_alive_on_exit: None,
764                detached: false,
765                size: None,
766                preserve_zoom: false,
767                full_size: false,
768                stdin_payload: None,
769            }))
770            .command_name(),
771            "split-window"
772        );
773        assert_eq!(
774            Request::SelectPaneAdjacent(SelectPaneAdjacentRequest {
775                target: pane(),
776                direction: SelectPaneDirection::Right,
777                preserve_zoom: false,
778            })
779            .command_name(),
780            "select-pane"
781        );
782        assert_eq!(
783            Request::SelectPaneMark(SelectPaneMarkRequest {
784                target: pane(),
785                clear: false,
786                title: None,
787            })
788            .command_name(),
789            "select-pane"
790        );
791        assert_eq!(
792            Request::SetOptionByName(Box::new(SetOptionByNameRequest {
793                scope: OptionScopeSelector::ServerGlobal,
794                name: "status".to_owned(),
795                value: Some("on".to_owned()),
796                mode: SetOptionMode::Replace,
797                only_if_unset: false,
798                unset: false,
799                unset_pane_overrides: false,
800                format: false,
801                format_target: None,
802            }))
803            .command_name(),
804            "set-option"
805        );
806        assert_eq!(
807            Request::SetHookMutation(SetHookMutationRequest {
808                scope: ScopeSelector::Global,
809                hook: crate::HookName::AfterNewSession,
810                command: None,
811                lifecycle: crate::HookLifecycle::Persistent,
812                append: false,
813                unset: true,
814                run_immediately: false,
815                index: None,
816            })
817            .command_name(),
818            "set-hook"
819        );
820        assert_eq!(
821            Request::AttachSessionExt2(Box::new(AttachSessionExt2Request {
822                target: Some(alpha()),
823                target_spec: None,
824                detach_other_clients: false,
825                kill_other_clients: false,
826                read_only: false,
827                skip_environment_update: false,
828                flags: None,
829                working_directory: None,
830                client_terminal: crate::ClientTerminalContext::default(),
831                client_size: None,
832            }))
833            .command_name(),
834            "attach-session"
835        );
836        assert_eq!(
837            Request::AttachSessionExt3(Box::new(AttachSessionExt3Request::from_ext2(
838                AttachSessionExt2Request {
839                    target: Some(alpha()),
840                    target_spec: None,
841                    detach_other_clients: false,
842                    kill_other_clients: false,
843                    read_only: false,
844                    skip_environment_update: false,
845                    flags: None,
846                    working_directory: None,
847                    client_terminal: crate::ClientTerminalContext::default(),
848                    client_size: None,
849                },
850                vec![crate::CAPABILITY_ATTACH_RENDER.to_owned()],
851            )))
852            .command_name(),
853            "attach-session"
854        );
855        assert_eq!(
856            Request::SwitchClientExt3(Box::new(SwitchClientExt3Request {
857                target_client: None,
858                target: Some("alpha:0.0".to_owned()),
859                key_table: None,
860                last_session: false,
861                next_session: false,
862                previous_session: false,
863                toggle_read_only: false,
864                sort_order: None,
865                skip_environment_update: false,
866                zoom: false,
867            }))
868            .command_name(),
869            "switch-client"
870        );
871        assert_eq!(
872            Request::ResolveTarget(ResolveTargetRequest {
873                target: Some("alpha:0.0".to_owned()),
874                target_type: ResolveTargetType::Pane,
875                window_index: false,
876                prefer_unattached: false,
877            })
878            .command_name(),
879            "resolve-target"
880        );
881        assert_eq!(
882            Request::Handshake(HandshakeRequest::current()).command_name(),
883            "handshake"
884        );
885        assert_eq!(
886            Request::SdkWaitForOutput(SdkWaitForOutputRequest {
887                owner_id: SdkWaitOwnerId::new(1),
888                wait_id: SdkWaitId::new(1),
889                target: pane(),
890                bytes: b"ready".to_vec(),
891                start: PaneOutputSubscriptionStart::Now,
892            })
893            .command_name(),
894            "sdk-wait-output"
895        );
896        assert_eq!(
897            Request::CancelSdkWait(CancelSdkWaitRequest {
898                owner_id: SdkWaitOwnerId::new(1),
899                wait_id: SdkWaitId::new(1),
900            })
901            .command_name(),
902            "cancel-sdk-wait"
903        );
904        assert_eq!(
905            Request::WaitFor(WaitForRequest {
906                channel: "ready".to_owned(),
907                mode: WaitForMode::Wait,
908            })
909            .command_name(),
910            "wait-for"
911        );
912    }
913
914    #[test]
915    fn pr6g_request_boxing_keeps_request_size_bounded() {
916        let request_size = size_of::<Request>();
917        let box_size = size_of::<Box<NewSessionExtRequest>>();
918
919        eprintln!("PR6G size baseline: Request={request_size} Box<T>={box_size}");
920        eprintln!(
921            "PR6G candidate sizes: NewSessionExt={} NewWindow={} SetOptionByName={} \
922             AttachSessionExt3={} SwitchClientExt3={} SendKeysExt2={} SourceFile={} WebShare={}",
923            size_of::<NewSessionExtRequest>(),
924            size_of::<NewWindowRequest>(),
925            size_of::<SetOptionByNameRequest>(),
926            size_of::<AttachSessionExt3Request>(),
927            size_of::<SwitchClientExt3Request>(),
928            size_of::<SendKeysExt2Request>(),
929            size_of::<SourceFileRequest>(),
930            size_of::<WebShareRequest>(),
931        );
932        let mut remaining = [
933            ("AttachSessionExt2", size_of::<AttachSessionExt2Request>()),
934            ("BindKey", size_of::<BindKeyRequest>()),
935            ("BreakPane", size_of::<BreakPaneRequest>()),
936            ("CapturePane", size_of::<CapturePaneRequest>()),
937            ("ClearHistory", size_of::<ClearHistoryRequest>()),
938            ("ClockMode", size_of::<ClockModeRequest>()),
939            ("ControlMode", size_of::<ControlModeRequest>()),
940            ("CopyMode", size_of::<CopyModeRequest>()),
941            ("CreateSessionLease", size_of::<CreateSessionLeaseRequest>()),
942            ("DeleteBuffer", size_of::<DeleteBufferRequest>()),
943            ("DetachClientExt", size_of::<DetachClientExtRequest>()),
944            ("DisplayPanes", size_of::<DisplayPanesRequest>()),
945            ("DisplayMessage", size_of::<DisplayMessageRequest>()),
946            ("DisplayMessageExt", size_of::<DisplayMessageExtRequest>()),
947            ("Handshake", size_of::<HandshakeRequest>()),
948            ("IfShell", size_of::<IfShellRequest>()),
949            ("JoinPane", size_of::<JoinPaneRequest>()),
950            ("KillPane", size_of::<KillPaneRequest>()),
951            ("KillWindow", size_of::<KillWindowRequest>()),
952            ("LinkWindow", size_of::<LinkWindowRequest>()),
953            ("ListClients", size_of::<ListClientsRequest>()),
954            ("ListKeys", size_of::<ListKeysRequest>()),
955            ("ListPanes", size_of::<ListPanesRequest>()),
956            ("ListSessions", size_of::<ListSessionsRequest>()),
957            ("ListWindows", size_of::<ListWindowsRequest>()),
958            ("LoadBuffer", size_of::<LoadBufferRequest>()),
959            ("MovePane", size_of::<MovePaneRequest>()),
960            ("MoveWindow", size_of::<MoveWindowRequest>()),
961            ("NewSession", size_of::<NewSessionRequest>()),
962            ("PasteBuffer", size_of::<PasteBufferRequest>()),
963            ("PaneInput", size_of::<PaneInputRequest>()),
964            ("PaneKill", size_of::<PaneKillRequest>()),
965            ("PaneResize", size_of::<PaneResizeRequest>()),
966            ("PaneRespawn", size_of::<PaneRespawnRequest>()),
967            ("PaneSelect", size_of::<PaneSelectRequest>()),
968            ("PaneSnapshot", size_of::<PaneSnapshotRequest>()),
969            ("PaneSnapshotRef", size_of::<PaneSnapshotRefRequest>()),
970            ("PipePane", size_of::<PipePaneRequest>()),
971            ("RefreshClient", size_of::<RefreshClientRequest>()),
972            ("RespawnPane", size_of::<RespawnPaneRequest>()),
973            ("RespawnWindow", size_of::<RespawnWindowRequest>()),
974            ("ResizePane", size_of::<ResizePaneRequest>()),
975            ("ResizeWindow", size_of::<ResizeWindowRequest>()),
976            ("RotateWindow", size_of::<RotateWindowRequest>()),
977            ("RunShell", size_of::<RunShellRequest>()),
978            ("SaveBuffer", size_of::<SaveBufferRequest>()),
979            ("SelectCustomLayout", size_of::<SelectCustomLayoutRequest>()),
980            ("SelectLayout", size_of::<SelectLayoutRequest>()),
981            ("SelectPane", size_of::<SelectPaneRequest>()),
982            ("SelectPaneAdjacent", size_of::<SelectPaneAdjacentRequest>()),
983            ("SelectPaneMark", size_of::<SelectPaneMarkRequest>()),
984            ("SetEnvironment", size_of::<SetEnvironmentRequest>()),
985            ("SetBuffer", size_of::<SetBufferRequest>()),
986            ("SetHook", size_of::<SetHookRequest>()),
987            ("SetHookMutation", size_of::<SetHookMutationRequest>()),
988            ("SetOption", size_of::<SetOptionRequest>()),
989            ("ShowBuffer", size_of::<ShowBufferRequest>()),
990            ("ShowEnvironment", size_of::<ShowEnvironmentRequest>()),
991            ("ShowHooks", size_of::<ShowHooksRequest>()),
992            ("ShowMessages", size_of::<ShowMessagesRequest>()),
993            ("ShowOptions", size_of::<ShowOptionsRequest>()),
994            ("SplitWindow", size_of::<SplitWindowRequest>()),
995            ("SplitWindowExt", size_of::<SplitWindowExtRequest>()),
996            (
997                "SubscribePaneOutput",
998                size_of::<SubscribePaneOutputRequest>(),
999            ),
1000            (
1001                "SubscribePaneOutputRef",
1002                size_of::<SubscribePaneOutputRefRequest>(),
1003            ),
1004            ("SwitchClientExt2", size_of::<SwitchClientExt2Request>()),
1005        ];
1006        remaining.sort_by_key(|(_, size)| std::cmp::Reverse(*size));
1007        eprintln!("PR6G largest remaining candidates: {remaining:?}");
1008
1009        assert!(
1010            request_size <= 96,
1011            "Request grew past the PR6G boxed payload budget: {request_size}"
1012        );
1013        assert_eq!(box_size, size_of::<usize>());
1014    }
1015
1016    #[test]
1017    fn pr6g_boxed_payloads_are_bincode_transparent_for_request_candidates() {
1018        assert_box_serializes_like_value(NewSessionExtRequest {
1019            session_name: Some(alpha()),
1020            working_directory: Some("/tmp".to_owned()),
1021            detached: true,
1022            size: Some(crate::TerminalSize {
1023                cols: 120,
1024                rows: 40,
1025            }),
1026            environment: Some(vec!["A=B".to_owned()]),
1027            group_target: None,
1028            attach_if_exists: false,
1029            detach_other_clients: false,
1030            kill_other_clients: false,
1031            flags: Some(vec!["read-only".to_owned()]),
1032            window_name: Some("main".to_owned()),
1033            print_session_info: true,
1034            print_format: Some("#{session_name}".to_owned()),
1035            command: Some(vec!["sh".to_owned(), "-lc".to_owned(), "true".to_owned()]),
1036            process_command: None,
1037            client_environment: Some(vec!["PATH=/bin".to_owned()]),
1038            skip_environment_update: false,
1039        });
1040        assert_box_serializes_like_value(SetOptionByNameRequest {
1041            scope: OptionScopeSelector::ServerGlobal,
1042            name: "@plugin".to_owned(),
1043            value: Some("enabled".to_owned()),
1044            mode: SetOptionMode::Replace,
1045            only_if_unset: false,
1046            unset: false,
1047            unset_pane_overrides: false,
1048            format: true,
1049            format_target: Some(crate::Target::Session(alpha())),
1050        });
1051        assert_box_serializes_like_value(ListWindowsRequest {
1052            target: alpha(),
1053            format: Some("#{window_index}".to_owned()),
1054            filter: Some("#{==:#{window_active},1}".to_owned()),
1055            sort_order: Some("index".to_owned()),
1056            reversed: true,
1057        });
1058        assert_box_serializes_like_value(ListPanesRequest {
1059            target: alpha(),
1060            target_window_index: Some(1),
1061            format: Some("#{pane_index}".to_owned()),
1062            filter: Some("#{==:#{pane_active},1}".to_owned()),
1063            sort_order: Some("index".to_owned()),
1064            reversed: true,
1065        });
1066        assert_box_serializes_like_value(DisplayPanesRequest {
1067            target: alpha(),
1068            duration_ms: Some(1_000),
1069            non_blocking: true,
1070            no_command: false,
1071            template: Some("select-pane -t '%%'".to_owned()),
1072            target_client: Some("/dev/pts/1".to_owned()),
1073        });
1074        assert_box_serializes_like_value(SetBufferRequest {
1075            name: Some("named".to_owned()),
1076            content: b"buffer".to_vec(),
1077            append: false,
1078            new_name: None,
1079            set_clipboard: true,
1080            target_client: Some("/dev/pts/1".to_owned()),
1081        });
1082        assert_box_serializes_like_value(LoadBufferRequest {
1083            path: "/tmp/input".to_owned(),
1084            cwd: Some(PathBuf::from("/tmp")),
1085            name: Some("loaded".to_owned()),
1086            set_clipboard: true,
1087            target_client: Some("/dev/pts/1".to_owned()),
1088        });
1089        assert_box_serializes_like_value(AttachSessionExt3Request::from_ext2(
1090            AttachSessionExt2Request {
1091                target: Some(alpha()),
1092                target_spec: Some("alpha:0.0".to_owned()),
1093                detach_other_clients: false,
1094                kill_other_clients: false,
1095                read_only: true,
1096                skip_environment_update: false,
1097                flags: Some(vec!["active-pane".to_owned()]),
1098                working_directory: Some("/tmp".to_owned()),
1099                client_terminal: crate::ClientTerminalContext::default(),
1100                client_size: Some(crate::TerminalSize { cols: 80, rows: 24 }),
1101            },
1102            vec![crate::CAPABILITY_ATTACH_RENDER.to_owned()],
1103        ));
1104        assert_box_serializes_like_value(WebShareRequest::Create(CreateWebShareRequest {
1105            scope: WebShareScope::Session(alpha()),
1106            public_base_url: Some("https://example.invalid".to_owned()),
1107            tunnel_provider: None,
1108            frontend_url: Some("https://share.example.invalid".to_owned()),
1109            ttl_seconds: Some(60),
1110            expires_at_unix: None,
1111            max_spectators: Some(8),
1112            max_operators: Some(1),
1113            url_options: WebShareUrlOptions::default(),
1114            require_pin: true,
1115            operator_pin: Some("123456".to_owned()),
1116            spectator_pin: Some("654321".to_owned()),
1117            terminal_palette: None,
1118            operator: true,
1119            spectator: true,
1120            controls: true,
1121            kill_session_on_expire: false,
1122        }));
1123    }
1124
1125    fn assert_box_serializes_like_value<T>(value: T)
1126    where
1127        T: Clone + Serialize,
1128    {
1129        let boxed = Box::new(value.clone());
1130        assert_eq!(
1131            bincode::serialize(&value).expect("value encodes"),
1132            bincode::serialize(&boxed).expect("boxed value encodes"),
1133            "Box<T> must be transparent before PR6G boxes enum variants"
1134        );
1135    }
1136}