use std::io::Write;
use termion;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[allow(missing_docs)]
pub struct TextFormat {
pub bold: bool,
pub italic: bool,
pub invert: bool,
pub underline: bool,
#[doc(hidden)]
_do_not_construct: (),
}
impl TextFormat {
fn set_terminal_attributes<W: Write>(self, terminal: &mut W) {
if self.bold {
write!(terminal, "{}", termion::style::Bold).expect("set bold style");
}
if self.italic {
write!(terminal, "{}", termion::style::Italic).expect("set italic style");
}
if self.invert {
write!(terminal, "{}", termion::style::Invert).expect("set invert style");
}
if self.underline {
write!(terminal, "{}", termion::style::Underline).expect("set underline style");
}
}
}
impl Default for TextFormat {
fn default() -> Self {
TextFormat {
bold: false,
italic: false,
invert: false,
underline: false,
_do_not_construct: (),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[allow(missing_docs)]
pub enum BoolModifyMode {
True,
False,
Toggle,
LeaveUnchanged,
}
impl BoolModifyMode {
pub fn on_top_of(self, other: Self) -> Self {
match (self, other) {
(BoolModifyMode::True, _) => BoolModifyMode::True,
(BoolModifyMode::False, _) => BoolModifyMode::False,
(BoolModifyMode::Toggle, BoolModifyMode::True) => BoolModifyMode::False,
(BoolModifyMode::Toggle, BoolModifyMode::False) => BoolModifyMode::True,
(BoolModifyMode::Toggle, BoolModifyMode::Toggle) => BoolModifyMode::LeaveUnchanged,
(BoolModifyMode::Toggle, BoolModifyMode::LeaveUnchanged) => BoolModifyMode::Toggle,
(BoolModifyMode::LeaveUnchanged, m) => m,
}
}
pub fn modify(self, target: &mut bool) {
match self {
BoolModifyMode::True => *target = true,
BoolModifyMode::False => *target = false,
BoolModifyMode::Toggle => *target ^= true,
BoolModifyMode::LeaveUnchanged => {}
}
}
}
impl ::std::convert::From<bool> for BoolModifyMode {
fn from(on: bool) -> Self {
if on {
BoolModifyMode::True
} else {
BoolModifyMode::False
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[allow(missing_docs)]
pub struct TextFormatModifier {
pub bold: BoolModifyMode,
pub italic: BoolModifyMode,
pub invert: BoolModifyMode,
pub underline: BoolModifyMode,
#[doc(hidden)]
_do_not_construct: (),
}
impl TextFormatModifier {
pub fn new() -> Self {
TextFormatModifier {
bold: BoolModifyMode::LeaveUnchanged,
italic: BoolModifyMode::LeaveUnchanged,
invert: BoolModifyMode::LeaveUnchanged,
underline: BoolModifyMode::LeaveUnchanged,
_do_not_construct: (),
}
}
pub fn bold<M: Into<BoolModifyMode>>(mut self, val: M) -> Self {
self.bold = val.into();
self
}
pub fn italic<M: Into<BoolModifyMode>>(mut self, val: M) -> Self {
self.italic = val.into();
self
}
pub fn invert<M: Into<BoolModifyMode>>(mut self, val: M) -> Self {
self.invert = val.into();
self
}
pub fn underline<M: Into<BoolModifyMode>>(mut self, val: M) -> Self {
self.underline = val.into();
self
}
pub fn on_top_of(self, other: TextFormatModifier) -> Self {
TextFormatModifier {
bold: self.bold.on_top_of(other.bold),
italic: self.italic.on_top_of(other.italic),
invert: self.invert.on_top_of(other.invert),
underline: self.underline.on_top_of(other.underline),
_do_not_construct: (),
}
}
pub fn modify(self, format: &mut TextFormat) {
self.bold.modify(&mut format.bold);
self.italic.modify(&mut format.italic);
self.invert.modify(&mut format.invert);
self.underline.modify(&mut format.underline);
}
}
impl Default for TextFormatModifier {
fn default() -> Self {
TextFormatModifier::new()
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[allow(missing_docs)]
pub enum Color {
Default,
Rgb { r: u8, g: u8, b: u8 },
Ansi(u8),
Black,
Blue,
Cyan,
Green,
Magenta,
Red,
White,
Yellow,
LightBlack,
LightBlue,
LightCyan,
LightGreen,
LightMagenta,
LightRed,
LightWhite,
LightYellow,
}
impl Default for Color {
fn default() -> Self {
Color::Default
}
}
impl Color {
pub fn ansi_rgb(r: u8, g: u8, b: u8) -> Self {
assert!(r < 6, "Invalid red value");
assert!(g < 6, "Invalid green value");
assert!(b < 6, "Invalid blue value");
Color::Ansi(termion::color::AnsiValue::rgb(r, g, b).0)
}
pub fn ansi_grayscale(v: u8 ) -> Self {
assert!(v < 24, "Invalid gray value");
Color::Ansi(termion::color::AnsiValue::grayscale(v).0)
}
fn set_terminal_attributes_fg<W: Write>(self, terminal: &mut W) -> ::std::io::Result<()> {
use termion::color::Fg as Target;
match self {
Color::Default => Ok(()),
Color::Rgb { r, g, b } => write!(terminal, "{}", Target(termion::color::Rgb(r, g, b))),
Color::Ansi(v) => write!(terminal, "{}", Target(termion::color::AnsiValue(v))),
Color::Black => write!(terminal, "{}", Target(termion::color::Black)),
Color::Blue => write!(terminal, "{}", Target(termion::color::Blue)),
Color::Cyan => write!(terminal, "{}", Target(termion::color::Cyan)),
Color::Magenta => write!(terminal, "{}", Target(termion::color::Magenta)),
Color::Green => write!(terminal, "{}", Target(termion::color::Green)),
Color::Red => write!(terminal, "{}", Target(termion::color::Red)),
Color::White => write!(terminal, "{}", Target(termion::color::White)),
Color::Yellow => write!(terminal, "{}", Target(termion::color::Yellow)),
Color::LightBlack => write!(terminal, "{}", Target(termion::color::LightBlack)),
Color::LightBlue => write!(terminal, "{}", Target(termion::color::LightBlue)),
Color::LightCyan => write!(terminal, "{}", Target(termion::color::LightCyan)),
Color::LightMagenta => write!(terminal, "{}", Target(termion::color::LightMagenta)),
Color::LightGreen => write!(terminal, "{}", Target(termion::color::LightGreen)),
Color::LightRed => write!(terminal, "{}", Target(termion::color::LightRed)),
Color::LightWhite => write!(terminal, "{}", Target(termion::color::LightWhite)),
Color::LightYellow => write!(terminal, "{}", Target(termion::color::LightYellow)),
}
}
fn set_terminal_attributes_bg<W: Write>(self, terminal: &mut W) -> ::std::io::Result<()> {
use termion::color::Bg as Target;
match self {
Color::Default => Ok(()),
Color::Rgb { r, g, b } => write!(terminal, "{}", Target(termion::color::Rgb(r, g, b))),
Color::Ansi(v) => write!(terminal, "{}", Target(termion::color::AnsiValue(v))),
Color::Black => write!(terminal, "{}", Target(termion::color::Black)),
Color::Blue => write!(terminal, "{}", Target(termion::color::Blue)),
Color::Cyan => write!(terminal, "{}", Target(termion::color::Cyan)),
Color::Magenta => write!(terminal, "{}", Target(termion::color::Magenta)),
Color::Green => write!(terminal, "{}", Target(termion::color::Green)),
Color::Red => write!(terminal, "{}", Target(termion::color::Red)),
Color::White => write!(terminal, "{}", Target(termion::color::White)),
Color::Yellow => write!(terminal, "{}", Target(termion::color::Yellow)),
Color::LightBlack => write!(terminal, "{}", Target(termion::color::LightBlack)),
Color::LightBlue => write!(terminal, "{}", Target(termion::color::LightBlue)),
Color::LightCyan => write!(terminal, "{}", Target(termion::color::LightCyan)),
Color::LightMagenta => write!(terminal, "{}", Target(termion::color::LightMagenta)),
Color::LightGreen => write!(terminal, "{}", Target(termion::color::LightGreen)),
Color::LightRed => write!(terminal, "{}", Target(termion::color::LightRed)),
Color::LightWhite => write!(terminal, "{}", Target(termion::color::LightWhite)),
Color::LightYellow => write!(terminal, "{}", Target(termion::color::LightYellow)),
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
pub struct Style {
fg_color: Color,
bg_color: Color,
format: TextFormat,
}
impl Style {
pub fn plain() -> Self {
Self::default()
}
pub fn format(&self) -> TextFormat {
self.format
}
pub(crate) fn set_terminal_attributes<W: Write>(self, terminal: &mut W) {
write!(
terminal,
"{}{}{}",
termion::style::Reset,
termion::color::Fg(termion::color::Reset),
termion::color::Bg(termion::color::Reset)
)
.expect("reset style");
self.fg_color
.set_terminal_attributes_fg(terminal)
.expect("write fg_color");
self.bg_color
.set_terminal_attributes_bg(terminal)
.expect("write bg_color");
self.format.set_terminal_attributes(terminal);
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
pub struct StyleModifier {
fg_color: Option<Color>,
bg_color: Option<Color>,
format: TextFormatModifier,
}
impl StyleModifier {
pub fn new() -> Self {
Self::default()
}
pub fn fg_color(mut self, fg_color: Color) -> Self {
self.fg_color = Some(fg_color);
self
}
pub fn bg_color(mut self, bg_color: Color) -> Self {
self.bg_color = Some(bg_color);
self
}
pub fn format(mut self, format: TextFormatModifier) -> Self {
self.format = format;
self
}
pub fn bold<M: Into<BoolModifyMode>>(mut self, val: M) -> Self {
self.format.bold = val.into();
self
}
pub fn italic<M: Into<BoolModifyMode>>(mut self, val: M) -> Self {
self.format.italic = val.into();
self
}
pub fn invert<M: Into<BoolModifyMode>>(mut self, val: M) -> Self {
self.format.invert = val.into();
self
}
pub fn underline<M: Into<BoolModifyMode>>(mut self, val: M) -> Self {
self.format.underline = val.into();
self
}
pub fn on_top_of(self, other: StyleModifier) -> Self {
StyleModifier {
fg_color: self.fg_color.or(other.fg_color),
bg_color: self.bg_color.or(other.bg_color),
format: self.format.on_top_of(other.format),
}
}
pub fn apply_to_default(self) -> Style {
let mut style = Style::default();
self.modify(&mut style);
style
}
pub fn apply(self, mut style: Style) -> Style {
self.modify(&mut style);
style
}
pub fn modify(self, style: &mut Style) {
if let Some(fg) = self.fg_color {
style.fg_color = fg;
}
if let Some(bg) = self.bg_color {
style.bg_color = bg;
}
self.format.modify(&mut style.format);
}
}