use std::io;
use std::sync::atomic::AtomicU8;
use std::sync::Arc;
use bytes::Bytes;
use tokio::io::{AsyncRead, AsyncWrite};
use tokio::sync::mpsc;
use crate::runtime::Runtime;
#[cfg(feature = "local")]
pub mod local;
#[cfg(feature = "ssh")]
pub mod ssh;
pub mod mock;
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum TerminalStatus {
Connected = 0,
Reconnecting = 1,
Offline = 2,
}
impl TerminalStatus {
pub fn from_u8(v: u8) -> Self {
match v {
0 => Self::Connected,
1 => Self::Reconnecting,
_ => Self::Offline,
}
}
}
pub trait TerminalBackend: AsyncRead + AsyncWrite + Send + Unpin + 'static {
fn resize(&mut self, cols: u16, rows: u16) -> io::Result<()>;
}
#[derive(Clone, Debug)]
pub enum CloseReason {
Requested,
RemoteClosed,
TransportError(String),
AuthFailed(String),
}
#[derive(Clone, Debug)]
pub enum BackendEvent {
Bytes(Bytes),
StatusChanged(TerminalStatus),
Lossy { dropped: usize },
InputLost { dropped: usize },
Closed { reason: CloseReason },
}
#[allow(dead_code)] pub(crate) enum UiToBackend {
Input(Bytes),
Resize { cols: u16, rows: u16 },
Shutdown,
}
#[allow(dead_code)] pub struct BackendHandle {
pub(crate) tx: mpsc::Sender<UiToBackend>,
pub(crate) rx: mpsc::Receiver<BackendEvent>,
pub(crate) status: Arc<AtomicU8>,
pub(crate) _runtime: Arc<Runtime>,
}
impl BackendHandle {
pub fn status(&self) -> TerminalStatus {
TerminalStatus::from_u8(self.status.load(std::sync::atomic::Ordering::Relaxed))
}
pub fn try_send_input(&self, bytes: Bytes) -> Result<(), mpsc::error::TrySendError<Bytes>> {
match self.tx.try_send(UiToBackend::Input(bytes)) {
Ok(()) => Ok(()),
Err(mpsc::error::TrySendError::Full(UiToBackend::Input(b))) => {
Err(mpsc::error::TrySendError::Full(b))
}
Err(mpsc::error::TrySendError::Closed(UiToBackend::Input(b))) => {
Err(mpsc::error::TrySendError::Closed(b))
}
Err(_) => unreachable!(),
}
}
pub fn try_resize(&self, cols: u16, rows: u16) {
let _ = self.tx.try_send(UiToBackend::Resize { cols, rows });
}
pub fn shutdown(&self) {
let _ = self.tx.try_send(UiToBackend::Shutdown);
}
}