use std::fmt;
use std::hash::{Hash, Hasher};
use std::sync::Arc;
use crate::formats::TmuxText;
use crate::internal::core::Core;
use crate::internal::listing;
use crate::query::Filterable;
use crate::snapshot::{ClientFields, ClientInfo};
use crate::target::ServerIdentity;
use crate::{Command, Error, ObjectKind};
#[derive(Clone)]
pub struct Client {
core: Arc<Core>,
info: ClientInfo,
}
impl Client {
pub(crate) const fn new(core: Arc<Core>, info: ClientInfo) -> Self {
Self { core, info }
}
#[must_use]
pub const fn name(&self) -> &TmuxText {
self.info.client_name()
}
#[must_use]
pub fn tty(&self) -> &TmuxText {
self.info.client_tty()
}
#[must_use]
pub fn term_name(&self) -> &TmuxText {
self.info.client_termname()
}
#[must_use]
pub fn pid(&self) -> u32 {
*self.info.client_pid()
}
#[must_use]
pub fn width(&self) -> u32 {
*self.info.client_width()
}
#[must_use]
pub fn height(&self) -> Option<u32> {
self.info.client_height().copied().available()
}
#[must_use]
pub fn created(&self) -> i64 {
*self.info.client_created()
}
#[must_use]
pub fn is_readonly(&self) -> bool {
*self.info.client_readonly()
}
#[must_use]
pub fn is_control_mode(&self) -> bool {
*self.info.client_control_mode()
}
pub(crate) fn server_identity(&self) -> &ServerIdentity {
self.core.configuration().identity()
}
pub async fn refresh(&mut self) -> Result<&mut Self, Error> {
let info = listing::clients(&self.core, None)
.await?
.into_iter()
.find(|info| info.client_name() == self.name())
.ok_or_else(|| Error::ObjectGone {
kind: ObjectKind::Client,
id: self.name().to_string_lossy().into_owned(),
})?;
self.info = info;
Ok(self)
}
pub async fn refreshed(&self) -> Result<Self, Error> {
let mut refreshed = self.clone();
refreshed.refresh().await?;
Ok(refreshed)
}
pub async fn detach(self) -> Result<(), Error> {
listing::mutate(
&self.core,
"detach-client",
Command::new("detach-client")
.arg("-t")
.arg(self.name().to_string_lossy().into_owned()),
)
.await
}
pub async fn suspend(&self) -> Result<(), Error> {
listing::mutate(
&self.core,
"suspend-client",
Command::new("suspend-client")
.arg("-t")
.arg(self.name().to_string_lossy().into_owned()),
)
.await
}
pub async fn redraw(&self) -> Result<(), Error> {
listing::mutate(
&self.core,
"refresh-client",
Command::new("refresh-client")
.arg("-t")
.arg(self.name().to_string_lossy().into_owned()),
)
.await
}
pub async fn switch_to(&self, session: &crate::Session) -> Result<(), Error> {
listing::mutate(
&self.core,
"switch-client",
Command::new("switch-client")
.arg("-c")
.arg(self.name().to_string_lossy().into_owned())
.arg("-t")
.arg(session.id().to_string()),
)
.await
}
}
impl PartialEq for Client {
fn eq(&self, other: &Self) -> bool {
self.server_identity() == other.server_identity() && self.name() == other.name()
}
}
impl Eq for Client {}
impl Hash for Client {
fn hash<H: Hasher>(&self, state: &mut H) {
self.server_identity().hash(state);
self.name().hash(state);
}
}
impl fmt::Debug for Client {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.debug_struct("Client").finish_non_exhaustive()
}
}
impl Filterable for Client {
type Fields = ClientFields<Self>;
const FILTER_TARGET: &'static str = <ClientInfo as Filterable>::FILTER_TARGET;
fn filter_fields() -> Self::Fields {
Self::Fields::for_target(Self::FILTER_TARGET)
}
fn __filter_matches(&self, predicate: &crate::query::__private::Predicate) -> bool {
self.info.__filter_matches(predicate)
}
fn __filter_validate(
predicate: &crate::query::__private::Predicate,
) -> Result<(), crate::query::FilterExpressionError> {
<ClientInfo as Filterable>::__filter_validate(predicate)
}
}
impl fmt::Display for Client {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(&self.name().to_string_lossy())
}
}