use std::fmt;
use std::time::Duration;
use crate::transport::TransportClient;
use crate::{PaneRef, Result, RmuxEndpoint, RmuxError, SessionName, WindowRef};
use rmux_proto::{HasSessionRequest, KillSessionRequest, ListSessionsRequest, Request, Response};
use super::{Pane, Window};
pub struct Session {
name: SessionName,
endpoint: RmuxEndpoint,
default_timeout: Option<Duration>,
transport: TransportClient,
created: bool,
creation_tags: Option<Vec<String>>,
}
impl Session {
pub(crate) fn new(
name: SessionName,
endpoint: RmuxEndpoint,
default_timeout: Option<Duration>,
transport: TransportClient,
created: bool,
creation_tags: Option<Vec<String>>,
) -> Self {
Self {
name,
endpoint,
default_timeout,
transport,
created,
creation_tags,
}
}
#[must_use]
pub fn name(&self) -> &SessionName {
&self.name
}
#[must_use]
pub fn endpoint(&self) -> &RmuxEndpoint {
&self.endpoint
}
#[must_use]
pub const fn configured_default_timeout(&self) -> Option<Duration> {
self.default_timeout
}
#[must_use]
pub const fn was_created(&self) -> bool {
self.created
}
#[must_use]
pub fn creation_tags(&self) -> Option<&[String]> {
self.creation_tags.as_deref()
}
pub async fn exists(&self) -> Result<bool> {
has_session(&self.transport, self.name.clone()).await
}
pub async fn is_listed(&self) -> Result<bool> {
Ok(list_session_names(&self.transport)
.await?
.iter()
.any(|candidate| candidate == &self.name))
}
pub async fn list_session_names(&self) -> Result<Vec<SessionName>> {
list_session_names(&self.transport).await
}
#[must_use]
pub fn window(&self, window_index: u32) -> Window {
Window::new(
WindowRef::new(self.name.clone(), window_index),
self.endpoint.clone(),
self.default_timeout,
self.transport.clone(),
)
}
#[must_use]
pub fn pane(&self, window_index: u32, pane_index: u32) -> Pane {
Pane::new(
PaneRef::new(self.name.clone(), window_index, pane_index),
self.endpoint.clone(),
self.default_timeout,
self.transport.clone(),
)
}
pub async fn kill(&self) -> Result<bool> {
kill_session(&self.transport, self.name.clone()).await
}
}
impl fmt::Debug for Session {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("Session")
.field("name", &self.name)
.field("created", &self.created)
.field("creation_tags", &self.creation_tags)
.finish_non_exhaustive()
}
}
pub(crate) async fn has_session(client: &TransportClient, name: SessionName) -> Result<bool> {
match client
.request(Request::HasSession(HasSessionRequest { target: name }))
.await?
{
Response::HasSession(response) => Ok(response.exists),
response => Err(unexpected_response("has-session", response)),
}
}
pub(crate) async fn kill_session(client: &TransportClient, name: SessionName) -> Result<bool> {
match client
.request(Request::KillSession(KillSessionRequest {
target: name,
kill_all_except_target: false,
clear_alerts: false,
}))
.await?
{
Response::KillSession(response) => Ok(response.existed),
response => Err(unexpected_response("kill-session", response)),
}
}
pub(crate) async fn list_session_names(client: &TransportClient) -> Result<Vec<SessionName>> {
let response = client
.request(Request::ListSessions(ListSessionsRequest {
format: Some("#{session_name}".to_owned()),
filter: None,
sort_order: Some("name".to_owned()),
reversed: false,
}))
.await?;
let output = match response {
Response::ListSessions(response) => response.output.stdout,
response => return Err(unexpected_response("list-sessions", response)),
};
String::from_utf8_lossy(&output)
.lines()
.map(SessionName::new)
.collect::<core::result::Result<Vec<_>, _>>()
.map_err(RmuxError::protocol)
}
pub(crate) fn unexpected_response(expected: &'static str, response: Response) -> RmuxError {
RmuxError::protocol(rmux_proto::RmuxError::Server(format!(
"rmux daemon sent `{}` response for `{expected}` request",
response.command_name()
)))
}