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, PaneInputRequest, PaneKillRequest,
35 PaneOutputCursorRequest, PaneOutputSubscriptionStart, PaneResizeRequest, PaneRespawnRequest,
36 PaneSelectRequest, PaneSnapshotRefRequest, PaneSnapshotRequest, PaneSplitSize, PipePaneRequest,
37 ResizePaneRequest, ResizePaneTargetActionRequest, RespawnPaneRequest,
38 SelectPaneAdjacentRequest, SelectPaneDirection, SelectPaneMarkRequest, SelectPaneRequest,
39 SendKeysExt2Request, SendKeysExtRequest, SendKeysRequest, SplitWindowExtRequest,
40 SplitWindowRequest, SplitWindowTarget, SplitWindowTargetActionRequest,
41 SubscribePaneOutputRefRequest, SubscribePaneOutputRequest, SwapPaneDirection, SwapPaneRequest,
42 UnsubscribePaneOutputRequest,
43};
44
45#[path = "request/window.rs"]
46mod window;
47pub use window::{
48 KillWindowRequest, LastWindowRequest, LinkWindowRequest, ListWindowsRequest, MoveWindowRequest,
49 MoveWindowTarget, NewWindowRequest, NextWindowRequest, PreviousWindowRequest,
50 RenameWindowRequest, ResizeWindowAdjustment, ResizeWindowRequest, RespawnWindowRequest,
51 RotateWindowDirection, RotateWindowRequest, SelectWindowRequest, SwapWindowRequest,
52};
53
54#[path = "request/target.rs"]
55mod target;
56pub use target::{ResolveTargetRequest, ResolveTargetType};
57
58#[path = "request/session.rs"]
59mod session;
60pub use session::{
61 CreateSessionLeaseRequest, HasSessionRequest, KillSessionRequest, ListSessionsRequest,
62 NewSessionExtRequest, NewSessionRequest, ReleaseSessionLeaseRequest, RenameSessionRequest,
63 RenewSessionLeaseRequest,
64};
65
66#[path = "request/server.rs"]
67mod server;
68pub use server::{
69 DaemonStatusRequest, KillServerRequest, LockClientRequest, LockServerRequest,
70 LockSessionRequest, ServerAccessRequest, ShutdownIfIdleRequest,
71};
72
73#[path = "request/client.rs"]
74mod client;
75pub use client::{
76 AttachSessionExt2Request, AttachSessionExt3Request, AttachSessionExtRequest,
77 AttachSessionRequest, DetachClientExtRequest, DetachClientRequest, ListClientsRequest,
78 RefreshClientRequest, SuspendClientRequest, SwitchClientExt2Request, SwitchClientExt3Request,
79 SwitchClientExtRequest, SwitchClientRequest,
80};
81
82#[path = "request/keys.rs"]
83mod keys;
84pub use keys::{
85 BindKeyRequest, ClockModeRequest, CopyModeRequest, ListKeysRequest, SendPrefixRequest,
86 UnbindKeyRequest,
87};
88
89#[path = "request/options.rs"]
90mod options;
91pub use options::{
92 SetEnvironmentMode, SetEnvironmentRequest, SetHookMutationRequest, SetHookRequest,
93 SetOptionByNameRequest, SetOptionRequest,
94};
95
96#[path = "request/buffer.rs"]
97mod buffer;
98pub use buffer::{
99 CapturePaneRequest, CapturePaneTargetActionRequest, ClearHistoryRequest, DeleteBufferRequest,
100 ListBuffersRequest, LoadBufferRequest, PasteBufferRequest, SaveBufferRequest, SetBufferRequest,
101 ShowBufferRequest,
102};
103
104#[path = "request/web.rs"]
105mod web;
106pub use web::{
107 CreateWebShareRequest, ListWebSharesRequest, LookupWebShareRequest, StopAllWebSharesRequest,
108 StopWebShareRequest, WebShareConfigRequest, WebShareRequest, WebShareScope, WebShareUrlOptions,
109 WebTerminalPalette, WebTerminalTheme,
110};
111
112#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
115pub enum Request {
116 NewSession(NewSessionRequest),
118 HasSession(HasSessionRequest),
120 KillSession(KillSessionRequest),
122 NewWindow(Box<NewWindowRequest>),
124 KillWindow(KillWindowRequest),
126 SelectWindow(SelectWindowRequest),
128 RenameWindow(RenameWindowRequest),
130 NextWindow(NextWindowRequest),
132 PreviousWindow(PreviousWindowRequest),
134 LastWindow(LastWindowRequest),
136 ListWindows(ListWindowsRequest),
138 MoveWindow(MoveWindowRequest),
140 SwapWindow(SwapWindowRequest),
142 RotateWindow(RotateWindowRequest),
144 SplitWindow(SplitWindowRequest),
146 SwapPane(SwapPaneRequest),
148 LastPane(LastPaneRequest),
150 JoinPane(JoinPaneRequest),
152 BreakPane(Box<BreakPaneRequest>),
154 KillPane(KillPaneRequest),
156 SelectLayout(SelectLayoutRequest),
158 ResizePane(ResizePaneRequest),
160 DisplayPanes(DisplayPanesRequest),
162 SelectPane(Box<SelectPaneRequest>),
164 SelectPaneAdjacent(SelectPaneAdjacentRequest),
166 SendKeys(SendKeysRequest),
168 AttachSession(AttachSessionRequest),
170 SwitchClient(SwitchClientRequest),
172 DetachClient(DetachClientRequest),
174 SetOption(SetOptionRequest),
176 SetEnvironment(Box<SetEnvironmentRequest>),
178 SetHook(SetHookRequest),
180 NextLayout(NextLayoutRequest),
182 PreviousLayout(PreviousLayoutRequest),
184 ShowOptions(ShowOptionsRequest),
186 ShowEnvironment(ShowEnvironmentRequest),
188 SetBuffer(SetBufferRequest),
190 ShowBuffer(ShowBufferRequest),
192 PasteBuffer(Box<PasteBufferRequest>),
194 ListBuffers(ListBuffersRequest),
196 DeleteBuffer(DeleteBufferRequest),
198 LoadBuffer(LoadBufferRequest),
200 SaveBuffer(SaveBufferRequest),
202 CapturePane(Box<CapturePaneRequest>),
204 DisplayMessage(DisplayMessageRequest),
206 RunShell(Box<RunShellRequest>),
208 IfShell(Box<IfShellRequest>),
210 WaitFor(WaitForRequest),
212 RenameSession(RenameSessionRequest),
214 ListSessions(ListSessionsRequest),
216 ListPanes(ListPanesRequest),
218 SourceFile(Box<SourceFileRequest>),
220 SetOptionByName(Box<SetOptionByNameRequest>),
222 SetHookMutation(SetHookMutationRequest),
224 ShowHooks(ShowHooksRequest),
226 SendKeysExt(SendKeysExtRequest),
228 SwitchClientExt(SwitchClientExtRequest),
230 BindKey(Box<BindKeyRequest>),
232 UnbindKey(UnbindKeyRequest),
234 ListKeys(Box<ListKeysRequest>),
236 SendPrefix(SendPrefixRequest),
238 ClearHistory(ClearHistoryRequest),
240 CopyMode(CopyModeRequest),
242 ControlMode(ControlModeRequest),
244 ClockMode(ClockModeRequest),
246 ShowMessages(ShowMessagesRequest),
248 NewSessionExt(Box<NewSessionExtRequest>),
250 AttachSessionExt(AttachSessionExtRequest),
252 SwitchClientExt2(Box<SwitchClientExt2Request>),
254 SelectCustomLayout(SelectCustomLayoutRequest),
256 SelectOldLayout(SelectOldLayoutRequest),
258 SpreadLayout(SpreadLayoutRequest),
260 KillServer(KillServerRequest),
262 LockServer(LockServerRequest),
264 LockSession(LockSessionRequest),
266 LockClient(LockClientRequest),
268 ServerAccess(ServerAccessRequest),
270 RefreshClient(Box<RefreshClientRequest>),
272 ListClients(Box<ListClientsRequest>),
274 SuspendClient(SuspendClientRequest),
276 DetachClientExt(DetachClientExtRequest),
278 AttachSessionExt2(Box<AttachSessionExt2Request>),
280 SwitchClientExt3(Box<SwitchClientExt3Request>),
282 ResizeWindow(ResizeWindowRequest),
284 RespawnWindow(Box<RespawnWindowRequest>),
286 MovePane(MovePaneRequest),
288 PipePane(PipePaneRequest),
290 RespawnPane(Box<RespawnPaneRequest>),
292 LinkWindow(LinkWindowRequest),
294 UnlinkWindow(UnlinkWindowRequest),
296 SelectPaneMark(SelectPaneMarkRequest),
298 ResolveTarget(ResolveTargetRequest),
300 SplitWindowExt(Box<SplitWindowExtRequest>),
302 Handshake(HandshakeRequest),
304 PaneSnapshot(PaneSnapshotRequest),
306 SubscribePaneOutput(SubscribePaneOutputRequest),
308 UnsubscribePaneOutput(UnsubscribePaneOutputRequest),
310 PaneOutputCursor(PaneOutputCursorRequest),
312 SdkWaitForOutput(SdkWaitForOutputRequest),
314 CancelSdkWait(CancelSdkWaitRequest),
316 PaneInput(PaneInputRequest),
318 PaneResize(PaneResizeRequest),
320 PaneKill(PaneKillRequest),
322 PaneRespawn(Box<PaneRespawnRequest>),
324 PaneSnapshotRef(PaneSnapshotRefRequest),
326 PaneSelect(PaneSelectRequest),
328 PaneBroadcastInput(PaneBroadcastInputRequest),
330 CreateSessionLease(CreateSessionLeaseRequest),
332 RenewSessionLease(RenewSessionLeaseRequest),
334 ReleaseSessionLease(ReleaseSessionLeaseRequest),
336 SubscribePaneOutputRef(SubscribePaneOutputRefRequest),
338 SdkWaitForOutputRef(SdkWaitForOutputRefRequest),
340 DaemonStatus(DaemonStatusRequest),
342 ShutdownIfIdle(ShutdownIfIdleRequest),
344 WebShare(Box<WebShareRequest>),
346 DisplayMessageExt(Box<DisplayMessageExtRequest>),
348 SendKeysExt2(Box<SendKeysExt2Request>),
350 AttachSessionExt3(Box<AttachSessionExt3Request>),
352 SplitWindowTargetAction(Box<SplitWindowTargetActionRequest>),
354 ResizePaneTargetAction(ResizePaneTargetActionRequest),
356 CapturePaneTargetAction(Box<CapturePaneTargetActionRequest>),
358}
359
360impl Request {
361 #[must_use]
363 pub const fn command_name(&self) -> &'static str {
364 match self {
365 Self::NewSession(_) => "new-session",
366 Self::HasSession(_) => "has-session",
367 Self::KillSession(_) => "kill-session",
368 Self::NewWindow(_) => "new-window",
369 Self::KillWindow(_) => "kill-window",
370 Self::SelectWindow(_) => "select-window",
371 Self::RenameWindow(_) => "rename-window",
372 Self::NextWindow(_) => "next-window",
373 Self::PreviousWindow(_) => "previous-window",
374 Self::LastWindow(_) => "last-window",
375 Self::ListWindows(_) => "list-windows",
376 Self::LinkWindow(_) => "link-window",
377 Self::MoveWindow(_) => "move-window",
378 Self::SwapWindow(_) => "swap-window",
379 Self::RotateWindow(_) => "rotate-window",
380 Self::SplitWindow(_) | Self::SplitWindowExt(_) | Self::SplitWindowTargetAction(_) => {
381 "split-window"
382 }
383 Self::SwapPane(_) => "swap-pane",
384 Self::LastPane(_) => "last-pane",
385 Self::JoinPane(_) => "join-pane",
386 Self::BreakPane(_) => "break-pane",
387 Self::KillPane(_) => "kill-pane",
388 Self::SelectLayout(_) => "select-layout",
389 Self::ResizePane(_) => "resize-pane",
390 Self::DisplayPanes(_) => "display-panes",
391 Self::SelectPane(_) | Self::SelectPaneAdjacent(_) | Self::SelectPaneMark(_) => {
392 "select-pane"
393 }
394 Self::SendKeys(_) | Self::SendKeysExt2(_) => "send-keys",
395 Self::AttachSession(_) => "attach-session",
396 Self::SwitchClient(_) => "switch-client",
397 Self::DetachClient(_) => "detach-client",
398 Self::SetOption(_) => "set-option",
399 Self::SetEnvironment(_) => "set-environment",
400 Self::SetHook(_) => "set-hook",
401 Self::NextLayout(_) => "next-layout",
402 Self::PreviousLayout(_) => "previous-layout",
403 Self::ShowOptions(_) => "show-options",
404 Self::ShowEnvironment(_) => "show-environment",
405 Self::SetBuffer(_) => "set-buffer",
406 Self::ShowBuffer(_) => "show-buffer",
407 Self::PasteBuffer(_) => "paste-buffer",
408 Self::ListBuffers(_) => "list-buffers",
409 Self::DeleteBuffer(_) => "delete-buffer",
410 Self::LoadBuffer(_) => "load-buffer",
411 Self::SaveBuffer(_) => "save-buffer",
412 Self::CapturePane(_) | Self::CapturePaneTargetAction(_) => "capture-pane",
413 Self::PaneSnapshot(_) => "pane-snapshot",
414 Self::SubscribePaneOutput(_) | Self::SubscribePaneOutputRef(_) => {
415 "subscribe-pane-output"
416 }
417 Self::UnsubscribePaneOutput(_) => "unsubscribe-pane-output",
418 Self::PaneOutputCursor(_) => "pane-output-cursor",
419 Self::SdkWaitForOutput(_) | Self::SdkWaitForOutputRef(_) => "sdk-wait-output",
420 Self::CancelSdkWait(_) => "cancel-sdk-wait",
421 Self::PaneInput(_) => "send-keys",
422 Self::PaneBroadcastInput(_) => "send-keys",
423 Self::CreateSessionLease(_) => "create-session-lease",
424 Self::RenewSessionLease(_) => "renew-session-lease",
425 Self::ReleaseSessionLease(_) => "release-session-lease",
426 Self::PaneResize(_) | Self::ResizePaneTargetAction(_) => "resize-pane",
427 Self::PaneKill(_) => "kill-pane",
428 Self::PaneRespawn(_) => "respawn-pane",
429 Self::PaneSnapshotRef(_) => "pane-snapshot",
430 Self::PaneSelect(_) => "select-pane",
431 Self::DisplayMessage(_) | Self::DisplayMessageExt(_) => "display-message",
432 Self::ResolveTarget(_) => "resolve-target",
433 Self::RunShell(_) => "run-shell",
434 Self::IfShell(_) => "if-shell",
435 Self::WaitFor(_) => "wait-for",
436 Self::RenameSession(_) => "rename-session",
437 Self::ListSessions(_) => "list-sessions",
438 Self::ListPanes(_) => "list-panes",
439 Self::SourceFile(_) => "source-file",
440 Self::UnlinkWindow(_) => "unlink-window",
441 Self::SetOptionByName(_) => "set-option",
442 Self::SetHookMutation(_) => "set-hook",
443 Self::ShowHooks(_) => "show-hooks",
444 Self::SendKeysExt(_) => "send-keys",
445 Self::SwitchClientExt(_) => "switch-client",
446 Self::BindKey(_) => "bind-key",
447 Self::UnbindKey(_) => "unbind-key",
448 Self::ListKeys(_) => "list-keys",
449 Self::SendPrefix(_) => "send-prefix",
450 Self::ClearHistory(_) => "clear-history",
451 Self::CopyMode(_) => "copy-mode",
452 Self::ControlMode(_) => "control-mode",
453 Self::ClockMode(_) => "clock-mode",
454 Self::ShowMessages(_) => "show-messages",
455 Self::NewSessionExt(_) => "new-session",
456 Self::AttachSessionExt(_) => "attach-session",
457 Self::SwitchClientExt2(_) => "switch-client",
458 Self::SelectCustomLayout(_) => "select-layout",
459 Self::SelectOldLayout(_) => "select-layout",
460 Self::SpreadLayout(_) => "select-layout",
461 Self::ResizeWindow(_) => "resize-window",
462 Self::RespawnWindow(_) => "respawn-window",
463 Self::MovePane(_) => "move-pane",
464 Self::PipePane(_) => "pipe-pane",
465 Self::RespawnPane(_) => "respawn-pane",
466 Self::KillServer(_) => "kill-server",
467 Self::LockServer(_) => "lock-server",
468 Self::LockSession(_) => "lock-session",
469 Self::LockClient(_) => "lock-client",
470 Self::ServerAccess(_) => "server-access",
471 Self::RefreshClient(_) => "refresh-client",
472 Self::ListClients(_) => "list-clients",
473 Self::SuspendClient(_) => "suspend-client",
474 Self::DetachClientExt(_) => "detach-client",
475 Self::AttachSessionExt2(_) | Self::AttachSessionExt3(_) => "attach-session",
476 Self::SwitchClientExt3(_) => "switch-client",
477 Self::Handshake(_) => "handshake",
478 Self::DaemonStatus(_) => "daemon-status",
479 Self::ShutdownIfIdle(_) => "shutdown-if-idle",
480 Self::WebShare(_) => "web-share",
481 }
482 }
483}
484
485#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
487pub struct ShowMessagesRequest {
488 pub jobs: bool,
490 pub terminals: bool,
492 pub target_client: Option<String>,
494}
495
496#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
498pub struct RunShellRequest {
499 pub command: String,
501 pub background: bool,
503 #[serde(default)]
505 pub as_commands: bool,
506 #[serde(default)]
508 pub show_stderr: bool,
509 #[serde(default)]
511 pub delay_seconds: Option<RunShellDelaySeconds>,
512 #[serde(default)]
514 pub start_directory: Option<PathBuf>,
515 #[serde(default)]
517 pub target: Option<PaneTarget>,
518 #[serde(default)]
520 pub source_depth: Option<usize>,
521}
522
523#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
525pub struct RunShellDelaySeconds(pub f64);
526
527impl RunShellDelaySeconds {
528 #[must_use]
530 pub const fn as_secs_f64(self) -> f64 {
531 self.0
532 }
533}
534
535impl PartialEq for RunShellDelaySeconds {
536 fn eq(&self, other: &Self) -> bool {
537 self.0.to_bits() == other.0.to_bits()
538 }
539}
540
541impl Eq for RunShellDelaySeconds {}
542
543#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
545pub struct IfShellRequest {
546 pub condition: String,
548 pub format_mode: bool,
550 pub then_command: String,
552 pub else_command: Option<String>,
554 pub target: Option<Target>,
556 pub caller_cwd: Option<PathBuf>,
558 #[serde(default)]
560 pub background: bool,
561}
562
563#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
565pub struct SourceFileRequest {
566 pub paths: Vec<String>,
568 pub quiet: bool,
570 pub parse_only: bool,
572 pub verbose: bool,
574 pub expand_paths: bool,
576 pub target: Option<PaneTarget>,
578 pub caller_cwd: Option<PathBuf>,
580 pub stdin: Option<String>,
582}
583
584#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
586pub struct UnlinkWindowRequest {
587 pub target: WindowTarget,
589 #[serde(default)]
591 pub kill_if_last: bool,
592}
593
594#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
596pub enum WaitForMode {
597 Wait,
599 Signal,
601 Lock,
603 Unlock,
605}
606
607#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
609pub struct WaitForRequest {
610 pub channel: String,
612 pub mode: WaitForMode,
614}
615
616#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
623pub struct SdkWaitForOutputRequest {
624 pub owner_id: SdkWaitOwnerId,
626 pub wait_id: SdkWaitId,
628 pub target: PaneTarget,
630 pub bytes: Vec<u8>,
632 pub start: PaneOutputSubscriptionStart,
634}
635
636#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
638pub struct SdkWaitForOutputRefRequest {
639 pub owner_id: SdkWaitOwnerId,
641 pub wait_id: SdkWaitId,
643 pub target: PaneTargetRef,
645 pub bytes: Vec<u8>,
647 pub start: PaneOutputSubscriptionStart,
649}
650
651#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
653pub struct CancelSdkWaitRequest {
654 pub owner_id: SdkWaitOwnerId,
656 pub wait_id: SdkWaitId,
658}
659
660#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
662pub struct ListPanesRequest {
663 pub target: SessionName,
665 #[serde(default)]
667 pub target_window_index: Option<u32>,
668 pub format: Option<String>,
670}
671
672#[cfg(test)]
673#[path = "request/compat_tests.rs"]
674mod compat_tests;
675
676#[cfg(test)]
677mod tests {
678 use super::*;
679 use crate::{
680 OptionScopeSelector, PaneTarget, ScopeSelector, SelectPaneDirection, SetOptionMode,
681 SplitDirection, WebShareScope,
682 };
683 use serde::Serialize;
684 use std::mem::size_of;
685
686 fn alpha() -> SessionName {
687 SessionName::new("alpha").expect("valid session")
688 }
689
690 fn pane() -> PaneTarget {
691 PaneTarget::new(alpha(), 0)
692 }
693
694 #[test]
695 fn request_command_names_cover_extended_aliases_and_internal_tags() {
696 assert_eq!(
697 Request::NewSessionExt(Box::new(NewSessionExtRequest {
698 session_name: None,
699 working_directory: None,
700 detached: true,
701 size: None,
702 environment: None,
703 group_target: None,
704 attach_if_exists: false,
705 detach_other_clients: false,
706 kill_other_clients: false,
707 flags: None,
708 window_name: None,
709 print_session_info: false,
710 print_format: None,
711 command: None,
712 process_command: None,
713 client_environment: None,
714 skip_environment_update: false,
715 }))
716 .command_name(),
717 "new-session"
718 );
719 assert_eq!(
720 Request::SplitWindowExt(Box::new(SplitWindowExtRequest {
721 target: SplitWindowTarget::Pane(pane()),
722 direction: SplitDirection::Vertical,
723 before: false,
724 environment: None,
725 command: None,
726 process_command: None,
727 start_directory: None,
728 keep_alive_on_exit: None,
729 detached: false,
730 size: None,
731 preserve_zoom: false,
732 full_size: false,
733 stdin_payload: None,
734 }))
735 .command_name(),
736 "split-window"
737 );
738 assert_eq!(
739 Request::SelectPaneAdjacent(SelectPaneAdjacentRequest {
740 target: pane(),
741 direction: SelectPaneDirection::Right,
742 preserve_zoom: false,
743 })
744 .command_name(),
745 "select-pane"
746 );
747 assert_eq!(
748 Request::SelectPaneMark(SelectPaneMarkRequest {
749 target: pane(),
750 clear: false,
751 title: None,
752 })
753 .command_name(),
754 "select-pane"
755 );
756 assert_eq!(
757 Request::SetOptionByName(Box::new(SetOptionByNameRequest {
758 scope: OptionScopeSelector::ServerGlobal,
759 name: "status".to_owned(),
760 value: Some("on".to_owned()),
761 mode: SetOptionMode::Replace,
762 only_if_unset: false,
763 unset: false,
764 unset_pane_overrides: false,
765 format: false,
766 format_target: None,
767 }))
768 .command_name(),
769 "set-option"
770 );
771 assert_eq!(
772 Request::SetHookMutation(SetHookMutationRequest {
773 scope: ScopeSelector::Global,
774 hook: crate::HookName::AfterNewSession,
775 command: None,
776 lifecycle: crate::HookLifecycle::Persistent,
777 append: false,
778 unset: true,
779 run_immediately: false,
780 index: None,
781 })
782 .command_name(),
783 "set-hook"
784 );
785 assert_eq!(
786 Request::AttachSessionExt2(Box::new(AttachSessionExt2Request {
787 target: Some(alpha()),
788 target_spec: None,
789 detach_other_clients: false,
790 kill_other_clients: false,
791 read_only: false,
792 skip_environment_update: false,
793 flags: None,
794 working_directory: None,
795 client_terminal: crate::ClientTerminalContext::default(),
796 client_size: None,
797 }))
798 .command_name(),
799 "attach-session"
800 );
801 assert_eq!(
802 Request::AttachSessionExt3(Box::new(AttachSessionExt3Request::from_ext2(
803 AttachSessionExt2Request {
804 target: Some(alpha()),
805 target_spec: None,
806 detach_other_clients: false,
807 kill_other_clients: false,
808 read_only: false,
809 skip_environment_update: false,
810 flags: None,
811 working_directory: None,
812 client_terminal: crate::ClientTerminalContext::default(),
813 client_size: None,
814 },
815 vec![crate::CAPABILITY_ATTACH_RENDER.to_owned()],
816 )))
817 .command_name(),
818 "attach-session"
819 );
820 assert_eq!(
821 Request::SwitchClientExt3(Box::new(SwitchClientExt3Request {
822 target_client: None,
823 target: Some("alpha:0.0".to_owned()),
824 key_table: None,
825 last_session: false,
826 next_session: false,
827 previous_session: false,
828 toggle_read_only: false,
829 sort_order: None,
830 skip_environment_update: false,
831 zoom: false,
832 }))
833 .command_name(),
834 "switch-client"
835 );
836 assert_eq!(
837 Request::ResolveTarget(ResolveTargetRequest {
838 target: Some("alpha:0.0".to_owned()),
839 target_type: ResolveTargetType::Pane,
840 window_index: false,
841 prefer_unattached: false,
842 })
843 .command_name(),
844 "resolve-target"
845 );
846 assert_eq!(
847 Request::Handshake(HandshakeRequest::current()).command_name(),
848 "handshake"
849 );
850 assert_eq!(
851 Request::SdkWaitForOutput(SdkWaitForOutputRequest {
852 owner_id: SdkWaitOwnerId::new(1),
853 wait_id: SdkWaitId::new(1),
854 target: pane(),
855 bytes: b"ready".to_vec(),
856 start: PaneOutputSubscriptionStart::Now,
857 })
858 .command_name(),
859 "sdk-wait-output"
860 );
861 assert_eq!(
862 Request::CancelSdkWait(CancelSdkWaitRequest {
863 owner_id: SdkWaitOwnerId::new(1),
864 wait_id: SdkWaitId::new(1),
865 })
866 .command_name(),
867 "cancel-sdk-wait"
868 );
869 assert_eq!(
870 Request::WaitFor(WaitForRequest {
871 channel: "ready".to_owned(),
872 mode: WaitForMode::Wait,
873 })
874 .command_name(),
875 "wait-for"
876 );
877 }
878
879 #[test]
880 fn pr6g_request_boxing_keeps_request_size_bounded() {
881 let request_size = size_of::<Request>();
882 let box_size = size_of::<Box<NewSessionExtRequest>>();
883
884 eprintln!("PR6G size baseline: Request={request_size} Box<T>={box_size}");
885 eprintln!(
886 "PR6G candidate sizes: NewSessionExt={} NewWindow={} SetOptionByName={} \
887 AttachSessionExt3={} SwitchClientExt3={} SendKeysExt2={} SourceFile={} WebShare={}",
888 size_of::<NewSessionExtRequest>(),
889 size_of::<NewWindowRequest>(),
890 size_of::<SetOptionByNameRequest>(),
891 size_of::<AttachSessionExt3Request>(),
892 size_of::<SwitchClientExt3Request>(),
893 size_of::<SendKeysExt2Request>(),
894 size_of::<SourceFileRequest>(),
895 size_of::<WebShareRequest>(),
896 );
897 let mut remaining = [
898 ("AttachSessionExt2", size_of::<AttachSessionExt2Request>()),
899 ("BindKey", size_of::<BindKeyRequest>()),
900 ("BreakPane", size_of::<BreakPaneRequest>()),
901 ("CapturePane", size_of::<CapturePaneRequest>()),
902 ("ClearHistory", size_of::<ClearHistoryRequest>()),
903 ("ClockMode", size_of::<ClockModeRequest>()),
904 ("ControlMode", size_of::<ControlModeRequest>()),
905 ("CopyMode", size_of::<CopyModeRequest>()),
906 ("CreateSessionLease", size_of::<CreateSessionLeaseRequest>()),
907 ("DeleteBuffer", size_of::<DeleteBufferRequest>()),
908 ("DetachClientExt", size_of::<DetachClientExtRequest>()),
909 ("DisplayPanes", size_of::<DisplayPanesRequest>()),
910 ("DisplayMessage", size_of::<DisplayMessageRequest>()),
911 ("DisplayMessageExt", size_of::<DisplayMessageExtRequest>()),
912 ("Handshake", size_of::<HandshakeRequest>()),
913 ("IfShell", size_of::<IfShellRequest>()),
914 ("JoinPane", size_of::<JoinPaneRequest>()),
915 ("KillPane", size_of::<KillPaneRequest>()),
916 ("KillWindow", size_of::<KillWindowRequest>()),
917 ("LinkWindow", size_of::<LinkWindowRequest>()),
918 ("ListClients", size_of::<ListClientsRequest>()),
919 ("ListKeys", size_of::<ListKeysRequest>()),
920 ("ListPanes", size_of::<ListPanesRequest>()),
921 ("ListSessions", size_of::<ListSessionsRequest>()),
922 ("ListWindows", size_of::<ListWindowsRequest>()),
923 ("LoadBuffer", size_of::<LoadBufferRequest>()),
924 ("MovePane", size_of::<MovePaneRequest>()),
925 ("MoveWindow", size_of::<MoveWindowRequest>()),
926 ("NewSession", size_of::<NewSessionRequest>()),
927 ("PasteBuffer", size_of::<PasteBufferRequest>()),
928 ("PaneInput", size_of::<PaneInputRequest>()),
929 ("PaneKill", size_of::<PaneKillRequest>()),
930 ("PaneResize", size_of::<PaneResizeRequest>()),
931 ("PaneRespawn", size_of::<PaneRespawnRequest>()),
932 ("PaneSelect", size_of::<PaneSelectRequest>()),
933 ("PaneSnapshot", size_of::<PaneSnapshotRequest>()),
934 ("PaneSnapshotRef", size_of::<PaneSnapshotRefRequest>()),
935 ("PipePane", size_of::<PipePaneRequest>()),
936 ("RefreshClient", size_of::<RefreshClientRequest>()),
937 ("RespawnPane", size_of::<RespawnPaneRequest>()),
938 ("RespawnWindow", size_of::<RespawnWindowRequest>()),
939 ("ResizePane", size_of::<ResizePaneRequest>()),
940 ("ResizeWindow", size_of::<ResizeWindowRequest>()),
941 ("RotateWindow", size_of::<RotateWindowRequest>()),
942 ("RunShell", size_of::<RunShellRequest>()),
943 ("SaveBuffer", size_of::<SaveBufferRequest>()),
944 ("SelectCustomLayout", size_of::<SelectCustomLayoutRequest>()),
945 ("SelectLayout", size_of::<SelectLayoutRequest>()),
946 ("SelectPane", size_of::<SelectPaneRequest>()),
947 ("SelectPaneAdjacent", size_of::<SelectPaneAdjacentRequest>()),
948 ("SelectPaneMark", size_of::<SelectPaneMarkRequest>()),
949 ("SetEnvironment", size_of::<SetEnvironmentRequest>()),
950 ("SetBuffer", size_of::<SetBufferRequest>()),
951 ("SetHook", size_of::<SetHookRequest>()),
952 ("SetHookMutation", size_of::<SetHookMutationRequest>()),
953 ("SetOption", size_of::<SetOptionRequest>()),
954 ("ShowBuffer", size_of::<ShowBufferRequest>()),
955 ("ShowEnvironment", size_of::<ShowEnvironmentRequest>()),
956 ("ShowHooks", size_of::<ShowHooksRequest>()),
957 ("ShowMessages", size_of::<ShowMessagesRequest>()),
958 ("ShowOptions", size_of::<ShowOptionsRequest>()),
959 ("SplitWindow", size_of::<SplitWindowRequest>()),
960 ("SplitWindowExt", size_of::<SplitWindowExtRequest>()),
961 (
962 "SubscribePaneOutput",
963 size_of::<SubscribePaneOutputRequest>(),
964 ),
965 (
966 "SubscribePaneOutputRef",
967 size_of::<SubscribePaneOutputRefRequest>(),
968 ),
969 ("SwitchClientExt2", size_of::<SwitchClientExt2Request>()),
970 ];
971 remaining.sort_by_key(|(_, size)| std::cmp::Reverse(*size));
972 eprintln!("PR6G largest remaining candidates: {remaining:?}");
973
974 assert!(
975 request_size <= 96,
976 "Request grew past the PR6G boxed payload budget: {request_size}"
977 );
978 assert_eq!(box_size, size_of::<usize>());
979 }
980
981 #[test]
982 fn pr6g_boxed_payloads_are_bincode_transparent_for_request_candidates() {
983 assert_box_serializes_like_value(NewSessionExtRequest {
984 session_name: Some(alpha()),
985 working_directory: Some("/tmp".to_owned()),
986 detached: true,
987 size: Some(crate::TerminalSize {
988 cols: 120,
989 rows: 40,
990 }),
991 environment: Some(vec!["A=B".to_owned()]),
992 group_target: None,
993 attach_if_exists: false,
994 detach_other_clients: false,
995 kill_other_clients: false,
996 flags: Some(vec!["read-only".to_owned()]),
997 window_name: Some("main".to_owned()),
998 print_session_info: true,
999 print_format: Some("#{session_name}".to_owned()),
1000 command: Some(vec!["sh".to_owned(), "-lc".to_owned(), "true".to_owned()]),
1001 process_command: None,
1002 client_environment: Some(vec!["PATH=/bin".to_owned()]),
1003 skip_environment_update: false,
1004 });
1005 assert_box_serializes_like_value(SetOptionByNameRequest {
1006 scope: OptionScopeSelector::ServerGlobal,
1007 name: "@plugin".to_owned(),
1008 value: Some("enabled".to_owned()),
1009 mode: SetOptionMode::Replace,
1010 only_if_unset: false,
1011 unset: false,
1012 unset_pane_overrides: false,
1013 format: true,
1014 format_target: Some(crate::Target::Session(alpha())),
1015 });
1016 assert_box_serializes_like_value(AttachSessionExt3Request::from_ext2(
1017 AttachSessionExt2Request {
1018 target: Some(alpha()),
1019 target_spec: Some("alpha:0.0".to_owned()),
1020 detach_other_clients: false,
1021 kill_other_clients: false,
1022 read_only: true,
1023 skip_environment_update: false,
1024 flags: Some(vec!["active-pane".to_owned()]),
1025 working_directory: Some("/tmp".to_owned()),
1026 client_terminal: crate::ClientTerminalContext::default(),
1027 client_size: Some(crate::TerminalSize { cols: 80, rows: 24 }),
1028 },
1029 vec![crate::CAPABILITY_ATTACH_RENDER.to_owned()],
1030 ));
1031 assert_box_serializes_like_value(WebShareRequest::Create(CreateWebShareRequest {
1032 scope: WebShareScope::Session(alpha()),
1033 public_base_url: Some("https://example.invalid".to_owned()),
1034 tunnel_provider: None,
1035 frontend_url: Some("https://share.example.invalid".to_owned()),
1036 ttl_seconds: Some(60),
1037 expires_at_unix: None,
1038 max_spectators: Some(8),
1039 max_operators: Some(1),
1040 url_options: WebShareUrlOptions::default(),
1041 require_pin: true,
1042 operator_pin: Some("123456".to_owned()),
1043 spectator_pin: Some("654321".to_owned()),
1044 terminal_palette: None,
1045 operator: true,
1046 spectator: true,
1047 controls: true,
1048 kill_session_on_expire: false,
1049 }));
1050 }
1051
1052 fn assert_box_serializes_like_value<T>(value: T)
1053 where
1054 T: Clone + Serialize,
1055 {
1056 let boxed = Box::new(value.clone());
1057 assert_eq!(
1058 bincode::serialize(&value).expect("value encodes"),
1059 bincode::serialize(&boxed).expect("boxed value encodes"),
1060 "Box<T> must be transparent before PR6G boxes enum variants"
1061 );
1062 }
1063}