use std::io;
use std::sync::{Arc, Mutex};
use gpui_kit::base::async_util;
use crate::terminal::engine::{ByteHandle, ByteSink, ByteSource, ExitStatus};
use crate::terminal::frame::Viewport;
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum StreamEvent {
Input(Vec<u8>),
Resize(Viewport),
Closed,
}
#[derive(Debug)]
pub struct StreamSource {
shared: Arc<Shared>,
}
#[derive(Clone, Debug)]
pub struct StreamPeer {
shared: Arc<Shared>,
events: async_util::Receiver<StreamEvent>,
}
#[derive(Debug)]
struct Shared {
state: Mutex<State>,
events: async_util::Sender<StreamEvent>,
}
#[derive(Debug, Default)]
struct State {
sink: Option<ByteSink>,
pending: Vec<u8>,
exit: Option<ExitStatus>,
}
impl Shared {
fn lock(&self) -> std::sync::MutexGuard<'_, State> {
self.state
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
}
}
impl StreamSource {
#[must_use]
pub fn new() -> (Self, StreamPeer) {
let (events, receiver) = async_util::unbounded();
let shared = Arc::new(Shared {
state: Mutex::new(State::default()),
events,
});
(
Self {
shared: shared.clone(),
},
StreamPeer {
shared,
events: receiver,
},
)
}
}
impl StreamPeer {
pub fn output(&self, bytes: &[u8]) {
let mut state = self.shared.lock();
match &state.sink {
Some(sink) => {
sink.write(bytes);
}
None => state.pending.extend_from_slice(bytes),
}
}
pub fn exit(&self, status: ExitStatus) {
let mut state = self.shared.lock();
state.exit = Some(status);
if let Some(sink) = &state.sink {
sink.finished();
}
}
pub fn events(&self) -> &async_util::Receiver<StreamEvent> {
&self.events
}
}
struct Handle {
shared: Arc<Shared>,
}
impl ByteSource for StreamSource {
fn start(
self: Box<Self>,
sink: ByteSink,
viewport: Viewport,
) -> io::Result<Box<dyn ByteHandle>> {
{
let mut state = self.shared.lock();
let pending = std::mem::take(&mut state.pending);
if !pending.is_empty() {
sink.write(&pending);
}
if state.exit.is_some() {
sink.finished();
}
state.sink = Some(sink);
}
let _ = self.shared.events.try_send(StreamEvent::Resize(viewport));
Ok(Box::new(Handle {
shared: self.shared,
}))
}
}
impl ByteHandle for Handle {
fn write(&self, bytes: &[u8]) -> io::Result<usize> {
let _ = self
.shared
.events
.try_send(StreamEvent::Input(bytes.to_vec()));
Ok(bytes.len())
}
fn resize(&self, viewport: Viewport) -> io::Result<()> {
let _ = self.shared.events.try_send(StreamEvent::Resize(viewport));
Ok(())
}
fn try_wait(&self) -> io::Result<Option<ExitStatus>> {
Ok(self.shared.lock().exit.clone())
}
fn close(&self) {
let _ = self.shared.events.try_send(StreamEvent::Closed);
self.shared.lock().sink = None;
}
}