#![crate_name = "ansi_term"]
#![crate_type = "rlib"]
#![crate_type = "dylib"]
#![feature(phase, macro_rules)]
#[phase(plugin)]
extern crate regex_macros;
extern crate regex;
use Colour::{Black, Red, Green, Yellow, Blue, Purple, Cyan, White, Fixed};
use Style::{Plain, Foreground, Styled};
use std::fmt;
pub struct ANSIString<'a> {
string: &'a str,
style: Style,
}
impl<'a> Copy for ANSIString<'a> { }
impl<'a> ANSIString<'a> {
pub fn new(contents: &'a str, style: Style) -> ANSIString<'a> {
ANSIString { string: contents, style: style }
}
}
impl<'a> fmt::Show for ANSIString<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.style {
Plain => write!(f, "{}", self.string),
Foreground(colour) => {
try!(f.write_str("\x1B["));
try!(colour.write_foreground_code(f));
write!(f, "m{}\x1B[0m", self.string)
},
Styled { foreground, background, bold, underline } => {
try!(f.write_str("\x1B["));
if bold {
try!(f.write_str("1;"));
}
if underline {
try!(f.write_str("4;"));
}
match background {
Some(c) => {
try!(c.write_background_code(f));
try!(f.write_str(";"));
},
None => {},
}
try!(foreground.write_foreground_code(f));
write!(f, "m{}\x1B[0m", self.string)
}
}
}
}
pub enum Colour {
Black, Red, Green, Yellow, Blue, Purple, Cyan, White, Fixed(u8),
}
impl Copy for Colour { }
impl Colour {
fn write_foreground_code(self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Black => f.write_str("30"),
Red => f.write_str("31"),
Green => f.write_str("32"),
Yellow => f.write_str("33"),
Blue => f.write_str("34"),
Purple => f.write_str("35"),
Cyan => f.write_str("36"),
White => f.write_str("37"),
Fixed(num) => write!(f, "38;5;{}", num),
}
}
fn write_background_code(self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Black => f.write_str("40"),
Red => f.write_str("41"),
Green => f.write_str("42"),
Yellow => f.write_str("43"),
Blue => f.write_str("44"),
Purple => f.write_str("45"),
Cyan => f.write_str("46"),
White => f.write_str("47"),
Fixed(num) => write!(f, "48;5;{}", num),
}
}
pub fn normal(self) -> Style {
Styled { foreground: self, background: None, bold: false, underline: false }
}
pub fn paint(self, input: &str) -> ANSIString {
ANSIString::new(input, Foreground(self))
}
pub fn underline(self) -> Style {
Styled { foreground: self, background: None, bold: false, underline: true }
}
pub fn bold(self) -> Style {
Styled { foreground: self, background: None, bold: true, underline: false }
}
pub fn on(self, background: Colour) -> Style {
Styled { foreground: self, background: Some(background), bold: false, underline: false }
}
}
pub enum Style {
Plain,
Foreground(Colour),
Styled { foreground: Colour, background: Option<Colour>, bold: bool, underline: bool, },
}
impl Copy for Style { }
impl Style {
pub fn paint(self, input: &str) -> ANSIString {
ANSIString::new(input, self)
}
pub fn bold(self) -> Style {
match self {
Plain => Styled { foreground: White, background: None, bold: true, underline: false },
Foreground(c) => Styled { foreground: c, background: None, bold: true, underline: false },
Styled { foreground, background, bold: _, underline } => Styled { foreground: foreground, background: background, bold: true, underline: underline },
}
}
pub fn underline(self) -> Style {
match self {
Plain => Styled { foreground: White, background: None, bold: false, underline: true },
Foreground(c) => Styled { foreground: c, background: None, bold: false, underline: true },
Styled { foreground, background, bold, underline: _ } => Styled { foreground: foreground, background: background, bold: bold, underline: true },
}
}
pub fn on(self, background: Colour) -> Style {
match self {
Plain => Styled { foreground: White,background: Some(background), bold: false, underline: false },
Foreground(c) => Styled { foreground: c, background: Some(background), bold: false, underline: false },
Styled { foreground, background: _, bold, underline } => Styled { foreground: foreground, background: Some(background), bold: bold, underline: underline },
}
}
}
pub fn strip_formatting(input: &str) -> String {
let re = regex!("\x1B\\[.+?m");
re.replace_all(input, "").to_string()
}
#[cfg(test)]
mod tests {
use super::strip_formatting;
use super::Style::Plain;
use super::Colour::{Black, Red, Green, Yellow, Blue, Purple, Cyan, White, Fixed};
macro_rules! test {
($name: ident: $style: expr $input: expr => $result: expr) => {
#[test]
fn $name() {
assert_eq!($style.paint($input).to_string(), $result.to_string())
}
};
}
test!(plain: Plain "text/plain" => "text/plain");
test!(red: Red "hi" => "\x1B[31mhi\x1B[0m");
test!(black: Black.normal() "hi" => "\x1B[30mhi\x1B[0m");
test!(yellow_bold: Yellow.bold() "hi" => "\x1B[1;33mhi\x1B[0m");
test!(yellow_bold_2: Yellow.normal().bold() "hi" => "\x1B[1;33mhi\x1B[0m");
test!(blue_underline: Blue.underline() "hi" => "\x1B[4;34mhi\x1B[0m");
test!(green_bold_ul: Green.bold().underline() "hi" => "\x1B[1;4;32mhi\x1B[0m");
test!(green_bold_ul_2: Green.underline().bold() "hi" => "\x1B[1;4;32mhi\x1B[0m");
test!(purple_on_white: Purple.on(White) "hi" => "\x1B[47;35mhi\x1B[0m");
test!(purple_on_white_2: Purple.normal().on(White) "hi" => "\x1B[47;35mhi\x1B[0m");
test!(cyan_bold_on_white: Cyan.bold().on(White) "hi" => "\x1B[1;47;36mhi\x1B[0m");
test!(cyan_ul_on_white: Cyan.underline().on(White) "hi" => "\x1B[4;47;36mhi\x1B[0m");
test!(cyan_bold_ul_on_white: Cyan.bold().underline().on(White) "hi" => "\x1B[1;4;47;36mhi\x1B[0m");
test!(cyan_ul_bold_on_white: Cyan.underline().bold().on(White) "hi" => "\x1B[1;4;47;36mhi\x1B[0m");
test!(fixed: Fixed(100) "hi" => "\x1B[38;5;100mhi\x1B[0m");
test!(fixed_on_purple: Fixed(100).on(Purple) "hi" => "\x1B[45;38;5;100mhi\x1B[0m");
test!(fixed_on_fixed: Fixed(100).on(Fixed(200)) "hi" => "\x1B[48;5;200;38;5;100mhi\x1B[0m");
#[test]
fn test_strip_formatting() {
let hi = strip_formatting(Blue.paint("hi").to_string().as_slice());
assert!(hi == "hi".to_string());
}
#[test]
fn test_strip_formatting_2() {
let hi = strip_formatting(Blue.on(Fixed(230)).bold().paint("hi").to_string().as_slice());
assert!(hi == "hi".to_string());
}
}