1 use std::io;
2 use std::io::prelude::*;
3 use termcolor::{ColorSpec, WriteColor};
4 
5 // Color tester from:
6 // https://github.com/wycats/language-reporting/blob/b021c87e0d4916b5f32756151bf215c220eee52d/crates/render-tree/src/stylesheet/accumulator.rs
7 
8 /// A facility for creating visually inspectable representations of colored output
9 /// so they can be easily tested.
10 ///
11 /// A new color is represented as `{style}` and a reset is represented by `{/}`.
12 ///
13 /// Attributes are printed in this order:
14 ///
15 /// - Foreground color as `fg:Color`
16 /// - Background color as `bg:Color`
17 /// - Bold as `bold`
18 /// - Underline as `underline`
19 /// - Intense as `bright`
20 ///
21 /// For example, the style "intense, bold red foreground" would be printed as:
22 ///
23 /// ```text
24 /// {fg:Red bold intense}
25 /// ```
26 ///
27 /// Since this implementation attempts to make it possible to faithfully
28 /// understand what real WriteColor implementations would do, it tries
29 /// to approximate the contract in the WriteColor trait: "Subsequent
30 /// writes to this write will use these settings until either reset is
31 /// called or new color settings are set.")
32 ///
33 /// - If set_color is called with a style, `{...}` is emitted containing the
34 ///   color attributes.
35 /// - If set_color is called with no style, `{/}` is emitted
36 /// - If reset is called, `{/}` is emitted.
37 pub struct ColorBuffer {
38     buf: Vec<u8>,
39     color: ColorSpec,
40 }
41 
42 impl ColorBuffer {
new() -> ColorBuffer43     pub fn new() -> ColorBuffer {
44         ColorBuffer {
45             buf: Vec::new(),
46             color: ColorSpec::new(),
47         }
48     }
49 
into_string(self) -> String50     pub fn into_string(self) -> String {
51         String::from_utf8(self.buf).unwrap()
52     }
53 }
54 
55 impl io::Write for ColorBuffer {
write(&mut self, buf: &[u8]) -> io::Result<usize>56     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
57         self.buf.extend(buf);
58         Ok(buf.len())
59     }
60 
flush(&mut self) -> io::Result<()>61     fn flush(&mut self) -> io::Result<()> {
62         Ok(())
63     }
64 }
65 
66 impl WriteColor for ColorBuffer {
supports_color(&self) -> bool67     fn supports_color(&self) -> bool {
68         true
69     }
70 
set_color(&mut self, spec: &ColorSpec) -> io::Result<()>71     fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
72         #![allow(unused_assignments)]
73 
74         if self.color == *spec {
75             return Ok(());
76         } else {
77             self.color = spec.clone();
78         }
79 
80         if spec.is_none() {
81             write!(self, "{{/}}")?;
82             return Ok(());
83         } else {
84             write!(self, "{{")?;
85         }
86 
87         let mut first = true;
88 
89         fn write_first(first: bool, write: &mut ColorBuffer) -> io::Result<bool> {
90             if !first {
91                 write!(write, " ")?;
92             }
93 
94             Ok(false)
95         };
96 
97         if let Some(fg) = spec.fg() {
98             first = write_first(first, self)?;
99             write!(self, "fg:{:?}", fg)?;
100         }
101 
102         if let Some(bg) = spec.bg() {
103             first = write_first(first, self)?;
104             write!(self, "bg:{:?}", bg)?;
105         }
106 
107         if spec.bold() {
108             first = write_first(first, self)?;
109             write!(self, "bold")?;
110         }
111 
112         if spec.underline() {
113             first = write_first(first, self)?;
114             write!(self, "underline")?;
115         }
116 
117         if spec.intense() {
118             first = write_first(first, self)?;
119             write!(self, "bright")?;
120         }
121 
122         write!(self, "}}")?;
123 
124         Ok(())
125     }
126 
reset(&mut self) -> io::Result<()>127     fn reset(&mut self) -> io::Result<()> {
128         let color = self.color.clone();
129 
130         if color != ColorSpec::new() {
131             write!(self, "{{/}}")?;
132             self.color = ColorSpec::new();
133         }
134 
135         Ok(())
136     }
137 }
138