Skip to main content

rmux_sdk/
command.rs

1//! SDK command DTOs.
2//!
3//! Commands in this module are serializable value objects. They can be mapped
4//! to `rmux-proto` requests, but they do not send IPC, start daemons, probe
5//! endpoints, or parse tmux command strings.
6
7use serde::{Deserialize, Serialize};
8
9use crate::{AttachSessionSpec, NewSessionSpec, RefreshClientSpec, RmuxEndpoint, SplitSpec};
10
11/// Captured result from the official SDK command escape hatch.
12///
13/// [`crate::Rmux::cmd`] runs the `rmux` binary with an endpoint selector
14/// injected by the SDK. Non-zero process exits are returned here rather than
15/// converted into SDK errors so callers can implement tmux-style command
16/// wrappers with precise stdout, stderr, and status handling.
17#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
18#[non_exhaustive]
19pub struct CommandRun {
20    /// Captured stdout bytes.
21    pub stdout: Vec<u8>,
22    /// Captured stderr bytes.
23    pub stderr: Vec<u8>,
24    /// Process exit code, or `None` when the process terminated without a
25    /// platform exit code.
26    pub exit: Option<i32>,
27}
28
29/// A detached command payload accepted by the SDK.
30#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
31pub enum RmuxCommandKind {
32    /// A protocol request supplied directly by the caller.
33    Request(rmux_proto::Request),
34    /// A `new-session` SDK spec.
35    NewSession(NewSessionSpec),
36    /// An `attach-session` SDK spec.
37    AttachSession(AttachSessionSpec),
38    /// A `split-window` SDK spec.
39    SplitWindow(SplitSpec),
40    /// A `refresh-client` SDK spec.
41    RefreshClient(RefreshClientSpec),
42}
43
44impl RmuxCommandKind {
45    /// Returns the stable public command name for this value object.
46    #[must_use]
47    pub fn command_name(&self) -> &'static str {
48        match self {
49            Self::Request(request) => request.command_name(),
50            Self::NewSession(_) => "new-session",
51            Self::AttachSession(_) => "attach-session",
52            Self::SplitWindow(_) => "split-window",
53            Self::RefreshClient(_) => "refresh-client",
54        }
55    }
56
57    /// Converts this value object into the corresponding protocol request.
58    #[must_use]
59    pub fn into_request(self) -> rmux_proto::Request {
60        self.into()
61    }
62}
63
64impl From<rmux_proto::Request> for RmuxCommandKind {
65    fn from(value: rmux_proto::Request) -> Self {
66        Self::Request(value)
67    }
68}
69
70impl From<NewSessionSpec> for RmuxCommandKind {
71    fn from(value: NewSessionSpec) -> Self {
72        Self::NewSession(value)
73    }
74}
75
76impl From<AttachSessionSpec> for RmuxCommandKind {
77    fn from(value: AttachSessionSpec) -> Self {
78        Self::AttachSession(value)
79    }
80}
81
82impl From<SplitSpec> for RmuxCommandKind {
83    fn from(value: SplitSpec) -> Self {
84        Self::SplitWindow(value)
85    }
86}
87
88impl From<RefreshClientSpec> for RmuxCommandKind {
89    fn from(value: RefreshClientSpec) -> Self {
90        Self::RefreshClient(value)
91    }
92}
93
94impl From<RmuxCommandKind> for rmux_proto::Request {
95    fn from(value: RmuxCommandKind) -> Self {
96        match value {
97            RmuxCommandKind::Request(request) => request,
98            RmuxCommandKind::NewSession(spec) => Self::NewSessionExt(spec.into()),
99            RmuxCommandKind::AttachSession(spec) => Self::AttachSessionExt2(spec.into()),
100            RmuxCommandKind::SplitWindow(spec) => Self::SplitWindowExt(spec.into()),
101            RmuxCommandKind::RefreshClient(spec) => Self::RefreshClient(spec.into()),
102        }
103    }
104}
105
106impl From<NewSessionSpec> for rmux_proto::Request {
107    fn from(value: NewSessionSpec) -> Self {
108        RmuxCommandKind::from(value).into()
109    }
110}
111
112impl From<AttachSessionSpec> for rmux_proto::Request {
113    fn from(value: AttachSessionSpec) -> Self {
114        RmuxCommandKind::from(value).into()
115    }
116}
117
118impl From<SplitSpec> for rmux_proto::Request {
119    fn from(value: SplitSpec) -> Self {
120        RmuxCommandKind::from(value).into()
121    }
122}
123
124impl From<RefreshClientSpec> for rmux_proto::Request {
125    fn from(value: RefreshClientSpec) -> Self {
126        RmuxCommandKind::from(value).into()
127    }
128}
129
130/// Endpoint-scoped SDK command DTO.
131#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
132pub struct RmuxCommand {
133    /// Endpoint selector associated with this command.
134    #[serde(default)]
135    pub endpoint: RmuxEndpoint,
136    /// Inert command payload.
137    pub command: RmuxCommandKind,
138}
139
140impl RmuxCommand {
141    /// Creates a command using the platform default endpoint selector.
142    #[must_use]
143    pub fn new(command: impl Into<RmuxCommandKind>) -> Self {
144        Self {
145            endpoint: RmuxEndpoint::default(),
146            command: command.into(),
147        }
148    }
149
150    /// Creates a command using an explicit endpoint selector.
151    #[must_use]
152    pub fn with_endpoint(endpoint: RmuxEndpoint, command: impl Into<RmuxCommandKind>) -> Self {
153        Self {
154            endpoint,
155            command: command.into(),
156        }
157    }
158
159    /// Returns the stable public command name for the command payload.
160    #[must_use]
161    pub fn command_name(&self) -> &'static str {
162        self.command.command_name()
163    }
164
165    /// Converts the command payload into its protocol request.
166    #[must_use]
167    pub fn into_request(self) -> rmux_proto::Request {
168        self.command.into_request()
169    }
170}
171
172impl From<RmuxCommandKind> for RmuxCommand {
173    fn from(value: RmuxCommandKind) -> Self {
174        Self::new(value)
175    }
176}
177
178impl From<rmux_proto::Request> for RmuxCommand {
179    fn from(value: rmux_proto::Request) -> Self {
180        Self::new(value)
181    }
182}
183
184impl From<NewSessionSpec> for RmuxCommand {
185    fn from(value: NewSessionSpec) -> Self {
186        Self::new(value)
187    }
188}
189
190impl From<AttachSessionSpec> for RmuxCommand {
191    fn from(value: AttachSessionSpec) -> Self {
192        Self::new(value)
193    }
194}
195
196impl From<SplitSpec> for RmuxCommand {
197    fn from(value: SplitSpec) -> Self {
198        Self::new(value)
199    }
200}
201
202impl From<RefreshClientSpec> for RmuxCommand {
203    fn from(value: RefreshClientSpec) -> Self {
204        Self::new(value)
205    }
206}
207
208impl From<RmuxCommand> for rmux_proto::Request {
209    fn from(value: RmuxCommand) -> Self {
210        value.into_request()
211    }
212}