1 | /*! |
2 | This crate provides a cross platform abstraction for writing colored text to |
3 | a terminal. Colors are written using either ANSI escape sequences or by |
4 | communicating with a Windows console. Much of this API was motivated by use |
5 | inside command line applications, where colors or styles can be configured |
6 | by the end user and/or the environment. |
7 | |
8 | This crate also provides platform independent support for writing colored text |
9 | to an in memory buffer. While this is easy to do with ANSI escape sequences |
10 | (because they are in the buffer themselves), it is trickier to do with the |
11 | Windows console API, which requires synchronous communication. |
12 | |
13 | # Organization |
14 | |
15 | The `WriteColor` trait extends the `io::Write` trait with methods for setting |
16 | colors or resetting them. |
17 | |
18 | `StandardStream` and `StandardStreamLock` both satisfy `WriteColor` and are |
19 | analogous to `std::io::Stdout` and `std::io::StdoutLock`, or `std::io::Stderr` |
20 | and `std::io::StderrLock`. |
21 | |
22 | `Buffer` is an in memory buffer that supports colored text. In a parallel |
23 | program, each thread might write to its own buffer. A buffer can be printed to |
24 | using a `BufferWriter`. The advantage of this design is that each thread can |
25 | work in parallel on a buffer without having to synchronize access to global |
26 | resources such as the Windows console. Moreover, this design also prevents |
27 | interleaving of buffer output. |
28 | |
29 | `Ansi` and `NoColor` both satisfy `WriteColor` for arbitrary implementors of |
30 | `io::Write`. These types are useful when you know exactly what you need. An |
31 | analogous type for the Windows console is not provided since it cannot exist. |
32 | |
33 | # Example: using `StandardStream` |
34 | |
35 | The `StandardStream` type in this crate works similarly to `std::io::Stdout`, |
36 | except it is augmented with methods for coloring by the `WriteColor` trait. |
37 | For example, to write some green text: |
38 | |
39 | ```rust,no_run |
40 | # fn test() -> Result<(), Box<::std::error::Error>> { |
41 | use std::io::Write; |
42 | use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; |
43 | |
44 | let mut stdout = StandardStream::stdout(ColorChoice::Always); |
45 | stdout.set_color(ColorSpec::new().set_fg(Some(Color::Green)))?; |
46 | writeln!(&mut stdout, "green text!" )?; |
47 | # Ok(()) } |
48 | ``` |
49 | |
50 | Note that any text written to the terminal now will be colored |
51 | green when using ANSI escape sequences, even if it is written via |
52 | stderr, and even if stderr had previously been set to `Color::Red`. |
53 | Users will need to manage any color changes themselves by calling |
54 | [`WriteColor::set_color`](trait.WriteColor.html#tymethod.set_color), and this |
55 | may include calling [`WriteColor::reset`](trait.WriteColor.html#tymethod.reset) |
56 | before the program exits to a shell. |
57 | |
58 | # Example: using `BufferWriter` |
59 | |
60 | A `BufferWriter` can create buffers and write buffers to stdout or stderr. It |
61 | does *not* implement `io::Write` or `WriteColor` itself. Instead, `Buffer` |
62 | implements `io::Write` and `io::WriteColor`. |
63 | |
64 | This example shows how to print some green text to stderr. |
65 | |
66 | ```rust,no_run |
67 | # fn test() -> Result<(), Box<::std::error::Error>> { |
68 | use std::io::Write; |
69 | use termcolor::{BufferWriter, Color, ColorChoice, ColorSpec, WriteColor}; |
70 | |
71 | let mut bufwtr = BufferWriter::stderr(ColorChoice::Always); |
72 | let mut buffer = bufwtr.buffer(); |
73 | buffer.set_color(ColorSpec::new().set_fg(Some(Color::Green)))?; |
74 | writeln!(&mut buffer, "green text!" )?; |
75 | bufwtr.print(&buffer)?; |
76 | # Ok(()) } |
77 | ``` |
78 | |
79 | # Detecting presence of a terminal |
80 | |
81 | In many scenarios when using color, one often wants to enable colors |
82 | automatically when writing to a terminal and disable colors automatically when |
83 | writing to anything else. The typical way to achieve this in Unix environments |
84 | is via libc's |
85 | [`isatty`](https://man7.org/linux/man-pages/man3/isatty.3.html) |
86 | function. |
87 | Unfortunately, this notoriously does not work well in Windows environments. To |
88 | work around that, the currently recommended solution is to use the |
89 | [`atty`](https://crates.io/crates/atty) |
90 | crate, which goes out of its way to get this as right as possible in Windows |
91 | environments. |
92 | |
93 | For example, in a command line application that exposes a `--color` flag, |
94 | your logic for how to enable colors might look like this: |
95 | |
96 | ```rust,ignore |
97 | use atty; |
98 | use termcolor::{ColorChoice, StandardStream}; |
99 | |
100 | let preference = argv.get_flag("color" ).unwrap_or("auto" ); |
101 | let mut choice = preference.parse::<ColorChoice>()?; |
102 | if choice == ColorChoice::Auto && !atty::is(atty::Stream::Stdout) { |
103 | choice = ColorChoice::Never; |
104 | } |
105 | let stdout = StandardStream::stdout(choice); |
106 | // ... write to stdout |
107 | ``` |
108 | |
109 | Currently, `termcolor` does not provide anything to do this for you. |
110 | */ |
111 | |
112 | #![deny (missing_debug_implementations, missing_docs)] |
113 | |
114 | // #[cfg(doctest)] |
115 | // use doc_comment::doctest; |
116 | // #[cfg(doctest)] |
117 | // doctest!("../README.md"); |
118 | |
119 | use std::env; |
120 | use std::error; |
121 | use std::fmt; |
122 | use std::io::{self, Write}; |
123 | use std::str::FromStr; |
124 | use std::sync::atomic::{AtomicBool, Ordering}; |
125 | #[cfg (windows)] |
126 | use std::sync::{Mutex, MutexGuard}; |
127 | |
128 | #[cfg (windows)] |
129 | use winapi_util::console as wincon; |
130 | |
131 | /// This trait describes the behavior of writers that support colored output. |
132 | pub trait WriteColor: io::Write { |
133 | /// Returns true if and only if the underlying writer supports colors. |
134 | fn supports_color(&self) -> bool; |
135 | |
136 | /// Set the color settings of the writer. |
137 | /// |
138 | /// Subsequent writes to this writer will use these settings until either |
139 | /// `reset` is called or new color settings are set. |
140 | /// |
141 | /// If there was a problem setting the color settings, then an error is |
142 | /// returned. |
143 | fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()>; |
144 | |
145 | /// Reset the current color settings to their original settings. |
146 | /// |
147 | /// If there was a problem resetting the color settings, then an error is |
148 | /// returned. |
149 | fn reset(&mut self) -> io::Result<()>; |
150 | |
151 | /// Returns true if and only if the underlying writer must synchronously |
152 | /// interact with an end user's device in order to control colors. By |
153 | /// default, this always returns `false`. |
154 | /// |
155 | /// In practice, this should return `true` if the underlying writer is |
156 | /// manipulating colors using the Windows console APIs. |
157 | /// |
158 | /// This is useful for writing generic code (such as a buffered writer) |
159 | /// that can perform certain optimizations when the underlying writer |
160 | /// doesn't rely on synchronous APIs. For example, ANSI escape sequences |
161 | /// can be passed through to the end user's device as is. |
162 | fn is_synchronous(&self) -> bool { |
163 | false |
164 | } |
165 | } |
166 | |
167 | impl<'a, T: ?Sized + WriteColor> WriteColor for &'a mut T { |
168 | fn supports_color(&self) -> bool { |
169 | (&**self).supports_color() |
170 | } |
171 | fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> { |
172 | (&mut **self).set_color(spec) |
173 | } |
174 | fn reset(&mut self) -> io::Result<()> { |
175 | (&mut **self).reset() |
176 | } |
177 | fn is_synchronous(&self) -> bool { |
178 | (&**self).is_synchronous() |
179 | } |
180 | } |
181 | |
182 | impl<T: ?Sized + WriteColor> WriteColor for Box<T> { |
183 | fn supports_color(&self) -> bool { |
184 | (&**self).supports_color() |
185 | } |
186 | fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> { |
187 | (&mut **self).set_color(spec) |
188 | } |
189 | fn reset(&mut self) -> io::Result<()> { |
190 | (&mut **self).reset() |
191 | } |
192 | fn is_synchronous(&self) -> bool { |
193 | (&**self).is_synchronous() |
194 | } |
195 | } |
196 | |
197 | /// ColorChoice represents the color preferences of an end user. |
198 | /// |
199 | /// The `Default` implementation for this type will select `Auto`, which tries |
200 | /// to do the right thing based on the current environment. |
201 | /// |
202 | /// The `FromStr` implementation for this type converts a lowercase kebab-case |
203 | /// string of the variant name to the corresponding variant. Any other string |
204 | /// results in an error. |
205 | #[derive (Clone, Copy, Debug, Eq, PartialEq)] |
206 | pub enum ColorChoice { |
207 | /// Try very hard to emit colors. This includes emitting ANSI colors |
208 | /// on Windows if the console API is unavailable. |
209 | Always, |
210 | /// AlwaysAnsi is like Always, except it never tries to use anything other |
211 | /// than emitting ANSI color codes. |
212 | AlwaysAnsi, |
213 | /// Try to use colors, but don't force the issue. If the console isn't |
214 | /// available on Windows, or if TERM=dumb, or if `NO_COLOR` is defined, for |
215 | /// example, then don't use colors. |
216 | Auto, |
217 | /// Never emit colors. |
218 | Never, |
219 | } |
220 | |
221 | /// The default is `Auto`. |
222 | impl Default for ColorChoice { |
223 | fn default() -> ColorChoice { |
224 | ColorChoice::Auto |
225 | } |
226 | } |
227 | |
228 | impl FromStr for ColorChoice { |
229 | type Err = ColorChoiceParseError; |
230 | |
231 | fn from_str(s: &str) -> Result<ColorChoice, ColorChoiceParseError> { |
232 | match s.to_lowercase().as_str() { |
233 | "always" => Ok(ColorChoice::Always), |
234 | "always-ansi" => Ok(ColorChoice::AlwaysAnsi), |
235 | "never" => Ok(ColorChoice::Never), |
236 | "auto" => Ok(ColorChoice::Auto), |
237 | unknown: &str => Err(ColorChoiceParseError { |
238 | unknown_choice: unknown.to_string(), |
239 | }), |
240 | } |
241 | } |
242 | } |
243 | |
244 | impl ColorChoice { |
245 | /// Returns true if we should attempt to write colored output. |
246 | fn should_attempt_color(&self) -> bool { |
247 | match *self { |
248 | ColorChoice::Always => true, |
249 | ColorChoice::AlwaysAnsi => true, |
250 | ColorChoice::Never => false, |
251 | ColorChoice::Auto => self.env_allows_color(), |
252 | } |
253 | } |
254 | |
255 | #[cfg (not(windows))] |
256 | fn env_allows_color(&self) -> bool { |
257 | match env::var_os("TERM" ) { |
258 | // If TERM isn't set, then we are in a weird environment that |
259 | // probably doesn't support colors. |
260 | None => return false, |
261 | Some(k) => { |
262 | if k == "dumb" { |
263 | return false; |
264 | } |
265 | } |
266 | } |
267 | // If TERM != dumb, then the only way we don't allow colors at this |
268 | // point is if NO_COLOR is set. |
269 | if env::var_os("NO_COLOR" ).is_some() { |
270 | return false; |
271 | } |
272 | true |
273 | } |
274 | |
275 | #[cfg (windows)] |
276 | fn env_allows_color(&self) -> bool { |
277 | // On Windows, if TERM isn't set, then we shouldn't automatically |
278 | // assume that colors aren't allowed. This is unlike Unix environments |
279 | // where TERM is more rigorously set. |
280 | if let Some(k) = env::var_os("TERM" ) { |
281 | if k == "dumb" { |
282 | return false; |
283 | } |
284 | } |
285 | // If TERM != dumb, then the only way we don't allow colors at this |
286 | // point is if NO_COLOR is set. |
287 | if env::var_os("NO_COLOR" ).is_some() { |
288 | return false; |
289 | } |
290 | true |
291 | } |
292 | |
293 | /// Returns true if this choice should forcefully use ANSI color codes. |
294 | /// |
295 | /// It's possible that ANSI is still the correct choice even if this |
296 | /// returns false. |
297 | #[cfg (windows)] |
298 | fn should_ansi(&self) -> bool { |
299 | match *self { |
300 | ColorChoice::Always => false, |
301 | ColorChoice::AlwaysAnsi => true, |
302 | ColorChoice::Never => false, |
303 | ColorChoice::Auto => { |
304 | match env::var("TERM" ) { |
305 | Err(_) => false, |
306 | // cygwin doesn't seem to support ANSI escape sequences |
307 | // and instead has its own variety. However, the Windows |
308 | // console API may be available. |
309 | Ok(k) => k != "dumb" && k != "cygwin" , |
310 | } |
311 | } |
312 | } |
313 | } |
314 | } |
315 | |
316 | /// An error that occurs when parsing a `ColorChoice` fails. |
317 | #[derive (Clone, Debug)] |
318 | pub struct ColorChoiceParseError { |
319 | unknown_choice: String, |
320 | } |
321 | |
322 | impl std::error::Error for ColorChoiceParseError {} |
323 | |
324 | impl fmt::Display for ColorChoiceParseError { |
325 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
326 | write!( |
327 | f, |
328 | "unrecognized color choice ' {}': valid choices are: \ |
329 | always, always-ansi, never, auto" , |
330 | self.unknown_choice, |
331 | ) |
332 | } |
333 | } |
334 | |
335 | /// `std::io` implements `Stdout` and `Stderr` (and their `Lock` variants) as |
336 | /// separate types, which makes it difficult to abstract over them. We use |
337 | /// some simple internal enum types to work around this. |
338 | |
339 | enum StandardStreamType { |
340 | Stdout, |
341 | Stderr, |
342 | StdoutBuffered, |
343 | StderrBuffered, |
344 | } |
345 | |
346 | #[derive (Debug)] |
347 | enum IoStandardStream { |
348 | Stdout(io::Stdout), |
349 | Stderr(io::Stderr), |
350 | StdoutBuffered(io::BufWriter<io::Stdout>), |
351 | StderrBuffered(io::BufWriter<io::Stderr>), |
352 | } |
353 | |
354 | impl IoStandardStream { |
355 | fn new(sty: StandardStreamType) -> IoStandardStream { |
356 | match sty { |
357 | StandardStreamType::Stdout => { |
358 | IoStandardStream::Stdout(io::stdout()) |
359 | } |
360 | StandardStreamType::Stderr => { |
361 | IoStandardStream::Stderr(io::stderr()) |
362 | } |
363 | StandardStreamType::StdoutBuffered => { |
364 | let wtr = io::BufWriter::new(io::stdout()); |
365 | IoStandardStream::StdoutBuffered(wtr) |
366 | } |
367 | StandardStreamType::StderrBuffered => { |
368 | let wtr = io::BufWriter::new(io::stderr()); |
369 | IoStandardStream::StderrBuffered(wtr) |
370 | } |
371 | } |
372 | } |
373 | |
374 | fn lock(&self) -> IoStandardStreamLock<'_> { |
375 | match *self { |
376 | IoStandardStream::Stdout(ref s) => { |
377 | IoStandardStreamLock::StdoutLock(s.lock()) |
378 | } |
379 | IoStandardStream::Stderr(ref s) => { |
380 | IoStandardStreamLock::StderrLock(s.lock()) |
381 | } |
382 | IoStandardStream::StdoutBuffered(_) |
383 | | IoStandardStream::StderrBuffered(_) => { |
384 | // We don't permit this case to ever occur in the public API, |
385 | // so it's OK to panic. |
386 | panic!("cannot lock a buffered standard stream" ) |
387 | } |
388 | } |
389 | } |
390 | } |
391 | |
392 | impl io::Write for IoStandardStream { |
393 | #[inline (always)] |
394 | fn write(&mut self, b: &[u8]) -> io::Result<usize> { |
395 | match *self { |
396 | IoStandardStream::Stdout(ref mut s: &mut Stdout) => s.write(buf:b), |
397 | IoStandardStream::Stderr(ref mut s: &mut Stderr) => s.write(buf:b), |
398 | IoStandardStream::StdoutBuffered(ref mut s: &mut BufWriter) => s.write(buf:b), |
399 | IoStandardStream::StderrBuffered(ref mut s: &mut BufWriter) => s.write(buf:b), |
400 | } |
401 | } |
402 | |
403 | #[inline (always)] |
404 | fn flush(&mut self) -> io::Result<()> { |
405 | match *self { |
406 | IoStandardStream::Stdout(ref mut s: &mut Stdout) => s.flush(), |
407 | IoStandardStream::Stderr(ref mut s: &mut Stderr) => s.flush(), |
408 | IoStandardStream::StdoutBuffered(ref mut s: &mut BufWriter) => s.flush(), |
409 | IoStandardStream::StderrBuffered(ref mut s: &mut BufWriter) => s.flush(), |
410 | } |
411 | } |
412 | } |
413 | |
414 | // Same rigmarole for the locked variants of the standard streams. |
415 | |
416 | #[derive (Debug)] |
417 | enum IoStandardStreamLock<'a> { |
418 | StdoutLock(io::StdoutLock<'a>), |
419 | StderrLock(io::StderrLock<'a>), |
420 | } |
421 | |
422 | impl<'a> io::Write for IoStandardStreamLock<'a> { |
423 | #[inline (always)] |
424 | fn write(&mut self, b: &[u8]) -> io::Result<usize> { |
425 | match *self { |
426 | IoStandardStreamLock::StdoutLock(ref mut s: &mut StdoutLock<'_>) => s.write(buf:b), |
427 | IoStandardStreamLock::StderrLock(ref mut s: &mut StderrLock<'_>) => s.write(buf:b), |
428 | } |
429 | } |
430 | |
431 | #[inline (always)] |
432 | fn flush(&mut self) -> io::Result<()> { |
433 | match *self { |
434 | IoStandardStreamLock::StdoutLock(ref mut s: &mut StdoutLock<'_>) => s.flush(), |
435 | IoStandardStreamLock::StderrLock(ref mut s: &mut StderrLock<'_>) => s.flush(), |
436 | } |
437 | } |
438 | } |
439 | |
440 | /// Satisfies `io::Write` and `WriteColor`, and supports optional coloring |
441 | /// to either of the standard output streams, stdout and stderr. |
442 | #[derive (Debug)] |
443 | pub struct StandardStream { |
444 | wtr: LossyStandardStream<WriterInner<IoStandardStream>>, |
445 | } |
446 | |
447 | /// `StandardStreamLock` is a locked reference to a `StandardStream`. |
448 | /// |
449 | /// This implements the `io::Write` and `WriteColor` traits, and is constructed |
450 | /// via the `Write::lock` method. |
451 | /// |
452 | /// The lifetime `'a` refers to the lifetime of the corresponding |
453 | /// `StandardStream`. |
454 | #[derive (Debug)] |
455 | pub struct StandardStreamLock<'a> { |
456 | wtr: LossyStandardStream<WriterInnerLock<'a, IoStandardStreamLock<'a>>>, |
457 | } |
458 | |
459 | /// Like `StandardStream`, but does buffered writing. |
460 | #[derive (Debug)] |
461 | pub struct BufferedStandardStream { |
462 | wtr: LossyStandardStream<WriterInner<IoStandardStream>>, |
463 | } |
464 | |
465 | /// WriterInner is a (limited) generic representation of a writer. It is |
466 | /// limited because W should only ever be stdout/stderr on Windows. |
467 | #[derive (Debug)] |
468 | enum WriterInner<W> { |
469 | NoColor(NoColor<W>), |
470 | Ansi(Ansi<W>), |
471 | #[cfg (windows)] |
472 | Windows { |
473 | wtr: W, |
474 | console: Mutex<wincon::Console>, |
475 | }, |
476 | } |
477 | |
478 | /// WriterInnerLock is a (limited) generic representation of a writer. It is |
479 | /// limited because W should only ever be stdout/stderr on Windows. |
480 | #[derive (Debug)] |
481 | enum WriterInnerLock<'a, W> { |
482 | NoColor(NoColor<W>), |
483 | Ansi(Ansi<W>), |
484 | /// What a gross hack. On Windows, we need to specify a lifetime for the |
485 | /// console when in a locked state, but obviously don't need to do that |
486 | /// on Unix, which makes the `'a` unused. To satisfy the compiler, we need |
487 | /// a PhantomData. |
488 | #[allow (dead_code)] |
489 | Unreachable(::std::marker::PhantomData<&'a ()>), |
490 | #[cfg (windows)] |
491 | Windows { |
492 | wtr: W, |
493 | console: MutexGuard<'a, wincon::Console>, |
494 | }, |
495 | } |
496 | |
497 | impl StandardStream { |
498 | /// Create a new `StandardStream` with the given color preferences that |
499 | /// writes to standard output. |
500 | /// |
501 | /// On Windows, if coloring is desired and a Windows console could not be |
502 | /// found, then ANSI escape sequences are used instead. |
503 | /// |
504 | /// The specific color/style settings can be configured when writing via |
505 | /// the `WriteColor` trait. |
506 | pub fn stdout(choice: ColorChoice) -> StandardStream { |
507 | let wtr = WriterInner::create(StandardStreamType::Stdout, choice); |
508 | StandardStream { wtr: LossyStandardStream::new(wtr) } |
509 | } |
510 | |
511 | /// Create a new `StandardStream` with the given color preferences that |
512 | /// writes to standard error. |
513 | /// |
514 | /// On Windows, if coloring is desired and a Windows console could not be |
515 | /// found, then ANSI escape sequences are used instead. |
516 | /// |
517 | /// The specific color/style settings can be configured when writing via |
518 | /// the `WriteColor` trait. |
519 | pub fn stderr(choice: ColorChoice) -> StandardStream { |
520 | let wtr = WriterInner::create(StandardStreamType::Stderr, choice); |
521 | StandardStream { wtr: LossyStandardStream::new(wtr) } |
522 | } |
523 | |
524 | /// Lock the underlying writer. |
525 | /// |
526 | /// The lock guard returned also satisfies `io::Write` and |
527 | /// `WriteColor`. |
528 | /// |
529 | /// This method is **not reentrant**. It may panic if `lock` is called |
530 | /// while a `StandardStreamLock` is still alive. |
531 | pub fn lock(&self) -> StandardStreamLock<'_> { |
532 | StandardStreamLock::from_stream(self) |
533 | } |
534 | } |
535 | |
536 | impl<'a> StandardStreamLock<'a> { |
537 | #[cfg (not(windows))] |
538 | fn from_stream(stream: &StandardStream) -> StandardStreamLock<'_> { |
539 | let locked = match *stream.wtr.get_ref() { |
540 | WriterInner::NoColor(ref w) => { |
541 | WriterInnerLock::NoColor(NoColor(w.0.lock())) |
542 | } |
543 | WriterInner::Ansi(ref w) => { |
544 | WriterInnerLock::Ansi(Ansi(w.0.lock())) |
545 | } |
546 | }; |
547 | StandardStreamLock { wtr: stream.wtr.wrap(locked) } |
548 | } |
549 | |
550 | #[cfg (windows)] |
551 | fn from_stream(stream: &StandardStream) -> StandardStreamLock { |
552 | let locked = match *stream.wtr.get_ref() { |
553 | WriterInner::NoColor(ref w) => { |
554 | WriterInnerLock::NoColor(NoColor(w.0.lock())) |
555 | } |
556 | WriterInner::Ansi(ref w) => { |
557 | WriterInnerLock::Ansi(Ansi(w.0.lock())) |
558 | } |
559 | #[cfg (windows)] |
560 | WriterInner::Windows { ref wtr, ref console } => { |
561 | WriterInnerLock::Windows { |
562 | wtr: wtr.lock(), |
563 | console: console.lock().unwrap(), |
564 | } |
565 | } |
566 | }; |
567 | StandardStreamLock { wtr: stream.wtr.wrap(locked) } |
568 | } |
569 | } |
570 | |
571 | impl BufferedStandardStream { |
572 | /// Create a new `BufferedStandardStream` with the given color preferences |
573 | /// that writes to standard output via a buffered writer. |
574 | /// |
575 | /// On Windows, if coloring is desired and a Windows console could not be |
576 | /// found, then ANSI escape sequences are used instead. |
577 | /// |
578 | /// The specific color/style settings can be configured when writing via |
579 | /// the `WriteColor` trait. |
580 | pub fn stdout(choice: ColorChoice) -> BufferedStandardStream { |
581 | let wtr = |
582 | WriterInner::create(StandardStreamType::StdoutBuffered, choice); |
583 | BufferedStandardStream { wtr: LossyStandardStream::new(wtr) } |
584 | } |
585 | |
586 | /// Create a new `BufferedStandardStream` with the given color preferences |
587 | /// that writes to standard error via a buffered writer. |
588 | /// |
589 | /// On Windows, if coloring is desired and a Windows console could not be |
590 | /// found, then ANSI escape sequences are used instead. |
591 | /// |
592 | /// The specific color/style settings can be configured when writing via |
593 | /// the `WriteColor` trait. |
594 | pub fn stderr(choice: ColorChoice) -> BufferedStandardStream { |
595 | let wtr = |
596 | WriterInner::create(StandardStreamType::StderrBuffered, choice); |
597 | BufferedStandardStream { wtr: LossyStandardStream::new(wtr) } |
598 | } |
599 | } |
600 | |
601 | impl WriterInner<IoStandardStream> { |
602 | /// Create a new inner writer for a standard stream with the given color |
603 | /// preferences. |
604 | #[cfg (not(windows))] |
605 | fn create( |
606 | sty: StandardStreamType, |
607 | choice: ColorChoice, |
608 | ) -> WriterInner<IoStandardStream> { |
609 | if choice.should_attempt_color() { |
610 | WriterInner::Ansi(Ansi(IoStandardStream::new(sty))) |
611 | } else { |
612 | WriterInner::NoColor(NoColor(IoStandardStream::new(sty))) |
613 | } |
614 | } |
615 | |
616 | /// Create a new inner writer for a standard stream with the given color |
617 | /// preferences. |
618 | /// |
619 | /// If coloring is desired and a Windows console could not be found, then |
620 | /// ANSI escape sequences are used instead. |
621 | #[cfg (windows)] |
622 | fn create( |
623 | sty: StandardStreamType, |
624 | choice: ColorChoice, |
625 | ) -> WriterInner<IoStandardStream> { |
626 | let mut con = match sty { |
627 | StandardStreamType::Stdout => wincon::Console::stdout(), |
628 | StandardStreamType::Stderr => wincon::Console::stderr(), |
629 | StandardStreamType::StdoutBuffered => wincon::Console::stdout(), |
630 | StandardStreamType::StderrBuffered => wincon::Console::stderr(), |
631 | }; |
632 | let is_console_virtual = con |
633 | .as_mut() |
634 | .map(|con| con.set_virtual_terminal_processing(true).is_ok()) |
635 | .unwrap_or(false); |
636 | if choice.should_attempt_color() { |
637 | if choice.should_ansi() || is_console_virtual { |
638 | WriterInner::Ansi(Ansi(IoStandardStream::new(sty))) |
639 | } else if let Ok(console) = con { |
640 | WriterInner::Windows { |
641 | wtr: IoStandardStream::new(sty), |
642 | console: Mutex::new(console), |
643 | } |
644 | } else { |
645 | WriterInner::Ansi(Ansi(IoStandardStream::new(sty))) |
646 | } |
647 | } else { |
648 | WriterInner::NoColor(NoColor(IoStandardStream::new(sty))) |
649 | } |
650 | } |
651 | } |
652 | |
653 | impl io::Write for StandardStream { |
654 | #[inline ] |
655 | fn write(&mut self, b: &[u8]) -> io::Result<usize> { |
656 | self.wtr.write(buf:b) |
657 | } |
658 | |
659 | #[inline ] |
660 | fn flush(&mut self) -> io::Result<()> { |
661 | self.wtr.flush() |
662 | } |
663 | } |
664 | |
665 | impl WriteColor for StandardStream { |
666 | #[inline ] |
667 | fn supports_color(&self) -> bool { |
668 | self.wtr.supports_color() |
669 | } |
670 | |
671 | #[inline ] |
672 | fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> { |
673 | self.wtr.set_color(spec) |
674 | } |
675 | |
676 | #[inline ] |
677 | fn reset(&mut self) -> io::Result<()> { |
678 | self.wtr.reset() |
679 | } |
680 | |
681 | #[inline ] |
682 | fn is_synchronous(&self) -> bool { |
683 | self.wtr.is_synchronous() |
684 | } |
685 | } |
686 | |
687 | impl<'a> io::Write for StandardStreamLock<'a> { |
688 | #[inline ] |
689 | fn write(&mut self, b: &[u8]) -> io::Result<usize> { |
690 | self.wtr.write(buf:b) |
691 | } |
692 | |
693 | #[inline ] |
694 | fn flush(&mut self) -> io::Result<()> { |
695 | self.wtr.flush() |
696 | } |
697 | } |
698 | |
699 | impl<'a> WriteColor for StandardStreamLock<'a> { |
700 | #[inline ] |
701 | fn supports_color(&self) -> bool { |
702 | self.wtr.supports_color() |
703 | } |
704 | |
705 | #[inline ] |
706 | fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> { |
707 | self.wtr.set_color(spec) |
708 | } |
709 | |
710 | #[inline ] |
711 | fn reset(&mut self) -> io::Result<()> { |
712 | self.wtr.reset() |
713 | } |
714 | |
715 | #[inline ] |
716 | fn is_synchronous(&self) -> bool { |
717 | self.wtr.is_synchronous() |
718 | } |
719 | } |
720 | |
721 | impl io::Write for BufferedStandardStream { |
722 | #[inline ] |
723 | fn write(&mut self, b: &[u8]) -> io::Result<usize> { |
724 | self.wtr.write(buf:b) |
725 | } |
726 | |
727 | #[inline ] |
728 | fn flush(&mut self) -> io::Result<()> { |
729 | self.wtr.flush() |
730 | } |
731 | } |
732 | |
733 | impl WriteColor for BufferedStandardStream { |
734 | #[inline ] |
735 | fn supports_color(&self) -> bool { |
736 | self.wtr.supports_color() |
737 | } |
738 | |
739 | #[inline ] |
740 | fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> { |
741 | if self.is_synchronous() { |
742 | self.wtr.flush()?; |
743 | } |
744 | self.wtr.set_color(spec) |
745 | } |
746 | |
747 | #[inline ] |
748 | fn reset(&mut self) -> io::Result<()> { |
749 | self.wtr.reset() |
750 | } |
751 | |
752 | #[inline ] |
753 | fn is_synchronous(&self) -> bool { |
754 | self.wtr.is_synchronous() |
755 | } |
756 | } |
757 | |
758 | impl<W: io::Write> io::Write for WriterInner<W> { |
759 | #[inline (always)] |
760 | fn write(&mut self, buf: &[u8]) -> io::Result<usize> { |
761 | match *self { |
762 | WriterInner::NoColor(ref mut wtr: &mut NoColor) => wtr.write(buf), |
763 | WriterInner::Ansi(ref mut wtr: &mut Ansi) => wtr.write(buf), |
764 | #[cfg (windows)] |
765 | WriterInner::Windows { ref mut wtr, .. } => wtr.write(buf), |
766 | } |
767 | } |
768 | |
769 | #[inline (always)] |
770 | fn flush(&mut self) -> io::Result<()> { |
771 | match *self { |
772 | WriterInner::NoColor(ref mut wtr: &mut NoColor) => wtr.flush(), |
773 | WriterInner::Ansi(ref mut wtr: &mut Ansi) => wtr.flush(), |
774 | #[cfg (windows)] |
775 | WriterInner::Windows { ref mut wtr, .. } => wtr.flush(), |
776 | } |
777 | } |
778 | } |
779 | |
780 | impl<W: io::Write> WriteColor for WriterInner<W> { |
781 | fn supports_color(&self) -> bool { |
782 | match *self { |
783 | WriterInner::NoColor(_) => false, |
784 | WriterInner::Ansi(_) => true, |
785 | #[cfg (windows)] |
786 | WriterInner::Windows { .. } => true, |
787 | } |
788 | } |
789 | |
790 | fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> { |
791 | match *self { |
792 | WriterInner::NoColor(ref mut wtr) => wtr.set_color(spec), |
793 | WriterInner::Ansi(ref mut wtr) => wtr.set_color(spec), |
794 | #[cfg (windows)] |
795 | WriterInner::Windows { ref mut wtr, ref console } => { |
796 | wtr.flush()?; |
797 | let mut console = console.lock().unwrap(); |
798 | spec.write_console(&mut *console) |
799 | } |
800 | } |
801 | } |
802 | |
803 | fn reset(&mut self) -> io::Result<()> { |
804 | match *self { |
805 | WriterInner::NoColor(ref mut wtr) => wtr.reset(), |
806 | WriterInner::Ansi(ref mut wtr) => wtr.reset(), |
807 | #[cfg (windows)] |
808 | WriterInner::Windows { ref mut wtr, ref mut console } => { |
809 | wtr.flush()?; |
810 | console.lock().unwrap().reset()?; |
811 | Ok(()) |
812 | } |
813 | } |
814 | } |
815 | |
816 | fn is_synchronous(&self) -> bool { |
817 | match *self { |
818 | WriterInner::NoColor(_) => false, |
819 | WriterInner::Ansi(_) => false, |
820 | #[cfg (windows)] |
821 | WriterInner::Windows { .. } => true, |
822 | } |
823 | } |
824 | } |
825 | |
826 | impl<'a, W: io::Write> io::Write for WriterInnerLock<'a, W> { |
827 | fn write(&mut self, buf: &[u8]) -> io::Result<usize> { |
828 | match *self { |
829 | WriterInnerLock::Unreachable(_) => unreachable!(), |
830 | WriterInnerLock::NoColor(ref mut wtr: &mut NoColor) => wtr.write(buf), |
831 | WriterInnerLock::Ansi(ref mut wtr: &mut Ansi) => wtr.write(buf), |
832 | #[cfg (windows)] |
833 | WriterInnerLock::Windows { ref mut wtr, .. } => wtr.write(buf), |
834 | } |
835 | } |
836 | |
837 | fn flush(&mut self) -> io::Result<()> { |
838 | match *self { |
839 | WriterInnerLock::Unreachable(_) => unreachable!(), |
840 | WriterInnerLock::NoColor(ref mut wtr: &mut NoColor) => wtr.flush(), |
841 | WriterInnerLock::Ansi(ref mut wtr: &mut Ansi) => wtr.flush(), |
842 | #[cfg (windows)] |
843 | WriterInnerLock::Windows { ref mut wtr, .. } => wtr.flush(), |
844 | } |
845 | } |
846 | } |
847 | |
848 | impl<'a, W: io::Write> WriteColor for WriterInnerLock<'a, W> { |
849 | fn supports_color(&self) -> bool { |
850 | match *self { |
851 | WriterInnerLock::Unreachable(_) => unreachable!(), |
852 | WriterInnerLock::NoColor(_) => false, |
853 | WriterInnerLock::Ansi(_) => true, |
854 | #[cfg (windows)] |
855 | WriterInnerLock::Windows { .. } => true, |
856 | } |
857 | } |
858 | |
859 | fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> { |
860 | match *self { |
861 | WriterInnerLock::Unreachable(_) => unreachable!(), |
862 | WriterInnerLock::NoColor(ref mut wtr) => wtr.set_color(spec), |
863 | WriterInnerLock::Ansi(ref mut wtr) => wtr.set_color(spec), |
864 | #[cfg (windows)] |
865 | WriterInnerLock::Windows { ref mut wtr, ref mut console } => { |
866 | wtr.flush()?; |
867 | spec.write_console(console) |
868 | } |
869 | } |
870 | } |
871 | |
872 | fn reset(&mut self) -> io::Result<()> { |
873 | match *self { |
874 | WriterInnerLock::Unreachable(_) => unreachable!(), |
875 | WriterInnerLock::NoColor(ref mut wtr) => wtr.reset(), |
876 | WriterInnerLock::Ansi(ref mut wtr) => wtr.reset(), |
877 | #[cfg (windows)] |
878 | WriterInnerLock::Windows { ref mut wtr, ref mut console } => { |
879 | wtr.flush()?; |
880 | console.reset()?; |
881 | Ok(()) |
882 | } |
883 | } |
884 | } |
885 | |
886 | fn is_synchronous(&self) -> bool { |
887 | match *self { |
888 | WriterInnerLock::Unreachable(_) => unreachable!(), |
889 | WriterInnerLock::NoColor(_) => false, |
890 | WriterInnerLock::Ansi(_) => false, |
891 | #[cfg (windows)] |
892 | WriterInnerLock::Windows { .. } => true, |
893 | } |
894 | } |
895 | } |
896 | |
897 | /// Writes colored buffers to stdout or stderr. |
898 | /// |
899 | /// Writable buffers can be obtained by calling `buffer` on a `BufferWriter`. |
900 | /// |
901 | /// This writer works with terminals that support ANSI escape sequences or |
902 | /// with a Windows console. |
903 | /// |
904 | /// It is intended for a `BufferWriter` to be put in an `Arc` and written to |
905 | /// from multiple threads simultaneously. |
906 | #[derive (Debug)] |
907 | pub struct BufferWriter { |
908 | stream: LossyStandardStream<IoStandardStream>, |
909 | printed: AtomicBool, |
910 | separator: Option<Vec<u8>>, |
911 | color_choice: ColorChoice, |
912 | #[cfg (windows)] |
913 | console: Option<Mutex<wincon::Console>>, |
914 | } |
915 | |
916 | impl BufferWriter { |
917 | /// Create a new `BufferWriter` that writes to a standard stream with the |
918 | /// given color preferences. |
919 | /// |
920 | /// The specific color/style settings can be configured when writing to |
921 | /// the buffers themselves. |
922 | #[cfg (not(windows))] |
923 | fn create(sty: StandardStreamType, choice: ColorChoice) -> BufferWriter { |
924 | BufferWriter { |
925 | stream: LossyStandardStream::new(IoStandardStream::new(sty)), |
926 | printed: AtomicBool::new(false), |
927 | separator: None, |
928 | color_choice: choice, |
929 | } |
930 | } |
931 | |
932 | /// Create a new `BufferWriter` that writes to a standard stream with the |
933 | /// given color preferences. |
934 | /// |
935 | /// If coloring is desired and a Windows console could not be found, then |
936 | /// ANSI escape sequences are used instead. |
937 | /// |
938 | /// The specific color/style settings can be configured when writing to |
939 | /// the buffers themselves. |
940 | #[cfg (windows)] |
941 | fn create(sty: StandardStreamType, choice: ColorChoice) -> BufferWriter { |
942 | let mut con = match sty { |
943 | StandardStreamType::Stdout => wincon::Console::stdout(), |
944 | StandardStreamType::Stderr => wincon::Console::stderr(), |
945 | StandardStreamType::StdoutBuffered => wincon::Console::stdout(), |
946 | StandardStreamType::StderrBuffered => wincon::Console::stderr(), |
947 | } |
948 | .ok(); |
949 | let is_console_virtual = con |
950 | .as_mut() |
951 | .map(|con| con.set_virtual_terminal_processing(true).is_ok()) |
952 | .unwrap_or(false); |
953 | // If we can enable ANSI on Windows, then we don't need the console |
954 | // anymore. |
955 | if is_console_virtual { |
956 | con = None; |
957 | } |
958 | let stream = LossyStandardStream::new(IoStandardStream::new(sty)); |
959 | BufferWriter { |
960 | stream, |
961 | printed: AtomicBool::new(false), |
962 | separator: None, |
963 | color_choice: choice, |
964 | console: con.map(Mutex::new), |
965 | } |
966 | } |
967 | |
968 | /// Create a new `BufferWriter` that writes to stdout with the given |
969 | /// color preferences. |
970 | /// |
971 | /// On Windows, if coloring is desired and a Windows console could not be |
972 | /// found, then ANSI escape sequences are used instead. |
973 | /// |
974 | /// The specific color/style settings can be configured when writing to |
975 | /// the buffers themselves. |
976 | pub fn stdout(choice: ColorChoice) -> BufferWriter { |
977 | BufferWriter::create(StandardStreamType::Stdout, choice) |
978 | } |
979 | |
980 | /// Create a new `BufferWriter` that writes to stderr with the given |
981 | /// color preferences. |
982 | /// |
983 | /// On Windows, if coloring is desired and a Windows console could not be |
984 | /// found, then ANSI escape sequences are used instead. |
985 | /// |
986 | /// The specific color/style settings can be configured when writing to |
987 | /// the buffers themselves. |
988 | pub fn stderr(choice: ColorChoice) -> BufferWriter { |
989 | BufferWriter::create(StandardStreamType::Stderr, choice) |
990 | } |
991 | |
992 | /// If set, the separator given is printed between buffers. By default, no |
993 | /// separator is printed. |
994 | /// |
995 | /// The default value is `None`. |
996 | pub fn separator(&mut self, sep: Option<Vec<u8>>) { |
997 | self.separator = sep; |
998 | } |
999 | |
1000 | /// Creates a new `Buffer` with the current color preferences. |
1001 | /// |
1002 | /// A `Buffer` satisfies both `io::Write` and `WriteColor`. A `Buffer` can |
1003 | /// be printed using the `print` method. |
1004 | #[cfg (not(windows))] |
1005 | pub fn buffer(&self) -> Buffer { |
1006 | Buffer::new(self.color_choice) |
1007 | } |
1008 | |
1009 | /// Creates a new `Buffer` with the current color preferences. |
1010 | /// |
1011 | /// A `Buffer` satisfies both `io::Write` and `WriteColor`. A `Buffer` can |
1012 | /// be printed using the `print` method. |
1013 | #[cfg (windows)] |
1014 | pub fn buffer(&self) -> Buffer { |
1015 | Buffer::new(self.color_choice, self.console.is_some()) |
1016 | } |
1017 | |
1018 | /// Prints the contents of the given buffer. |
1019 | /// |
1020 | /// It is safe to call this from multiple threads simultaneously. In |
1021 | /// particular, all buffers are written atomically. No interleaving will |
1022 | /// occur. |
1023 | pub fn print(&self, buf: &Buffer) -> io::Result<()> { |
1024 | if buf.is_empty() { |
1025 | return Ok(()); |
1026 | } |
1027 | let mut stream = self.stream.wrap(self.stream.get_ref().lock()); |
1028 | if let Some(ref sep) = self.separator { |
1029 | if self.printed.load(Ordering::SeqCst) { |
1030 | stream.write_all(sep)?; |
1031 | stream.write_all(b" \n" )?; |
1032 | } |
1033 | } |
1034 | match buf.0 { |
1035 | BufferInner::NoColor(ref b) => stream.write_all(&b.0)?, |
1036 | BufferInner::Ansi(ref b) => stream.write_all(&b.0)?, |
1037 | #[cfg (windows)] |
1038 | BufferInner::Windows(ref b) => { |
1039 | // We guarantee by construction that we have a console here. |
1040 | // Namely, a BufferWriter is the only way to produce a Buffer. |
1041 | let console_mutex = self |
1042 | .console |
1043 | .as_ref() |
1044 | .expect("got Windows buffer but have no Console" ); |
1045 | let mut console = console_mutex.lock().unwrap(); |
1046 | b.print(&mut *console, &mut stream)?; |
1047 | } |
1048 | } |
1049 | self.printed.store(true, Ordering::SeqCst); |
1050 | Ok(()) |
1051 | } |
1052 | } |
1053 | |
1054 | /// Write colored text to memory. |
1055 | /// |
1056 | /// `Buffer` is a platform independent abstraction for printing colored text to |
1057 | /// an in memory buffer. When the buffer is printed using a `BufferWriter`, the |
1058 | /// color information will be applied to the output device (a tty on Unix and a |
1059 | /// console on Windows). |
1060 | /// |
1061 | /// A `Buffer` is typically created by calling the `BufferWriter.buffer` |
1062 | /// method, which will take color preferences and the environment into |
1063 | /// account. However, buffers can also be manually created using `no_color`, |
1064 | /// `ansi` or `console` (on Windows). |
1065 | #[derive (Debug)] |
1066 | pub struct Buffer(BufferInner); |
1067 | |
1068 | /// BufferInner is an enumeration of different buffer types. |
1069 | #[derive (Debug)] |
1070 | enum BufferInner { |
1071 | /// No coloring information should be applied. This ignores all coloring |
1072 | /// directives. |
1073 | NoColor(NoColor<Vec<u8>>), |
1074 | /// Apply coloring using ANSI escape sequences embedded into the buffer. |
1075 | Ansi(Ansi<Vec<u8>>), |
1076 | /// Apply coloring using the Windows console APIs. This buffer saves |
1077 | /// color information in memory and only interacts with the console when |
1078 | /// the buffer is printed. |
1079 | #[cfg (windows)] |
1080 | Windows(WindowsBuffer), |
1081 | } |
1082 | |
1083 | impl Buffer { |
1084 | /// Create a new buffer with the given color settings. |
1085 | #[cfg (not(windows))] |
1086 | fn new(choice: ColorChoice) -> Buffer { |
1087 | if choice.should_attempt_color() { |
1088 | Buffer::ansi() |
1089 | } else { |
1090 | Buffer::no_color() |
1091 | } |
1092 | } |
1093 | |
1094 | /// Create a new buffer with the given color settings. |
1095 | /// |
1096 | /// On Windows, one can elect to create a buffer capable of being written |
1097 | /// to a console. Only enable it if a console is available. |
1098 | /// |
1099 | /// If coloring is desired and `console` is false, then ANSI escape |
1100 | /// sequences are used instead. |
1101 | #[cfg (windows)] |
1102 | fn new(choice: ColorChoice, console: bool) -> Buffer { |
1103 | if choice.should_attempt_color() { |
1104 | if !console || choice.should_ansi() { |
1105 | Buffer::ansi() |
1106 | } else { |
1107 | Buffer::console() |
1108 | } |
1109 | } else { |
1110 | Buffer::no_color() |
1111 | } |
1112 | } |
1113 | |
1114 | /// Create a buffer that drops all color information. |
1115 | pub fn no_color() -> Buffer { |
1116 | Buffer(BufferInner::NoColor(NoColor(vec![]))) |
1117 | } |
1118 | |
1119 | /// Create a buffer that uses ANSI escape sequences. |
1120 | pub fn ansi() -> Buffer { |
1121 | Buffer(BufferInner::Ansi(Ansi(vec![]))) |
1122 | } |
1123 | |
1124 | /// Create a buffer that can be written to a Windows console. |
1125 | #[cfg (windows)] |
1126 | pub fn console() -> Buffer { |
1127 | Buffer(BufferInner::Windows(WindowsBuffer::new())) |
1128 | } |
1129 | |
1130 | /// Returns true if and only if this buffer is empty. |
1131 | pub fn is_empty(&self) -> bool { |
1132 | self.len() == 0 |
1133 | } |
1134 | |
1135 | /// Returns the length of this buffer in bytes. |
1136 | pub fn len(&self) -> usize { |
1137 | match self.0 { |
1138 | BufferInner::NoColor(ref b) => b.0.len(), |
1139 | BufferInner::Ansi(ref b) => b.0.len(), |
1140 | #[cfg (windows)] |
1141 | BufferInner::Windows(ref b) => b.buf.len(), |
1142 | } |
1143 | } |
1144 | |
1145 | /// Clears this buffer. |
1146 | pub fn clear(&mut self) { |
1147 | match self.0 { |
1148 | BufferInner::NoColor(ref mut b) => b.0.clear(), |
1149 | BufferInner::Ansi(ref mut b) => b.0.clear(), |
1150 | #[cfg (windows)] |
1151 | BufferInner::Windows(ref mut b) => b.clear(), |
1152 | } |
1153 | } |
1154 | |
1155 | /// Consume this buffer and return the underlying raw data. |
1156 | /// |
1157 | /// On Windows, this unrecoverably drops all color information associated |
1158 | /// with the buffer. |
1159 | pub fn into_inner(self) -> Vec<u8> { |
1160 | match self.0 { |
1161 | BufferInner::NoColor(b) => b.0, |
1162 | BufferInner::Ansi(b) => b.0, |
1163 | #[cfg (windows)] |
1164 | BufferInner::Windows(b) => b.buf, |
1165 | } |
1166 | } |
1167 | |
1168 | /// Return the underlying data of the buffer. |
1169 | pub fn as_slice(&self) -> &[u8] { |
1170 | match self.0 { |
1171 | BufferInner::NoColor(ref b) => &b.0, |
1172 | BufferInner::Ansi(ref b) => &b.0, |
1173 | #[cfg (windows)] |
1174 | BufferInner::Windows(ref b) => &b.buf, |
1175 | } |
1176 | } |
1177 | |
1178 | /// Return the underlying data of the buffer as a mutable slice. |
1179 | pub fn as_mut_slice(&mut self) -> &mut [u8] { |
1180 | match self.0 { |
1181 | BufferInner::NoColor(ref mut b) => &mut b.0, |
1182 | BufferInner::Ansi(ref mut b) => &mut b.0, |
1183 | #[cfg (windows)] |
1184 | BufferInner::Windows(ref mut b) => &mut b.buf, |
1185 | } |
1186 | } |
1187 | } |
1188 | |
1189 | impl io::Write for Buffer { |
1190 | #[inline ] |
1191 | fn write(&mut self, buf: &[u8]) -> io::Result<usize> { |
1192 | match self.0 { |
1193 | BufferInner::NoColor(ref mut w: &mut NoColor>) => w.write(buf), |
1194 | BufferInner::Ansi(ref mut w: &mut Ansi>) => w.write(buf), |
1195 | #[cfg (windows)] |
1196 | BufferInner::Windows(ref mut w) => w.write(buf), |
1197 | } |
1198 | } |
1199 | |
1200 | #[inline ] |
1201 | fn flush(&mut self) -> io::Result<()> { |
1202 | match self.0 { |
1203 | BufferInner::NoColor(ref mut w: &mut NoColor>) => w.flush(), |
1204 | BufferInner::Ansi(ref mut w: &mut Ansi>) => w.flush(), |
1205 | #[cfg (windows)] |
1206 | BufferInner::Windows(ref mut w) => w.flush(), |
1207 | } |
1208 | } |
1209 | } |
1210 | |
1211 | impl WriteColor for Buffer { |
1212 | #[inline ] |
1213 | fn supports_color(&self) -> bool { |
1214 | match self.0 { |
1215 | BufferInner::NoColor(_) => false, |
1216 | BufferInner::Ansi(_) => true, |
1217 | #[cfg (windows)] |
1218 | BufferInner::Windows(_) => true, |
1219 | } |
1220 | } |
1221 | |
1222 | #[inline ] |
1223 | fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> { |
1224 | match self.0 { |
1225 | BufferInner::NoColor(ref mut w) => w.set_color(spec), |
1226 | BufferInner::Ansi(ref mut w) => w.set_color(spec), |
1227 | #[cfg (windows)] |
1228 | BufferInner::Windows(ref mut w) => w.set_color(spec), |
1229 | } |
1230 | } |
1231 | |
1232 | #[inline ] |
1233 | fn reset(&mut self) -> io::Result<()> { |
1234 | match self.0 { |
1235 | BufferInner::NoColor(ref mut w) => w.reset(), |
1236 | BufferInner::Ansi(ref mut w) => w.reset(), |
1237 | #[cfg (windows)] |
1238 | BufferInner::Windows(ref mut w) => w.reset(), |
1239 | } |
1240 | } |
1241 | |
1242 | #[inline ] |
1243 | fn is_synchronous(&self) -> bool { |
1244 | false |
1245 | } |
1246 | } |
1247 | |
1248 | /// Satisfies `WriteColor` but ignores all color options. |
1249 | #[derive (Debug)] |
1250 | pub struct NoColor<W>(W); |
1251 | |
1252 | impl<W: Write> NoColor<W> { |
1253 | /// Create a new writer that satisfies `WriteColor` but drops all color |
1254 | /// information. |
1255 | pub fn new(wtr: W) -> NoColor<W> { |
1256 | NoColor(wtr) |
1257 | } |
1258 | |
1259 | /// Consume this `NoColor` value and return the inner writer. |
1260 | pub fn into_inner(self) -> W { |
1261 | self.0 |
1262 | } |
1263 | |
1264 | /// Return a reference to the inner writer. |
1265 | pub fn get_ref(&self) -> &W { |
1266 | &self.0 |
1267 | } |
1268 | |
1269 | /// Return a mutable reference to the inner writer. |
1270 | pub fn get_mut(&mut self) -> &mut W { |
1271 | &mut self.0 |
1272 | } |
1273 | } |
1274 | |
1275 | impl<W: io::Write> io::Write for NoColor<W> { |
1276 | #[inline ] |
1277 | fn write(&mut self, buf: &[u8]) -> io::Result<usize> { |
1278 | self.0.write(buf) |
1279 | } |
1280 | |
1281 | #[inline ] |
1282 | fn flush(&mut self) -> io::Result<()> { |
1283 | self.0.flush() |
1284 | } |
1285 | } |
1286 | |
1287 | impl<W: io::Write> WriteColor for NoColor<W> { |
1288 | #[inline ] |
1289 | fn supports_color(&self) -> bool { |
1290 | false |
1291 | } |
1292 | |
1293 | #[inline ] |
1294 | fn set_color(&mut self, _: &ColorSpec) -> io::Result<()> { |
1295 | Ok(()) |
1296 | } |
1297 | |
1298 | #[inline ] |
1299 | fn reset(&mut self) -> io::Result<()> { |
1300 | Ok(()) |
1301 | } |
1302 | |
1303 | #[inline ] |
1304 | fn is_synchronous(&self) -> bool { |
1305 | false |
1306 | } |
1307 | } |
1308 | |
1309 | /// Satisfies `WriteColor` using standard ANSI escape sequences. |
1310 | #[derive (Debug)] |
1311 | pub struct Ansi<W>(W); |
1312 | |
1313 | impl<W: Write> Ansi<W> { |
1314 | /// Create a new writer that satisfies `WriteColor` using standard ANSI |
1315 | /// escape sequences. |
1316 | pub fn new(wtr: W) -> Ansi<W> { |
1317 | Ansi(wtr) |
1318 | } |
1319 | |
1320 | /// Consume this `Ansi` value and return the inner writer. |
1321 | pub fn into_inner(self) -> W { |
1322 | self.0 |
1323 | } |
1324 | |
1325 | /// Return a reference to the inner writer. |
1326 | pub fn get_ref(&self) -> &W { |
1327 | &self.0 |
1328 | } |
1329 | |
1330 | /// Return a mutable reference to the inner writer. |
1331 | pub fn get_mut(&mut self) -> &mut W { |
1332 | &mut self.0 |
1333 | } |
1334 | } |
1335 | |
1336 | impl<W: io::Write> io::Write for Ansi<W> { |
1337 | #[inline ] |
1338 | fn write(&mut self, buf: &[u8]) -> io::Result<usize> { |
1339 | self.0.write(buf) |
1340 | } |
1341 | |
1342 | // Adding this method here is not required because it has a default impl, |
1343 | // but it seems to provide a perf improvement in some cases when using |
1344 | // a `BufWriter` with lots of writes. |
1345 | // |
1346 | // See https://github.com/BurntSushi/termcolor/pull/56 for more details |
1347 | // and a minimized example. |
1348 | #[inline ] |
1349 | fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { |
1350 | self.0.write_all(buf) |
1351 | } |
1352 | |
1353 | #[inline ] |
1354 | fn flush(&mut self) -> io::Result<()> { |
1355 | self.0.flush() |
1356 | } |
1357 | } |
1358 | |
1359 | impl<W: io::Write> WriteColor for Ansi<W> { |
1360 | #[inline ] |
1361 | fn supports_color(&self) -> bool { |
1362 | true |
1363 | } |
1364 | |
1365 | #[inline ] |
1366 | fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> { |
1367 | if spec.reset { |
1368 | self.reset()?; |
1369 | } |
1370 | if spec.bold { |
1371 | self.write_str(" \x1B[1m" )?; |
1372 | } |
1373 | if spec.dimmed { |
1374 | self.write_str(" \x1B[2m" )?; |
1375 | } |
1376 | if spec.italic { |
1377 | self.write_str(" \x1B[3m" )?; |
1378 | } |
1379 | if spec.underline { |
1380 | self.write_str(" \x1B[4m" )?; |
1381 | } |
1382 | if spec.strikethrough { |
1383 | self.write_str(" \x1B[9m" )?; |
1384 | } |
1385 | if let Some(ref c) = spec.fg_color { |
1386 | self.write_color(true, c, spec.intense)?; |
1387 | } |
1388 | if let Some(ref c) = spec.bg_color { |
1389 | self.write_color(false, c, spec.intense)?; |
1390 | } |
1391 | Ok(()) |
1392 | } |
1393 | |
1394 | #[inline ] |
1395 | fn reset(&mut self) -> io::Result<()> { |
1396 | self.write_str(" \x1B[0m" ) |
1397 | } |
1398 | |
1399 | #[inline ] |
1400 | fn is_synchronous(&self) -> bool { |
1401 | false |
1402 | } |
1403 | } |
1404 | |
1405 | impl<W: io::Write> Ansi<W> { |
1406 | fn write_str(&mut self, s: &str) -> io::Result<()> { |
1407 | self.write_all(s.as_bytes()) |
1408 | } |
1409 | |
1410 | fn write_color( |
1411 | &mut self, |
1412 | fg: bool, |
1413 | c: &Color, |
1414 | intense: bool, |
1415 | ) -> io::Result<()> { |
1416 | macro_rules! write_intense { |
1417 | ($clr:expr) => { |
1418 | if fg { |
1419 | self.write_str(concat!(" \x1B[38;5;" , $clr, "m" )) |
1420 | } else { |
1421 | self.write_str(concat!(" \x1B[48;5;" , $clr, "m" )) |
1422 | } |
1423 | }; |
1424 | } |
1425 | macro_rules! write_normal { |
1426 | ($clr:expr) => { |
1427 | if fg { |
1428 | self.write_str(concat!(" \x1B[3" , $clr, "m" )) |
1429 | } else { |
1430 | self.write_str(concat!(" \x1B[4" , $clr, "m" )) |
1431 | } |
1432 | }; |
1433 | } |
1434 | macro_rules! write_var_ansi_code { |
1435 | ($pre:expr, $($code:expr),+) => {{ |
1436 | // The loop generates at worst a literal of the form |
1437 | // '255,255,255m' which is 12-bytes. |
1438 | // The largest `pre` expression we currently use is 7 bytes. |
1439 | // This gives us the maximum of 19-bytes for our work buffer. |
1440 | let pre_len = $pre.len(); |
1441 | assert!(pre_len <= 7); |
1442 | let mut fmt = [0u8; 19]; |
1443 | fmt[..pre_len].copy_from_slice($pre); |
1444 | let mut i = pre_len - 1; |
1445 | $( |
1446 | let c1: u8 = ($code / 100) % 10; |
1447 | let c2: u8 = ($code / 10) % 10; |
1448 | let c3: u8 = $code % 10; |
1449 | let mut printed = false; |
1450 | |
1451 | if c1 != 0 { |
1452 | printed = true; |
1453 | i += 1; |
1454 | fmt[i] = b'0' + c1; |
1455 | } |
1456 | if c2 != 0 || printed { |
1457 | i += 1; |
1458 | fmt[i] = b'0' + c2; |
1459 | } |
1460 | // If we received a zero value we must still print a value. |
1461 | i += 1; |
1462 | fmt[i] = b'0' + c3; |
1463 | i += 1; |
1464 | fmt[i] = b';' ; |
1465 | )+ |
1466 | |
1467 | fmt[i] = b'm' ; |
1468 | self.write_all(&fmt[0..i+1]) |
1469 | }} |
1470 | } |
1471 | macro_rules! write_custom { |
1472 | ($ansi256:expr) => { |
1473 | if fg { |
1474 | write_var_ansi_code!(b" \x1B[38;5;" , $ansi256) |
1475 | } else { |
1476 | write_var_ansi_code!(b" \x1B[48;5;" , $ansi256) |
1477 | } |
1478 | }; |
1479 | |
1480 | ($r:expr, $g:expr, $b:expr) => {{ |
1481 | if fg { |
1482 | write_var_ansi_code!(b" \x1B[38;2;" , $r, $g, $b) |
1483 | } else { |
1484 | write_var_ansi_code!(b" \x1B[48;2;" , $r, $g, $b) |
1485 | } |
1486 | }}; |
1487 | } |
1488 | if intense { |
1489 | match *c { |
1490 | Color::Black => write_intense!("8" ), |
1491 | Color::Blue => write_intense!("12" ), |
1492 | Color::Green => write_intense!("10" ), |
1493 | Color::Red => write_intense!("9" ), |
1494 | Color::Cyan => write_intense!("14" ), |
1495 | Color::Magenta => write_intense!("13" ), |
1496 | Color::Yellow => write_intense!("11" ), |
1497 | Color::White => write_intense!("15" ), |
1498 | Color::Ansi256(c) => write_custom!(c), |
1499 | Color::Rgb(r, g, b) => write_custom!(r, g, b), |
1500 | Color::__Nonexhaustive => unreachable!(), |
1501 | } |
1502 | } else { |
1503 | match *c { |
1504 | Color::Black => write_normal!("0" ), |
1505 | Color::Blue => write_normal!("4" ), |
1506 | Color::Green => write_normal!("2" ), |
1507 | Color::Red => write_normal!("1" ), |
1508 | Color::Cyan => write_normal!("6" ), |
1509 | Color::Magenta => write_normal!("5" ), |
1510 | Color::Yellow => write_normal!("3" ), |
1511 | Color::White => write_normal!("7" ), |
1512 | Color::Ansi256(c) => write_custom!(c), |
1513 | Color::Rgb(r, g, b) => write_custom!(r, g, b), |
1514 | Color::__Nonexhaustive => unreachable!(), |
1515 | } |
1516 | } |
1517 | } |
1518 | } |
1519 | |
1520 | impl WriteColor for io::Sink { |
1521 | fn supports_color(&self) -> bool { |
1522 | false |
1523 | } |
1524 | |
1525 | fn set_color(&mut self, _: &ColorSpec) -> io::Result<()> { |
1526 | Ok(()) |
1527 | } |
1528 | |
1529 | fn reset(&mut self) -> io::Result<()> { |
1530 | Ok(()) |
1531 | } |
1532 | } |
1533 | |
1534 | /// An in-memory buffer that provides Windows console coloring. |
1535 | /// |
1536 | /// This doesn't actually communicate with the Windows console. Instead, it |
1537 | /// acts like a normal buffer but also saves the color information associated |
1538 | /// with positions in the buffer. It is only when the buffer is written to the |
1539 | /// console that coloring is actually applied. |
1540 | /// |
1541 | /// This is roughly isomorphic to the ANSI based approach (i.e., |
1542 | /// `Ansi<Vec<u8>>`), except with ANSI, the color information is embedded |
1543 | /// directly into the buffer. |
1544 | /// |
1545 | /// Note that there is no way to write something generic like |
1546 | /// `WindowsConsole<W: io::Write>` since coloring on Windows is tied |
1547 | /// specifically to the console APIs, and therefore can't work on arbitrary |
1548 | /// writers. |
1549 | #[cfg (windows)] |
1550 | #[derive (Clone, Debug)] |
1551 | struct WindowsBuffer { |
1552 | /// The actual content that should be printed. |
1553 | buf: Vec<u8>, |
1554 | /// A sequence of position oriented color specifications. Namely, each |
1555 | /// element is a position and a color spec, where the color spec should |
1556 | /// be applied at the position inside of `buf`. |
1557 | /// |
1558 | /// A missing color spec implies the underlying console should be reset. |
1559 | colors: Vec<(usize, Option<ColorSpec>)>, |
1560 | } |
1561 | |
1562 | #[cfg (windows)] |
1563 | impl WindowsBuffer { |
1564 | /// Create a new empty buffer for Windows console coloring. |
1565 | fn new() -> WindowsBuffer { |
1566 | WindowsBuffer { buf: vec![], colors: vec![] } |
1567 | } |
1568 | |
1569 | /// Push the given color specification into this buffer. |
1570 | /// |
1571 | /// This has the effect of setting the given color information at the |
1572 | /// current position in the buffer. |
1573 | fn push(&mut self, spec: Option<ColorSpec>) { |
1574 | let pos = self.buf.len(); |
1575 | self.colors.push((pos, spec)); |
1576 | } |
1577 | |
1578 | /// Print the contents to the given stream handle, and use the console |
1579 | /// for coloring. |
1580 | fn print( |
1581 | &self, |
1582 | console: &mut wincon::Console, |
1583 | stream: &mut LossyStandardStream<IoStandardStreamLock>, |
1584 | ) -> io::Result<()> { |
1585 | let mut last = 0; |
1586 | for &(pos, ref spec) in &self.colors { |
1587 | stream.write_all(&self.buf[last..pos])?; |
1588 | stream.flush()?; |
1589 | last = pos; |
1590 | match *spec { |
1591 | None => console.reset()?, |
1592 | Some(ref spec) => spec.write_console(console)?, |
1593 | } |
1594 | } |
1595 | stream.write_all(&self.buf[last..])?; |
1596 | stream.flush() |
1597 | } |
1598 | |
1599 | /// Clear the buffer. |
1600 | fn clear(&mut self) { |
1601 | self.buf.clear(); |
1602 | self.colors.clear(); |
1603 | } |
1604 | } |
1605 | |
1606 | #[cfg (windows)] |
1607 | impl io::Write for WindowsBuffer { |
1608 | #[inline ] |
1609 | fn write(&mut self, buf: &[u8]) -> io::Result<usize> { |
1610 | self.buf.extend_from_slice(buf); |
1611 | Ok(buf.len()) |
1612 | } |
1613 | |
1614 | #[inline ] |
1615 | fn flush(&mut self) -> io::Result<()> { |
1616 | Ok(()) |
1617 | } |
1618 | } |
1619 | |
1620 | #[cfg (windows)] |
1621 | impl WriteColor for WindowsBuffer { |
1622 | #[inline ] |
1623 | fn supports_color(&self) -> bool { |
1624 | true |
1625 | } |
1626 | |
1627 | #[inline ] |
1628 | fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> { |
1629 | self.push(Some(spec.clone())); |
1630 | Ok(()) |
1631 | } |
1632 | |
1633 | #[inline ] |
1634 | fn reset(&mut self) -> io::Result<()> { |
1635 | self.push(None); |
1636 | Ok(()) |
1637 | } |
1638 | |
1639 | #[inline ] |
1640 | fn is_synchronous(&self) -> bool { |
1641 | false |
1642 | } |
1643 | } |
1644 | |
1645 | /// A color specification. |
1646 | #[derive (Clone, Debug, Eq, PartialEq)] |
1647 | pub struct ColorSpec { |
1648 | fg_color: Option<Color>, |
1649 | bg_color: Option<Color>, |
1650 | bold: bool, |
1651 | intense: bool, |
1652 | underline: bool, |
1653 | dimmed: bool, |
1654 | italic: bool, |
1655 | reset: bool, |
1656 | strikethrough: bool, |
1657 | } |
1658 | |
1659 | impl Default for ColorSpec { |
1660 | fn default() -> ColorSpec { |
1661 | ColorSpec { |
1662 | fg_color: None, |
1663 | bg_color: None, |
1664 | bold: false, |
1665 | intense: false, |
1666 | underline: false, |
1667 | dimmed: false, |
1668 | italic: false, |
1669 | reset: true, |
1670 | strikethrough: false, |
1671 | } |
1672 | } |
1673 | } |
1674 | |
1675 | impl ColorSpec { |
1676 | /// Create a new color specification that has no colors or styles. |
1677 | pub fn new() -> ColorSpec { |
1678 | ColorSpec::default() |
1679 | } |
1680 | |
1681 | /// Get the foreground color. |
1682 | pub fn fg(&self) -> Option<&Color> { |
1683 | self.fg_color.as_ref() |
1684 | } |
1685 | |
1686 | /// Set the foreground color. |
1687 | pub fn set_fg(&mut self, color: Option<Color>) -> &mut ColorSpec { |
1688 | self.fg_color = color; |
1689 | self |
1690 | } |
1691 | |
1692 | /// Get the background color. |
1693 | pub fn bg(&self) -> Option<&Color> { |
1694 | self.bg_color.as_ref() |
1695 | } |
1696 | |
1697 | /// Set the background color. |
1698 | pub fn set_bg(&mut self, color: Option<Color>) -> &mut ColorSpec { |
1699 | self.bg_color = color; |
1700 | self |
1701 | } |
1702 | |
1703 | /// Get whether this is bold or not. |
1704 | /// |
1705 | /// Note that the bold setting has no effect in a Windows console. |
1706 | pub fn bold(&self) -> bool { |
1707 | self.bold |
1708 | } |
1709 | |
1710 | /// Set whether the text is bolded or not. |
1711 | /// |
1712 | /// Note that the bold setting has no effect in a Windows console. |
1713 | pub fn set_bold(&mut self, yes: bool) -> &mut ColorSpec { |
1714 | self.bold = yes; |
1715 | self |
1716 | } |
1717 | |
1718 | /// Get whether this is dimmed or not. |
1719 | /// |
1720 | /// Note that the dimmed setting has no effect in a Windows console. |
1721 | pub fn dimmed(&self) -> bool { |
1722 | self.dimmed |
1723 | } |
1724 | |
1725 | /// Set whether the text is dimmed or not. |
1726 | /// |
1727 | /// Note that the dimmed setting has no effect in a Windows console. |
1728 | pub fn set_dimmed(&mut self, yes: bool) -> &mut ColorSpec { |
1729 | self.dimmed = yes; |
1730 | self |
1731 | } |
1732 | |
1733 | /// Get whether this is italic or not. |
1734 | /// |
1735 | /// Note that the italic setting has no effect in a Windows console. |
1736 | pub fn italic(&self) -> bool { |
1737 | self.italic |
1738 | } |
1739 | |
1740 | /// Set whether the text is italicized or not. |
1741 | /// |
1742 | /// Note that the italic setting has no effect in a Windows console. |
1743 | pub fn set_italic(&mut self, yes: bool) -> &mut ColorSpec { |
1744 | self.italic = yes; |
1745 | self |
1746 | } |
1747 | |
1748 | /// Get whether this is underline or not. |
1749 | /// |
1750 | /// Note that the underline setting has no effect in a Windows console. |
1751 | pub fn underline(&self) -> bool { |
1752 | self.underline |
1753 | } |
1754 | |
1755 | /// Set whether the text is underlined or not. |
1756 | /// |
1757 | /// Note that the underline setting has no effect in a Windows console. |
1758 | pub fn set_underline(&mut self, yes: bool) -> &mut ColorSpec { |
1759 | self.underline = yes; |
1760 | self |
1761 | } |
1762 | |
1763 | /// Get whether this is strikethrough or not. |
1764 | /// |
1765 | /// Note that the strikethrough setting has no effect in a Windows console. |
1766 | pub fn strikethrough(&self) -> bool { |
1767 | self.strikethrough |
1768 | } |
1769 | |
1770 | /// Set whether the text is strikethrough or not. |
1771 | /// |
1772 | /// Note that the strikethrough setting has no effect in a Windows console. |
1773 | pub fn set_strikethrough(&mut self, yes: bool) -> &mut ColorSpec { |
1774 | self.strikethrough = yes; |
1775 | self |
1776 | } |
1777 | |
1778 | /// Get whether reset is enabled or not. |
1779 | /// |
1780 | /// reset is enabled by default. When disabled and using ANSI escape |
1781 | /// sequences, a "reset" code will be emitted every time a `ColorSpec`'s |
1782 | /// settings are applied. |
1783 | /// |
1784 | /// Note that the reset setting has no effect in a Windows console. |
1785 | pub fn reset(&self) -> bool { |
1786 | self.reset |
1787 | } |
1788 | |
1789 | /// Set whether to reset the terminal whenever color settings are applied. |
1790 | /// |
1791 | /// reset is enabled by default. When disabled and using ANSI escape |
1792 | /// sequences, a "reset" code will be emitted every time a `ColorSpec`'s |
1793 | /// settings are applied. |
1794 | /// |
1795 | /// Typically this is useful if callers have a requirement to more |
1796 | /// scrupulously manage the exact sequence of escape codes that are emitted |
1797 | /// when using ANSI for colors. |
1798 | /// |
1799 | /// Note that the reset setting has no effect in a Windows console. |
1800 | pub fn set_reset(&mut self, yes: bool) -> &mut ColorSpec { |
1801 | self.reset = yes; |
1802 | self |
1803 | } |
1804 | |
1805 | /// Get whether this is intense or not. |
1806 | /// |
1807 | /// On Unix-like systems, this will output the ANSI escape sequence |
1808 | /// that will print a high-intensity version of the color |
1809 | /// specified. |
1810 | /// |
1811 | /// On Windows systems, this will output the ANSI escape sequence |
1812 | /// that will print a brighter version of the color specified. |
1813 | pub fn intense(&self) -> bool { |
1814 | self.intense |
1815 | } |
1816 | |
1817 | /// Set whether the text is intense or not. |
1818 | /// |
1819 | /// On Unix-like systems, this will output the ANSI escape sequence |
1820 | /// that will print a high-intensity version of the color |
1821 | /// specified. |
1822 | /// |
1823 | /// On Windows systems, this will output the ANSI escape sequence |
1824 | /// that will print a brighter version of the color specified. |
1825 | pub fn set_intense(&mut self, yes: bool) -> &mut ColorSpec { |
1826 | self.intense = yes; |
1827 | self |
1828 | } |
1829 | |
1830 | /// Returns true if this color specification has no colors or styles. |
1831 | pub fn is_none(&self) -> bool { |
1832 | self.fg_color.is_none() |
1833 | && self.bg_color.is_none() |
1834 | && !self.bold |
1835 | && !self.underline |
1836 | && !self.dimmed |
1837 | && !self.italic |
1838 | && !self.intense |
1839 | && !self.strikethrough |
1840 | } |
1841 | |
1842 | /// Clears this color specification so that it has no color/style settings. |
1843 | pub fn clear(&mut self) { |
1844 | self.fg_color = None; |
1845 | self.bg_color = None; |
1846 | self.bold = false; |
1847 | self.underline = false; |
1848 | self.intense = false; |
1849 | self.dimmed = false; |
1850 | self.italic = false; |
1851 | self.strikethrough = false; |
1852 | } |
1853 | |
1854 | /// Writes this color spec to the given Windows console. |
1855 | #[cfg (windows)] |
1856 | fn write_console(&self, console: &mut wincon::Console) -> io::Result<()> { |
1857 | let fg_color = self.fg_color.and_then(|c| c.to_windows(self.intense)); |
1858 | if let Some((intense, color)) = fg_color { |
1859 | console.fg(intense, color)?; |
1860 | } |
1861 | let bg_color = self.bg_color.and_then(|c| c.to_windows(self.intense)); |
1862 | if let Some((intense, color)) = bg_color { |
1863 | console.bg(intense, color)?; |
1864 | } |
1865 | Ok(()) |
1866 | } |
1867 | } |
1868 | |
1869 | /// The set of available colors for the terminal foreground/background. |
1870 | /// |
1871 | /// The `Ansi256` and `Rgb` colors will only output the correct codes when |
1872 | /// paired with the `Ansi` `WriteColor` implementation. |
1873 | /// |
1874 | /// The `Ansi256` and `Rgb` color types are not supported when writing colors |
1875 | /// on Windows using the console. If they are used on Windows, then they are |
1876 | /// silently ignored and no colors will be emitted. |
1877 | /// |
1878 | /// This set may expand over time. |
1879 | /// |
1880 | /// This type has a `FromStr` impl that can parse colors from their human |
1881 | /// readable form. The format is as follows: |
1882 | /// |
1883 | /// 1. Any of the explicitly listed colors in English. They are matched |
1884 | /// case insensitively. |
1885 | /// 2. A single 8-bit integer, in either decimal or hexadecimal format. |
1886 | /// 3. A triple of 8-bit integers separated by a comma, where each integer is |
1887 | /// in decimal or hexadecimal format. |
1888 | /// |
1889 | /// Hexadecimal numbers are written with a `0x` prefix. |
1890 | #[allow (missing_docs)] |
1891 | #[derive (Clone, Copy, Debug, Eq, PartialEq)] |
1892 | pub enum Color { |
1893 | Black, |
1894 | Blue, |
1895 | Green, |
1896 | Red, |
1897 | Cyan, |
1898 | Magenta, |
1899 | Yellow, |
1900 | White, |
1901 | Ansi256(u8), |
1902 | Rgb(u8, u8, u8), |
1903 | #[doc (hidden)] |
1904 | __Nonexhaustive, |
1905 | } |
1906 | |
1907 | impl Color { |
1908 | /// Translate this color to a wincon::Color. |
1909 | #[cfg (windows)] |
1910 | fn to_windows( |
1911 | self, |
1912 | intense: bool, |
1913 | ) -> Option<(wincon::Intense, wincon::Color)> { |
1914 | use wincon::Intense::{No, Yes}; |
1915 | |
1916 | let color = match self { |
1917 | Color::Black => wincon::Color::Black, |
1918 | Color::Blue => wincon::Color::Blue, |
1919 | Color::Green => wincon::Color::Green, |
1920 | Color::Red => wincon::Color::Red, |
1921 | Color::Cyan => wincon::Color::Cyan, |
1922 | Color::Magenta => wincon::Color::Magenta, |
1923 | Color::Yellow => wincon::Color::Yellow, |
1924 | Color::White => wincon::Color::White, |
1925 | Color::Ansi256(0) => return Some((No, wincon::Color::Black)), |
1926 | Color::Ansi256(1) => return Some((No, wincon::Color::Red)), |
1927 | Color::Ansi256(2) => return Some((No, wincon::Color::Green)), |
1928 | Color::Ansi256(3) => return Some((No, wincon::Color::Yellow)), |
1929 | Color::Ansi256(4) => return Some((No, wincon::Color::Blue)), |
1930 | Color::Ansi256(5) => return Some((No, wincon::Color::Magenta)), |
1931 | Color::Ansi256(6) => return Some((No, wincon::Color::Cyan)), |
1932 | Color::Ansi256(7) => return Some((No, wincon::Color::White)), |
1933 | Color::Ansi256(8) => return Some((Yes, wincon::Color::Black)), |
1934 | Color::Ansi256(9) => return Some((Yes, wincon::Color::Red)), |
1935 | Color::Ansi256(10) => return Some((Yes, wincon::Color::Green)), |
1936 | Color::Ansi256(11) => return Some((Yes, wincon::Color::Yellow)), |
1937 | Color::Ansi256(12) => return Some((Yes, wincon::Color::Blue)), |
1938 | Color::Ansi256(13) => return Some((Yes, wincon::Color::Magenta)), |
1939 | Color::Ansi256(14) => return Some((Yes, wincon::Color::Cyan)), |
1940 | Color::Ansi256(15) => return Some((Yes, wincon::Color::White)), |
1941 | Color::Ansi256(_) => return None, |
1942 | Color::Rgb(_, _, _) => return None, |
1943 | Color::__Nonexhaustive => unreachable!(), |
1944 | }; |
1945 | let intense = if intense { Yes } else { No }; |
1946 | Some((intense, color)) |
1947 | } |
1948 | |
1949 | /// Parses a numeric color string, either ANSI or RGB. |
1950 | fn from_str_numeric(s: &str) -> Result<Color, ParseColorError> { |
1951 | // The "ansi256" format is a single number (decimal or hex) |
1952 | // corresponding to one of 256 colors. |
1953 | // |
1954 | // The "rgb" format is a triple of numbers (decimal or hex) delimited |
1955 | // by a comma corresponding to one of 256^3 colors. |
1956 | |
1957 | fn parse_number(s: &str) -> Option<u8> { |
1958 | use std::u8; |
1959 | |
1960 | if s.starts_with("0x" ) { |
1961 | u8::from_str_radix(&s[2..], 16).ok() |
1962 | } else { |
1963 | u8::from_str_radix(s, 10).ok() |
1964 | } |
1965 | } |
1966 | |
1967 | let codes: Vec<&str> = s.split(',' ).collect(); |
1968 | if codes.len() == 1 { |
1969 | if let Some(n) = parse_number(&codes[0]) { |
1970 | Ok(Color::Ansi256(n)) |
1971 | } else { |
1972 | if s.chars().all(|c| c.is_digit(16)) { |
1973 | Err(ParseColorError { |
1974 | kind: ParseColorErrorKind::InvalidAnsi256, |
1975 | given: s.to_string(), |
1976 | }) |
1977 | } else { |
1978 | Err(ParseColorError { |
1979 | kind: ParseColorErrorKind::InvalidName, |
1980 | given: s.to_string(), |
1981 | }) |
1982 | } |
1983 | } |
1984 | } else if codes.len() == 3 { |
1985 | let mut v = vec![]; |
1986 | for code in codes { |
1987 | let n = parse_number(code).ok_or_else(|| ParseColorError { |
1988 | kind: ParseColorErrorKind::InvalidRgb, |
1989 | given: s.to_string(), |
1990 | })?; |
1991 | v.push(n); |
1992 | } |
1993 | Ok(Color::Rgb(v[0], v[1], v[2])) |
1994 | } else { |
1995 | Err(if s.contains("," ) { |
1996 | ParseColorError { |
1997 | kind: ParseColorErrorKind::InvalidRgb, |
1998 | given: s.to_string(), |
1999 | } |
2000 | } else { |
2001 | ParseColorError { |
2002 | kind: ParseColorErrorKind::InvalidName, |
2003 | given: s.to_string(), |
2004 | } |
2005 | }) |
2006 | } |
2007 | } |
2008 | } |
2009 | |
2010 | /// An error from parsing an invalid color specification. |
2011 | #[derive (Clone, Debug, Eq, PartialEq)] |
2012 | pub struct ParseColorError { |
2013 | kind: ParseColorErrorKind, |
2014 | given: String, |
2015 | } |
2016 | |
2017 | #[derive (Clone, Debug, Eq, PartialEq)] |
2018 | enum ParseColorErrorKind { |
2019 | InvalidName, |
2020 | InvalidAnsi256, |
2021 | InvalidRgb, |
2022 | } |
2023 | |
2024 | impl ParseColorError { |
2025 | /// Return the string that couldn't be parsed as a valid color. |
2026 | pub fn invalid(&self) -> &str { |
2027 | &self.given |
2028 | } |
2029 | } |
2030 | |
2031 | impl error::Error for ParseColorError { |
2032 | fn description(&self) -> &str { |
2033 | use self::ParseColorErrorKind::*; |
2034 | match self.kind { |
2035 | InvalidName => "unrecognized color name" , |
2036 | InvalidAnsi256 => "invalid ansi256 color number" , |
2037 | InvalidRgb => "invalid RGB color triple" , |
2038 | } |
2039 | } |
2040 | } |
2041 | |
2042 | impl fmt::Display for ParseColorError { |
2043 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
2044 | use self::ParseColorErrorKind::*; |
2045 | match self.kind { |
2046 | InvalidName => write!( |
2047 | f, |
2048 | "unrecognized color name ' {}'. Choose from: \ |
2049 | black, blue, green, red, cyan, magenta, yellow, \ |
2050 | white" , |
2051 | self.given |
2052 | ), |
2053 | InvalidAnsi256 => write!( |
2054 | f, |
2055 | "unrecognized ansi256 color number, \ |
2056 | should be '[0-255]' (or a hex number), but is ' {}'" , |
2057 | self.given |
2058 | ), |
2059 | InvalidRgb => write!( |
2060 | f, |
2061 | "unrecognized RGB color triple, \ |
2062 | should be '[0-255],[0-255],[0-255]' (or a hex \ |
2063 | triple), but is ' {}'" , |
2064 | self.given |
2065 | ), |
2066 | } |
2067 | } |
2068 | } |
2069 | |
2070 | impl FromStr for Color { |
2071 | type Err = ParseColorError; |
2072 | |
2073 | fn from_str(s: &str) -> Result<Color, ParseColorError> { |
2074 | match &*s.to_lowercase() { |
2075 | "black" => Ok(Color::Black), |
2076 | "blue" => Ok(Color::Blue), |
2077 | "green" => Ok(Color::Green), |
2078 | "red" => Ok(Color::Red), |
2079 | "cyan" => Ok(Color::Cyan), |
2080 | "magenta" => Ok(Color::Magenta), |
2081 | "yellow" => Ok(Color::Yellow), |
2082 | "white" => Ok(Color::White), |
2083 | _ => Color::from_str_numeric(s), |
2084 | } |
2085 | } |
2086 | } |
2087 | |
2088 | #[derive (Debug)] |
2089 | struct LossyStandardStream<W> { |
2090 | wtr: W, |
2091 | #[cfg (windows)] |
2092 | is_console: bool, |
2093 | } |
2094 | |
2095 | impl<W: io::Write> LossyStandardStream<W> { |
2096 | #[cfg (not(windows))] |
2097 | fn new(wtr: W) -> LossyStandardStream<W> { |
2098 | LossyStandardStream { wtr } |
2099 | } |
2100 | |
2101 | #[cfg (windows)] |
2102 | fn new(wtr: W) -> LossyStandardStream<W> { |
2103 | let is_console = wincon::Console::stdout().is_ok() |
2104 | || wincon::Console::stderr().is_ok(); |
2105 | LossyStandardStream { wtr, is_console } |
2106 | } |
2107 | |
2108 | #[cfg (not(windows))] |
2109 | fn wrap<Q: io::Write>(&self, wtr: Q) -> LossyStandardStream<Q> { |
2110 | LossyStandardStream::new(wtr) |
2111 | } |
2112 | |
2113 | #[cfg (windows)] |
2114 | fn wrap<Q: io::Write>(&self, wtr: Q) -> LossyStandardStream<Q> { |
2115 | LossyStandardStream { wtr, is_console: self.is_console } |
2116 | } |
2117 | |
2118 | fn get_ref(&self) -> &W { |
2119 | &self.wtr |
2120 | } |
2121 | } |
2122 | |
2123 | impl<W: WriteColor> WriteColor for LossyStandardStream<W> { |
2124 | fn supports_color(&self) -> bool { |
2125 | self.wtr.supports_color() |
2126 | } |
2127 | fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> { |
2128 | self.wtr.set_color(spec) |
2129 | } |
2130 | fn reset(&mut self) -> io::Result<()> { |
2131 | self.wtr.reset() |
2132 | } |
2133 | fn is_synchronous(&self) -> bool { |
2134 | self.wtr.is_synchronous() |
2135 | } |
2136 | } |
2137 | |
2138 | impl<W: io::Write> io::Write for LossyStandardStream<W> { |
2139 | #[cfg (not(windows))] |
2140 | fn write(&mut self, buf: &[u8]) -> io::Result<usize> { |
2141 | self.wtr.write(buf) |
2142 | } |
2143 | |
2144 | #[cfg (windows)] |
2145 | fn write(&mut self, buf: &[u8]) -> io::Result<usize> { |
2146 | if self.is_console { |
2147 | write_lossy_utf8(&mut self.wtr, buf) |
2148 | } else { |
2149 | self.wtr.write(buf) |
2150 | } |
2151 | } |
2152 | |
2153 | fn flush(&mut self) -> io::Result<()> { |
2154 | self.wtr.flush() |
2155 | } |
2156 | } |
2157 | |
2158 | #[cfg (windows)] |
2159 | fn write_lossy_utf8<W: io::Write>(mut w: W, buf: &[u8]) -> io::Result<usize> { |
2160 | match ::std::str::from_utf8(buf) { |
2161 | Ok(s) => w.write(s.as_bytes()), |
2162 | Err(ref e) if e.valid_up_to() == 0 => { |
2163 | w.write(b" \xEF\xBF\xBD" )?; |
2164 | Ok(1) |
2165 | } |
2166 | Err(e) => w.write(&buf[..e.valid_up_to()]), |
2167 | } |
2168 | } |
2169 | |
2170 | #[cfg (test)] |
2171 | mod tests { |
2172 | use super::{ |
2173 | Ansi, Color, ColorSpec, ParseColorError, ParseColorErrorKind, |
2174 | StandardStream, WriteColor, |
2175 | }; |
2176 | |
2177 | fn assert_is_send<T: Send>() {} |
2178 | |
2179 | #[test ] |
2180 | fn standard_stream_is_send() { |
2181 | assert_is_send::<StandardStream>(); |
2182 | } |
2183 | |
2184 | #[test ] |
2185 | fn test_simple_parse_ok() { |
2186 | let color = "green" .parse::<Color>(); |
2187 | assert_eq!(color, Ok(Color::Green)); |
2188 | } |
2189 | |
2190 | #[test ] |
2191 | fn test_256_parse_ok() { |
2192 | let color = "7" .parse::<Color>(); |
2193 | assert_eq!(color, Ok(Color::Ansi256(7))); |
2194 | |
2195 | let color = "32" .parse::<Color>(); |
2196 | assert_eq!(color, Ok(Color::Ansi256(32))); |
2197 | |
2198 | let color = "0xFF" .parse::<Color>(); |
2199 | assert_eq!(color, Ok(Color::Ansi256(0xFF))); |
2200 | } |
2201 | |
2202 | #[test ] |
2203 | fn test_256_parse_err_out_of_range() { |
2204 | let color = "256" .parse::<Color>(); |
2205 | assert_eq!( |
2206 | color, |
2207 | Err(ParseColorError { |
2208 | kind: ParseColorErrorKind::InvalidAnsi256, |
2209 | given: "256" .to_string(), |
2210 | }) |
2211 | ); |
2212 | } |
2213 | |
2214 | #[test ] |
2215 | fn test_rgb_parse_ok() { |
2216 | let color = "0,0,0" .parse::<Color>(); |
2217 | assert_eq!(color, Ok(Color::Rgb(0, 0, 0))); |
2218 | |
2219 | let color = "0,128,255" .parse::<Color>(); |
2220 | assert_eq!(color, Ok(Color::Rgb(0, 128, 255))); |
2221 | |
2222 | let color = "0x0,0x0,0x0" .parse::<Color>(); |
2223 | assert_eq!(color, Ok(Color::Rgb(0, 0, 0))); |
2224 | |
2225 | let color = "0x33,0x66,0xFF" .parse::<Color>(); |
2226 | assert_eq!(color, Ok(Color::Rgb(0x33, 0x66, 0xFF))); |
2227 | } |
2228 | |
2229 | #[test ] |
2230 | fn test_rgb_parse_err_out_of_range() { |
2231 | let color = "0,0,256" .parse::<Color>(); |
2232 | assert_eq!( |
2233 | color, |
2234 | Err(ParseColorError { |
2235 | kind: ParseColorErrorKind::InvalidRgb, |
2236 | given: "0,0,256" .to_string(), |
2237 | }) |
2238 | ); |
2239 | } |
2240 | |
2241 | #[test ] |
2242 | fn test_rgb_parse_err_bad_format() { |
2243 | let color = "0,0" .parse::<Color>(); |
2244 | assert_eq!( |
2245 | color, |
2246 | Err(ParseColorError { |
2247 | kind: ParseColorErrorKind::InvalidRgb, |
2248 | given: "0,0" .to_string(), |
2249 | }) |
2250 | ); |
2251 | |
2252 | let color = "not_a_color" .parse::<Color>(); |
2253 | assert_eq!( |
2254 | color, |
2255 | Err(ParseColorError { |
2256 | kind: ParseColorErrorKind::InvalidName, |
2257 | given: "not_a_color" .to_string(), |
2258 | }) |
2259 | ); |
2260 | } |
2261 | |
2262 | #[test ] |
2263 | fn test_var_ansi_write_rgb() { |
2264 | let mut buf = Ansi::new(vec![]); |
2265 | let _ = buf.write_color(true, &Color::Rgb(254, 253, 255), false); |
2266 | assert_eq!(buf.0, b" \x1B[38;2;254;253;255m" ); |
2267 | } |
2268 | |
2269 | #[test ] |
2270 | fn test_reset() { |
2271 | let spec = ColorSpec::new(); |
2272 | let mut buf = Ansi::new(vec![]); |
2273 | buf.set_color(&spec).unwrap(); |
2274 | assert_eq!(buf.0, b" \x1B[0m" ); |
2275 | } |
2276 | |
2277 | #[test ] |
2278 | fn test_no_reset() { |
2279 | let mut spec = ColorSpec::new(); |
2280 | spec.set_reset(false); |
2281 | |
2282 | let mut buf = Ansi::new(vec![]); |
2283 | buf.set_color(&spec).unwrap(); |
2284 | assert_eq!(buf.0, b"" ); |
2285 | } |
2286 | |
2287 | #[test ] |
2288 | fn test_var_ansi_write_256() { |
2289 | let mut buf = Ansi::new(vec![]); |
2290 | let _ = buf.write_color(false, &Color::Ansi256(7), false); |
2291 | assert_eq!(buf.0, b" \x1B[48;5;7m" ); |
2292 | |
2293 | let mut buf = Ansi::new(vec![]); |
2294 | let _ = buf.write_color(false, &Color::Ansi256(208), false); |
2295 | assert_eq!(buf.0, b" \x1B[48;5;208m" ); |
2296 | } |
2297 | |
2298 | fn all_attributes() -> Vec<ColorSpec> { |
2299 | let mut result = vec![]; |
2300 | for fg in vec![None, Some(Color::Red)] { |
2301 | for bg in vec![None, Some(Color::Red)] { |
2302 | for bold in vec![false, true] { |
2303 | for underline in vec![false, true] { |
2304 | for intense in vec![false, true] { |
2305 | for italic in vec![false, true] { |
2306 | for strikethrough in vec![false, true] { |
2307 | for dimmed in vec![false, true] { |
2308 | let mut color = ColorSpec::new(); |
2309 | color.set_fg(fg); |
2310 | color.set_bg(bg); |
2311 | color.set_bold(bold); |
2312 | color.set_underline(underline); |
2313 | color.set_intense(intense); |
2314 | color.set_italic(italic); |
2315 | color.set_dimmed(dimmed); |
2316 | color.set_strikethrough(strikethrough); |
2317 | result.push(color); |
2318 | } |
2319 | } |
2320 | } |
2321 | } |
2322 | } |
2323 | } |
2324 | } |
2325 | } |
2326 | result |
2327 | } |
2328 | |
2329 | #[test ] |
2330 | fn test_is_none() { |
2331 | for (i, color) in all_attributes().iter().enumerate() { |
2332 | assert_eq!( |
2333 | i == 0, |
2334 | color.is_none(), |
2335 | " {:?} => {}" , |
2336 | color, |
2337 | color.is_none() |
2338 | ) |
2339 | } |
2340 | } |
2341 | |
2342 | #[test ] |
2343 | fn test_clear() { |
2344 | for color in all_attributes() { |
2345 | let mut color1 = color.clone(); |
2346 | color1.clear(); |
2347 | assert!(color1.is_none(), " {:?} => {:?}" , color, color1); |
2348 | } |
2349 | } |
2350 | } |
2351 | |