#![crate_name = "ansi_term"]
#![crate_type = "rlib"]
#![crate_type = "dylib"]
#![warn(missing_copy_implementations)]
#![warn(missing_docs)]
#![warn(trivial_casts, trivial_numeric_casts)]
#![warn(unused_extern_crates, unused_qualifications)]
use std::borrow::Cow;
use std::default::Default;
use std::fmt;
use std::io;
use std::ops::Deref;
use Colour::*;
use Difference::*;
trait AnyWrite {
type str : ?Sized;
type Error;
fn write_fmt(&mut self, fmt: fmt::Arguments) -> Result<(), Self::Error>;
fn write_str(&mut self, s: &Self::str) -> Result<(), Self::Error>;
}
impl<'a> AnyWrite for fmt::Write + 'a {
type str = str;
type Error = fmt::Error;
fn write_fmt(&mut self, fmt: fmt::Arguments) -> Result<(), Self::Error> {
fmt::Write::write_fmt(self, fmt)
}
fn write_str(&mut self, s: &Self::str) -> Result<(), Self::Error> {
fmt::Write::write_str(self, s)
}
}
impl<'a> AnyWrite for io::Write + 'a {
type str = [u8];
type Error = io::Error;
fn write_fmt(&mut self, fmt: fmt::Arguments) -> Result<(), Self::Error> {
io::Write::write_fmt(self, fmt)
}
fn write_str(&mut self, s: &Self::str) -> Result<(), Self::Error> {
io::Write::write_all(self, s)
}
}
#[derive(PartialEq, Debug, Clone)]
pub struct ANSIGenericString<'a, S: 'a + ToOwned + ?Sized>
where <S as ToOwned>::Owned: std::fmt::Debug {
style: Style,
string: Cow<'a, S>,
}
impl<'a, S: 'a + ToOwned + ?Sized> ANSIGenericString<'a, S>
where <S as ToOwned>::Owned: std::fmt::Debug {
fn write_to_any<W: AnyWrite<str=S> + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> {
try!(self.style.write_prefix(w));
try!(w.write_str(&self.string));
self.style.write_suffix(w)
}
}
pub type ANSIString<'a> = ANSIGenericString<'a, str>;
pub type ANSIByteString<'a> = ANSIGenericString<'a, [u8]>;
#[derive(Clone, Copy, Debug)]
pub struct Prefix(Style);
#[derive(Clone, Copy, Debug)]
pub struct Suffix(Style);
#[derive(Clone, Copy, Debug)]
pub struct Infix(Style, Style);
impl<'a> fmt::Display for ANSIString<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let w: &mut fmt::Write = f;
self.write_to_any(w)
}
}
impl<'a> ANSIByteString<'a> {
pub fn write_to<W: io::Write>(&self, w: &mut W) -> io::Result<()> {
let w: &mut io::Write = w;
self.write_to_any(w)
}
}
impl<'a, I, S: 'a + ToOwned + ?Sized> From<I> for ANSIGenericString<'a, S>
where I: Into<Cow<'a, S>>,
<S as ToOwned>::Owned: std::fmt::Debug {
fn from(input: I) -> ANSIGenericString<'a, S> {
ANSIGenericString {
string: input.into(),
style: Style::default(),
}
}
}
impl<'a, S: 'a + ToOwned + ?Sized> Deref for ANSIGenericString<'a, S>
where <S as ToOwned>::Owned: std::fmt::Debug {
type Target = S;
fn deref(&self) -> &S {
self.string.deref()
}
}
impl fmt::Display for Prefix {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let f: &mut fmt::Write = f;
try!(self.0.write_prefix(f));
Ok(())
}
}
impl fmt::Display for Suffix {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let f: &mut fmt::Write = f;
try!(self.0.write_suffix(f));
Ok(())
}
}
impl fmt::Display for Infix {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.0.difference(&self.1) {
ExtraStyles(style) => {
let f: &mut fmt::Write = f;
try!(style.write_prefix(f))
},
Reset => {
let f: &mut fmt::Write = f;
try!(f.write_str("\x1B[0m"));
try!(self.0.write_prefix(f));
},
NoDifference => { },
}
Ok(())
}
}
#[derive(PartialEq, Clone, Copy, Debug)]
pub enum Colour {
Black,
Red,
Green,
Yellow,
Blue,
Purple,
Cyan,
White,
Fixed(u8),
RGB(u8, u8, u8),
}
pub use Colour as Color;
impl Colour {
fn write_foreground_code<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> {
match *self {
Black => write!(f, "30"),
Red => write!(f, "31"),
Green => write!(f, "32"),
Yellow => write!(f, "33"),
Blue => write!(f, "34"),
Purple => write!(f, "35"),
Cyan => write!(f, "36"),
White => write!(f, "37"),
Fixed(num) => write!(f, "38;5;{}", &num),
RGB(r,g,b) => write!(f, "38;2;{};{};{}", &r, &g, &b),
}
}
fn write_background_code<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> {
match *self {
Black => write!(f, "40"),
Red => write!(f, "41"),
Green => write!(f, "42"),
Yellow => write!(f, "43"),
Blue => write!(f, "44"),
Purple => write!(f, "45"),
Cyan => write!(f, "46"),
White => write!(f, "47"),
Fixed(num) => write!(f, "48;5;{}", &num),
RGB(r,g,b) => write!(f, "48;2;{};{};{}", &r, &g, &b),
}
}
pub fn normal(self) -> Style {
Style { foreground: Some(self), .. Style::default() }
}
pub fn paint<'a, I, S: 'a + ToOwned + ?Sized>(self, input: I) -> ANSIGenericString<'a, S>
where I: Into<Cow<'a, S>>,
<S as ToOwned>::Owned: std::fmt::Debug {
ANSIGenericString {
string: input.into(),
style: self.normal(),
}
}
pub fn prefix(self) -> Prefix {
Prefix(self.normal())
}
pub fn suffix(self) -> Suffix {
Suffix(self.normal())
}
pub fn infix(self, other: Colour) -> Infix {
Infix(self.normal(), other.normal())
}
pub fn bold(self) -> Style {
Style { foreground: Some(self), is_bold: true, .. Style::default() }
}
pub fn dimmed(self) -> Style {
Style { foreground: Some(self), is_dimmed: true, .. Style::default() }
}
pub fn italic(self) -> Style {
Style { foreground: Some(self), is_italic: true, .. Style::default() }
}
pub fn underline(self) -> Style {
Style { foreground: Some(self), is_underline: true, .. Style::default() }
}
pub fn blink(self) -> Style {
Style { foreground: Some(self), is_blink: true, .. Style::default() }
}
pub fn reverse(self) -> Style {
Style { foreground: Some(self), is_reverse: true, .. Style::default() }
}
pub fn hidden(self) -> Style {
Style { foreground: Some(self), is_hidden: true, .. Style::default() }
}
pub fn strikethrough(self) -> Style {
Style { foreground: Some(self), is_strikethrough: true, .. Style::default() }
}
pub fn on(self, background: Colour) -> Style {
Style { foreground: Some(self), background: Some(background), .. Style::default() }
}
}
#[derive(PartialEq, Clone, Copy, Debug)]
pub struct Style {
foreground: Option<Colour>,
background: Option<Colour>,
is_bold: bool,
is_dimmed: bool,
is_italic: bool,
is_underline: bool,
is_blink: bool,
is_reverse: bool,
is_hidden: bool,
is_strikethrough: bool
}
impl Style {
pub fn new() -> Style {
Style::default()
}
pub fn paint<'a, I, S: 'a + ToOwned + ?Sized>(self, input: I) -> ANSIGenericString<'a, S>
where I: Into<Cow<'a, S>>,
<S as ToOwned>::Owned: std::fmt::Debug {
ANSIGenericString {
string: input.into(),
style: self,
}
}
pub fn prefix(self) -> Prefix {
Prefix(self)
}
pub fn suffix(self) -> Suffix {
Suffix(self)
}
pub fn infix(self, other: Style) -> Infix {
Infix(self, other)
}
pub fn bold(&self) -> Style {
Style { is_bold: true, .. *self }
}
pub fn dimmed(&self) -> Style {
Style { is_dimmed: true, .. *self }
}
pub fn italic(&self) -> Style {
Style { is_italic: true, .. *self }
}
pub fn underline(&self) -> Style {
Style { is_underline: true, .. *self }
}
pub fn blink(&self) -> Style {
Style { is_blink: true, .. *self }
}
pub fn reverse(&self) -> Style {
Style { is_reverse: true, .. *self }
}
pub fn hidden(&self) -> Style {
Style { is_hidden: true, .. *self }
}
pub fn strikethrough(&self) -> Style {
Style { is_strikethrough: true, .. *self }
}
pub fn fg(&self, foreground: Colour) -> Style {
Style { foreground: Some(foreground), .. *self }
}
pub fn on(&self, background: Colour) -> Style {
Style { background: Some(background), .. *self }
}
fn write_prefix<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> {
if self.is_plain() {
return Ok(());
}
try!(write!(f, "\x1B["));
let mut written_anything = false;
{
let mut write_char = |c| {
if written_anything { try!(write!(f, ";")); }
written_anything = true;
try!(write!(f, "{}", c));
Ok(())
};
if self.is_bold { try!(write_char('1')); }
if self.is_dimmed { try!(write_char('2')); }
if self.is_italic { try!(write_char('3')); }
if self.is_underline { try!(write_char('4')); }
if self.is_blink { try!(write_char('5')); }
if self.is_reverse { try!(write_char('7')); }
if self.is_hidden { try!(write_char('8')); }
if self.is_strikethrough { try!(write_char('9')); }
}
if let Some(bg) = self.background {
if written_anything { try!(write!(f, ";")); }
written_anything = true;
try!(bg.write_background_code(f));
}
if let Some(fg) = self.foreground {
if written_anything { try!(write!(f, ";")); }
try!(fg.write_foreground_code(f));
}
try!(write!(f, "m"));
Ok(())
}
fn write_suffix<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> {
if self.is_plain() {
Ok(())
}
else {
write!(f, "\x1B[0m")
}
}
fn difference(&self, next: &Style) -> Difference {
if self == next {
return NoDifference;
}
if self.is_bold && !next.is_bold {
return Reset;
}
if self.is_dimmed && !next.is_dimmed {
return Reset;
}
if self.is_italic && !next.is_italic {
return Reset;
}
if self.is_underline && !next.is_underline {
return Reset;
}
if self.is_blink && !next.is_blink {
return Reset;
}
if self.is_reverse && !next.is_reverse {
return Reset;
}
if self.is_hidden && !next.is_hidden {
return Reset;
}
if self.is_strikethrough && !next.is_strikethrough {
return Reset;
}
if self.foreground.is_some() && next.foreground.is_none() {
return Reset;
}
if self.background.is_some() && next.background.is_none() {
return Reset;
}
let mut extra_styles = Style::default();
if self.is_bold != next.is_bold {
extra_styles.is_bold = true;
}
if self.is_dimmed != next.is_dimmed {
extra_styles.is_dimmed = true;
}
if self.is_italic != next.is_italic {
extra_styles.is_italic = true;
}
if self.is_underline != next.is_underline {
extra_styles.is_underline = true;
}
if self.is_blink != next.is_blink {
extra_styles.is_blink = true;
}
if self.is_reverse != next.is_reverse {
extra_styles.is_reverse = true;
}
if self.is_hidden != next.is_hidden {
extra_styles.is_hidden = true;
}
if self.is_strikethrough != next.is_strikethrough {
extra_styles.is_strikethrough = true;
}
if self.foreground != next.foreground {
extra_styles.foreground = next.foreground;
}
if self.background != next.background {
extra_styles.background = next.background;
}
ExtraStyles(extra_styles)
}
fn is_plain(self) -> bool {
self == Style::default()
}
}
impl Default for Style {
fn default() -> Style {
Style {
foreground: None,
background: None,
is_bold: false,
is_dimmed: false,
is_italic: false,
is_underline: false,
is_blink: false,
is_reverse: false,
is_hidden: false,
is_strikethrough: false,
}
}
}
#[derive(PartialEq, Clone, Copy, Debug)]
enum Difference {
ExtraStyles(Style),
Reset,
NoDifference,
}
pub struct ANSIGenericStrings<'a, S: 'a + ToOwned + ?Sized>
(pub &'a [ANSIGenericString<'a, S>])
where <S as ToOwned>::Owned: std::fmt::Debug;
pub type ANSIStrings<'a> = ANSIGenericStrings<'a, str>;
#[allow(non_snake_case)]
pub fn ANSIStrings<'a>(arg: &'a [ANSIString<'a>]) -> ANSIStrings<'a> {
ANSIGenericStrings(arg)
}
pub type ANSIByteStrings<'a> = ANSIGenericStrings<'a, [u8]>;
#[allow(non_snake_case)]
pub fn ANSIByteStrings<'a>(arg: &'a [ANSIByteString<'a>]) -> ANSIByteStrings<'a> {
ANSIGenericStrings(arg)
}
impl<'a, S: 'a + ToOwned + ?Sized> ANSIGenericStrings<'a, S>
where <S as ToOwned>::Owned: std::fmt::Debug {
fn write_to_any<W: AnyWrite<str=S> + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> {
let first = match self.0.first() {
None => return Ok(()),
Some(f) => f,
};
try!(first.style.write_prefix(w));
try!(w.write_str(&first.string));
for window in self.0.windows(2) {
match window[0].style.difference(&window[1].style) {
ExtraStyles(style) => try!(style.write_prefix(w)),
Reset => {
try!(write!(w, "\x1B[0m"));
try!(window[1].style.write_prefix(w));
},
NoDifference => { },
}
try!(w.write_str(&window[1].string));
}
if let Some(last) = self.0.last() {
if !last.style.is_plain() {
try!(write!(w, "\x1B[0m"));
}
}
Ok(())
}
}
impl<'a> fmt::Display for ANSIStrings<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let f: &mut fmt::Write = f;
self.write_to_any(f)
}
}
impl<'a> ANSIByteStrings<'a> {
pub fn write_to<W: io::Write>(&self, w: &mut W) -> io::Result<()> {
let w: &mut io::Write = w;
self.write_to_any(w)
}
}
#[cfg(test)]
mod tests {
pub use super::{Style, ANSIStrings};
pub use super::Colour::*;
macro_rules! test {
($name: ident: $style: expr; $input: expr => $result: expr) => {
#[test]
fn $name() {
assert_eq!($style.paint($input).to_string(), $result.to_string());
let mut v = Vec::new();
$style.paint($input.as_bytes()).write_to(&mut v).unwrap();
assert_eq!(v.as_slice(), $result.as_bytes());
}
};
}
test!(plain: Style::default(); "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!(yellow_on_blue: Style::new().on(Blue).fg(Yellow); "hi" => "\x1B[44;33mhi\x1B[0m");
test!(yellow_on_blue_2: Cyan.on(Blue).fg(Yellow); "hi" => "\x1B[44;33mhi\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!(rgb: RGB(70,130,180); "hi" => "\x1B[38;2;70;130;180mhi\x1B[0m");
test!(rgb_on_blue: RGB(70,130,180).on(Blue); "hi" => "\x1B[44;38;2;70;130;180mhi\x1B[0m");
test!(blue_on_rgb: Blue.on(RGB(70,130,180)); "hi" => "\x1B[48;2;70;130;180;34mhi\x1B[0m");
test!(rgb_on_rgb: RGB(70,130,180).on(RGB(5,10,15)); "hi" => "\x1B[48;2;5;10;15;38;2;70;130;180mhi\x1B[0m");
test!(bold: Style::new().bold(); "hi" => "\x1B[1mhi\x1B[0m");
test!(underline: Style::new().underline(); "hi" => "\x1B[4mhi\x1B[0m");
test!(bunderline: Style::new().bold().underline(); "hi" => "\x1B[1;4mhi\x1B[0m");
test!(dimmed: Style::new().dimmed(); "hi" => "\x1B[2mhi\x1B[0m");
test!(italic: Style::new().italic(); "hi" => "\x1B[3mhi\x1B[0m");
test!(blink: Style::new().blink(); "hi" => "\x1B[5mhi\x1B[0m");
test!(reverse: Style::new().reverse(); "hi" => "\x1B[7mhi\x1B[0m");
test!(hidden: Style::new().hidden(); "hi" => "\x1B[8mhi\x1B[0m");
test!(stricken: Style::new().strikethrough(); "hi" => "\x1B[9mhi\x1B[0m");
mod difference {
use super::*;
use super::super::Difference::*;
#[test]
fn diff() {
let expected = ExtraStyles(Style::new().bold());
let got = Green.normal().difference(&Green.bold());
assert_eq!(expected, got)
}
#[test]
fn dlb() {
let got = Green.bold().difference(&Green.normal());
assert_eq!(Reset, got)
}
#[test]
fn nothing() {
assert_eq!(NoDifference, Green.bold().difference(&Green.bold()));
}
#[test]
fn nothing_2() {
assert_eq!(NoDifference, Green.normal().difference(&Green.normal()));
}
#[test]
fn colour_change() {
assert_eq!(ExtraStyles(Blue.normal()), Red.normal().difference(&Blue.normal()))
}
#[test]
fn removal_of_dimmed() {
let dimmed = Style::new().dimmed();
let normal = Style::default();
assert_eq!(Reset, dimmed.difference(&normal));
}
#[test]
fn addition_of_dimmed() {
let dimmed = Style::new().dimmed();
let normal = Style::default();
let extra_styles = ExtraStyles(dimmed);
assert_eq!(extra_styles, normal.difference(&dimmed));
}
#[test]
fn removal_of_blink() {
let blink = Style::new().blink();
let normal = Style::default();
assert_eq!(Reset, blink.difference(&normal));
}
#[test]
fn addition_of_blink() {
let blink = Style::new().blink();
let normal = Style::default();
let extra_styles = ExtraStyles(blink);
assert_eq!(extra_styles, normal.difference(&blink));
}
#[test]
fn removal_of_reverse() {
let reverse = Style::new().reverse();
let normal = Style::default();
assert_eq!(Reset, reverse.difference(&normal));
}
#[test]
fn addition_of_reverse() {
let reverse = Style::new().reverse();
let normal = Style::default();
let extra_styles = ExtraStyles(reverse);
assert_eq!(extra_styles, normal.difference(&reverse));
}
#[test]
fn removal_of_hidden() {
let hidden = Style::new().hidden();
let normal = Style::default();
assert_eq!(Reset, hidden.difference(&normal));
}
#[test]
fn addition_of_hidden() {
let hidden = Style::new().hidden();
let normal = Style::default();
let extra_styles = ExtraStyles(hidden);
assert_eq!(extra_styles, normal.difference(&hidden));
}
#[test]
fn removal_of_strikethrough() {
let strikethrough = Style::new().strikethrough();
let normal = Style::default();
assert_eq!(Reset, strikethrough.difference(&normal));
}
#[test]
fn addition_of_strikethrough() {
let strikethrough = Style::new().strikethrough();
let normal = Style::default();
let extra_styles = ExtraStyles(strikethrough);
assert_eq!(extra_styles, normal.difference(&strikethrough));
}
#[test]
fn no_control_codes_for_plain() {
let one = Style::default().paint("one");
let two = Style::default().paint("two");
let output = format!("{}", ANSIStrings( &[ one, two ] ));
assert_eq!(&*output, "onetwo");
}
}
}