use std::fmt;
use std::path::PathBuf;
use std::time::Duration;
use crate::events::streams::{PaneLineStream, PaneOutputStart, PaneOutputStream};
use crate::handles::split::SplitDirection;
use crate::transport::TransportClient;
use crate::{
ArmedWait, CollectedPaneOutput, InfoSnapshot, PaneExitState, PaneId, PaneRef, PaneSnapshot,
PaneTextMatch, ProcessSpec, Result, RmuxEndpoint, TerminalSizeSpec,
};
#[path = "pane/info.rs"]
mod info;
#[path = "pane/input.rs"]
mod input;
#[path = "pane/lifecycle.rs"]
mod lifecycle;
#[path = "pane/snapshot.rs"]
mod snapshot;
#[path = "pane/split.rs"]
mod split;
#[path = "pane/target.rs"]
mod target;
use info::{current_pane_entry, pane_info_snapshot};
use input::{resize_to_size, send_key, send_text};
use lifecycle::{close_pane, respawn_pane};
use snapshot::pane_snapshot;
use split::split_pane;
pub(crate) use target::is_already_closed_pane_error;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum PaneCloseOutcome {
Closed {
target: PaneRef,
window_destroyed: bool,
},
AlreadyClosed {
target: PaneRef,
},
}
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct PaneRespawnOptions {
pub kill: bool,
pub start_directory: Option<PathBuf>,
pub process: ProcessSpec,
}
#[derive(Clone)]
pub struct Pane {
target: PaneRef,
endpoint: RmuxEndpoint,
default_timeout: Option<Duration>,
transport: TransportClient,
}
impl Pane {
pub(crate) fn new(
target: PaneRef,
endpoint: RmuxEndpoint,
default_timeout: Option<Duration>,
transport: TransportClient,
) -> Self {
Self {
target,
endpoint,
default_timeout,
transport,
}
}
#[must_use]
pub const fn target(&self) -> &PaneRef {
&self.target
}
#[must_use]
pub const fn endpoint(&self) -> &RmuxEndpoint {
&self.endpoint
}
#[must_use]
pub const fn configured_default_timeout(&self) -> Option<Duration> {
self.default_timeout
}
pub(crate) const fn transport(&self) -> &TransportClient {
&self.transport
}
pub async fn wait_for(&self, bytes: impl AsRef<[u8]>) -> Result<()> {
crate::wait::wait_for_bytes(self, bytes.as_ref().to_vec()).await
}
pub async fn wait_for_next(&self, bytes: impl AsRef<[u8]>) -> Result<ArmedWait> {
crate::wait::wait_for_next_bytes(self, bytes.as_ref().to_vec()).await
}
pub async fn wait_for_text(&self, text: impl AsRef<str>) -> Result<()> {
crate::wait::wait_for_text(self, text.as_ref().to_owned()).await
}
pub async fn wait_for_text_next(&self, text: impl AsRef<str>) -> Result<ArmedWait> {
crate::wait::wait_for_text_next(self, text.as_ref().to_owned()).await
}
pub async fn wait_exit(&self) -> Result<Option<PaneExitState>> {
crate::wait::wait_exit(self).await
}
pub async fn wait_for_exit(&self) -> Result<Option<PaneExitState>> {
self.wait_exit().await
}
pub async fn output_stream(&self) -> Result<PaneOutputStream> {
self.output_stream_starting_at(PaneOutputStart::Now).await
}
pub async fn output_stream_starting_at(
&self,
start: PaneOutputStart,
) -> Result<PaneOutputStream> {
PaneOutputStream::open(self.transport.clone(), self.target.to_proto(), start).await
}
pub async fn collect_output_until_exit(&self, max_bytes: usize) -> Result<CollectedPaneOutput> {
crate::extract::collect_output_until_exit(self, max_bytes).await
}
pub async fn collect_output_until_exit_starting_at(
&self,
start: PaneOutputStart,
max_bytes: usize,
) -> Result<CollectedPaneOutput> {
crate::extract::collect_output_until_exit_starting_at(self, start, max_bytes).await
}
pub async fn line_stream(&self) -> Result<PaneLineStream> {
self.line_stream_starting_at(PaneOutputStart::Now).await
}
pub async fn line_stream_starting_at(&self, start: PaneOutputStart) -> Result<PaneLineStream> {
let inner = self.output_stream_starting_at(start).await?;
Ok(PaneLineStream::wrap(inner))
}
pub async fn id(&self) -> Result<Option<PaneId>> {
Ok(current_pane_entry(&self.transport, &self.target)
.await?
.map(|entry| entry.pane_id))
}
pub async fn exists(&self) -> Result<bool> {
Ok(self.id().await?.is_some())
}
pub async fn info(&self) -> Result<InfoSnapshot> {
pane_info_snapshot(&self.transport, &self.target).await
}
pub async fn snapshot(&self) -> Result<PaneSnapshot> {
pane_snapshot(&self.transport, &self.target).await
}
pub async fn find_text(&self, text: impl AsRef<str>) -> Result<Option<PaneTextMatch>> {
crate::extract::find_text(self, text.as_ref().to_owned()).await
}
pub async fn find_text_all(&self, text: impl AsRef<str>) -> Result<Vec<PaneTextMatch>> {
crate::extract::find_text_all(self, text.as_ref().to_owned()).await
}
pub async fn send_text(&self, text: impl AsRef<str>) -> Result<()> {
send_text(&self.transport, &self.target, text.as_ref()).await
}
pub async fn send_key(&self, key: impl Into<String>) -> Result<()> {
send_key(&self.transport, &self.target, key.into()).await
}
pub async fn resize(&self, size: TerminalSizeSpec) -> Result<()> {
resize_to_size(&self.transport, &self.target, size).await
}
pub async fn close(self) -> Result<PaneCloseOutcome> {
close_pane(&self.transport, self.target).await
}
pub fn detach(self) {}
pub async fn split(&self, direction: SplitDirection) -> Result<Self> {
let new_target = split_pane(&self.transport, &self.target, direction).await?;
Ok(Self::new(
new_target,
self.endpoint.clone(),
self.default_timeout,
self.transport.clone(),
))
}
pub async fn respawn(&self, options: PaneRespawnOptions) -> Result<PaneRef> {
respawn_pane(&self.transport, &self.target, options).await
}
}
impl fmt::Debug for Pane {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("Pane")
.field("target", &self.target)
.finish_non_exhaustive()
}
}
#[cfg(test)]
#[path = "pane/tests.rs"]
mod tests;