1 use style::{Colour, Style}; 2 3 use std::fmt; 4 5 use write::AnyWrite; 6 7 8 // ---- generating ANSI codes ---- 9 10 impl Style { 11 12 /// Write any ANSI codes that go *before* a piece of text. These should be 13 /// the codes to set the terminal to a different colour or font style. write_prefix<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error>14 fn write_prefix<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> { 15 16 // If there are actually no styles here, then don’t write *any* codes 17 // as the prefix. An empty ANSI code may not affect the terminal 18 // output at all, but a user may just want a code-free string. 19 if self.is_plain() { 20 return Ok(()); 21 } 22 23 // Write the codes’ prefix, then write numbers, separated by 24 // semicolons, for each text style we want to apply. 25 write!(f, "\x1B[")?; 26 let mut written_anything = false; 27 28 { 29 let mut write_char = |c| { 30 if written_anything { write!(f, ";")?; } 31 written_anything = true; 32 write!(f, "{}", c)?; 33 Ok(()) 34 }; 35 36 if self.is_bold { write_char('1')? } 37 if self.is_dimmed { write_char('2')? } 38 if self.is_italic { write_char('3')? } 39 if self.is_underline { write_char('4')? } 40 if self.is_blink { write_char('5')? } 41 if self.is_reverse { write_char('7')? } 42 if self.is_hidden { write_char('8')? } 43 if self.is_strikethrough { write_char('9')? } 44 } 45 46 // The foreground and background colours, if specified, need to be 47 // handled specially because the number codes are more complicated. 48 // (see `write_background_code` and `write_foreground_code`) 49 if let Some(bg) = self.background { 50 if written_anything { write!(f, ";")?; } 51 written_anything = true; 52 bg.write_background_code(f)?; 53 } 54 55 if let Some(fg) = self.foreground { 56 if written_anything { write!(f, ";")?; } 57 fg.write_foreground_code(f)?; 58 } 59 60 // All the codes end with an `m`, because reasons. 61 write!(f, "m")?; 62 63 Ok(()) 64 } 65 66 /// Write any ANSI codes that go *after* a piece of text. These should be 67 /// the codes to *reset* the terminal back to its normal colour and style. write_suffix<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error>68 fn write_suffix<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> { 69 if self.is_plain() { 70 Ok(()) 71 } 72 else { 73 write!(f, "{}", RESET) 74 } 75 } 76 } 77 78 79 /// The code to send to reset all styles and return to `Style::default()`. 80 pub static RESET: &str = "\x1B[0m"; 81 82 83 84 impl Colour { write_foreground_code<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error>85 fn write_foreground_code<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> { 86 match *self { 87 Colour::Black => write!(f, "30"), 88 Colour::Red => write!(f, "31"), 89 Colour::Green => write!(f, "32"), 90 Colour::Yellow => write!(f, "33"), 91 Colour::Blue => write!(f, "34"), 92 Colour::Purple => write!(f, "35"), 93 Colour::Cyan => write!(f, "36"), 94 Colour::White => write!(f, "37"), 95 Colour::Fixed(num) => write!(f, "38;5;{}", &num), 96 Colour::RGB(r,g,b) => write!(f, "38;2;{};{};{}", &r, &g, &b), 97 } 98 } 99 write_background_code<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error>100 fn write_background_code<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> { 101 match *self { 102 Colour::Black => write!(f, "40"), 103 Colour::Red => write!(f, "41"), 104 Colour::Green => write!(f, "42"), 105 Colour::Yellow => write!(f, "43"), 106 Colour::Blue => write!(f, "44"), 107 Colour::Purple => write!(f, "45"), 108 Colour::Cyan => write!(f, "46"), 109 Colour::White => write!(f, "47"), 110 Colour::Fixed(num) => write!(f, "48;5;{}", &num), 111 Colour::RGB(r,g,b) => write!(f, "48;2;{};{};{}", &r, &g, &b), 112 } 113 } 114 } 115 116 117 /// Like `ANSIString`, but only displays the style prefix. 118 #[derive(Clone, Copy, Debug)] 119 pub struct Prefix(Style); 120 121 /// Like `ANSIString`, but only displays the difference between two 122 /// styles. 123 #[derive(Clone, Copy, Debug)] 124 pub struct Infix(Style, Style); 125 126 /// Like `ANSIString`, but only displays the style suffix. 127 #[derive(Clone, Copy, Debug)] 128 pub struct Suffix(Style); 129 130 131 impl Style { 132 133 /// The prefix for this style. prefix(self) -> Prefix134 pub fn prefix(self) -> Prefix { 135 Prefix(self) 136 } 137 138 /// The infix between this style and another. infix(self, other: Style) -> Infix139 pub fn infix(self, other: Style) -> Infix { 140 Infix(self, other) 141 } 142 143 /// The suffix for this style. suffix(self) -> Suffix144 pub fn suffix(self) -> Suffix { 145 Suffix(self) 146 } 147 } 148 149 150 impl Colour { 151 152 /// The prefix for this colour. prefix(self) -> Prefix153 pub fn prefix(self) -> Prefix { 154 Prefix(self.normal()) 155 } 156 157 /// The infix between this colour and another. infix(self, other: Colour) -> Infix158 pub fn infix(self, other: Colour) -> Infix { 159 Infix(self.normal(), other.normal()) 160 } 161 162 /// The suffix for this colour. suffix(self) -> Suffix163 pub fn suffix(self) -> Suffix { 164 Suffix(self.normal()) 165 } 166 } 167 168 169 impl fmt::Display for Prefix { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result170 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 171 let f: &mut fmt::Write = f; 172 self.0.write_prefix(f) 173 } 174 } 175 176 177 impl fmt::Display for Infix { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result178 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 179 use difference::Difference; 180 181 match Difference::between(&self.0, &self.1) { 182 Difference::ExtraStyles(style) => { 183 let f: &mut fmt::Write = f; 184 style.write_prefix(f) 185 }, 186 Difference::Reset => { 187 let f: &mut fmt::Write = f; 188 write!(f, "{}{}", RESET, self.0.prefix()) 189 }, 190 Difference::NoDifference => { 191 Ok(()) // nothing to write 192 }, 193 } 194 } 195 } 196 197 198 impl fmt::Display for Suffix { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result199 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 200 let f: &mut fmt::Write = f; 201 self.0.write_suffix(f) 202 } 203 } 204 205 206 207 #[cfg(test)] 208 mod test { 209 use style::Style; 210 use style::Colour::*; 211 212 macro_rules! test { 213 ($name: ident: $style: expr; $input: expr => $result: expr) => { 214 #[test] 215 fn $name() { 216 assert_eq!($style.paint($input).to_string(), $result.to_string()); 217 218 let mut v = Vec::new(); 219 $style.paint($input.as_bytes()).write_to(&mut v).unwrap(); 220 assert_eq!(v.as_slice(), $result.as_bytes()); 221 } 222 }; 223 } 224 225 test!(plain: Style::default(); "text/plain" => "text/plain"); 226 test!(red: Red; "hi" => "\x1B[31mhi\x1B[0m"); 227 test!(black: Black.normal(); "hi" => "\x1B[30mhi\x1B[0m"); 228 test!(yellow_bold: Yellow.bold(); "hi" => "\x1B[1;33mhi\x1B[0m"); 229 test!(yellow_bold_2: Yellow.normal().bold(); "hi" => "\x1B[1;33mhi\x1B[0m"); 230 test!(blue_underline: Blue.underline(); "hi" => "\x1B[4;34mhi\x1B[0m"); 231 test!(green_bold_ul: Green.bold().underline(); "hi" => "\x1B[1;4;32mhi\x1B[0m"); 232 test!(green_bold_ul_2: Green.underline().bold(); "hi" => "\x1B[1;4;32mhi\x1B[0m"); 233 test!(purple_on_white: Purple.on(White); "hi" => "\x1B[47;35mhi\x1B[0m"); 234 test!(purple_on_white_2: Purple.normal().on(White); "hi" => "\x1B[47;35mhi\x1B[0m"); 235 test!(yellow_on_blue: Style::new().on(Blue).fg(Yellow); "hi" => "\x1B[44;33mhi\x1B[0m"); 236 test!(yellow_on_blue_2: Cyan.on(Blue).fg(Yellow); "hi" => "\x1B[44;33mhi\x1B[0m"); 237 test!(cyan_bold_on_white: Cyan.bold().on(White); "hi" => "\x1B[1;47;36mhi\x1B[0m"); 238 test!(cyan_ul_on_white: Cyan.underline().on(White); "hi" => "\x1B[4;47;36mhi\x1B[0m"); 239 test!(cyan_bold_ul_on_white: Cyan.bold().underline().on(White); "hi" => "\x1B[1;4;47;36mhi\x1B[0m"); 240 test!(cyan_ul_bold_on_white: Cyan.underline().bold().on(White); "hi" => "\x1B[1;4;47;36mhi\x1B[0m"); 241 test!(fixed: Fixed(100); "hi" => "\x1B[38;5;100mhi\x1B[0m"); 242 test!(fixed_on_purple: Fixed(100).on(Purple); "hi" => "\x1B[45;38;5;100mhi\x1B[0m"); 243 test!(fixed_on_fixed: Fixed(100).on(Fixed(200)); "hi" => "\x1B[48;5;200;38;5;100mhi\x1B[0m"); 244 test!(rgb: RGB(70,130,180); "hi" => "\x1B[38;2;70;130;180mhi\x1B[0m"); 245 test!(rgb_on_blue: RGB(70,130,180).on(Blue); "hi" => "\x1B[44;38;2;70;130;180mhi\x1B[0m"); 246 test!(blue_on_rgb: Blue.on(RGB(70,130,180)); "hi" => "\x1B[48;2;70;130;180;34mhi\x1B[0m"); 247 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"); 248 test!(bold: Style::new().bold(); "hi" => "\x1B[1mhi\x1B[0m"); 249 test!(underline: Style::new().underline(); "hi" => "\x1B[4mhi\x1B[0m"); 250 test!(bunderline: Style::new().bold().underline(); "hi" => "\x1B[1;4mhi\x1B[0m"); 251 test!(dimmed: Style::new().dimmed(); "hi" => "\x1B[2mhi\x1B[0m"); 252 test!(italic: Style::new().italic(); "hi" => "\x1B[3mhi\x1B[0m"); 253 test!(blink: Style::new().blink(); "hi" => "\x1B[5mhi\x1B[0m"); 254 test!(reverse: Style::new().reverse(); "hi" => "\x1B[7mhi\x1B[0m"); 255 test!(hidden: Style::new().hidden(); "hi" => "\x1B[8mhi\x1B[0m"); 256 test!(stricken: Style::new().strikethrough(); "hi" => "\x1B[9mhi\x1B[0m"); 257 258 } 259