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