tari_log4rs/encode/writer/
console.rs1use std::{fmt, io};
6
7use crate::encode::{self, Style};
8
9pub struct ConsoleWriter(imp::Writer);
11
12impl ConsoleWriter {
13 pub fn stdout() -> Option<ConsoleWriter> {
18 imp::Writer::stdout().map(ConsoleWriter)
19 }
20
21 pub fn stderr() -> Option<ConsoleWriter> {
26 imp::Writer::stderr().map(ConsoleWriter)
27 }
28
29 pub fn lock(&self) -> ConsoleWriterLock {
31 ConsoleWriterLock(self.0.lock())
32 }
33}
34
35impl io::Write for ConsoleWriter {
36 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
37 self.0.write(buf)
38 }
39
40 fn flush(&mut self) -> io::Result<()> {
41 self.0.flush()
42 }
43
44 fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
45 self.0.write_all(buf)
46 }
47
48 fn write_fmt(&mut self, fmt: fmt::Arguments) -> io::Result<()> {
49 self.0.write_fmt(fmt)
50 }
51}
52
53impl encode::Write for ConsoleWriter {
54 fn set_style(&mut self, style: &Style) -> io::Result<()> {
55 self.0.set_style(style)
56 }
57}
58
59pub struct ConsoleWriterLock<'a>(imp::WriterLock<'a>);
61
62impl<'a> io::Write for ConsoleWriterLock<'a> {
63 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
64 self.0.write(buf)
65 }
66
67 fn flush(&mut self) -> io::Result<()> {
68 self.0.flush()
69 }
70
71 fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
72 self.0.write_all(buf)
73 }
74
75 fn write_fmt(&mut self, fmt: fmt::Arguments) -> io::Result<()> {
76 self.0.write_fmt(fmt)
77 }
78}
79
80impl<'a> encode::Write for ConsoleWriterLock<'a> {
81 fn set_style(&mut self, style: &Style) -> io::Result<()> {
82 self.0.set_style(style)
83 }
84}
85
86#[cfg(unix)]
87mod imp {
88 use std::{fmt, io};
89
90 use crate::{
91 encode::{self, writer::ansi::AnsiWriter, Style},
92 priv_io::{StdWriter, StdWriterLock},
93 };
94
95 pub struct Writer(AnsiWriter<StdWriter>);
96
97 impl Writer {
98 pub fn stdout() -> Option<Writer> {
99 if unsafe { libc::isatty(libc::STDOUT_FILENO) } != 1 {
100 return None;
101 }
102
103 Some(Writer(AnsiWriter(StdWriter::stdout())))
104 }
105
106 pub fn stderr() -> Option<Writer> {
107 if unsafe { libc::isatty(libc::STDERR_FILENO) } != 1 {
108 return None;
109 }
110
111 Some(Writer(AnsiWriter(StdWriter::stderr())))
112 }
113
114 pub fn lock(&self) -> WriterLock {
115 WriterLock(AnsiWriter((self.0).0.lock()))
116 }
117 }
118
119 impl io::Write for Writer {
120 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
121 self.0.write(buf)
122 }
123
124 fn flush(&mut self) -> io::Result<()> {
125 self.0.flush()
126 }
127
128 fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
129 self.0.write_all(buf)
130 }
131
132 fn write_fmt(&mut self, fmt: fmt::Arguments) -> io::Result<()> {
133 self.0.write_fmt(fmt)
134 }
135 }
136
137 impl encode::Write for Writer {
138 fn set_style(&mut self, style: &Style) -> io::Result<()> {
139 self.0.set_style(style)
140 }
141 }
142
143 pub struct WriterLock<'a>(AnsiWriter<StdWriterLock<'a>>);
144
145 impl<'a> io::Write for WriterLock<'a> {
146 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
147 self.0.write(buf)
148 }
149
150 fn flush(&mut self) -> io::Result<()> {
151 self.0.flush()
152 }
153
154 fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
155 self.0.write_all(buf)
156 }
157
158 fn write_fmt(&mut self, fmt: fmt::Arguments) -> io::Result<()> {
159 self.0.write_fmt(fmt)
160 }
161 }
162
163 impl<'a> encode::Write for WriterLock<'a> {
164 fn set_style(&mut self, style: &Style) -> io::Result<()> {
165 self.0.set_style(style)
166 }
167 }
168}
169
170#[cfg(windows)]
171mod imp {
172 use std::{
173 fmt,
174 io::{self, Write},
175 mem,
176 };
177 use winapi::{
178 shared::minwindef,
179 um::{handleapi, processenv, winbase, wincon, winnt},
180 };
181
182 use crate::{
183 encode::{self, Color, Style},
184 priv_io::{StdWriter, StdWriterLock},
185 };
186
187 struct RawConsole {
188 handle: winnt::HANDLE,
189 defaults: minwindef::WORD,
190 }
191
192 unsafe impl Sync for RawConsole {}
193 unsafe impl Send for RawConsole {}
194
195 impl RawConsole {
196 fn set_style(&self, style: &Style) -> io::Result<()> {
197 let mut attrs = self.defaults;
198
199 if let Some(text) = style.text {
200 attrs &= !((wincon::FOREGROUND_RED
201 | wincon::FOREGROUND_GREEN
202 | wincon::FOREGROUND_BLUE) as minwindef::WORD);
203 attrs |= match text {
204 Color::Black => 0,
205 Color::Red => wincon::FOREGROUND_RED,
206 Color::Green => wincon::FOREGROUND_GREEN,
207 Color::Yellow => wincon::FOREGROUND_RED | wincon::FOREGROUND_GREEN,
208 Color::Blue => wincon::FOREGROUND_BLUE,
209 Color::Magenta => wincon::FOREGROUND_RED | wincon::FOREGROUND_BLUE,
210 Color::Cyan => wincon::FOREGROUND_GREEN | wincon::FOREGROUND_BLUE,
211 Color::White => {
212 wincon::FOREGROUND_RED | wincon::FOREGROUND_GREEN | wincon::FOREGROUND_BLUE
213 }
214 } as minwindef::WORD;
215 }
216
217 if let Some(background) = style.background {
218 attrs &= !((wincon::BACKGROUND_RED
219 | wincon::BACKGROUND_GREEN
220 | wincon::BACKGROUND_BLUE) as minwindef::WORD);
221 attrs |= match background {
222 Color::Black => 0,
223 Color::Red => wincon::BACKGROUND_RED,
224 Color::Green => wincon::BACKGROUND_GREEN,
225 Color::Yellow => wincon::BACKGROUND_RED | wincon::BACKGROUND_GREEN,
226 Color::Blue => wincon::BACKGROUND_BLUE,
227 Color::Magenta => wincon::BACKGROUND_RED | wincon::BACKGROUND_BLUE,
228 Color::Cyan => wincon::BACKGROUND_GREEN | wincon::BACKGROUND_BLUE,
229 Color::White => {
230 wincon::BACKGROUND_RED | wincon::BACKGROUND_GREEN | wincon::BACKGROUND_BLUE
231 }
232 } as minwindef::WORD;
233 }
234
235 if let Some(intense) = style.intense {
236 if intense {
237 attrs |= wincon::FOREGROUND_INTENSITY as minwindef::WORD;
238 } else {
239 attrs &= !(wincon::FOREGROUND_INTENSITY as minwindef::WORD);
240 }
241 }
242
243 if unsafe { wincon::SetConsoleTextAttribute(self.handle, attrs) } == 0 {
244 Err(io::Error::last_os_error())
245 } else {
246 Ok(())
247 }
248 }
249 }
250
251 pub struct Writer {
252 console: RawConsole,
253 inner: StdWriter,
254 }
255
256 impl Writer {
257 pub fn stdout() -> Option<Writer> {
258 unsafe {
259 let handle = processenv::GetStdHandle(winbase::STD_OUTPUT_HANDLE);
260 if handle.is_null() || handle == handleapi::INVALID_HANDLE_VALUE {
261 return None;
262 }
263
264 let mut info = mem::zeroed();
265 if wincon::GetConsoleScreenBufferInfo(handle, &mut info) == 0 {
266 return None;
267 }
268
269 Some(Writer {
270 console: RawConsole {
271 handle,
272 defaults: info.wAttributes,
273 },
274 inner: StdWriter::stdout(),
275 })
276 }
277 }
278
279 pub fn stderr() -> Option<Writer> {
280 unsafe {
281 let handle = processenv::GetStdHandle(winbase::STD_ERROR_HANDLE);
282 if handle.is_null() || handle == handleapi::INVALID_HANDLE_VALUE {
283 return None;
284 }
285
286 let mut info = mem::zeroed();
287 if wincon::GetConsoleScreenBufferInfo(handle, &mut info) == 0 {
288 return None;
289 }
290
291 Some(Writer {
292 console: RawConsole {
293 handle,
294 defaults: info.wAttributes,
295 },
296 inner: StdWriter::stderr(),
297 })
298 }
299 }
300
301 pub fn lock<'a>(&'a self) -> WriterLock<'a> {
302 WriterLock {
303 console: &self.console,
304 inner: self.inner.lock(),
305 }
306 }
307 }
308
309 impl io::Write for Writer {
310 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
311 self.inner.write(buf)
312 }
313
314 fn flush(&mut self) -> io::Result<()> {
315 self.inner.flush()
316 }
317
318 fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
319 self.inner.write_all(buf)
320 }
321
322 fn write_fmt(&mut self, fmt: fmt::Arguments) -> io::Result<()> {
323 self.inner.write_fmt(fmt)
324 }
325 }
326
327 impl encode::Write for Writer {
328 fn set_style(&mut self, style: &Style) -> io::Result<()> {
329 self.inner.flush()?;
330 self.console.set_style(style)
331 }
332 }
333
334 pub struct WriterLock<'a> {
335 console: &'a RawConsole,
336 inner: StdWriterLock<'a>,
337 }
338
339 impl<'a> io::Write for WriterLock<'a> {
340 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
341 self.inner.write(buf)
342 }
343
344 fn flush(&mut self) -> io::Result<()> {
345 self.inner.flush()
346 }
347
348 fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
349 self.inner.write_all(buf)
350 }
351
352 fn write_fmt(&mut self, fmt: fmt::Arguments) -> io::Result<()> {
353 self.inner.write_fmt(fmt)
354 }
355 }
356
357 impl<'a> encode::Write for WriterLock<'a> {
358 fn set_style(&mut self, style: &Style) -> io::Result<()> {
359 self.inner.flush()?;
360 self.console.set_style(style)
361 }
362 }
363}
364
365#[cfg(test)]
366mod test {
367 use std::io::Write;
368
369 use super::*;
370 use crate::encode::{Color, Style, Write as EncodeWrite};
371
372 #[test]
373 fn basic() {
374 let w = match ConsoleWriter::stdout() {
375 Some(w) => w,
376 None => return,
377 };
378 let mut w = w.lock();
379
380 w.write_all(b"normal ").unwrap();
381 w.set_style(
382 Style::new()
383 .text(Color::Red)
384 .background(Color::Blue)
385 .intense(true),
386 )
387 .unwrap();
388 w.write_all(b"styled").unwrap();
389 w.set_style(Style::new().text(Color::Green)).unwrap();
390 w.write_all(b" styled2").unwrap();
391 w.set_style(&Style::new()).unwrap();
392 w.write_all(b" normal\n").unwrap();
393 w.flush().unwrap();
394 }
395}