render_tree/stylesheet/
accumulator.rs

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