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.roundtrip(&Request::ListPanes(ListPanesRequest {
144            target,
145            target_window_index,
146            format,
147        }))
148    }
149
150    /// Sends a `switch-client` request over the detached RPC channel.
151    pub fn switch_client(&mut self, target: SessionName) -> Result<Response, ClientError> {
152        self.roundtrip(&Request::SwitchClient(SwitchClientRequest { target }))
153    }
154
155    /// Sends an extended `switch-client` request over the detached RPC channel.
156    pub fn switch_client_extended(
157        &mut self,
158        target: Option<SessionName>,
159        key_table: Option<String>,
160    ) -> Result<Response, ClientError> {
161        self.roundtrip(&Request::SwitchClientExt(SwitchClientExtRequest {
162            target,
163            key_table,
164        }))
165    }
166
167    /// Sends a further-extended `switch-client` request over the detached RPC channel.
168    pub fn switch_client_with_session_flags(
169        &mut self,
170        request: SwitchClientExt2Request,
171    ) -> Result<Response, ClientError> {
172        self.roundtrip(&Request::SwitchClientExt2(Box::new(request)))
173    }
174
175    /// Sends the most recent `switch-client` request shape over the detached RPC channel.
176    pub fn switch_client_with_target_selector(
177        &mut self,
178        request: SwitchClientExt3Request,
179    ) -> Result<Response, ClientError> {
180        self.roundtrip(&Request::SwitchClientExt3(Box::new(request)))
181    }
182
183    /// Sends a `detach-client` request over the detached RPC channel.
184    pub fn detach_client(&mut self) -> Result<Response, ClientError> {
185        self.roundtrip(&Request::DetachClient(DetachClientRequest))
186    }
187
188    /// Sends an extended `detach-client` request over the detached RPC channel.
189    pub fn detach_client_extended(
190        &mut self,
191        request: DetachClientExtRequest,
192    ) -> Result<Response, ClientError> {
193        self.roundtrip(&Request::DetachClientExt(request))
194    }
195
196    /// Sends a `refresh-client` request over the detached RPC channel.
197    pub fn refresh_client(
198        &mut self,
199        request: RefreshClientRequest,
200    ) -> Result<Response, ClientError> {
201        self.roundtrip(&Request::RefreshClient(Box::new(request)))
202    }
203
204    /// Sends a `list-clients` request over the detached RPC channel.
205    pub fn list_clients(&mut self, request: ListClientsRequest) -> Result<Response, ClientError> {
206        self.roundtrip(&Request::ListClients(Box::new(request)))
207    }
208
209    /// Sends a `suspend-client` request over the detached RPC channel.
210    pub fn suspend_client(
211        &mut self,
212        request: SuspendClientRequest,
213    ) -> Result<Response, ClientError> {
214        self.roundtrip(&Request::SuspendClient(request))
215    }
216
217    /// Requests an attach upgrade and, on success, yields the raw Unix stream.
218    ///
219    /// Once this method returns [`AttachTransition::Upgraded`], the detached
220    /// framing codec is no longer in the data path for the connection.
221    pub fn begin_attach(mut self, target: SessionName) -> Result<AttachTransition, ClientError> {
222        self.write_request(&Request::AttachSession(AttachSessionRequest { target }))?;
223        let response = self.read_response()?;
224
225        match response {
226            Response::AttachSession(response) => Ok(AttachTransition::Upgraded(
227                self.into_attach_upgrade(response)?,
228            )),
229            other => Ok(AttachTransition::Rejected(other)),
230        }
231    }
232
233    /// Requests an extended attach upgrade and, on success, yields the raw Unix stream.
234    pub fn begin_attach_extended(
235        mut self,
236        request: AttachSessionExtRequest,
237    ) -> Result<AttachTransition, ClientError> {
238        self.write_request(&Request::AttachSessionExt(request))?;
239        let response = self.read_response()?;
240
241        match response {
242            Response::AttachSession(response) => Ok(AttachTransition::Upgraded(
243                self.into_attach_upgrade(response)?,
244            )),
245            other => Ok(AttachTransition::Rejected(other)),
246        }
247    }
248
249    /// Sends the most recent `attach-session` request shape over the detached RPC channel.
250    pub fn begin_attach_with_target_spec(
251        mut self,
252        request: AttachSessionExt2Request,
253    ) -> Result<AttachTransition, ClientError> {
254        self.write_request(&Request::AttachSessionExt2(Box::new(request)))?;
255        let response = self.read_response()?;
256
257        match response {
258            Response::AttachSession(response) => Ok(AttachTransition::Upgraded(
259                self.into_attach_upgrade(response)?,
260            )),
261            other => Ok(AttachTransition::Rejected(other)),
262        }
263    }
264
265    /// Sends a capability-aware `attach-session` request over the detached RPC channel.
266    pub fn begin_attach_with_capabilities(
267        mut self,
268        request: AttachSessionExt3Request,
269    ) -> Result<AttachTransition, ClientError> {
270        self.write_request(&Request::AttachSessionExt3(Box::new(request)))?;
271        let response = self.read_response()?;
272
273        match response {
274            Response::AttachSession(response) => Ok(AttachTransition::Upgraded(
275                self.into_attach_upgrade(response)?,
276            )),
277            other => Ok(AttachTransition::Rejected(other)),
278        }
279    }
280
281    /// Sends the most recent `attach-session` request shape over the detached RPC channel.
282    pub fn attach_session_with_target_spec_detached(
283        &mut self,
284        request: AttachSessionExt2Request,
285    ) -> Result<Response, ClientError> {
286        self.roundtrip(&Request::AttachSessionExt2(Box::new(request)))
287    }
288}