1use style::{Colour, Style};
2
3use std::fmt;
4
5use write::AnyWrite;
6
7
8// ---- generating ANSI codes ----
9
10impl Style {
11
12 /// Write any bytes that go *before* a piece of text to the given writer.
13 fn write_prefix<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> {
14
15 // If there are actually no styles here, then don’t write *any* codes
16 // as the prefix. An empty ANSI code may not affect the terminal
17 // output at all, but a user may just want a code-free string.
18 if self.is_plain() {
19 return Ok(());
20 }
21
22 // Write the codes’ prefix, then write numbers, separated by
23 // semicolons, for each text style we want to apply.
24 write!(f, "\x1B[")?;
25 let mut written_anything = false;
26
27 {
28 let mut write_char = |c| {
29 if written_anything { write!(f, ";")?; }
30 written_anything = true;
31 write!(f, "{}", c)?;
32 Ok(())
33 };
34
35 if self.is_bold { write_char('1')? }
36 if self.is_dimmed { write_char('2')? }
37 if self.is_italic { write_char('3')? }
38 if self.is_underline { write_char('4')? }
39 if self.is_blink { write_char('5')? }
40 if self.is_reverse { write_char('7')? }
41 if self.is_hidden { write_char('8')? }
42 if self.is_strikethrough { write_char('9')? }
43 }
44
45 // The foreground and background colours, if specified, need to be
46 // handled specially because the number codes are more complicated.
47 // (see `write_background_code` and `write_foreground_code`)
48 if let Some(bg) = self.background {
49 if written_anything { write!(f, ";")?; }
50 written_anything = true;
51 bg.write_background_code(f)?;
52 }
53
54 if let Some(fg) = self.foreground {
55 if written_anything { write!(f, ";")?; }
56 fg.write_foreground_code(f)?;
57 }
58
59 // All the codes end with an `m`, because reasons.
60 write!(f, "m")?;
61
62 Ok(())
63 }
64
65 /// Write any bytes that go *after* a piece of text to the given writer.
66 fn write_suffix<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> {
67 if self.is_plain() {
68 Ok(())
69 }
70 else {
71 write!(f, "{}", RESET)
72 }
73 }
74}
75
76
77/// The code to send to reset all styles and return to `Style::default()`.
78pub static RESET: &str = "\x1B[0m";
79
80
81
82impl Colour {
83 fn write_foreground_code<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> {
84 match *self {
85 Colour::Black => write!(f, "30"),
86 Colour::Red => write!(f, "31"),
87 Colour::Green => write!(f, "32"),
88 Colour::Yellow => write!(f, "33"),
89 Colour::Blue => write!(f, "34"),
90 Colour::Purple => write!(f, "35"),
91 Colour::Cyan => write!(f, "36"),
92 Colour::White => write!(f, "37"),
93 Colour::Fixed(num) => write!(f, "38;5;{}", &num),
94 Colour::RGB(r,g,b) => write!(f, "38;2;{};{};{}", &r, &g, &b),
95 }
96 }
97
98 fn write_background_code<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> {
99 match *self {
100 Colour::Black => write!(f, "40"),
101 Colour::Red => write!(f, "41"),
102 Colour::Green => write!(f, "42"),
103 Colour::Yellow => write!(f, "43"),
104 Colour::Blue => write!(f, "44"),
105 Colour::Purple => write!(f, "45"),
106 Colour::Cyan => write!(f, "46"),
107 Colour::White => write!(f, "47"),
108 Colour::Fixed(num) => write!(f, "48;5;{}", &num),
109 Colour::RGB(r,g,b) => write!(f, "48;2;{};{};{}", &r, &g, &b),
110 }
111 }
112}
113
114
115/// Like `ANSIString`, but only displays the style prefix.
116///
117/// This type implements the `Display` trait, meaning it can be written to a
118/// `std::fmt` formatting without doing any extra allocation, and written to a
119/// string with the `.to_string()` method. For examples, see
120/// [`Style::prefix`](struct.Style.html#method.prefix).
121#[derive(Clone, Copy, Debug)]
122pub struct Prefix(Style);
123
124/// Like `ANSIString`, but only displays the difference between two
125/// styles.
126///
127/// This type implements the `Display` trait, meaning it can be written to a
128/// `std::fmt` formatting without doing any extra allocation, and written to a
129/// string with the `.to_string()` method. For examples, see
130/// [`Style::infix`](struct.Style.html#method.infix).
131#[derive(Clone, Copy, Debug)]
132pub struct Infix(Style, Style);
133
134/// Like `ANSIString`, but only displays the style suffix.
135///
136/// This type implements the `Display` trait, meaning it can be written to a
137/// `std::fmt` formatting without doing any extra allocation, and written to a
138/// string with the `.to_string()` method. For examples, see
139/// [`Style::suffix`](struct.Style.html#method.suffix).
140#[derive(Clone, Copy, Debug)]
141pub struct Suffix(Style);
142
143
144impl Style {
145
146 /// The prefix bytes for this style. These are the bytes that tell the
147 /// terminal to use a different colour or font style.
148 ///
149 /// # Examples
150 ///
151 /// ```
152 /// use ansi_term::{Style, Colour::Blue};
153 ///
154 /// let style = Style::default().bold();
155 /// assert_eq!("\x1b[1m",
156 /// style.prefix().to_string());
157 ///
158 /// let style = Blue.bold();
159 /// assert_eq!("\x1b[1;34m",
160 /// style.prefix().to_string());
161 ///
162 /// let style = Style::default();
163 /// assert_eq!("",
164 /// style.prefix().to_string());
165 /// ```
166 pub fn prefix(self) -> Prefix {
167 Prefix(self)
168 }
169
170 /// The infix bytes between this style and `next` style. These are the bytes
171 /// that tell the terminal to change the style to `next`. These may include
172 /// a reset followed by the next colour and style, depending on the two styles.
173 ///
174 /// # Examples
175 ///
176 /// ```
177 /// use ansi_term::{Style, Colour::Green};
178 ///
179 /// let style = Style::default().bold();
180 /// assert_eq!("\x1b[32m",
181 /// style.infix(Green.bold()).to_string());
182 ///
183 /// let style = Green.normal();
184 /// assert_eq!("\x1b[1m",
185 /// style.infix(Green.bold()).to_string());
186 ///
187 /// let style = Style::default();
188 /// assert_eq!("",
189 /// style.infix(style).to_string());
190 /// ```
191 pub fn infix(self, next: Style) -> Infix {
192 Infix(self, next)
193 }
194
195 /// The suffix for this style. These are the bytes that tell the terminal
196 /// to reset back to its normal colour and font style.
197 ///
198 /// # Examples
199 ///
200 /// ```
201 /// use ansi_term::{Style, Colour::Green};
202 ///
203 /// let style = Style::default().bold();
204 /// assert_eq!("\x1b[0m",
205 /// style.suffix().to_string());
206 ///
207 /// let style = Green.normal().bold();
208 /// assert_eq!("\x1b[0m",
209 /// style.suffix().to_string());
210 ///
211 /// let style = Style::default();
212 /// assert_eq!("",
213 /// style.suffix().to_string());
214 /// ```
215 pub fn suffix(self) -> Suffix {
216 Suffix(self)
217 }
218}
219
220
221impl Colour {
222
223 /// The prefix bytes for this colour as a `Style`. These are the bytes
224 /// that tell the terminal to use a different colour or font style.
225 ///
226 /// See also [`Style::prefix`](struct.Style.html#method.prefix).
227 ///
228 /// # Examples
229 ///
230 /// ```
231 /// use ansi_term::Colour::Green;
232 ///
233 /// assert_eq!("\x1b[0m",
234 /// Green.suffix().to_string());
235 /// ```
236 pub fn prefix(self) -> Prefix {
237 Prefix(self.normal())
238 }
239
240 /// The infix bytes between this colour and `next` colour. These are the bytes
241 /// that tell the terminal to use the `next` colour, or to do nothing if
242 /// the two colours are equal.
243 ///
244 /// See also [`Style::infix`](struct.Style.html#method.infix).
245 ///
246 /// # Examples
247 ///
248 /// ```
249 /// use ansi_term::Colour::{Red, Yellow};
250 ///
251 /// assert_eq!("\x1b[33m",
252 /// Red.infix(Yellow).to_string());
253 /// ```
254 pub fn infix(self, next: Colour) -> Infix {
255 Infix(self.normal(), next.normal())
256 }
257
258 /// The suffix for this colour as a `Style`. These are the bytes that
259 /// tell the terminal to reset back to its normal colour and font style.
260 ///
261 /// See also [`Style::suffix`](struct.Style.html#method.suffix).
262 ///
263 /// # Examples
264 ///
265 /// ```
266 /// use ansi_term::Colour::Purple;
267 ///
268 /// assert_eq!("\x1b[0m",
269 /// Purple.suffix().to_string());
270 /// ```
271 pub fn suffix(self) -> Suffix {
272 Suffix(self.normal())
273 }
274}
275
276
277impl fmt::Display for Prefix {
278 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
279 let f: &mut fmt::Write = f;
280 self.0.write_prefix(f)
281 }
282}
283
284
285impl fmt::Display for Infix {
286 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
287 use difference::Difference;
288
289 match Difference::between(&self.0, &self.1) {
290 Difference::ExtraStyles(style: Style) => {
291 let f: &mut fmt::Write = f;
292 style.write_prefix(f)
293 },
294 Difference::Reset => {
295 let f: &mut fmt::Write = f;
296 write!(f, "{}{}", RESET, self.1.prefix())
297 },
298 Difference::NoDifference => {
299 Ok(()) // nothing to write
300 },
301 }
302 }
303}
304
305
306impl fmt::Display for Suffix {
307 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
308 let f: &mut fmt::Write = f;
309 self.0.write_suffix(f)
310 }
311}
312
313
314
315#[cfg(test)]
316mod test {
317 use style::Style;
318 use style::Colour::*;
319
320 macro_rules! test {
321 ($name: ident: $style: expr; $input: expr => $result: expr) => {
322 #[test]
323 fn $name() {
324 assert_eq!($style.paint($input).to_string(), $result.to_string());
325
326 let mut v = Vec::new();
327 $style.paint($input.as_bytes()).write_to(&mut v).unwrap();
328 assert_eq!(v.as_slice(), $result.as_bytes());
329 }
330 };
331 }
332
333 test!(plain: Style::default(); "text/plain" => "text/plain");
334 test!(red: Red; "hi" => "\x1B[31mhi\x1B[0m");
335 test!(black: Black.normal(); "hi" => "\x1B[30mhi\x1B[0m");
336 test!(yellow_bold: Yellow.bold(); "hi" => "\x1B[1;33mhi\x1B[0m");
337 test!(yellow_bold_2: Yellow.normal().bold(); "hi" => "\x1B[1;33mhi\x1B[0m");
338 test!(blue_underline: Blue.underline(); "hi" => "\x1B[4;34mhi\x1B[0m");
339 test!(green_bold_ul: Green.bold().underline(); "hi" => "\x1B[1;4;32mhi\x1B[0m");
340 test!(green_bold_ul_2: Green.underline().bold(); "hi" => "\x1B[1;4;32mhi\x1B[0m");
341 test!(purple_on_white: Purple.on(White); "hi" => "\x1B[47;35mhi\x1B[0m");
342 test!(purple_on_white_2: Purple.normal().on(White); "hi" => "\x1B[47;35mhi\x1B[0m");
343 test!(yellow_on_blue: Style::new().on(Blue).fg(Yellow); "hi" => "\x1B[44;33mhi\x1B[0m");
344 test!(yellow_on_blue_2: Cyan.on(Blue).fg(Yellow); "hi" => "\x1B[44;33mhi\x1B[0m");
345 test!(cyan_bold_on_white: Cyan.bold().on(White); "hi" => "\x1B[1;47;36mhi\x1B[0m");
346 test!(cyan_ul_on_white: Cyan.underline().on(White); "hi" => "\x1B[4;47;36mhi\x1B[0m");
347 test!(cyan_bold_ul_on_white: Cyan.bold().underline().on(White); "hi" => "\x1B[1;4;47;36mhi\x1B[0m");
348 test!(cyan_ul_bold_on_white: Cyan.underline().bold().on(White); "hi" => "\x1B[1;4;47;36mhi\x1B[0m");
349 test!(fixed: Fixed(100); "hi" => "\x1B[38;5;100mhi\x1B[0m");
350 test!(fixed_on_purple: Fixed(100).on(Purple); "hi" => "\x1B[45;38;5;100mhi\x1B[0m");
351 test!(fixed_on_fixed: Fixed(100).on(Fixed(200)); "hi" => "\x1B[48;5;200;38;5;100mhi\x1B[0m");
352 test!(rgb: RGB(70,130,180); "hi" => "\x1B[38;2;70;130;180mhi\x1B[0m");
353 test!(rgb_on_blue: RGB(70,130,180).on(Blue); "hi" => "\x1B[44;38;2;70;130;180mhi\x1B[0m");
354 test!(blue_on_rgb: Blue.on(RGB(70,130,180)); "hi" => "\x1B[48;2;70;130;180;34mhi\x1B[0m");
355 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");
356 test!(bold: Style::new().bold(); "hi" => "\x1B[1mhi\x1B[0m");
357 test!(underline: Style::new().underline(); "hi" => "\x1B[4mhi\x1B[0m");
358 test!(bunderline: Style::new().bold().underline(); "hi" => "\x1B[1;4mhi\x1B[0m");
359 test!(dimmed: Style::new().dimmed(); "hi" => "\x1B[2mhi\x1B[0m");
360 test!(italic: Style::new().italic(); "hi" => "\x1B[3mhi\x1B[0m");
361 test!(blink: Style::new().blink(); "hi" => "\x1B[5mhi\x1B[0m");
362 test!(reverse: Style::new().reverse(); "hi" => "\x1B[7mhi\x1B[0m");
363 test!(hidden: Style::new().hidden(); "hi" => "\x1B[8mhi\x1B[0m");
364 test!(stricken: Style::new().strikethrough(); "hi" => "\x1B[9mhi\x1B[0m");
365
366 #[test]
367 fn test_infix() {
368 assert_eq!(Style::new().dimmed().infix(Style::new()).to_string(), "\x1B[0m");
369 assert_eq!(White.dimmed().infix(White.normal()).to_string(), "\x1B[0m\x1B[37m");
370 assert_eq!(White.normal().infix(White.bold()).to_string(), "\x1B[1m");
371 assert_eq!(White.normal().infix(Blue.normal()).to_string(), "\x1B[34m");
372 assert_eq!(Blue.bold().infix(Blue.bold()).to_string(), "");
373 }
374}
375