siranga/src/io.rs
2025-04-10 17:32:12 +02:00

69 lines
1.8 KiB
Rust

use crossterm::{
execute,
terminal::{EnterAlternateScreen, LeaveAlternateScreen},
};
use russh::{ChannelId, server::Handle};
use tokio::sync::mpsc::{UnboundedSender, unbounded_channel};
use tracing::error;
pub struct TerminalHandle {
sender: UnboundedSender<Vec<u8>>,
sink: Vec<u8>,
}
impl TerminalHandle {
pub async fn start(handle: Handle, channel_id: ChannelId) -> std::io::Result<Self> {
let (sender, mut receiver) = unbounded_channel::<Vec<u8>>();
tokio::spawn(async move {
while let Some(data) = receiver.recv().await {
let result = handle.data(channel_id, data.into()).await;
if let Err(e) = result {
error!("Failed to send data: {e:?}");
};
}
if let Err(e) = handle.close(channel_id).await {
error!("Failed to close session: {e:?}");
}
});
let mut terminal_handle = Self {
sender,
sink: Vec::new(),
};
execute!(terminal_handle, EnterAlternateScreen)?;
Ok(terminal_handle)
}
pub fn leave_alternate_screen(&mut self) -> std::io::Result<()> {
execute!(self, LeaveAlternateScreen)
}
}
impl Drop for TerminalHandle {
fn drop(&mut self) {
self.leave_alternate_screen().ok();
}
}
impl std::io::Write for TerminalHandle {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.sink.extend_from_slice(buf);
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
let result = self.sender.send(self.sink.clone());
if let Err(e) = result {
return Err(std::io::Error::new(std::io::ErrorKind::BrokenPipe, e));
}
self.sink.clear();
Ok(())
}
}