rmux_sdk/handles/
session.rs1use std::fmt;
4use std::time::Duration;
5
6use crate::transport::TransportClient;
7use crate::{PaneId, PaneRef, Result, RmuxEndpoint, RmuxError, SessionName, WindowRef};
8use rmux_proto::{HasSessionRequest, KillSessionRequest, ListSessionsRequest, Request, Response};
9
10use super::{Pane, Window};
11
12pub struct Session {
18 name: SessionName,
19 endpoint: RmuxEndpoint,
20 default_timeout: Option<Duration>,
21 transport: TransportClient,
22 created: bool,
23 creation_tags: Option<Vec<String>>,
24}
25
26impl Session {
27 pub(crate) fn new(
28 name: SessionName,
29 endpoint: RmuxEndpoint,
30 default_timeout: Option<Duration>,
31 transport: TransportClient,
32 created: bool,
33 creation_tags: Option<Vec<String>>,
34 ) -> Self {
35 Self {
36 name,
37 endpoint,
38 default_timeout,
39 transport,
40 created,
41 creation_tags,
42 }
43 }
44
45 #[must_use]
47 pub fn name(&self) -> &SessionName {
48 &self.name
49 }
50
51 #[must_use]
53 pub fn endpoint(&self) -> &RmuxEndpoint {
54 &self.endpoint
55 }
56
57 #[must_use]
59 pub const fn configured_default_timeout(&self) -> Option<Duration> {
60 self.default_timeout
61 }
62
63 pub(crate) const fn transport(&self) -> &TransportClient {
64 &self.transport
65 }
66
67 #[must_use]
72 pub const fn was_created(&self) -> bool {
73 self.created
74 }
75
76 #[must_use]
79 pub fn creation_tags(&self) -> Option<&[String]> {
80 self.creation_tags.as_deref()
81 }
82
83 pub async fn exists(&self) -> Result<bool> {
85 has_session(&self.transport, self.name.clone()).await
86 }
87
88 pub async fn is_listed(&self) -> Result<bool> {
91 Ok(list_session_names(&self.transport)
92 .await?
93 .iter()
94 .any(|candidate| candidate == &self.name))
95 }
96
97 pub async fn list_session_names(&self) -> Result<Vec<SessionName>> {
99 list_session_names(&self.transport).await
100 }
101
102 #[must_use]
110 pub fn window(&self, window_index: u32) -> Window {
111 Window::new(
112 WindowRef::new(self.name.clone(), window_index),
113 self.endpoint.clone(),
114 self.default_timeout,
115 self.transport.clone(),
116 )
117 }
118
119 #[must_use]
126 pub fn pane(&self, window_index: u32, pane_index: u32) -> Pane {
127 Pane::new(
128 PaneRef::new(self.name.clone(), window_index, pane_index),
129 self.endpoint.clone(),
130 self.default_timeout,
131 self.transport.clone(),
132 )
133 }
134
135 pub async fn pane_by_id(&self, pane_id: PaneId) -> Result<Pane> {
143 let target = super::pane::resolve_pane_ref_for_id(&self.transport, &self.name, pane_id)
144 .await?
145 .ok_or_else(|| pane_not_found(&self.name, pane_id))?;
146 Ok(Pane::new_by_id(
147 target,
148 pane_id,
149 self.endpoint.clone(),
150 self.default_timeout,
151 self.transport.clone(),
152 ))
153 }
154
155 pub async fn new_window(&self) -> Result<Window> {
160 self.new_window_with().await
161 }
162
163 #[must_use]
170 pub fn new_window_with(&self) -> crate::NewWindowBuilder<'_> {
171 crate::NewWindowBuilder::new(self)
172 }
173
174 #[must_use]
182 pub fn layout(&self) -> crate::SessionLayoutBuilder<'_> {
183 crate::SessionLayoutBuilder::new(self)
184 }
185
186 pub async fn kill(&self) -> Result<bool> {
191 kill_session(&self.transport, self.name.clone()).await
192 }
193}
194
195fn pane_not_found(session_name: &SessionName, pane_id: PaneId) -> RmuxError {
196 RmuxError::protocol(rmux_proto::RmuxError::pane_not_found(
197 session_name.clone(),
198 pane_id,
199 ))
200}
201
202impl fmt::Debug for Session {
203 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
204 formatter
205 .debug_struct("Session")
206 .field("name", &self.name)
207 .field("created", &self.created)
208 .field("creation_tags", &self.creation_tags)
209 .finish_non_exhaustive()
210 }
211}
212
213pub(crate) async fn has_session(client: &TransportClient, name: SessionName) -> Result<bool> {
214 match client
215 .request(Request::HasSession(HasSessionRequest { target: name }))
216 .await?
217 {
218 Response::HasSession(response) => Ok(response.exists),
219 response => Err(unexpected_response("has-session", response)),
220 }
221}
222
223pub(crate) async fn kill_session(client: &TransportClient, name: SessionName) -> Result<bool> {
224 match client
225 .request(Request::KillSession(KillSessionRequest {
226 target: name,
227 kill_all_except_target: false,
228 clear_alerts: false,
229 }))
230 .await?
231 {
232 Response::KillSession(response) => Ok(response.existed),
233 response => Err(unexpected_response("kill-session", response)),
234 }
235}
236
237pub(crate) async fn list_session_names(client: &TransportClient) -> Result<Vec<SessionName>> {
238 let response = client
239 .request(Request::ListSessions(ListSessionsRequest {
240 format: Some("#{session_name}".to_owned()),
241 filter: None,
242 sort_order: Some("name".to_owned()),
243 reversed: false,
244 }))
245 .await?;
246
247 let output = match response {
248 Response::ListSessions(response) => response.output.stdout,
249 response => return Err(unexpected_response("list-sessions", response)),
250 };
251
252 String::from_utf8_lossy(&output)
253 .lines()
254 .map(SessionName::new)
255 .collect::<core::result::Result<Vec<_>, _>>()
256 .map_err(RmuxError::protocol)
257}
258
259pub(crate) fn unexpected_response(expected: &'static str, response: Response) -> RmuxError {
260 RmuxError::protocol(rmux_proto::RmuxError::Server(format!(
261 "rmux daemon sent `{}` response for `{expected}` request",
262 response.command_name()
263 )))
264}