use crossterm::{
ExecutableCommand, cursor,
event::{self, Event},
style::{Attribute, Color, SetAttribute, SetForegroundColor},
terminal::{self, ClearType},
};
use std::io::{self, Stdout, Write, stdout};
pub trait Terminal {
fn read_event(&mut self) -> io::Result<Event>;
fn size(&self) -> io::Result<(u16, u16)>;
fn enable_raw_mode(&mut self) -> io::Result<()>;
fn disable_raw_mode(&mut self) -> io::Result<()>;
fn move_to_column(&mut self, col: u16) -> io::Result<()>;
fn move_up(&mut self, n: u16) -> io::Result<()>;
fn move_down(&mut self, n: u16) -> io::Result<()>;
fn clear_current_line(&mut self) -> io::Result<()>;
fn clear_until_newline(&mut self) -> io::Result<()>;
fn clear_all(&mut self) -> io::Result<()>;
fn write_str(&mut self, s: &str) -> io::Result<()>;
fn set_reverse(&mut self, on: bool) -> io::Result<()>;
fn set_dim(&mut self, on: bool) -> io::Result<()>;
fn set_fg_color(&mut self, color: Color) -> io::Result<()>;
fn reset_style(&mut self) -> io::Result<()>;
fn set_bold(&mut self, on: bool) -> io::Result<()>;
fn set_underline(&mut self, on: bool) -> io::Result<()>;
fn write_char(&mut self, ch: char) -> io::Result<()>;
fn hide_cursor(&mut self) -> io::Result<()>;
fn show_cursor(&mut self) -> io::Result<()>;
fn flush(&mut self) -> io::Result<()>;
}
pub struct CrosstermTerminal {
stdout: Stdout,
}
impl Default for CrosstermTerminal {
fn default() -> Self {
Self { stdout: stdout() }
}
}
impl CrosstermTerminal {
pub fn new() -> Self {
Self::default()
}
}
impl Terminal for CrosstermTerminal {
fn read_event(&mut self) -> io::Result<Event> {
use std::time::Duration;
loop {
if event::poll(Duration::from_millis(50))? {
return event::read();
}
if crate::signal::has_pending_exit_signal() {
return Err(io::Error::new(io::ErrorKind::Interrupted, "signal pending"));
}
}
}
fn size(&self) -> io::Result<(u16, u16)> {
terminal::size()
}
fn enable_raw_mode(&mut self) -> io::Result<()> {
terminal::enable_raw_mode()
}
fn disable_raw_mode(&mut self) -> io::Result<()> {
terminal::disable_raw_mode()
}
fn move_to_column(&mut self, col: u16) -> io::Result<()> {
self.stdout.execute(cursor::MoveToColumn(col))?;
Ok(())
}
fn move_up(&mut self, n: u16) -> io::Result<()> {
self.stdout.execute(cursor::MoveUp(n))?;
Ok(())
}
fn move_down(&mut self, n: u16) -> io::Result<()> {
if n > 0 {
self.stdout.execute(cursor::MoveDown(n))?;
}
Ok(())
}
fn clear_current_line(&mut self) -> io::Result<()> {
self.stdout
.execute(terminal::Clear(ClearType::CurrentLine))?;
Ok(())
}
fn clear_until_newline(&mut self) -> io::Result<()> {
self.stdout
.execute(terminal::Clear(ClearType::UntilNewLine))?;
Ok(())
}
fn clear_all(&mut self) -> io::Result<()> {
self.stdout.execute(terminal::Clear(ClearType::All))?;
self.stdout.execute(cursor::MoveTo(0, 0))?;
Ok(())
}
fn write_str(&mut self, s: &str) -> io::Result<()> {
write!(self.stdout, "{}", s)?;
Ok(())
}
fn set_reverse(&mut self, on: bool) -> io::Result<()> {
if on {
self.stdout.execute(SetAttribute(Attribute::Reverse))?;
} else {
self.stdout.execute(SetAttribute(Attribute::NoReverse))?;
}
Ok(())
}
fn set_dim(&mut self, on: bool) -> io::Result<()> {
if on {
self.stdout.execute(SetAttribute(Attribute::Dim))?;
} else {
self.stdout
.execute(SetAttribute(Attribute::NormalIntensity))?;
}
Ok(())
}
fn set_fg_color(&mut self, color: Color) -> io::Result<()> {
self.stdout.execute(SetForegroundColor(color))?;
Ok(())
}
fn reset_style(&mut self) -> io::Result<()> {
self.stdout.execute(SetAttribute(Attribute::Reset))?;
Ok(())
}
fn set_bold(&mut self, on: bool) -> io::Result<()> {
if on {
self.stdout.execute(SetAttribute(Attribute::Bold))?;
} else {
self.stdout
.execute(SetAttribute(Attribute::NormalIntensity))?;
}
Ok(())
}
fn set_underline(&mut self, on: bool) -> io::Result<()> {
if on {
self.stdout.execute(SetAttribute(Attribute::Underlined))?;
} else {
self.stdout.execute(SetAttribute(Attribute::NoUnderline))?;
}
Ok(())
}
fn write_char(&mut self, ch: char) -> io::Result<()> {
write!(self.stdout, "{}", ch)?;
Ok(())
}
fn hide_cursor(&mut self) -> io::Result<()> {
self.stdout.execute(cursor::Hide)?;
Ok(())
}
fn show_cursor(&mut self) -> io::Result<()> {
self.stdout.execute(cursor::Show)?;
Ok(())
}
fn flush(&mut self) -> io::Result<()> {
self.stdout.flush()
}
}