use std::collections::HashSet;
use termion;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[allow(missing_docs)]
pub enum Modifier {
Bold,
Underline,
}
impl Modifier {
fn ansi(self) -> String {
match self {
Modifier::Bold => format!("{}", termion::style::Bold),
Modifier::Underline => format!("{}", termion::style::Underline),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[allow(missing_docs)]
pub enum Color {
Reset,
Black,
White,
Red,
Green,
Yellow,
Blue,
Magenta,
Cyan,
LightBlack,
LightWhite,
LightRed,
LightGreen,
LightYellow,
LightBlue,
LightMagenta,
LightCyan,
}
impl Color {
fn termion(self) -> Box<dyn termion::color::Color> {
match self {
Color::Reset => Box::new(termion::color::Reset),
Color::Black => Box::new(termion::color::Black),
Color::White => Box::new(termion::color::White),
Color::Red => Box::new(termion::color::Red),
Color::Green => Box::new(termion::color::Green),
Color::Yellow => Box::new(termion::color::Yellow),
Color::Blue => Box::new(termion::color::Blue),
Color::Magenta => Box::new(termion::color::Magenta),
Color::Cyan => Box::new(termion::color::Cyan),
Color::LightBlack => Box::new(termion::color::LightBlack),
Color::LightWhite => Box::new(termion::color::LightWhite),
Color::LightRed => Box::new(termion::color::LightRed),
Color::LightGreen => Box::new(termion::color::LightGreen),
Color::LightYellow => Box::new(termion::color::LightYellow),
Color::LightBlue => Box::new(termion::color::LightBlue),
Color::LightMagenta => Box::new(termion::color::LightMagenta),
Color::LightCyan => Box::new(termion::color::LightCyan),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Style {
pub foreground: Option<Color>,
pub background: Option<Color>,
pub modifiers: HashSet<Modifier>,
}
impl Default for Style {
fn default() -> Style {
Style {
foreground: None,
background: None,
modifiers: HashSet::new(),
}
}
}
impl Style {
pub fn combine(&self, other: &Style) -> Style {
Style {
foreground: other.foreground.or(self.foreground),
background: other.background.or(self.background),
modifiers: &other.modifiers | &self.modifiers,
}
}
pub fn ansi(&self) -> String {
let mut s = "".to_owned();
if let Some(color) = self.foreground {
s.push_str(&format!("{}", termion::color::Fg(&*color.termion())));
}
if let Some(color) = self.background {
s.push_str(&format!("{}", termion::color::Bg(&*color.termion())));
}
for modifier in &self.modifiers {
s.push_str(&modifier.ansi())
}
s
}
}
#[cfg(feature = "to_tui")]
impl From<Modifier> for tui::style::Modifier {
fn from(modifier: Modifier) -> tui::style::Modifier {
match modifier {
Modifier::Bold => tui::style::Modifier::Bold,
Modifier::Underline => tui::style::Modifier::Underline,
}
}
}
#[cfg(feature = "to_tui")]
impl From<Color> for tui::style::Color {
fn from(color: Color) -> tui::style::Color {
match color {
Color::Reset => tui::style::Color::Reset,
Color::Black => tui::style::Color::Black,
Color::White => tui::style::Color::Gray,
Color::Red => tui::style::Color::Red,
Color::Green => tui::style::Color::Green,
Color::Yellow => tui::style::Color::Yellow,
Color::Blue => tui::style::Color::Blue,
Color::Magenta => tui::style::Color::Magenta,
Color::Cyan => tui::style::Color::Cyan,
Color::LightBlack => tui::style::Color::DarkGray,
Color::LightWhite => tui::style::Color::White,
Color::LightRed => tui::style::Color::LightRed,
Color::LightGreen => tui::style::Color::LightGreen,
Color::LightYellow => tui::style::Color::LightYellow,
Color::LightBlue => tui::style::Color::LightBlue,
Color::LightMagenta => tui::style::Color::LightMagenta,
Color::LightCyan => tui::style::Color::LightCyan,
}
}
}
#[cfg(feature = "to_tui")]
impl From<Style> for tui::style::Style {
fn from(style: Style) -> tui::style::Style {
assert!(
style.modifiers.len() <= 1,
"tui can't stack modifiers, got {:?}",
style.modifiers
);
tui::style::Style {
fg: style.foreground.unwrap_or(Color::Reset).into(),
bg: style.background.unwrap_or(Color::Reset).into(),
modifier: style
.modifiers
.into_iter()
.next()
.map_or(tui::style::Modifier::Reset, |m| m.into()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn color_override() {
let red_foreground = Style {
foreground: Some(Color::Red),
..Style::default()
};
let green_background = Style {
background: Some(Color::Green),
..Style::default()
};
assert_eq!(
red_foreground.combine(&green_background),
Style {
foreground: Some(Color::Red),
background: Some(Color::Green),
..Style::default()
}
);
assert_eq!(
red_foreground.combine(&green_background),
green_background.combine(&red_foreground)
)
}
}