use futures::{StreamExt, stream::BoxStream};
use ratatui::buffer::Buffer;
use std::{fmt::Debug, io};
mod cross_terminal;
pub use cross_terminal::CrossTerminal;
pub trait TerminalImpl: Send {
type Event: Clone + Debug;
fn event_stream(&mut self) -> io::Result<BoxStream<'static, Self::Event>>;
fn received_ctrl_c(event: Self::Event) -> bool;
fn draw<F>(&mut self, f: F) -> io::Result<()>
where
F: FnOnce(&mut ratatui::Frame);
fn insert_before<F>(&mut self, height: u16, draw_fn: F) -> io::Result<()>
where
F: FnOnce(&mut Buffer);
}
pub struct Terminal<T = CrossTerminal>
where
T: TerminalImpl,
{
inner: Box<T>,
event_stream: BoxStream<'static, T::Event>,
}
impl<T> Terminal<T>
where
T: TerminalImpl,
{
pub fn new(inner: T) -> io::Result<Self> {
let mut inner = Box::new(inner);
Ok(Self {
event_stream: inner.event_stream()?,
inner,
})
}
pub fn draw<F>(&mut self, f: F) -> io::Result<()>
where
F: FnOnce(&mut ratatui::Frame),
{
self.inner.draw(f)
}
pub fn insert_before<F>(&mut self, height: u16, draw_fn: F) -> io::Result<()>
where
F: FnOnce(&mut Buffer),
{
self.inner.insert_before(height, draw_fn)
}
pub async fn next_event(&mut self) -> Option<T::Event> {
self.event_stream.next().await
}
}
#[doc(hidden)]
pub trait UpdaterTerminal {
fn insert_before(
&mut self,
height: u16,
draw_fn: Box<dyn FnOnce(&mut Buffer)>,
) -> io::Result<()>;
}
impl<T> UpdaterTerminal for Terminal<T>
where
T: TerminalImpl<Event = crossterm::event::Event>,
{
fn insert_before(
&mut self,
height: u16,
draw_fn: Box<dyn FnOnce(&mut Buffer)>,
) -> io::Result<()> {
Terminal::insert_before(self, height, draw_fn)
}
}