1/// A style is a collection of properties that can format a string
2/// using ANSI escape codes.
3///
4/// # Examples
5///
6/// ```
7/// use nu_ansi_term::{Style, Color};
8///
9/// let style = Style::new().bold().on(Color::Black);
10/// println!("{}", style.paint("Bold on black"));
11/// ```
12#[derive(PartialEq, Clone, Copy)]
13#[cfg_attr(
14 feature = "derive_serde_style",
15 derive(serde::Deserialize, serde::Serialize)
16)]
17pub struct Style {
18 /// The style's foreground color, if it has one.
19 pub foreground: Option<Color>,
20
21 /// The style's background color, if it has one.
22 pub background: Option<Color>,
23
24 /// Whether this style is bold.
25 pub is_bold: bool,
26
27 /// Whether this style is dimmed.
28 pub is_dimmed: bool,
29
30 /// Whether this style is italic.
31 pub is_italic: bool,
32
33 /// Whether this style is underlined.
34 pub is_underline: bool,
35
36 /// Whether this style is blinking.
37 pub is_blink: bool,
38
39 /// Whether this style has reverse colors.
40 pub is_reverse: bool,
41
42 /// Whether this style is hidden.
43 pub is_hidden: bool,
44
45 /// Whether this style is struckthrough.
46 pub is_strikethrough: bool,
47}
48
49impl Style {
50 /// Creates a new Style with no properties set.
51 ///
52 /// # Examples
53 ///
54 /// ```
55 /// use nu_ansi_term::Style;
56 ///
57 /// let style = Style::new();
58 /// println!("{}", style.paint("hi"));
59 /// ```
60 pub fn new() -> Style {
61 Style::default()
62 }
63
64 /// Returns a `Style` with the bold property set.
65 ///
66 /// # Examples
67 ///
68 /// ```
69 /// use nu_ansi_term::Style;
70 ///
71 /// let style = Style::new().bold();
72 /// println!("{}", style.paint("hey"));
73 /// ```
74 pub fn bold(&self) -> Style {
75 Style {
76 is_bold: true,
77 ..*self
78 }
79 }
80
81 /// Returns a `Style` with the dimmed property set.
82 ///
83 /// # Examples
84 ///
85 /// ```
86 /// use nu_ansi_term::Style;
87 ///
88 /// let style = Style::new().dimmed();
89 /// println!("{}", style.paint("sup"));
90 /// ```
91 pub fn dimmed(&self) -> Style {
92 Style {
93 is_dimmed: true,
94 ..*self
95 }
96 }
97
98 /// Returns a `Style` with the italic property set.
99 ///
100 /// # Examples
101 ///
102 /// ```
103 /// use nu_ansi_term::Style;
104 ///
105 /// let style = Style::new().italic();
106 /// println!("{}", style.paint("greetings"));
107 /// ```
108 pub fn italic(&self) -> Style {
109 Style {
110 is_italic: true,
111 ..*self
112 }
113 }
114
115 /// Returns a `Style` with the underline property set.
116 ///
117 /// # Examples
118 ///
119 /// ```
120 /// use nu_ansi_term::Style;
121 ///
122 /// let style = Style::new().underline();
123 /// println!("{}", style.paint("salutations"));
124 /// ```
125 pub fn underline(&self) -> Style {
126 Style {
127 is_underline: true,
128 ..*self
129 }
130 }
131
132 /// Returns a `Style` with the blink property set.
133 /// # Examples
134 ///
135 /// ```
136 /// use nu_ansi_term::Style;
137 ///
138 /// let style = Style::new().blink();
139 /// println!("{}", style.paint("wazzup"));
140 /// ```
141 pub fn blink(&self) -> Style {
142 Style {
143 is_blink: true,
144 ..*self
145 }
146 }
147
148 /// Returns a `Style` with the reverse property set.
149 ///
150 /// # Examples
151 ///
152 /// ```
153 /// use nu_ansi_term::Style;
154 ///
155 /// let style = Style::new().reverse();
156 /// println!("{}", style.paint("aloha"));
157 /// ```
158 pub fn reverse(&self) -> Style {
159 Style {
160 is_reverse: true,
161 ..*self
162 }
163 }
164
165 /// Returns a `Style` with the hidden property set.
166 ///
167 /// # Examples
168 ///
169 /// ```
170 /// use nu_ansi_term::Style;
171 ///
172 /// let style = Style::new().hidden();
173 /// println!("{}", style.paint("ahoy"));
174 /// ```
175 pub fn hidden(&self) -> Style {
176 Style {
177 is_hidden: true,
178 ..*self
179 }
180 }
181
182 /// Returns a `Style` with the strikethrough property set.
183 ///
184 /// # Examples
185 ///
186 /// ```
187 /// use nu_ansi_term::Style;
188 ///
189 /// let style = Style::new().strikethrough();
190 /// println!("{}", style.paint("yo"));
191 /// ```
192 pub fn strikethrough(&self) -> Style {
193 Style {
194 is_strikethrough: true,
195 ..*self
196 }
197 }
198
199 /// Returns a `Style` with the foreground color property set.
200 ///
201 /// # Examples
202 ///
203 /// ```
204 /// use nu_ansi_term::{Style, Color};
205 ///
206 /// let style = Style::new().fg(Color::Yellow);
207 /// println!("{}", style.paint("hi"));
208 /// ```
209 pub fn fg(&self, foreground: Color) -> Style {
210 Style {
211 foreground: Some(foreground),
212 ..*self
213 }
214 }
215
216 /// Returns a `Style` with the background color property set.
217 ///
218 /// # Examples
219 ///
220 /// ```
221 /// use nu_ansi_term::{Style, Color};
222 ///
223 /// let style = Style::new().on(Color::Blue);
224 /// println!("{}", style.paint("eyyyy"));
225 /// ```
226 pub fn on(&self, background: Color) -> Style {
227 Style {
228 background: Some(background),
229 ..*self
230 }
231 }
232
233 /// Return true if this `Style` has no actual styles, and can be written
234 /// without any control characters.
235 ///
236 /// # Examples
237 ///
238 /// ```
239 /// use nu_ansi_term::Style;
240 ///
241 /// assert_eq!(true, Style::default().is_plain());
242 /// assert_eq!(false, Style::default().bold().is_plain());
243 /// ```
244 pub fn is_plain(self) -> bool {
245 self == Style::default()
246 }
247}
248
249impl Default for Style {
250 /// Returns a style with *no* properties set. Formatting text using this
251 /// style returns the exact same text.
252 ///
253 /// ```
254 /// use nu_ansi_term::Style;
255 /// assert_eq!(None, Style::default().foreground);
256 /// assert_eq!(None, Style::default().background);
257 /// assert_eq!(false, Style::default().is_bold);
258 /// assert_eq!("txt", Style::default().paint("txt").to_string());
259 /// ```
260 fn default() -> Style {
261 Style {
262 foreground: None,
263 background: None,
264 is_bold: false,
265 is_dimmed: false,
266 is_italic: false,
267 is_underline: false,
268 is_blink: false,
269 is_reverse: false,
270 is_hidden: false,
271 is_strikethrough: false,
272 }
273 }
274}
275
276// ---- colors ----
277
278/// A color is one specific type of ANSI escape code, and can refer
279/// to either the foreground or background color.
280///
281/// These use the standard numeric sequences.
282/// See <http://invisible-island.net/xterm/ctlseqs/ctlseqs.html>
283#[derive(PartialEq, Clone, Copy, Debug)]
284#[cfg_attr(
285 feature = "derive_serde_style",
286 derive(serde::Deserialize, serde::Serialize)
287)]
288pub enum Color {
289 /// Color #0 (foreground code `30`, background code `40`).
290 ///
291 /// This is not necessarily the background color, and using it as one may
292 /// render the text hard to read on terminals with dark backgrounds.
293 Black,
294
295 /// Color #0 (foreground code `90`, background code `100`).
296 DarkGray,
297
298 /// Color #1 (foreground code `31`, background code `41`).
299 Red,
300
301 /// Color #1 (foreground code `91`, background code `101`).
302 LightRed,
303
304 /// Color #2 (foreground code `32`, background code `42`).
305 Green,
306
307 /// Color #2 (foreground code `92`, background code `102`).
308 LightGreen,
309
310 /// Color #3 (foreground code `33`, background code `43`).
311 Yellow,
312
313 /// Color #3 (foreground code `93`, background code `103`).
314 LightYellow,
315
316 /// Color #4 (foreground code `34`, background code `44`).
317 Blue,
318
319 /// Color #4 (foreground code `94`, background code `104`).
320 LightBlue,
321
322 /// Color #5 (foreground code `35`, background code `45`).
323 Purple,
324
325 /// Color #5 (foreground code `95`, background code `105`).
326 LightPurple,
327
328 /// Color #5 (foreground code `35`, background code `45`).
329 Magenta,
330
331 /// Color #5 (foreground code `95`, background code `105`).
332 LightMagenta,
333
334 /// Color #6 (foreground code `36`, background code `46`).
335 Cyan,
336
337 /// Color #6 (foreground code `96`, background code `106`).
338 LightCyan,
339
340 /// Color #7 (foreground code `37`, background code `47`).
341 ///
342 /// As above, this is not necessarily the foreground color, and may be
343 /// hard to read on terminals with light backgrounds.
344 White,
345
346 /// Color #7 (foreground code `97`, background code `107`).
347 LightGray,
348
349 /// A color number from 0 to 255, for use in 256-color terminal
350 /// environments.
351 ///
352 /// - colors 0 to 7 are the `Black` to `White` variants respectively.
353 /// These colors can usually be changed in the terminal emulator.
354 /// - colors 8 to 15 are brighter versions of the eight colors above.
355 /// These can also usually be changed in the terminal emulator, or it
356 /// could be configured to use the original colors and show the text in
357 /// bold instead. It varies depending on the program.
358 /// - colors 16 to 231 contain several palettes of bright colors,
359 /// arranged in six squares measuring six by six each.
360 /// - colors 232 to 255 are shades of grey from black to white.
361 ///
362 /// It might make more sense to look at a [color chart][cc].
363 ///
364 /// [cc]: https://upload.wikimedia.org/wikipedia/commons/1/15/Xterm_256color_chart.svg
365 Fixed(u8),
366
367 /// A 24-bit Rgb color, as specified by ISO-8613-3.
368 Rgb(u8, u8, u8),
369
370 /// The default color (foreground code `39`, background codr `49`).
371 Default,
372}
373
374impl Default for Color {
375 fn default() -> Self {
376 Color::White
377 }
378}
379
380impl Color {
381 /// Returns a `Style` with the foreground color set to this color.
382 ///
383 /// # Examples
384 ///
385 /// ```
386 /// use nu_ansi_term::Color;
387 ///
388 /// let style = Color::Red.normal();
389 /// println!("{}", style.paint("hi"));
390 /// ```
391 pub fn normal(self) -> Style {
392 Style {
393 foreground: Some(self),
394 ..Style::default()
395 }
396 }
397
398 /// Returns a `Style` with the foreground color set to this color and the
399 /// bold property set.
400 ///
401 /// # Examples
402 ///
403 /// ```
404 /// use nu_ansi_term::Color;
405 ///
406 /// let style = Color::Green.bold();
407 /// println!("{}", style.paint("hey"));
408 /// ```
409 pub fn bold(self) -> Style {
410 Style {
411 foreground: Some(self),
412 is_bold: true,
413 ..Style::default()
414 }
415 }
416
417 /// Returns a `Style` with the foreground color set to this color and the
418 /// dimmed property set.
419 ///
420 /// # Examples
421 ///
422 /// ```
423 /// use nu_ansi_term::Color;
424 ///
425 /// let style = Color::Yellow.dimmed();
426 /// println!("{}", style.paint("sup"));
427 /// ```
428 pub fn dimmed(self) -> Style {
429 Style {
430 foreground: Some(self),
431 is_dimmed: true,
432 ..Style::default()
433 }
434 }
435
436 /// Returns a `Style` with the foreground color set to this color and the
437 /// italic property set.
438 ///
439 /// # Examples
440 ///
441 /// ```
442 /// use nu_ansi_term::Color;
443 ///
444 /// let style = Color::Blue.italic();
445 /// println!("{}", style.paint("greetings"));
446 /// ```
447 pub fn italic(self) -> Style {
448 Style {
449 foreground: Some(self),
450 is_italic: true,
451 ..Style::default()
452 }
453 }
454
455 /// Returns a `Style` with the foreground color set to this color and the
456 /// underline property set.
457 ///
458 /// # Examples
459 ///
460 /// ```
461 /// use nu_ansi_term::Color;
462 ///
463 /// let style = Color::Purple.underline();
464 /// println!("{}", style.paint("salutations"));
465 /// ```
466 pub fn underline(self) -> Style {
467 Style {
468 foreground: Some(self),
469 is_underline: true,
470 ..Style::default()
471 }
472 }
473
474 /// Returns a `Style` with the foreground color set to this color and the
475 /// blink property set.
476 ///
477 /// # Examples
478 ///
479 /// ```
480 /// use nu_ansi_term::Color;
481 ///
482 /// let style = Color::Cyan.blink();
483 /// println!("{}", style.paint("wazzup"));
484 /// ```
485 pub fn blink(self) -> Style {
486 Style {
487 foreground: Some(self),
488 is_blink: true,
489 ..Style::default()
490 }
491 }
492
493 /// Returns a `Style` with the foreground color set to this color and the
494 /// reverse property set.
495 ///
496 /// # Examples
497 ///
498 /// ```
499 /// use nu_ansi_term::Color;
500 ///
501 /// let style = Color::Black.reverse();
502 /// println!("{}", style.paint("aloha"));
503 /// ```
504 pub fn reverse(self) -> Style {
505 Style {
506 foreground: Some(self),
507 is_reverse: true,
508 ..Style::default()
509 }
510 }
511
512 /// Returns a `Style` with the foreground color set to this color and the
513 /// hidden property set.
514 ///
515 /// # Examples
516 ///
517 /// ```
518 /// use nu_ansi_term::Color;
519 ///
520 /// let style = Color::White.hidden();
521 /// println!("{}", style.paint("ahoy"));
522 /// ```
523 pub fn hidden(self) -> Style {
524 Style {
525 foreground: Some(self),
526 is_hidden: true,
527 ..Style::default()
528 }
529 }
530
531 /// Returns a `Style` with the foreground color set to this color and the
532 /// strikethrough property set.
533 ///
534 /// # Examples
535 ///
536 /// ```
537 /// use nu_ansi_term::Color;
538 ///
539 /// let style = Color::Fixed(244).strikethrough();
540 /// println!("{}", style.paint("yo"));
541 /// ```
542 pub fn strikethrough(self) -> Style {
543 Style {
544 foreground: Some(self),
545 is_strikethrough: true,
546 ..Style::default()
547 }
548 }
549
550 /// Returns a `Style` with the foreground color set to this color and the
551 /// background color property set to the given color.
552 ///
553 /// # Examples
554 ///
555 /// ```
556 /// use nu_ansi_term::Color;
557 ///
558 /// let style = Color::Rgb(31, 31, 31).on(Color::White);
559 /// println!("{}", style.paint("eyyyy"));
560 /// ```
561 pub fn on(self, background: Color) -> Style {
562 Style {
563 foreground: Some(self),
564 background: Some(background),
565 ..Style::default()
566 }
567 }
568}
569
570impl From<Color> for Style {
571 /// You can turn a `Color` into a `Style` with the foreground color set
572 /// with the `From` trait.
573 ///
574 /// ```
575 /// use nu_ansi_term::{Style, Color};
576 /// let green_foreground = Style::default().fg(Color::Green);
577 /// assert_eq!(green_foreground, Color::Green.normal());
578 /// assert_eq!(green_foreground, Color::Green.into());
579 /// assert_eq!(green_foreground, Style::from(Color::Green));
580 /// ```
581 fn from(color: Color) -> Style {
582 color.normal()
583 }
584}
585
586#[cfg(test)]
587#[cfg(feature = "derive_serde_style")]
588mod serde_json_tests {
589 use super::{Color, Style};
590
591 #[test]
592 fn color_serialization() {
593 let colors = &[
594 Color::Red,
595 Color::Blue,
596 Color::Rgb(123, 123, 123),
597 Color::Fixed(255),
598 ];
599
600 assert_eq!(
601 serde_json::to_string(&colors).unwrap(),
602 String::from("[\"Red\",\"Blue\",{\"Rgb\":[123,123,123]},{\"Fixed\":255}]")
603 );
604 }
605
606 #[test]
607 fn color_deserialization() {
608 let colors = [
609 Color::Red,
610 Color::Blue,
611 Color::Rgb(123, 123, 123),
612 Color::Fixed(255),
613 ];
614
615 for color in colors {
616 let serialized = serde_json::to_string(&color).unwrap();
617 let deserialized: Color = serde_json::from_str(&serialized).unwrap();
618
619 assert_eq!(color, deserialized);
620 }
621 }
622
623 #[test]
624 fn style_serialization() {
625 let style = Style::default();
626
627 assert_eq!(serde_json::to_string(&style).unwrap(), "{\"foreground\":null,\"background\":null,\"is_bold\":false,\"is_dimmed\":false,\"is_italic\":false,\"is_underline\":false,\"is_blink\":false,\"is_reverse\":false,\"is_hidden\":false,\"is_strikethrough\":false}".to_string());
628 }
629}
630