extern crate term;
use std::default::Default;
use std::fmt;
use std::cell::RefCell;
pub trait ToStyle : Sized {
fn to_style(self) -> Style;
fn to_mapped_style<F>(self, func: F) -> Style
where F: FnOnce(&mut Style)
{
let mut s = self.to_style();
func(&mut s);
s
}
fn fg(self, c: Color) -> Style {
self.to_mapped_style(|s| s.fg = c)
}
fn bg(self, c: Color) -> Style {
self.to_mapped_style(|s| s.bg = c)
}
fn bold(self) -> Style {
self.to_mapped_style(|s| s.set_bold(Some(true)))
}
fn dim(self) -> Style {
self.to_mapped_style(|s| s.set_dim(Some(true)))
}
fn underline(self) -> Style {
self.to_mapped_style(|s| s.set_underline(Some(true)))
}
fn not_underline(self) -> Style {
self.to_mapped_style(|s| s.set_underline(Some(false)))
}
fn blink(self) -> Style {
self.to_mapped_style(|s| s.set_blink(Some(true)))
}
fn reverse(self) -> Style {
self.to_mapped_style(|s| s.set_reverse(Some(true)))
}
fn secure(self) -> Style {
self.to_mapped_style(|s| s.set_secure(Some(true)))
}
fn paint<T>(&self, obj: T) -> Painted<T>
where Self: Clone
{
Painted {
style: self.clone().to_style(),
obj: obj,
}
}
fn with<F, R>(&self, f: F) -> R
where F: FnOnce() -> R,
Self: Clone
{
let new = self.clone().to_style();
let before = CURR_STYLE.with(|curr| curr.borrow().clone());
let _ = new.apply();
CURR_STYLE.with(|curr| *curr.borrow_mut() = before.and(new));
let out = f();
let _ = before.revert_to();
CURR_STYLE.with(|curr| *curr.borrow_mut() = before);
out
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Color {
NotSet,
Black,
Red,
Green,
Yellow,
Blue,
Magenta,
Cyan,
White,
BrightBlack,
BrightRed,
BrightGreen,
BrightYellow,
BrightBlue,
BrightMagenta,
BrightCyan,
BrightWhite,
Custom(u32),
}
impl Color {
fn term_constant(&self) -> Option<term::color::Color> {
match *self {
Color::NotSet => None,
Color::Black => Some(term::color::BLACK),
Color::Red => Some(term::color::RED),
Color::Green => Some(term::color::GREEN),
Color::Yellow => Some(term::color::YELLOW),
Color::Blue => Some(term::color::BLUE),
Color::Magenta => Some(term::color::MAGENTA),
Color::Cyan => Some(term::color::CYAN),
Color::White => Some(term::color::WHITE),
Color::BrightBlack => Some(term::color::BRIGHT_BLACK),
Color::BrightRed => Some(term::color::BRIGHT_RED),
Color::BrightGreen => Some(term::color::BRIGHT_GREEN),
Color::BrightYellow => Some(term::color::BRIGHT_YELLOW),
Color::BrightBlue => Some(term::color::BRIGHT_BLUE),
Color::BrightMagenta => Some(term::color::BRIGHT_MAGENTA),
Color::BrightCyan => Some(term::color::BRIGHT_CYAN),
Color::BrightWhite => Some(term::color::BRIGHT_WHITE),
Color::Custom(c) => Some(c)
}
}
}
impl Default for Color {
fn default() -> Self {
Color::NotSet
}
}
impl ToStyle for Color {
fn to_style(self) -> Style {
Style {
fg: self,
.. Style::default()
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Attr {
Plain,
Bold,
Dim,
Underline,
Blink,
Reverse,
Secure,
}
impl ToStyle for Attr {
fn to_style(self) -> Style {
let mut s = Style::default();
match self {
Attr::Plain => {},
Attr::Bold => s.set_bold(Some(true)),
Attr::Dim => s.set_dim(Some(true)),
Attr::Underline => s.set_underline(Some(true)),
Attr::Blink => s.set_blink(Some(true)),
Attr::Reverse => s.set_reverse(Some(true)),
Attr::Secure => s.set_secure(Some(true)),
}
s
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct Style {
pub fg: Color,
pub bg: Color,
bold_dim_underline_blink: u8,
reverse_secure: u8,
}
impl Default for Style {
fn default() -> Self {
Style {
fg: Color::default(),
bg: Color::default(),
bold_dim_underline_blink: 0,
reverse_secure: 0,
}
}
}
thread_local!(
static TERM: RefCell<Option<Box<term::StdoutTerminal>>> = RefCell::new(term::stdout())
);
thread_local!(
static CURR_STYLE: RefCell<Style> = RefCell::new(Style::default())
);
macro_rules! gen_getter {
($getter:ident, $setter:ident, $var:ident, $pos:expr) => {
pub fn $getter(&self) -> Option<bool> {
match (self.$var >> ($pos * 2)) & 0b11 {
0b10 => Some(false),
0b11 => Some(true),
_ => None,
}
}
pub fn $setter(&mut self, v: Option<bool>) {
match v {
None => {
self.$var &= !(0b11 << ($pos*2));
},
Some(false) => {
self.$var &= !(0b01 << ($pos*2));
self.$var |= 0b10 << ($pos*2);
},
Some(true) => {
self.$var |= 0b11 << ($pos*2);
},
}
}
}
}
impl Style {
gen_getter!(get_bold, set_bold, bold_dim_underline_blink, 3);
gen_getter!(get_dim, set_dim, bold_dim_underline_blink, 2);
gen_getter!(get_underline, set_underline, bold_dim_underline_blink, 1);
gen_getter!(get_blink, set_blink, bold_dim_underline_blink, 0);
gen_getter!(get_reverse, set_reverse, reverse_secure, 3);
gen_getter!(get_secure, set_secure, reverse_secure, 2);
fn apply(&self) -> Result<(), fmt::Error> {
macro_rules! try_term {
($e:expr) => { $e.map_err(|_| fmt::Error)? }
}
TERM.with(|term_opt| {
let mut tmut = term_opt.borrow_mut();
let t = match tmut.as_mut() {
None => return Err(fmt::Error),
Some(t) => t,
};
if let Some(c) = self.fg.term_constant() {
try_term!(t.fg(c));
}
if let Some(c) = self.bg.term_constant() {
try_term!(t.bg(c));
}
if let Some(true) = self.get_bold() {
try_term!(t.attr(term::Attr::Bold));
}
if let Some(true) = self.get_dim() {
try_term!(t.attr(term::Attr::Dim));
}
if let Some(u) = self.get_underline() {
try_term!(t.attr(term::Attr::Underline(u)));
}
if let Some(true) = self.get_blink() {
try_term!(t.attr(term::Attr::Blink));
}
if let Some(true) = self.get_reverse() {
try_term!(t.attr(term::Attr::Reverse))
}
if let Some(true) = self.get_secure() {
try_term!(t.attr(term::Attr::Secure))
}
Ok(())
})
}
fn and(&self, o: Style) -> Style {
let ax = self.bold_dim_underline_blink;
let ay = o.bold_dim_underline_blink;
let bx = self.reverse_secure;
let by = o.reverse_secure;
let az = ((ax | ay) & 0b10101010) | (((ay >> 1) & ay | !(ay >> 1) & ax) & 0b01010101);
let bz = ((bx | by) & 0b10101010) | (((by >> 1) & by | !(by >> 1) & bx) & 0b01010101);
Style {
fg: if o.fg == Color::NotSet { self.fg } else { o.fg },
bg: if o.bg == Color::NotSet { self.bg } else { o.bg },
bold_dim_underline_blink: az,
reverse_secure: bz,
}
}
fn revert_to(&self) -> Result<(), fmt::Error> {
TERM.with(|term_opt| {
let mut tmut = term_opt.borrow_mut();
tmut.as_mut()
.and_then(|t| t.reset().ok())
.ok_or(fmt::Error)
})?;
self.apply()
}
}
impl ToStyle for Style {
fn to_style(self) -> Style {
self
}
}
pub struct Painted<T> {
style: Style,
obj: T,
}
macro_rules! impl_format {
($symbol:expr, $fmt:ident) => {
impl<T: fmt::$fmt> fmt::$fmt for Painted<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
self.style.with(|| fmt::$fmt::fmt(&self.obj, f))
}
}
}
}
impl_format!("{}", Display);
impl_format!("{:?}", Debug);
impl_format!("{:o}", Octal);
impl_format!("{:x}", LowerHex);
impl_format!("{:X}", UpperHex);
impl_format!("{:p}", Pointer);
impl_format!("{:b}", Binary);
impl_format!("{:e}", LowerExp);
impl_format!("{:E}", UpperExp);
#[cfg(test)]
mod test {
use super::Color::*;
use super::Attr::*;
use super::{ToStyle, Style};
#[test]
fn modifier_order() {
assert_eq!(Plain.bold().fg(Red), Plain.fg(Red).bold());
assert_eq!(Plain.bold().bg(Red), Plain.bg(Red).bold());
assert_eq!(Plain.underline().fg(Red), Plain.fg(Red).underline());
assert_eq!(Red.to_style(), Plain.fg(Red));
assert_eq!(Bold.to_style(), Plain.bold());
}
#[test]
fn modifier_override() {
assert_eq!(Plain.fg(Red).fg(Blue), Plain.fg(Blue));
assert_eq!(Plain.fg(Red).fg(Blue), Blue.to_style());
assert_eq!(Red.fg(Blue), Plain.fg(Blue));
assert_eq!(Red.fg(Blue), Blue.to_style());
}
#[test]
fn style_and() {
let s1 = Style::default().bold().not_underline();
let s2 = Style::default().underline();
let s3 = Style::default().bold();
let r1 = Style::default().bold().underline();
let r2 = Style::default().bold().not_underline();
assert_eq!(s2.and(s1), r2);
assert_eq!(s2.and(s1).and(s3), r2);
assert_eq!(s2.and(s3), r1);
}
}