Skip to main content

rmux_client/commands/
session.rs

1use rmux_proto::request::{
2    AttachSessionExt2Request, AttachSessionExt3Request, AttachSessionExtRequest,
3    CreateSessionLeaseRequest, DetachClientExtRequest, ListClientsRequest, NewSessionExtRequest,
4    RefreshClientRequest, ReleaseSessionLeaseRequest, RenewSessionLeaseRequest, Request,
5    SuspendClientRequest, SwitchClientExt2Request, SwitchClientExt3Request,
6};
7use rmux_proto::{
8    AttachSessionRequest, DetachClientRequest, HasSessionRequest, KillSessionRequest,
9    ListPanesRequest, ListSessionsRequest, NewSessionRequest, RenameSessionRequest, Response,
10    SessionName, SwitchClientExtRequest, SwitchClientRequest, TerminalSize,
11    CAPABILITY_SDK_SESSION_LEASE,
12};
13
14use crate::{
15    connection::{AttachTransition, Connection},
16    ClientError,
17};
18
19impl Connection {
20    /// Sends a `new-session` request over the detached RPC channel.
21    pub fn new_session(
22        &mut self,
23        session_name: SessionName,
24        detached: bool,
25        size: Option<TerminalSize>,
26    ) -> Result<Response, ClientError> {
27        self.new_session_with_environment(session_name, detached, size, None)
28    }
29
30    /// Sends a `new-session` request with explicit spawn environment overrides.
31    pub fn new_session_with_environment(
32        &mut self,
33        session_name: SessionName,
34        detached: bool,
35        size: Option<TerminalSize>,
36        environment: Option<Vec<String>>,
37    ) -> Result<Response, ClientError> {
38        self.roundtrip(&Request::NewSession(NewSessionRequest {
39            session_name,
40            detached,
41            size,
42            environment,
43        }))
44    }
45
46    /// Sends an extended `new-session` request with grouped-session and attach-if-exists semantics.
47    pub fn new_session_extended(
48        &mut self,
49        request: NewSessionExtRequest,
50    ) -> Result<Response, ClientError> {
51        self.roundtrip(&Request::NewSessionExt(Box::new(request)))
52    }
53
54    /// Sends a `has-session` request over the detached RPC channel.
55    pub fn has_session(&mut self, target: SessionName) -> Result<Response, ClientError> {
56        self.roundtrip(&Request::HasSession(HasSessionRequest { target }))
57    }
58
59    /// Sends a `kill-session` request over the detached RPC channel.
60    pub fn kill_session(&mut self, request: KillSessionRequest) -> Result<Response, ClientError> {
61        self.roundtrip(&Request::KillSession(request))
62    }
63
64    /// Sends a `rename-session` request over the detached RPC channel.
65    pub fn rename_session(
66        &mut self,
67        target: SessionName,
68        new_name: SessionName,
69    ) -> Result<Response, ClientError> {
70        self.roundtrip(&Request::RenameSession(RenameSessionRequest {
71            target,
72            new_name,
73        }))
74    }
75
76    /// Creates an SDK-owned session lease.
77    pub fn create_session_lease(
78        &mut self,
79        session_name: SessionName,
80        ttl_millis: u64,
81    ) -> Result<Response, ClientError> {
82        if !self.supports_capability(CAPABILITY_SDK_SESSION_LEASE)? {
83            return Err(ClientError::Protocol(
84                rmux_proto::RmuxError::UnsupportedCapability {
85                    feature: CAPABILITY_SDK_SESSION_LEASE.to_owned(),
86                    supported: Vec::new(),
87                },
88            ));
89        }
90        self.roundtrip(&Request::CreateSessionLease(CreateSessionLeaseRequest {
91            session_name,
92            ttl_millis,
93        }))
94    }
95
96    /// Renews an SDK-owned session lease.
97    pub fn renew_session_lease(
98        &mut self,
99        session_name: SessionName,
100        token: u64,
101        ttl_millis: u64,
102    ) -> Result<Response, ClientError> {
103        self.roundtrip(&Request::RenewSessionLease(RenewSessionLeaseRequest {
104            session_name,
105            token,
106            ttl_millis,
107        }))
108    }
109
110    /// Releases an SDK-owned session lease.
111    pub fn release_session_lease(
112        &mut self,
113        session_name: SessionName,
114        token: u64,
115    ) -> Result<Response, ClientError> {
116        self.roundtrip(&Request::ReleaseSessionLease(ReleaseSessionLeaseRequest {
117            session_name,
118            token,
119        }))
120    }
121
122    /// Sends a `list-sessions` request over the detached RPC channel.
123    pub fn list_sessions(&mut self, request: ListSessionsRequest) -> Result<Response, ClientError> {
124        self.roundtrip(&Request::ListSessions(request))
125    }
126
127    /// Sends a `list-panes` request over the detached RPC channel.
128    pub fn list_panes(
129        &mut self,
130        target: SessionName,
131        format: Option<String>,
132    ) -> Result<Response, ClientError> {
133        self.list_panes_in_window(target, None, format)
134    }
135
136    /// Sends a `list-panes` request scoped to an optional window index.
137    pub fn list_panes_in_window(
138        &mut self,
139        target: SessionName,
140        target_window_index: Option<u32>,
141        format: Option<String>,
142    ) -> Result<Response, ClientError> {
143        self.list_panes_in_window_with_options(
144            target,
145            target_window_index,
146            format,
147            None,
148            None,
149            false,
150        )
151    }
152
153    /// Sends a `list-panes` request scoped to an optional window index, with
154    /// filtering and ordering options.
155    pub fn list_panes_in_window_with_options(
156        &mut self,
157        target: SessionName,
158        target_window_index: Option<u32>,
159        format: Option<String>,
160        filter: Option<String>,
161        sort_order: Option<String>,
162        reversed: bool,
163    ) -> Result<Response, ClientError> {
164        self.roundtrip(&Request::ListPanes(Box::new(ListPanesRequest {
165            target,
166            target_window_index,
167            format,
168            filter,
169            sort_order,
170            reversed,
171        })))
172    }
173
174    /// Sends a `switch-client` request over the detached RPC channel.
175    pub fn switch_client(&mut self, target: SessionName) -> Result<Response, ClientError> {
176        self.roundtrip(&Request::SwitchClient(SwitchClientRequest { target }))
177    }
178
179    /// Sends an extended `switch-client` request over the detached RPC channel.
180    pub fn switch_client_extended(
181        &mut self,
182        target: Option<SessionName>,
183        key_table: Option<String>,
184    ) -> Result<Response, ClientError> {
185        self.roundtrip(&Request::SwitchClientExt(SwitchClientExtRequest {
186            target,
187            key_table,
188        }))
189    }
190
191    /// Sends a further-extended `switch-client` request over the detached RPC channel.
192    pub fn switch_client_with_session_flags(
193        &mut self,
194        request: SwitchClientExt2Request,
195    ) -> Result<Response, ClientError> {
196        self.roundtrip(&Request::SwitchClientExt2(Box::new(request)))
197    }
198
199    /// Sends the most recent `switch-client` request shape over the detached RPC channel.
200    pub fn switch_client_with_target_selector(
201        &mut self,
202        request: SwitchClientExt3Request,
203    ) -> Result<Response, ClientError> {
204        self.roundtrip(&Request::SwitchClientExt3(Box::new(request)))
205    }
206
207    /// Sends a `detach-client` request over the detached RPC channel.
208    pub fn detach_client(&mut self) -> Result<Response, ClientError> {
209        self.roundtrip(&Request::DetachClient(DetachClientRequest))
210    }
211
212    /// Sends an extended `detach-client` request over the detached RPC channel.
213    pub fn detach_client_extended(
214        &mut self,
215        request: DetachClientExtRequest,
216    ) -> Result<Response, ClientError> {
217        self.roundtrip(&Request::DetachClientExt(request))
218    }
219
220    /// Sends a `refresh-client` request over the detached RPC channel.
221    pub fn refresh_client(
222        &mut self,
223        request: RefreshClientRequest,
224    ) -> Result<Response, ClientError> {
225        self.roundtrip(&Request::RefreshClient(Box::new(request)))
226    }
227
228    /// Sends a `list-clients` request over the detached RPC channel.
229    pub fn list_clients(&mut self, request: ListClientsRequest) -> Result<Response, ClientError> {
230        self.roundtrip(&Request::ListClients(Box::new(request)))
231    }
232
233    /// Sends a `suspend-client` request over the detached RPC channel.
234    pub fn suspend_client(
235        &mut self,
236        request: SuspendClientRequest,
237    ) -> Result<Response, ClientError> {
238        self.roundtrip(&Request::SuspendClient(request))
239    }
240
241    /// Requests an attach upgrade and, on success, yields the raw Unix stream.
242    ///
243    /// Once this method returns [`AttachTransition::Upgraded`], the detached
244    /// framing codec is no longer in the data path for the connection.
245    pub fn begin_attach(mut self, target: SessionName) -> Result<AttachTransition, ClientError> {
246        self.write_request(&Request::AttachSession(AttachSessionRequest { target }))?;
247        let response = self.read_response()?;
248
249        match response {
250            Response::AttachSession(response) => Ok(AttachTransition::Upgraded(
251                self.into_attach_upgrade(response)?,
252            )),
253            other => Ok(AttachTransition::Rejected(other)),
254        }
255    }
256
257    /// Requests an extended attach upgrade and, on success, yields the raw Unix stream.
258    pub fn begin_attach_extended(
259        mut self,
260        request: AttachSessionExtRequest,
261    ) -> Result<AttachTransition, ClientError> {
262        self.write_request(&Request::AttachSessionExt(request))?;
263        let response = self.read_response()?;
264
265        match response {
266            Response::AttachSession(response) => Ok(AttachTransition::Upgraded(
267                self.into_attach_upgrade(response)?,
268            )),
269            other => Ok(AttachTransition::Rejected(other)),
270        }
271    }
272
273    /// Sends the most recent `attach-session` request shape over the detached RPC channel.
274    pub fn begin_attach_with_target_spec(
275        mut self,
276        request: AttachSessionExt2Request,
277    ) -> Result<AttachTransition, ClientError> {
278        self.write_request(&Request::AttachSessionExt2(Box::new(request)))?;
279        let response = self.read_response()?;
280
281        match response {
282            Response::AttachSession(response) => Ok(AttachTransition::Upgraded(
283                self.into_attach_upgrade(response)?,
284            )),
285            other => Ok(AttachTransition::Rejected(other)),
286        }
287    }
288
289    /// Sends a capability-aware `attach-session` request over the detached RPC channel.
290    pub fn begin_attach_with_capabilities(
291        mut self,
292        request: AttachSessionExt3Request,
293    ) -> Result<AttachTransition, ClientError> {
294        self.write_request(&Request::AttachSessionExt3(Box::new(request)))?;
295        let response = self.read_response()?;
296
297        match response {
298            Response::AttachSession(response) => Ok(AttachTransition::Upgraded(
299                self.into_attach_upgrade(response)?,
300            )),
301            other => Ok(AttachTransition::Rejected(other)),
302        }
303    }
304
305    /// Sends the most recent `attach-session` request shape over the detached RPC channel.
306    pub fn attach_session_with_target_spec_detached(
307        &mut self,
308        request: AttachSessionExt2Request,
309    ) -> Result<Response, ClientError> {
310        self.roundtrip(&Request::AttachSessionExt2(Box::new(request)))
311    }
312}