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::{
17 DisplayMessageDurationMillis, DisplayMessageDurationParseError, DisplayMessageExtRequest,
18 DisplayMessageRequest,
19};
20
21#[path = "request/show.rs"]
22mod show;
23pub use show::ShowHooksRequest;
24pub use show::{ShowEnvironmentRequest, ShowOptionsRequest};
25
26#[path = "request/layout.rs"]
27mod layout;
28pub use layout::{
29 NextLayoutRequest, PreviousLayoutRequest, SelectCustomLayoutRequest, SelectLayoutRequest,
30 SelectLayoutTarget, SelectOldLayoutRequest, SpreadLayoutRequest,
31};
32
33#[path = "request/pane.rs"]
34mod pane;
35pub use pane::{
36 BreakPaneRequest, DisplayPanesRequest, JoinPaneRequest, KillPaneRequest, LastPaneRequest,
37 MovePaneRequest, PaneBroadcastInputRequest, PaneForegroundStateRequest, PaneInputRequest,
38 PaneKillRequest, PaneOptionGetRequest, PaneOptionSetRequest, PaneOutputCursorRequest,
39 PaneOutputSubscriptionStart, PaneResizeRequest, PaneRespawnRequest, PaneSelectRequest,
40 PaneSnapshotRefRequest, PaneSnapshotRequest, PaneSplitSize, PaneStateCursorRequest,
41 PaneStreamCursorRequest, PaneStreamMode, PipePaneRequest, ResizePaneRequest,
42 ResizePaneTargetActionRequest, RespawnPaneRequest, SelectPaneAdjacentRequest,
43 SelectPaneDirection, SelectPaneMarkRequest, SelectPaneRequest, SendKeysExt2Request,
44 SendKeysExtRequest, SendKeysRequest, SplitWindowExtRequest, SplitWindowIdentityRequest,
45 SplitWindowRequest, SplitWindowTarget, SplitWindowTargetActionRequest,
46 SubscribePaneOutputRefRequest, SubscribePaneOutputRequest, SubscribePaneStateRequest,
47 SubscribePaneStreamRequest, SwapPaneDirection, SwapPaneRequest, UnsubscribePaneOutputRequest,
48 UnsubscribePaneStateRequest, UnsubscribePaneStreamRequest,
49};
50
51#[path = "request/window.rs"]
52mod window;
53pub use window::{
54 KillWindowRequest, LastWindowRequest, LinkWindowRequest, ListWindowsRequest, MoveWindowRequest,
55 MoveWindowTarget, NewWindowRequest, NextWindowRequest, PreviousWindowRequest,
56 RenameWindowRequest, ResizeWindowAdjustment, ResizeWindowRequest, RespawnWindowRequest,
57 RotateWindowDirection, RotateWindowRequest, SelectWindowRequest, SwapWindowRequest,
58};
59
60#[path = "request/target.rs"]
61mod target;
62pub use target::{ResolveTargetRequest, ResolveTargetType};
63
64#[path = "request/session.rs"]
65mod session;
66pub use session::{
67 CreateSessionLeaseRequest, HasSessionRequest, KillSessionRequest, ListSessionsRequest,
68 NewSessionExtRequest, NewSessionRequest, ReleaseSessionLeaseRequest, RenameSessionRequest,
69 RenewSessionLeaseRequest,
70};
71
72#[path = "request/server.rs"]
73mod server;
74pub use server::{
75 DaemonStatusRequest, KillServerRequest, LockClientRequest, LockServerRequest,
76 LockSessionRequest, ServerAccessRequest, ShutdownIfIdleRequest,
77};
78
79#[path = "request/client.rs"]
80mod client;
81pub use client::{
82 AttachSessionExt2Request, AttachSessionExt3Request, AttachSessionExtRequest,
83 AttachSessionRequest, DetachClientExtRequest, DetachClientRequest, ListClientsRequest,
84 RefreshClientRequest, SuspendClientRequest, SwitchClientExt2Request, SwitchClientExt3Request,
85 SwitchClientExtRequest, SwitchClientRequest,
86};
87
88#[path = "request/keys.rs"]
89mod keys;
90pub use keys::{
91 BindKeyRequest, ClockModeRequest, CopyModeRequest, ListKeysRequest, SendPrefixRequest,
92 UnbindKeyRequest,
93};
94
95#[path = "request/options.rs"]
96mod options;
97pub use options::{
98 SetEnvironmentMode, SetEnvironmentRequest, SetHookMutationRequest, SetHookRequest,
99 SetOptionByNameRequest, SetOptionRequest,
100};
101
102#[path = "request/buffer.rs"]
103mod buffer;
104pub use buffer::{
105 CapturePaneRequest, CapturePaneTargetActionRequest, ClearHistoryRequest, DeleteBufferRequest,
106 ListBuffersRequest, LoadBufferRequest, PasteBufferRequest, SaveBufferRequest, SetBufferRequest,
107 ShowBufferRequest,
108};
109
110#[path = "request/web.rs"]
111mod web;
112pub use web::{
113 CreateWebShareRequest, ListWebSharesRequest, LookupWebShareRequest, StopAllWebSharesRequest,
114 StopWebShareRequest, WebShareConfigRequest, WebShareRequest, WebShareScope, WebShareUrlOptions,
115 WebTerminalPalette, WebTerminalTheme,
116};
117
118#[non_exhaustive]
121#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
122pub enum Request {
123 NewSession(NewSessionRequest),
125 HasSession(HasSessionRequest),
127 KillSession(KillSessionRequest),
129 NewWindow(Box<NewWindowRequest>),
131 KillWindow(KillWindowRequest),
133 SelectWindow(SelectWindowRequest),
135 RenameWindow(RenameWindowRequest),
137 NextWindow(NextWindowRequest),
139 PreviousWindow(PreviousWindowRequest),
141 LastWindow(LastWindowRequest),
143 ListWindows(Box<ListWindowsRequest>),
145 MoveWindow(MoveWindowRequest),
147 SwapWindow(SwapWindowRequest),
149 RotateWindow(RotateWindowRequest),
151 SplitWindow(SplitWindowRequest),
153 SwapPane(SwapPaneRequest),
155 LastPane(LastPaneRequest),
157 JoinPane(JoinPaneRequest),
159 BreakPane(Box<BreakPaneRequest>),
161 KillPane(KillPaneRequest),
163 SelectLayout(SelectLayoutRequest),
165 ResizePane(ResizePaneRequest),
167 DisplayPanes(Box<DisplayPanesRequest>),
169 SelectPane(Box<SelectPaneRequest>),
171 SelectPaneAdjacent(SelectPaneAdjacentRequest),
173 SendKeys(SendKeysRequest),
175 AttachSession(AttachSessionRequest),
177 SwitchClient(SwitchClientRequest),
179 DetachClient(DetachClientRequest),
181 SetOption(SetOptionRequest),
183 SetEnvironment(Box<SetEnvironmentRequest>),
185 SetHook(SetHookRequest),
187 NextLayout(NextLayoutRequest),
189 PreviousLayout(PreviousLayoutRequest),
191 ShowOptions(ShowOptionsRequest),
193 ShowEnvironment(ShowEnvironmentRequest),
195 SetBuffer(Box<SetBufferRequest>),
197 ShowBuffer(ShowBufferRequest),
199 PasteBuffer(Box<PasteBufferRequest>),
201 ListBuffers(ListBuffersRequest),
203 DeleteBuffer(DeleteBufferRequest),
205 LoadBuffer(Box<LoadBufferRequest>),
207 SaveBuffer(SaveBufferRequest),
209 CapturePane(Box<CapturePaneRequest>),
211 DisplayMessage(DisplayMessageRequest),
213 RunShell(Box<RunShellRequest>),
215 IfShell(Box<IfShellRequest>),
217 WaitFor(WaitForRequest),
219 RenameSession(RenameSessionRequest),
221 ListSessions(ListSessionsRequest),
223 ListPanes(Box<ListPanesRequest>),
225 SourceFile(Box<SourceFileRequest>),
227 SetOptionByName(Box<SetOptionByNameRequest>),
229 SetHookMutation(SetHookMutationRequest),
231 ShowHooks(ShowHooksRequest),
233 SendKeysExt(SendKeysExtRequest),
235 SwitchClientExt(SwitchClientExtRequest),
237 BindKey(Box<BindKeyRequest>),
239 UnbindKey(UnbindKeyRequest),
241 ListKeys(Box<ListKeysRequest>),
243 SendPrefix(SendPrefixRequest),
245 ClearHistory(ClearHistoryRequest),
247 CopyMode(CopyModeRequest),
249 ControlMode(ControlModeRequest),
251 ClockMode(ClockModeRequest),
253 ShowMessages(ShowMessagesRequest),
255 NewSessionExt(Box<NewSessionExtRequest>),
257 AttachSessionExt(AttachSessionExtRequest),
259 SwitchClientExt2(Box<SwitchClientExt2Request>),
261 SelectCustomLayout(SelectCustomLayoutRequest),
263 SelectOldLayout(SelectOldLayoutRequest),
265 SpreadLayout(SpreadLayoutRequest),
267 KillServer(KillServerRequest),
269 LockServer(LockServerRequest),
271 LockSession(LockSessionRequest),
273 LockClient(LockClientRequest),
275 ServerAccess(ServerAccessRequest),
277 RefreshClient(Box<RefreshClientRequest>),
279 ListClients(Box<ListClientsRequest>),
281 SuspendClient(SuspendClientRequest),
283 DetachClientExt(DetachClientExtRequest),
285 AttachSessionExt2(Box<AttachSessionExt2Request>),
287 SwitchClientExt3(Box<SwitchClientExt3Request>),
289 ResizeWindow(ResizeWindowRequest),
291 RespawnWindow(Box<RespawnWindowRequest>),
293 MovePane(MovePaneRequest),
295 PipePane(PipePaneRequest),
297 RespawnPane(Box<RespawnPaneRequest>),
299 LinkWindow(LinkWindowRequest),
301 UnlinkWindow(UnlinkWindowRequest),
303 SelectPaneMark(SelectPaneMarkRequest),
305 ResolveTarget(ResolveTargetRequest),
307 SplitWindowExt(Box<SplitWindowExtRequest>),
309 Handshake(HandshakeRequest),
311 PaneSnapshot(PaneSnapshotRequest),
313 SubscribePaneOutput(SubscribePaneOutputRequest),
315 UnsubscribePaneOutput(UnsubscribePaneOutputRequest),
317 PaneOutputCursor(PaneOutputCursorRequest),
319 SdkWaitForOutput(SdkWaitForOutputRequest),
321 CancelSdkWait(CancelSdkWaitRequest),
323 PaneInput(PaneInputRequest),
325 PaneResize(PaneResizeRequest),
327 PaneKill(PaneKillRequest),
329 PaneRespawn(Box<PaneRespawnRequest>),
331 PaneSnapshotRef(PaneSnapshotRefRequest),
333 PaneSelect(PaneSelectRequest),
335 PaneBroadcastInput(PaneBroadcastInputRequest),
337 CreateSessionLease(CreateSessionLeaseRequest),
339 RenewSessionLease(RenewSessionLeaseRequest),
341 ReleaseSessionLease(ReleaseSessionLeaseRequest),
343 SubscribePaneOutputRef(SubscribePaneOutputRefRequest),
345 SdkWaitForOutputRef(SdkWaitForOutputRefRequest),
347 DaemonStatus(DaemonStatusRequest),
349 ShutdownIfIdle(ShutdownIfIdleRequest),
351 WebShare(Box<WebShareRequest>),
353 DisplayMessageExt(Box<DisplayMessageExtRequest>),
355 SendKeysExt2(Box<SendKeysExt2Request>),
357 AttachSessionExt3(Box<AttachSessionExt3Request>),
359 SplitWindowTargetAction(Box<SplitWindowTargetActionRequest>),
361 ResizePaneTargetAction(ResizePaneTargetActionRequest),
363 CapturePaneTargetAction(Box<CapturePaneTargetActionRequest>),
365 PaneOptionSet(PaneOptionSetRequest),
367 PaneOptionGet(PaneOptionGetRequest),
369 SubscribePaneState(SubscribePaneStateRequest),
371 PaneStateCursor(PaneStateCursorRequest),
373 UnsubscribePaneState(UnsubscribePaneStateRequest),
375 PaneForegroundState(PaneForegroundStateRequest),
377 SplitWindowIdentity(Box<SplitWindowIdentityRequest>),
379 SubscribePaneStream(SubscribePaneStreamRequest),
381 PaneStreamCursor(PaneStreamCursorRequest),
383 UnsubscribePaneStream(UnsubscribePaneStreamRequest),
385}
386
387impl Request {
388 #[must_use]
390 pub const fn command_name(&self) -> &'static str {
391 match self {
392 Self::NewSession(_) => "new-session",
393 Self::HasSession(_) => "has-session",
394 Self::KillSession(_) => "kill-session",
395 Self::NewWindow(_) => "new-window",
396 Self::KillWindow(_) => "kill-window",
397 Self::SelectWindow(_) => "select-window",
398 Self::RenameWindow(_) => "rename-window",
399 Self::NextWindow(_) => "next-window",
400 Self::PreviousWindow(_) => "previous-window",
401 Self::LastWindow(_) => "last-window",
402 Self::ListWindows(_) => "list-windows",
403 Self::LinkWindow(_) => "link-window",
404 Self::MoveWindow(_) => "move-window",
405 Self::SwapWindow(_) => "swap-window",
406 Self::RotateWindow(_) => "rotate-window",
407 Self::SplitWindow(_)
408 | Self::SplitWindowExt(_)
409 | Self::SplitWindowTargetAction(_)
410 | Self::SplitWindowIdentity(_) => "split-window",
411 Self::SwapPane(_) => "swap-pane",
412 Self::LastPane(_) => "last-pane",
413 Self::JoinPane(_) => "join-pane",
414 Self::BreakPane(_) => "break-pane",
415 Self::KillPane(_) => "kill-pane",
416 Self::SelectLayout(_) => "select-layout",
417 Self::ResizePane(_) => "resize-pane",
418 Self::DisplayPanes(_) => "display-panes",
419 Self::SelectPane(_) | Self::SelectPaneAdjacent(_) | Self::SelectPaneMark(_) => {
420 "select-pane"
421 }
422 Self::SendKeys(_) | Self::SendKeysExt2(_) => "send-keys",
423 Self::AttachSession(_) => "attach-session",
424 Self::SwitchClient(_) => "switch-client",
425 Self::DetachClient(_) => "detach-client",
426 Self::SetOption(_) => "set-option",
427 Self::SetEnvironment(_) => "set-environment",
428 Self::SetHook(_) => "set-hook",
429 Self::NextLayout(_) => "next-layout",
430 Self::PreviousLayout(_) => "previous-layout",
431 Self::ShowOptions(_) => "show-options",
432 Self::ShowEnvironment(_) => "show-environment",
433 Self::SetBuffer(_) => "set-buffer",
434 Self::ShowBuffer(_) => "show-buffer",
435 Self::PasteBuffer(_) => "paste-buffer",
436 Self::ListBuffers(_) => "list-buffers",
437 Self::DeleteBuffer(_) => "delete-buffer",
438 Self::LoadBuffer(_) => "load-buffer",
439 Self::SaveBuffer(_) => "save-buffer",
440 Self::CapturePane(_) | Self::CapturePaneTargetAction(_) => "capture-pane",
441 Self::PaneSnapshot(_) => "pane-snapshot",
442 Self::SubscribePaneOutput(_) | Self::SubscribePaneOutputRef(_) => {
443 "subscribe-pane-output"
444 }
445 Self::UnsubscribePaneOutput(_) => "unsubscribe-pane-output",
446 Self::PaneOutputCursor(_) => "pane-output-cursor",
447 Self::SubscribePaneStream(_) => "subscribe-pane-stream",
448 Self::PaneStreamCursor(_) => "pane-stream-cursor",
449 Self::UnsubscribePaneStream(_) => "unsubscribe-pane-stream",
450 Self::SdkWaitForOutput(_) | Self::SdkWaitForOutputRef(_) => "sdk-wait-output",
451 Self::CancelSdkWait(_) => "cancel-sdk-wait",
452 Self::PaneInput(_) => "send-keys",
453 Self::PaneBroadcastInput(_) => "send-keys",
454 Self::CreateSessionLease(_) => "create-session-lease",
455 Self::RenewSessionLease(_) => "renew-session-lease",
456 Self::ReleaseSessionLease(_) => "release-session-lease",
457 Self::PaneResize(_) | Self::ResizePaneTargetAction(_) => "resize-pane",
458 Self::PaneKill(_) => "kill-pane",
459 Self::PaneRespawn(_) => "respawn-pane",
460 Self::PaneSnapshotRef(_) => "pane-snapshot",
461 Self::PaneSelect(_) => "select-pane",
462 Self::PaneOptionSet(_) => "pane-option-set",
463 Self::PaneOptionGet(_) => "pane-option-get",
464 Self::SubscribePaneState(_) => "subscribe-pane-state",
465 Self::PaneStateCursor(_) => "pane-state-cursor",
466 Self::UnsubscribePaneState(_) => "unsubscribe-pane-state",
467 Self::PaneForegroundState(_) => "pane-foreground-state",
468 Self::DisplayMessage(_) | Self::DisplayMessageExt(_) => "display-message",
469 Self::ResolveTarget(_) => "resolve-target",
470 Self::RunShell(_) => "run-shell",
471 Self::IfShell(_) => "if-shell",
472 Self::WaitFor(_) => "wait-for",
473 Self::RenameSession(_) => "rename-session",
474 Self::ListSessions(_) => "list-sessions",
475 Self::ListPanes(_) => "list-panes",
476 Self::SourceFile(_) => "source-file",
477 Self::UnlinkWindow(_) => "unlink-window",
478 Self::SetOptionByName(_) => "set-option",
479 Self::SetHookMutation(_) => "set-hook",
480 Self::ShowHooks(_) => "show-hooks",
481 Self::SendKeysExt(_) => "send-keys",
482 Self::SwitchClientExt(_) => "switch-client",
483 Self::BindKey(_) => "bind-key",
484 Self::UnbindKey(_) => "unbind-key",
485 Self::ListKeys(_) => "list-keys",
486 Self::SendPrefix(_) => "send-prefix",
487 Self::ClearHistory(_) => "clear-history",
488 Self::CopyMode(_) => "copy-mode",
489 Self::ControlMode(_) => "control-mode",
490 Self::ClockMode(_) => "clock-mode",
491 Self::ShowMessages(_) => "show-messages",
492 Self::NewSessionExt(_) => "new-session",
493 Self::AttachSessionExt(_) => "attach-session",
494 Self::SwitchClientExt2(_) => "switch-client",
495 Self::SelectCustomLayout(_) => "select-layout",
496 Self::SelectOldLayout(_) => "select-layout",
497 Self::SpreadLayout(_) => "select-layout",
498 Self::ResizeWindow(_) => "resize-window",
499 Self::RespawnWindow(_) => "respawn-window",
500 Self::MovePane(_) => "move-pane",
501 Self::PipePane(_) => "pipe-pane",
502 Self::RespawnPane(_) => "respawn-pane",
503 Self::KillServer(_) => "kill-server",
504 Self::LockServer(_) => "lock-server",
505 Self::LockSession(_) => "lock-session",
506 Self::LockClient(_) => "lock-client",
507 Self::ServerAccess(_) => "server-access",
508 Self::RefreshClient(_) => "refresh-client",
509 Self::ListClients(_) => "list-clients",
510 Self::SuspendClient(_) => "suspend-client",
511 Self::DetachClientExt(_) => "detach-client",
512 Self::AttachSessionExt2(_) | Self::AttachSessionExt3(_) => "attach-session",
513 Self::SwitchClientExt3(_) => "switch-client",
514 Self::Handshake(_) => "handshake",
515 Self::DaemonStatus(_) => "daemon-status",
516 Self::ShutdownIfIdle(_) => "shutdown-if-idle",
517 Self::WebShare(_) => "web-share",
518 }
519 }
520}
521
522#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
524pub struct ShowMessagesRequest {
525 pub jobs: bool,
527 pub terminals: bool,
529 pub target_client: Option<String>,
531}
532
533#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
535pub struct RunShellRequest {
536 pub command: String,
538 #[serde(default)]
540 pub arguments: Vec<String>,
541 pub background: bool,
543 #[serde(default)]
545 pub as_commands: bool,
546 #[serde(default)]
548 pub show_stderr: bool,
549 #[serde(default)]
551 pub delay_seconds: Option<RunShellDelaySeconds>,
552 #[serde(default)]
554 pub start_directory: Option<PathBuf>,
555 #[serde(default)]
557 pub target: Option<PaneTarget>,
558 #[serde(default)]
560 pub source_depth: Option<usize>,
561}
562
563#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
565pub struct RunShellDelaySeconds(pub f64);
566
567impl RunShellDelaySeconds {
568 #[must_use]
570 pub const fn as_secs_f64(self) -> f64 {
571 self.0
572 }
573}
574
575impl PartialEq for RunShellDelaySeconds {
576 fn eq(&self, other: &Self) -> bool {
577 self.0.to_bits() == other.0.to_bits()
578 }
579}
580
581impl Eq for RunShellDelaySeconds {}
582
583#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
585pub struct IfShellRequest {
586 pub condition: String,
588 pub format_mode: bool,
590 pub then_command: String,
592 pub else_command: Option<String>,
594 pub target: Option<Target>,
596 pub caller_cwd: Option<PathBuf>,
598 #[serde(default)]
600 pub background: bool,
601}
602
603#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
605pub struct SourceFileRequest {
606 pub paths: Vec<String>,
608 pub quiet: bool,
610 pub parse_only: bool,
612 pub verbose: bool,
614 pub expand_paths: bool,
616 pub target: Option<PaneTarget>,
618 pub caller_cwd: Option<PathBuf>,
620 pub stdin: Option<String>,
622}
623
624#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
626pub struct UnlinkWindowRequest {
627 pub target: WindowTarget,
629 #[serde(default)]
631 pub kill_if_last: bool,
632}
633
634#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
636pub enum WaitForMode {
637 Wait,
639 Signal,
641 Lock,
643 Unlock,
645}
646
647#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
649pub struct WaitForRequest {
650 pub channel: String,
652 pub mode: WaitForMode,
654}
655
656#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
663pub struct SdkWaitForOutputRequest {
664 pub owner_id: SdkWaitOwnerId,
666 pub wait_id: SdkWaitId,
668 pub target: PaneTarget,
670 pub bytes: Vec<u8>,
672 pub start: PaneOutputSubscriptionStart,
674}
675
676#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
678pub struct SdkWaitForOutputRefRequest {
679 pub owner_id: SdkWaitOwnerId,
681 pub wait_id: SdkWaitId,
683 pub target: PaneTargetRef,
685 pub bytes: Vec<u8>,
687 pub start: PaneOutputSubscriptionStart,
689}
690
691#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
693pub struct CancelSdkWaitRequest {
694 pub owner_id: SdkWaitOwnerId,
696 pub wait_id: SdkWaitId,
698}
699
700#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
702pub struct ListPanesRequest {
703 pub target: SessionName,
705 #[serde(default)]
707 pub target_window_index: Option<u32>,
708 pub format: Option<String>,
710 #[serde(default)]
712 pub filter: Option<String>,
713 #[serde(default)]
715 pub sort_order: Option<String>,
716 #[serde(default)]
718 pub reversed: bool,
719}
720
721#[cfg(test)]
722#[path = "request/compat_tests.rs"]
723mod compat_tests;
724
725#[cfg(test)]
726mod tests {
727 use super::*;
728 use crate::{
729 OptionScopeSelector, PaneTarget, ScopeSelector, SelectPaneDirection, SetOptionMode,
730 SplitDirection, WebShareScope,
731 };
732 use serde::Serialize;
733 use std::mem::size_of;
734
735 fn alpha() -> SessionName {
736 SessionName::new("alpha").expect("valid session")
737 }
738
739 fn pane() -> PaneTarget {
740 PaneTarget::new(alpha(), 0)
741 }
742
743 #[test]
744 fn request_command_names_cover_extended_aliases_and_internal_tags() {
745 assert_eq!(
746 Request::NewSessionExt(Box::new(NewSessionExtRequest {
747 session_name: None,
748 working_directory: None,
749 detached: true,
750 size: None,
751 environment: None,
752 group_target: None,
753 attach_if_exists: false,
754 detach_other_clients: false,
755 kill_other_clients: false,
756 flags: None,
757 window_name: None,
758 print_session_info: false,
759 print_format: None,
760 command: None,
761 process_command: None,
762 client_environment: None,
763 skip_environment_update: false,
764 }))
765 .command_name(),
766 "new-session"
767 );
768 assert_eq!(
769 Request::SplitWindowExt(Box::new(SplitWindowExtRequest {
770 target: SplitWindowTarget::Pane(pane()),
771 direction: SplitDirection::Vertical,
772 before: false,
773 environment: None,
774 command: None,
775 process_command: None,
776 start_directory: None,
777 keep_alive_on_exit: None,
778 detached: false,
779 size: None,
780 preserve_zoom: false,
781 full_size: false,
782 stdin_payload: None,
783 }))
784 .command_name(),
785 "split-window"
786 );
787 assert_eq!(
788 Request::SelectPaneAdjacent(SelectPaneAdjacentRequest {
789 target: pane(),
790 direction: SelectPaneDirection::Right,
791 preserve_zoom: false,
792 })
793 .command_name(),
794 "select-pane"
795 );
796 assert_eq!(
797 Request::SelectPaneMark(SelectPaneMarkRequest {
798 target: pane(),
799 clear: false,
800 title: None,
801 })
802 .command_name(),
803 "select-pane"
804 );
805 assert_eq!(
806 Request::SetOptionByName(Box::new(SetOptionByNameRequest {
807 scope: OptionScopeSelector::ServerGlobal,
808 name: "status".to_owned(),
809 value: Some("on".to_owned()),
810 mode: SetOptionMode::Replace,
811 only_if_unset: false,
812 unset: false,
813 unset_pane_overrides: false,
814 format: false,
815 format_target: None,
816 }))
817 .command_name(),
818 "set-option"
819 );
820 assert_eq!(
821 Request::SetHookMutation(SetHookMutationRequest {
822 scope: ScopeSelector::Global,
823 hook: crate::HookName::AfterNewSession,
824 command: None,
825 lifecycle: crate::HookLifecycle::Persistent,
826 append: false,
827 unset: true,
828 run_immediately: false,
829 index: None,
830 })
831 .command_name(),
832 "set-hook"
833 );
834 assert_eq!(
835 Request::AttachSessionExt2(Box::new(AttachSessionExt2Request {
836 target: Some(alpha()),
837 target_spec: None,
838 detach_other_clients: false,
839 kill_other_clients: false,
840 read_only: false,
841 skip_environment_update: false,
842 flags: None,
843 working_directory: None,
844 client_terminal: crate::ClientTerminalContext::default(),
845 client_size: None,
846 }))
847 .command_name(),
848 "attach-session"
849 );
850 assert_eq!(
851 Request::AttachSessionExt3(Box::new(AttachSessionExt3Request::from_ext2(
852 AttachSessionExt2Request {
853 target: Some(alpha()),
854 target_spec: None,
855 detach_other_clients: false,
856 kill_other_clients: false,
857 read_only: false,
858 skip_environment_update: false,
859 flags: None,
860 working_directory: None,
861 client_terminal: crate::ClientTerminalContext::default(),
862 client_size: None,
863 },
864 vec![crate::CAPABILITY_ATTACH_RENDER.to_owned()],
865 )))
866 .command_name(),
867 "attach-session"
868 );
869 assert_eq!(
870 Request::SwitchClientExt3(Box::new(SwitchClientExt3Request {
871 target_client: None,
872 target: Some("alpha:0.0".to_owned()),
873 key_table: None,
874 last_session: false,
875 next_session: false,
876 previous_session: false,
877 toggle_read_only: false,
878 sort_order: None,
879 skip_environment_update: false,
880 zoom: false,
881 }))
882 .command_name(),
883 "switch-client"
884 );
885 assert_eq!(
886 Request::ResolveTarget(ResolveTargetRequest {
887 target: Some("alpha:0.0".to_owned()),
888 target_type: ResolveTargetType::Pane,
889 window_index: false,
890 prefer_unattached: false,
891 })
892 .command_name(),
893 "resolve-target"
894 );
895 assert_eq!(
896 Request::Handshake(HandshakeRequest::current()).command_name(),
897 "handshake"
898 );
899 assert_eq!(
900 Request::SdkWaitForOutput(SdkWaitForOutputRequest {
901 owner_id: SdkWaitOwnerId::new(1),
902 wait_id: SdkWaitId::new(1),
903 target: pane(),
904 bytes: b"ready".to_vec(),
905 start: PaneOutputSubscriptionStart::Now,
906 })
907 .command_name(),
908 "sdk-wait-output"
909 );
910 assert_eq!(
911 Request::CancelSdkWait(CancelSdkWaitRequest {
912 owner_id: SdkWaitOwnerId::new(1),
913 wait_id: SdkWaitId::new(1),
914 })
915 .command_name(),
916 "cancel-sdk-wait"
917 );
918 assert_eq!(
919 Request::WaitFor(WaitForRequest {
920 channel: "ready".to_owned(),
921 mode: WaitForMode::Wait,
922 })
923 .command_name(),
924 "wait-for"
925 );
926 }
927
928 #[test]
929 fn pr6g_request_boxing_keeps_request_size_bounded() {
930 let request_size = size_of::<Request>();
931 let box_size = size_of::<Box<NewSessionExtRequest>>();
932
933 eprintln!("PR6G size baseline: Request={request_size} Box<T>={box_size}");
934 eprintln!(
935 "PR6G candidate sizes: NewSessionExt={} NewWindow={} SetOptionByName={} \
936 AttachSessionExt3={} SwitchClientExt3={} SendKeysExt2={} SourceFile={} WebShare={}",
937 size_of::<NewSessionExtRequest>(),
938 size_of::<NewWindowRequest>(),
939 size_of::<SetOptionByNameRequest>(),
940 size_of::<AttachSessionExt3Request>(),
941 size_of::<SwitchClientExt3Request>(),
942 size_of::<SendKeysExt2Request>(),
943 size_of::<SourceFileRequest>(),
944 size_of::<WebShareRequest>(),
945 );
946 let mut remaining = [
947 ("AttachSessionExt2", size_of::<AttachSessionExt2Request>()),
948 ("BindKey", size_of::<BindKeyRequest>()),
949 ("BreakPane", size_of::<BreakPaneRequest>()),
950 ("CapturePane", size_of::<CapturePaneRequest>()),
951 ("ClearHistory", size_of::<ClearHistoryRequest>()),
952 ("ClockMode", size_of::<ClockModeRequest>()),
953 ("ControlMode", size_of::<ControlModeRequest>()),
954 ("CopyMode", size_of::<CopyModeRequest>()),
955 ("CreateSessionLease", size_of::<CreateSessionLeaseRequest>()),
956 ("DeleteBuffer", size_of::<DeleteBufferRequest>()),
957 ("DetachClientExt", size_of::<DetachClientExtRequest>()),
958 ("DisplayPanes", size_of::<DisplayPanesRequest>()),
959 ("DisplayMessage", size_of::<DisplayMessageRequest>()),
960 ("DisplayMessageExt", size_of::<DisplayMessageExtRequest>()),
961 ("Handshake", size_of::<HandshakeRequest>()),
962 ("IfShell", size_of::<IfShellRequest>()),
963 ("JoinPane", size_of::<JoinPaneRequest>()),
964 ("KillPane", size_of::<KillPaneRequest>()),
965 ("KillWindow", size_of::<KillWindowRequest>()),
966 ("LinkWindow", size_of::<LinkWindowRequest>()),
967 ("ListClients", size_of::<ListClientsRequest>()),
968 ("ListKeys", size_of::<ListKeysRequest>()),
969 ("ListPanes", size_of::<ListPanesRequest>()),
970 ("ListSessions", size_of::<ListSessionsRequest>()),
971 ("ListWindows", size_of::<ListWindowsRequest>()),
972 ("LoadBuffer", size_of::<LoadBufferRequest>()),
973 ("MovePane", size_of::<MovePaneRequest>()),
974 ("MoveWindow", size_of::<MoveWindowRequest>()),
975 ("NewSession", size_of::<NewSessionRequest>()),
976 ("PasteBuffer", size_of::<PasteBufferRequest>()),
977 ("PaneInput", size_of::<PaneInputRequest>()),
978 ("PaneKill", size_of::<PaneKillRequest>()),
979 ("PaneResize", size_of::<PaneResizeRequest>()),
980 ("PaneRespawn", size_of::<PaneRespawnRequest>()),
981 ("PaneSelect", size_of::<PaneSelectRequest>()),
982 ("PaneSnapshot", size_of::<PaneSnapshotRequest>()),
983 ("PaneSnapshotRef", size_of::<PaneSnapshotRefRequest>()),
984 ("PipePane", size_of::<PipePaneRequest>()),
985 ("RefreshClient", size_of::<RefreshClientRequest>()),
986 ("RespawnPane", size_of::<RespawnPaneRequest>()),
987 ("RespawnWindow", size_of::<RespawnWindowRequest>()),
988 ("ResizePane", size_of::<ResizePaneRequest>()),
989 ("ResizeWindow", size_of::<ResizeWindowRequest>()),
990 ("RotateWindow", size_of::<RotateWindowRequest>()),
991 ("RunShell", size_of::<RunShellRequest>()),
992 ("SaveBuffer", size_of::<SaveBufferRequest>()),
993 ("SelectCustomLayout", size_of::<SelectCustomLayoutRequest>()),
994 ("SelectLayout", size_of::<SelectLayoutRequest>()),
995 ("SelectPane", size_of::<SelectPaneRequest>()),
996 ("SelectPaneAdjacent", size_of::<SelectPaneAdjacentRequest>()),
997 ("SelectPaneMark", size_of::<SelectPaneMarkRequest>()),
998 ("SetEnvironment", size_of::<SetEnvironmentRequest>()),
999 ("SetBuffer", size_of::<SetBufferRequest>()),
1000 ("SetHook", size_of::<SetHookRequest>()),
1001 ("SetHookMutation", size_of::<SetHookMutationRequest>()),
1002 ("SetOption", size_of::<SetOptionRequest>()),
1003 ("ShowBuffer", size_of::<ShowBufferRequest>()),
1004 ("ShowEnvironment", size_of::<ShowEnvironmentRequest>()),
1005 ("ShowHooks", size_of::<ShowHooksRequest>()),
1006 ("ShowMessages", size_of::<ShowMessagesRequest>()),
1007 ("ShowOptions", size_of::<ShowOptionsRequest>()),
1008 ("SplitWindow", size_of::<SplitWindowRequest>()),
1009 ("SplitWindowExt", size_of::<SplitWindowExtRequest>()),
1010 (
1011 "SubscribePaneOutput",
1012 size_of::<SubscribePaneOutputRequest>(),
1013 ),
1014 (
1015 "SubscribePaneOutputRef",
1016 size_of::<SubscribePaneOutputRefRequest>(),
1017 ),
1018 ("SwitchClientExt2", size_of::<SwitchClientExt2Request>()),
1019 ];
1020 remaining.sort_by_key(|(_, size)| std::cmp::Reverse(*size));
1021 eprintln!("PR6G largest remaining candidates: {remaining:?}");
1022
1023 assert!(
1024 request_size <= 96,
1025 "Request grew past the PR6G boxed payload budget: {request_size}"
1026 );
1027 assert_eq!(box_size, size_of::<usize>());
1028 }
1029
1030 #[test]
1031 fn pr6g_boxed_payloads_are_bincode_transparent_for_request_candidates() {
1032 assert_box_serializes_like_value(NewSessionExtRequest {
1033 session_name: Some(alpha()),
1034 working_directory: Some("/tmp".to_owned()),
1035 detached: true,
1036 size: Some(crate::TerminalSize {
1037 cols: 120,
1038 rows: 40,
1039 }),
1040 environment: Some(vec!["A=B".to_owned()]),
1041 group_target: None,
1042 attach_if_exists: false,
1043 detach_other_clients: false,
1044 kill_other_clients: false,
1045 flags: Some(vec!["read-only".to_owned()]),
1046 window_name: Some("main".to_owned()),
1047 print_session_info: true,
1048 print_format: Some("#{session_name}".to_owned()),
1049 command: Some(vec!["sh".to_owned(), "-lc".to_owned(), "true".to_owned()]),
1050 process_command: None,
1051 client_environment: Some(vec!["PATH=/bin".to_owned()]),
1052 skip_environment_update: false,
1053 });
1054 assert_box_serializes_like_value(SetOptionByNameRequest {
1055 scope: OptionScopeSelector::ServerGlobal,
1056 name: "@plugin".to_owned(),
1057 value: Some("enabled".to_owned()),
1058 mode: SetOptionMode::Replace,
1059 only_if_unset: false,
1060 unset: false,
1061 unset_pane_overrides: false,
1062 format: true,
1063 format_target: Some(crate::Target::Session(alpha())),
1064 });
1065 assert_box_serializes_like_value(ListWindowsRequest {
1066 target: alpha(),
1067 format: Some("#{window_index}".to_owned()),
1068 filter: Some("#{==:#{window_active},1}".to_owned()),
1069 sort_order: Some("index".to_owned()),
1070 reversed: true,
1071 });
1072 assert_box_serializes_like_value(ListPanesRequest {
1073 target: alpha(),
1074 target_window_index: Some(1),
1075 format: Some("#{pane_index}".to_owned()),
1076 filter: Some("#{==:#{pane_active},1}".to_owned()),
1077 sort_order: Some("index".to_owned()),
1078 reversed: true,
1079 });
1080 assert_box_serializes_like_value(DisplayPanesRequest {
1081 target: alpha(),
1082 duration_ms: Some(1_000),
1083 non_blocking: true,
1084 no_command: false,
1085 template: Some("select-pane -t '%%'".to_owned()),
1086 target_client: Some("/dev/pts/1".to_owned()),
1087 });
1088 assert_box_serializes_like_value(SetBufferRequest {
1089 name: Some("named".to_owned()),
1090 content: b"buffer".to_vec(),
1091 append: false,
1092 new_name: None,
1093 set_clipboard: true,
1094 target_client: Some("/dev/pts/1".to_owned()),
1095 });
1096 assert_box_serializes_like_value(LoadBufferRequest {
1097 path: "/tmp/input".to_owned(),
1098 cwd: Some(PathBuf::from("/tmp")),
1099 name: Some("loaded".to_owned()),
1100 set_clipboard: true,
1101 target_client: Some("/dev/pts/1".to_owned()),
1102 });
1103 assert_box_serializes_like_value(AttachSessionExt3Request::from_ext2(
1104 AttachSessionExt2Request {
1105 target: Some(alpha()),
1106 target_spec: Some("alpha:0.0".to_owned()),
1107 detach_other_clients: false,
1108 kill_other_clients: false,
1109 read_only: true,
1110 skip_environment_update: false,
1111 flags: Some(vec!["active-pane".to_owned()]),
1112 working_directory: Some("/tmp".to_owned()),
1113 client_terminal: crate::ClientTerminalContext::default(),
1114 client_size: Some(crate::TerminalSize { cols: 80, rows: 24 }),
1115 },
1116 vec![crate::CAPABILITY_ATTACH_RENDER.to_owned()],
1117 ));
1118 assert_box_serializes_like_value(WebShareRequest::Create(CreateWebShareRequest {
1119 scope: WebShareScope::Session(alpha()),
1120 public_base_url: Some("https://example.invalid".to_owned()),
1121 tunnel_provider: None,
1122 frontend_url: Some("https://share.example.invalid".to_owned()),
1123 ttl_seconds: Some(60),
1124 expires_at_unix: None,
1125 max_spectators: Some(8),
1126 max_operators: Some(1),
1127 url_options: WebShareUrlOptions::default(),
1128 require_pin: true,
1129 operator_pin: Some("123456".to_owned()),
1130 spectator_pin: Some("654321".to_owned()),
1131 terminal_palette: None,
1132 operator: true,
1133 spectator: true,
1134 controls: true,
1135 kill_session_on_expire: false,
1136 }));
1137 }
1138
1139 fn assert_box_serializes_like_value<T>(value: T)
1140 where
1141 T: Clone + Serialize,
1142 {
1143 let boxed = Box::new(value.clone());
1144 assert_eq!(
1145 bincode::serialize(&value).expect("value encodes"),
1146 bincode::serialize(&boxed).expect("boxed value encodes"),
1147 "Box<T> must be transparent before PR6G boxes enum variants"
1148 );
1149 }
1150}