1use std::fmt;
2use std::io::{self, Write};
3
4use crate::termcolor::{self, ColorSpec, StandardStream, WriteColor};
5
6pub(super) enum Stream {
7 Stdout,
8 Stderr,
9}
10
11impl Stream {
12 fn find<'a>(
13 &self,
14 stdout: &'a mut StandardStream,
15 stderr: &'a mut StandardStream,
16 ) -> &'a mut StandardStream {
17 match self {
18 Stream::Stdout => stdout,
19 Stream::Stderr => stderr,
20 }
21 }
22}
23
24pub(super) enum Color {
25 Error,
26 Passed,
27 Ignore,
28 Highlight,
29 Important,
30 Progress,
31}
32
33impl Color {
34 fn find<'a>(&self, colors: &'a Colors) -> &'a ColorSpec {
35 match self {
36 Color::Error => &colors.error,
37 Color::Passed => &colors.passed,
38 Color::Ignore => &colors.ignored,
39 Color::Highlight => &colors.highlight,
40 Color::Important => &colors.important,
41 Color::Progress => &colors.progress,
42 }
43 }
44}
45
46pub(super) struct Io<'io> {
47 pub(super) stdout: &'io mut StandardStream,
48 pub(super) stderr: &'io mut StandardStream,
49 colors: Option<Colors>,
50}
51
52impl<'io> Io<'io> {
53 pub(super) fn new(stdout: &'io mut StandardStream, stderr: &'io mut StandardStream) -> Self {
54 Self {
55 stdout,
56 stderr,
57 colors: None,
58 }
59 }
60
61 pub(super) fn with_color(
62 &mut self,
63 stream: Stream,
64 color: Color,
65 ) -> io::Result<&mut ColorStream> {
66 let stream = stream.find(self.stdout, self.stderr);
67 let colors = self.colors.get_or_insert_with(Colors::new);
68 stream.set_color(color.find(colors))?;
69 Ok(ColorStream::new(stream))
70 }
71
72 pub(super) fn section(
73 &mut self,
74 title: impl fmt::Display,
75 stream: Stream,
76 color: Color,
77 ) -> io::Result<Section<'_>> {
78 let io = stream.find(self.stdout, self.stderr);
79 let colors = self.colors.get_or_insert_with(Colors::new);
80
81 io.set_color(color.find(colors))?;
82 write!(io, "{title:>12}")?;
83 io.reset()?;
84
85 Ok(Section { io, colors })
86 }
87
88 pub(super) fn write(
89 &mut self,
90 title: impl fmt::Display,
91 stream: Stream,
92 color: Color,
93 ) -> io::Result<()> {
94 let stream = stream.find(self.stdout, self.stderr);
95 let colors = self.colors.get_or_insert_with(Colors::new);
96
97 stream.set_color(color.find(colors))?;
98 write!(stream, "{title}")?;
99 stream.reset()?;
100 Ok(())
101 }
102}
103
104#[derive(Default)]
105struct Colors {
106 error: ColorSpec,
107 passed: ColorSpec,
108 highlight: ColorSpec,
109 important: ColorSpec,
110 progress: ColorSpec,
111 ignored: ColorSpec,
112}
113
114impl Colors {
115 fn new() -> Self {
116 let mut this = Self::default();
117 this.error.set_fg(Some(termcolor::Color::Red));
118 this.passed.set_fg(Some(termcolor::Color::Green));
119 this.highlight.set_fg(Some(termcolor::Color::Green));
120 this.highlight.set_bold(true);
121 this.important.set_fg(Some(termcolor::Color::White));
122 this.important.set_bold(true);
123 this.progress.set_fg(Some(termcolor::Color::Cyan));
124 this.progress.set_bold(true);
125 this.ignored.set_fg(Some(termcolor::Color::Yellow));
126 this.ignored.set_bold(true);
127 this
128 }
129}
130
131pub(super) struct Section<'a> {
132 pub(super) io: &'a mut StandardStream,
133 colors: &'a Colors,
134}
135
136impl Section<'_> {
137 pub(super) fn append(&mut self, text: impl fmt::Display) -> io::Result<&mut Self> {
138 write!(self.io, "{text}")?;
139 Ok(self)
140 }
141
142 pub(super) fn flush(&mut self) -> io::Result<&mut Self> {
144 self.io.flush()?;
145 Ok(self)
146 }
147
148 pub(super) fn append_with(
149 &mut self,
150 text: impl fmt::Display,
151 color: Color,
152 ) -> io::Result<&mut Self> {
153 self.io.set_color(color.find(self.colors))?;
154 write!(self.io, "{text}")?;
155 self.io.reset()?;
156 Ok(self)
157 }
158
159 pub(super) fn error(&mut self, text: impl fmt::Display) -> io::Result<&mut Self> {
160 self.append_with(text, Color::Error)?;
161 Ok(self)
162 }
163
164 pub(super) fn passed(&mut self, text: impl fmt::Display) -> io::Result<&mut Self> {
165 self.append_with(text, Color::Passed)?;
166 Ok(self)
167 }
168
169 pub(super) fn close(&mut self) -> io::Result<()> {
170 writeln!(self.io)?;
171 Ok(())
172 }
173}
174
175#[repr(transparent)]
176pub(super) struct ColorStream(StandardStream);
177
178impl ColorStream {
179 fn new(io: &mut StandardStream) -> &mut Self {
180 unsafe { &mut *(io as *mut StandardStream as *mut Self) }
181 }
182
183 pub(super) fn close(&mut self) -> io::Result<()> {
184 self.0.reset()
185 }
186}
187
188impl io::Write for ColorStream {
189 #[inline]
190 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
191 self.0.write(buf)
192 }
193
194 #[inline]
195 fn flush(&mut self) -> io::Result<()> {
196 self.0.flush()
197 }
198}