use std::{
io::{stdin, stdout, BufRead, Write},
ops::Neg,
};
use colored::Colorize;
use crate::{Node, Runtime};
#[allow(missing_docs)]
pub enum Color {
White,
Black,
Red,
Green,
Blue,
Cyan,
Magenta,
Yellow,
}
impl Neg for Color {
type Output = Self;
fn neg(self) -> Self::Output {
use Color::*;
match self {
White => Black,
Black => White,
Red => Cyan,
Cyan => Red,
Green => Magenta,
Magenta => Green,
Blue => Yellow,
Yellow => Blue,
}
}
}
pub trait Frontend {
fn background_color(&self) -> Color;
fn set_background_color(&mut self, color: Color);
fn clear(&mut self);
fn print_colored<S: ToString>(&mut self, text: S, color: Color);
fn newline(&mut self);
fn input(&mut self) -> String;
fn print<S: ToString>(&mut self, text: S) {
self.print_colored(text, -self.background_color());
}
fn println<S: ToString>(&mut self, text: S) {
self.print(text);
self.newline();
}
}
pub struct CliFrontend {
background_color: colored::Color,
}
impl CliFrontend {
pub fn new() -> Self {
Self::default()
}
pub fn with_bg_color(color: Color) -> Self {
let mut clif = Self::new();
clif.set_background_color(color);
clif
}
}
impl Default for CliFrontend {
fn default() -> Self {
CliFrontend {
background_color: colored::Color::Black,
}
}
}
impl Frontend for CliFrontend {
fn background_color(&self) -> Color {
use Color::*;
match self.background_color {
colored::Color::Black => Black,
colored::Color::White => White,
colored::Color::Red => Red,
colored::Color::Green => Green,
colored::Color::Blue => Blue,
colored::Color::Cyan => Cyan,
colored::Color::Magenta => Magenta,
colored::Color::Yellow => Yellow,
_ => Black,
}
}
fn set_background_color(&mut self, color: Color) {
use Color::*;
self.background_color = match color {
White => colored::Color::Black,
Black => colored::Color::White,
Red => colored::Color::Red,
Green => colored::Color::Green,
Blue => colored::Color::Blue,
Cyan => colored::Color::Cyan,
Magenta => colored::Color::Magenta,
Yellow => colored::Color::Yellow,
};
}
fn clear(&mut self) {
let (_, height) = terminal_size::terminal_size()
.unwrap_or_else(|| panic!("{}", "Unable to get terminal size".bright_red()));
println!("{}", (0..height.0).map(|_| '\n').collect::<String>());
}
fn print_colored<S: ToString>(&mut self, text: S, color: Color) {
use Color::*;
let color = match color {
Black => Colorize::black,
White => Colorize::white,
Red => Colorize::red,
Green => Colorize::green,
Blue => Colorize::blue,
Cyan => Colorize::cyan,
Magenta => Colorize::magenta,
Yellow => Colorize::yellow,
};
print!(
"{}",
color(text.to_string().as_ref()).on_color(self.background_color)
);
let _ = stdout().flush();
}
fn newline(&mut self) {
println!();
}
fn input(&mut self) -> String {
stdin()
.lock()
.lines()
.next()
.and_then(Result::ok)
.unwrap_or_else(|| panic!("{}", "Failed to read line".bright_red()))
}
}
impl<'a, N, F> Frontend for Runtime<'a, N, F>
where
N: Node,
F: Frontend,
{
fn background_color(&self) -> Color {
self.frontend.background_color()
}
fn set_background_color(&mut self, color: Color) {
self.frontend.set_background_color(color)
}
fn clear(&mut self) {
self.frontend.clear()
}
fn print_colored<S: ToString>(&mut self, text: S, color: Color) {
self.frontend.print_colored(text, color)
}
fn newline(&mut self) {
self.frontend.newline()
}
fn input(&mut self) -> String {
self.frontend.input()
}
}