1use 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#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
117pub enum Request {
118 NewSession(NewSessionRequest),
120 HasSession(HasSessionRequest),
122 KillSession(KillSessionRequest),
124 NewWindow(Box<NewWindowRequest>),
126 KillWindow(KillWindowRequest),
128 SelectWindow(SelectWindowRequest),
130 RenameWindow(RenameWindowRequest),
132 NextWindow(NextWindowRequest),
134 PreviousWindow(PreviousWindowRequest),
136 LastWindow(LastWindowRequest),
138 ListWindows(Box<ListWindowsRequest>),
140 MoveWindow(MoveWindowRequest),
142 SwapWindow(SwapWindowRequest),
144 RotateWindow(RotateWindowRequest),
146 SplitWindow(SplitWindowRequest),
148 SwapPane(SwapPaneRequest),
150 LastPane(LastPaneRequest),
152 JoinPane(JoinPaneRequest),
154 BreakPane(Box<BreakPaneRequest>),
156 KillPane(KillPaneRequest),
158 SelectLayout(SelectLayoutRequest),
160 ResizePane(ResizePaneRequest),
162 DisplayPanes(Box<DisplayPanesRequest>),
164 SelectPane(Box<SelectPaneRequest>),
166 SelectPaneAdjacent(SelectPaneAdjacentRequest),
168 SendKeys(SendKeysRequest),
170 AttachSession(AttachSessionRequest),
172 SwitchClient(SwitchClientRequest),
174 DetachClient(DetachClientRequest),
176 SetOption(SetOptionRequest),
178 SetEnvironment(Box<SetEnvironmentRequest>),
180 SetHook(SetHookRequest),
182 NextLayout(NextLayoutRequest),
184 PreviousLayout(PreviousLayoutRequest),
186 ShowOptions(ShowOptionsRequest),
188 ShowEnvironment(ShowEnvironmentRequest),
190 SetBuffer(Box<SetBufferRequest>),
192 ShowBuffer(ShowBufferRequest),
194 PasteBuffer(Box<PasteBufferRequest>),
196 ListBuffers(ListBuffersRequest),
198 DeleteBuffer(DeleteBufferRequest),
200 LoadBuffer(Box<LoadBufferRequest>),
202 SaveBuffer(SaveBufferRequest),
204 CapturePane(Box<CapturePaneRequest>),
206 DisplayMessage(DisplayMessageRequest),
208 RunShell(Box<RunShellRequest>),
210 IfShell(Box<IfShellRequest>),
212 WaitFor(WaitForRequest),
214 RenameSession(RenameSessionRequest),
216 ListSessions(ListSessionsRequest),
218 ListPanes(Box<ListPanesRequest>),
220 SourceFile(Box<SourceFileRequest>),
222 SetOptionByName(Box<SetOptionByNameRequest>),
224 SetHookMutation(SetHookMutationRequest),
226 ShowHooks(ShowHooksRequest),
228 SendKeysExt(SendKeysExtRequest),
230 SwitchClientExt(SwitchClientExtRequest),
232 BindKey(Box<BindKeyRequest>),
234 UnbindKey(UnbindKeyRequest),
236 ListKeys(Box<ListKeysRequest>),
238 SendPrefix(SendPrefixRequest),
240 ClearHistory(ClearHistoryRequest),
242 CopyMode(CopyModeRequest),
244 ControlMode(ControlModeRequest),
246 ClockMode(ClockModeRequest),
248 ShowMessages(ShowMessagesRequest),
250 NewSessionExt(Box<NewSessionExtRequest>),
252 AttachSessionExt(AttachSessionExtRequest),
254 SwitchClientExt2(Box<SwitchClientExt2Request>),
256 SelectCustomLayout(SelectCustomLayoutRequest),
258 SelectOldLayout(SelectOldLayoutRequest),
260 SpreadLayout(SpreadLayoutRequest),
262 KillServer(KillServerRequest),
264 LockServer(LockServerRequest),
266 LockSession(LockSessionRequest),
268 LockClient(LockClientRequest),
270 ServerAccess(ServerAccessRequest),
272 RefreshClient(Box<RefreshClientRequest>),
274 ListClients(Box<ListClientsRequest>),
276 SuspendClient(SuspendClientRequest),
278 DetachClientExt(DetachClientExtRequest),
280 AttachSessionExt2(Box<AttachSessionExt2Request>),
282 SwitchClientExt3(Box<SwitchClientExt3Request>),
284 ResizeWindow(ResizeWindowRequest),
286 RespawnWindow(Box<RespawnWindowRequest>),
288 MovePane(MovePaneRequest),
290 PipePane(PipePaneRequest),
292 RespawnPane(Box<RespawnPaneRequest>),
294 LinkWindow(LinkWindowRequest),
296 UnlinkWindow(UnlinkWindowRequest),
298 SelectPaneMark(SelectPaneMarkRequest),
300 ResolveTarget(ResolveTargetRequest),
302 SplitWindowExt(Box<SplitWindowExtRequest>),
304 Handshake(HandshakeRequest),
306 PaneSnapshot(PaneSnapshotRequest),
308 SubscribePaneOutput(SubscribePaneOutputRequest),
310 UnsubscribePaneOutput(UnsubscribePaneOutputRequest),
312 PaneOutputCursor(PaneOutputCursorRequest),
314 SdkWaitForOutput(SdkWaitForOutputRequest),
316 CancelSdkWait(CancelSdkWaitRequest),
318 PaneInput(PaneInputRequest),
320 PaneResize(PaneResizeRequest),
322 PaneKill(PaneKillRequest),
324 PaneRespawn(Box<PaneRespawnRequest>),
326 PaneSnapshotRef(PaneSnapshotRefRequest),
328 PaneSelect(PaneSelectRequest),
330 PaneBroadcastInput(PaneBroadcastInputRequest),
332 CreateSessionLease(CreateSessionLeaseRequest),
334 RenewSessionLease(RenewSessionLeaseRequest),
336 ReleaseSessionLease(ReleaseSessionLeaseRequest),
338 SubscribePaneOutputRef(SubscribePaneOutputRefRequest),
340 SdkWaitForOutputRef(SdkWaitForOutputRefRequest),
342 DaemonStatus(DaemonStatusRequest),
344 ShutdownIfIdle(ShutdownIfIdleRequest),
346 WebShare(Box<WebShareRequest>),
348 DisplayMessageExt(Box<DisplayMessageExtRequest>),
350 SendKeysExt2(Box<SendKeysExt2Request>),
352 AttachSessionExt3(Box<AttachSessionExt3Request>),
354 SplitWindowTargetAction(Box<SplitWindowTargetActionRequest>),
356 ResizePaneTargetAction(ResizePaneTargetActionRequest),
358 CapturePaneTargetAction(Box<CapturePaneTargetActionRequest>),
360 PaneOptionSet(PaneOptionSetRequest),
362 PaneOptionGet(PaneOptionGetRequest),
364 SubscribePaneState(SubscribePaneStateRequest),
366 PaneStateCursor(PaneStateCursorRequest),
368 UnsubscribePaneState(UnsubscribePaneStateRequest),
370 PaneForegroundState(PaneForegroundStateRequest),
372 SplitWindowIdentity(Box<SplitWindowIdentityRequest>),
374}
375
376impl Request {
377 #[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#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
510pub struct ShowMessagesRequest {
511 pub jobs: bool,
513 pub terminals: bool,
515 pub target_client: Option<String>,
517}
518
519#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
521pub struct RunShellRequest {
522 pub command: String,
524 #[serde(default)]
526 pub arguments: Vec<String>,
527 pub background: bool,
529 #[serde(default)]
531 pub as_commands: bool,
532 #[serde(default)]
534 pub show_stderr: bool,
535 #[serde(default)]
537 pub delay_seconds: Option<RunShellDelaySeconds>,
538 #[serde(default)]
540 pub start_directory: Option<PathBuf>,
541 #[serde(default)]
543 pub target: Option<PaneTarget>,
544 #[serde(default)]
546 pub source_depth: Option<usize>,
547}
548
549#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
551pub struct RunShellDelaySeconds(pub f64);
552
553impl RunShellDelaySeconds {
554 #[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#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
571pub struct IfShellRequest {
572 pub condition: String,
574 pub format_mode: bool,
576 pub then_command: String,
578 pub else_command: Option<String>,
580 pub target: Option<Target>,
582 pub caller_cwd: Option<PathBuf>,
584 #[serde(default)]
586 pub background: bool,
587}
588
589#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
591pub struct SourceFileRequest {
592 pub paths: Vec<String>,
594 pub quiet: bool,
596 pub parse_only: bool,
598 pub verbose: bool,
600 pub expand_paths: bool,
602 pub target: Option<PaneTarget>,
604 pub caller_cwd: Option<PathBuf>,
606 pub stdin: Option<String>,
608}
609
610#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
612pub struct UnlinkWindowRequest {
613 pub target: WindowTarget,
615 #[serde(default)]
617 pub kill_if_last: bool,
618}
619
620#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
622pub enum WaitForMode {
623 Wait,
625 Signal,
627 Lock,
629 Unlock,
631}
632
633#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
635pub struct WaitForRequest {
636 pub channel: String,
638 pub mode: WaitForMode,
640}
641
642#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
649pub struct SdkWaitForOutputRequest {
650 pub owner_id: SdkWaitOwnerId,
652 pub wait_id: SdkWaitId,
654 pub target: PaneTarget,
656 pub bytes: Vec<u8>,
658 pub start: PaneOutputSubscriptionStart,
660}
661
662#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
664pub struct SdkWaitForOutputRefRequest {
665 pub owner_id: SdkWaitOwnerId,
667 pub wait_id: SdkWaitId,
669 pub target: PaneTargetRef,
671 pub bytes: Vec<u8>,
673 pub start: PaneOutputSubscriptionStart,
675}
676
677#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
679pub struct CancelSdkWaitRequest {
680 pub owner_id: SdkWaitOwnerId,
682 pub wait_id: SdkWaitId,
684}
685
686#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
688pub struct ListPanesRequest {
689 pub target: SessionName,
691 #[serde(default)]
693 pub target_window_index: Option<u32>,
694 pub format: Option<String>,
696 #[serde(default)]
698 pub filter: Option<String>,
699 #[serde(default)]
701 pub sort_order: Option<String>,
702 #[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}