use crate::result::Result;
use std::env;
use std::fmt::{Debug, Formatter};
use termion::cursor::DetectCursorPos;
use termion::terminal_size;
pub struct Device<'a>(&'a mut dyn std::io::Write);
impl Device<'_> {
pub(crate) fn new<'a>(writer: &'a mut dyn std::io::Write) -> Device<'a> {
Device(writer)
}
pub(crate) fn size(&self) -> Result<(u16, u16)> {
Ok(terminal_size()?)
}
pub(crate) fn write(&mut self, text: &str) -> Result<()> {
Ok(self.0.write_all(text.as_ref())?)
}
pub(crate) fn clear(&mut self) -> Result<()> {
Ok(write!(self.0, "{}", termion::clear::All)?)
}
pub(crate) fn position(&mut self) -> Result<(u16, u16)> {
let position = self.0.cursor_pos()?;
Ok((position.0 - 1, position.1 - 1))
}
pub(crate) fn set_visible(&mut self, visible: bool) -> Result<()> {
match visible {
true => Ok(write!(self.0, "{}", termion::cursor::Show)?),
false => Ok(write!(self.0, "{}", termion::cursor::Hide)?),
}
}
pub(crate) fn goto(&mut self, column: u16, row: u16) -> Result<()> {
Ok(write!(
self.0,
"{}",
termion::cursor::Goto(column + 1, row + 1)
)?)
}
pub(crate) fn move_up(&mut self, rows: u16) -> Result<()> {
Ok(write!(self.0, "{}", termion::cursor::Up(rows))?)
}
pub(crate) fn move_down(&mut self, rows: u16) -> Result<()> {
Ok(write!(self.0, "{}", "\n".repeat(rows as usize))?)
}
pub(crate) fn move_left(&mut self, columns: u16) -> Result<()> {
Ok(write!(self.0, "{}", termion::cursor::Left(columns))?)
}
pub(crate) fn move_right(&mut self, columns: u16) -> Result<()> {
Ok(write!(self.0, "{}", termion::cursor::Right(columns))?)
}
pub(crate) fn save_position(&mut self) -> Result<()> {
if is_terminal_app() {
Ok(write!(self.0, "\\u001B[7")?)
} else {
Ok(write!(self.0, "{}", termion::cursor::Save)?)
}
}
pub(crate) fn restore_position(&mut self) -> Result<()> {
if is_terminal_app() {
Ok(write!(self.0, "\\u001B[8")?)
} else {
Ok(write!(self.0, "{}", termion::cursor::Restore)?)
}
}
pub(crate) fn clear_line(&mut self) -> Result<()> {
Ok(write!(self.0, "{}", termion::clear::CurrentLine)?)
}
pub(crate) fn clear_until_newline(&mut self) -> Result<()> {
Ok(write!(self.0, "{}", termion::clear::UntilNewline)?)
}
pub(crate) fn flush(&mut self) -> Result<()> {
Ok(self.0.flush()?)
}
}
impl Debug for Device<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_str("Stdout device with writer")
}
}
pub(crate) fn is_terminal_app() -> bool {
env::var("TERM_PROGRAM").unwrap_or_default() == "Apple_Terminal"
}