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.
supports_color(&self) -> bool134 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.
set_color(&mut self, spec: &ColorSpec) -> io::Result<()>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.
reset(&mut self) -> io::Result<()>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.
is_synchronous(&self) -> bool162 fn is_synchronous(&self) -> bool {
163 false
164 }
165 }
166
167 impl<'a, T: ?Sized + WriteColor> WriteColor for &'a mut T {
supports_color(&self) -> bool168 fn supports_color(&self) -> bool {
169 (&**self).supports_color()
170 }
set_color(&mut self, spec: &ColorSpec) -> io::Result<()>171 fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
172 (&mut **self).set_color(spec)
173 }
reset(&mut self) -> io::Result<()>174 fn reset(&mut self) -> io::Result<()> {
175 (&mut **self).reset()
176 }
is_synchronous(&self) -> bool177 fn is_synchronous(&self) -> bool {
178 (&**self).is_synchronous()
179 }
180 }
181
182 impl<T: ?Sized + WriteColor> WriteColor for Box<T> {
supports_color(&self) -> bool183 fn supports_color(&self) -> bool {
184 (&**self).supports_color()
185 }
set_color(&mut self, spec: &ColorSpec) -> io::Result<()>186 fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
187 (&mut **self).set_color(spec)
188 }
reset(&mut self) -> io::Result<()>189 fn reset(&mut self) -> io::Result<()> {
190 (&mut **self).reset()
191 }
is_synchronous(&self) -> bool192 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 {
default() -> ColorChoice223 fn default() -> ColorChoice {
224 ColorChoice::Auto
225 }
226 }
227
228 impl FromStr for ColorChoice {
229 type Err = ColorChoiceParseError;
230
from_str(s: &str) -> Result<ColorChoice, ColorChoiceParseError>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 => 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.
should_attempt_color(&self) -> bool246 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))]
env_allows_color(&self) -> bool256 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)]
env_allows_color(&self) -> bool276 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)]
should_ansi(&self) -> bool298 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 {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result325 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 {
new(sty: StandardStreamType) -> IoStandardStream355 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
lock(&self) -> IoStandardStreamLock<'_>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)]
write(&mut self, b: &[u8]) -> io::Result<usize>394 fn write(&mut self, b: &[u8]) -> io::Result<usize> {
395 match *self {
396 IoStandardStream::Stdout(ref mut s) => s.write(b),
397 IoStandardStream::Stderr(ref mut s) => s.write(b),
398 IoStandardStream::StdoutBuffered(ref mut s) => s.write(b),
399 IoStandardStream::StderrBuffered(ref mut s) => s.write(b),
400 }
401 }
402
403 #[inline(always)]
flush(&mut self) -> io::Result<()>404 fn flush(&mut self) -> io::Result<()> {
405 match *self {
406 IoStandardStream::Stdout(ref mut s) => s.flush(),
407 IoStandardStream::Stderr(ref mut s) => s.flush(),
408 IoStandardStream::StdoutBuffered(ref mut s) => s.flush(),
409 IoStandardStream::StderrBuffered(ref mut s) => 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)]
write(&mut self, b: &[u8]) -> io::Result<usize>424 fn write(&mut self, b: &[u8]) -> io::Result<usize> {
425 match *self {
426 IoStandardStreamLock::StdoutLock(ref mut s) => s.write(b),
427 IoStandardStreamLock::StderrLock(ref mut s) => s.write(b),
428 }
429 }
430
431 #[inline(always)]
flush(&mut self) -> io::Result<()>432 fn flush(&mut self) -> io::Result<()> {
433 match *self {
434 IoStandardStreamLock::StdoutLock(ref mut s) => s.flush(),
435 IoStandardStreamLock::StderrLock(ref mut s) => 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.
stdout(choice: ColorChoice) -> StandardStream506 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.
stderr(choice: ColorChoice) -> StandardStream519 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.
lock(&self) -> StandardStreamLock<'_>531 pub fn lock(&self) -> StandardStreamLock<'_> {
532 StandardStreamLock::from_stream(self)
533 }
534 }
535
536 impl<'a> StandardStreamLock<'a> {
537 #[cfg(not(windows))]
from_stream(stream: &StandardStream) -> StandardStreamLock<'_>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)]
from_stream(stream: &StandardStream) -> StandardStreamLock551 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.
stdout(choice: ColorChoice) -> BufferedStandardStream580 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.
stderr(choice: ColorChoice) -> BufferedStandardStream594 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))]
create( sty: StandardStreamType, choice: ColorChoice, ) -> WriterInner<IoStandardStream>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)]
create( sty: StandardStreamType, choice: ColorChoice, ) -> WriterInner<IoStandardStream>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]
write(&mut self, b: &[u8]) -> io::Result<usize>655 fn write(&mut self, b: &[u8]) -> io::Result<usize> {
656 self.wtr.write(b)
657 }
658
659 #[inline]
flush(&mut self) -> io::Result<()>660 fn flush(&mut self) -> io::Result<()> {
661 self.wtr.flush()
662 }
663 }
664
665 impl WriteColor for StandardStream {
666 #[inline]
supports_color(&self) -> bool667 fn supports_color(&self) -> bool {
668 self.wtr.supports_color()
669 }
670
671 #[inline]
set_color(&mut self, spec: &ColorSpec) -> io::Result<()>672 fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
673 self.wtr.set_color(spec)
674 }
675
676 #[inline]
reset(&mut self) -> io::Result<()>677 fn reset(&mut self) -> io::Result<()> {
678 self.wtr.reset()
679 }
680
681 #[inline]
is_synchronous(&self) -> bool682 fn is_synchronous(&self) -> bool {
683 self.wtr.is_synchronous()
684 }
685 }
686
687 impl<'a> io::Write for StandardStreamLock<'a> {
688 #[inline]
write(&mut self, b: &[u8]) -> io::Result<usize>689 fn write(&mut self, b: &[u8]) -> io::Result<usize> {
690 self.wtr.write(b)
691 }
692
693 #[inline]
flush(&mut self) -> io::Result<()>694 fn flush(&mut self) -> io::Result<()> {
695 self.wtr.flush()
696 }
697 }
698
699 impl<'a> WriteColor for StandardStreamLock<'a> {
700 #[inline]
supports_color(&self) -> bool701 fn supports_color(&self) -> bool {
702 self.wtr.supports_color()
703 }
704
705 #[inline]
set_color(&mut self, spec: &ColorSpec) -> io::Result<()>706 fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
707 self.wtr.set_color(spec)
708 }
709
710 #[inline]
reset(&mut self) -> io::Result<()>711 fn reset(&mut self) -> io::Result<()> {
712 self.wtr.reset()
713 }
714
715 #[inline]
is_synchronous(&self) -> bool716 fn is_synchronous(&self) -> bool {
717 self.wtr.is_synchronous()
718 }
719 }
720
721 impl io::Write for BufferedStandardStream {
722 #[inline]
write(&mut self, b: &[u8]) -> io::Result<usize>723 fn write(&mut self, b: &[u8]) -> io::Result<usize> {
724 self.wtr.write(b)
725 }
726
727 #[inline]
flush(&mut self) -> io::Result<()>728 fn flush(&mut self) -> io::Result<()> {
729 self.wtr.flush()
730 }
731 }
732
733 impl WriteColor for BufferedStandardStream {
734 #[inline]
supports_color(&self) -> bool735 fn supports_color(&self) -> bool {
736 self.wtr.supports_color()
737 }
738
739 #[inline]
set_color(&mut self, spec: &ColorSpec) -> io::Result<()>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]
reset(&mut self) -> io::Result<()>748 fn reset(&mut self) -> io::Result<()> {
749 self.wtr.reset()
750 }
751
752 #[inline]
is_synchronous(&self) -> bool753 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)]
write(&mut self, buf: &[u8]) -> io::Result<usize>760 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
761 match *self {
762 WriterInner::NoColor(ref mut wtr) => wtr.write(buf),
763 WriterInner::Ansi(ref mut wtr) => wtr.write(buf),
764 #[cfg(windows)]
765 WriterInner::Windows { ref mut wtr, .. } => wtr.write(buf),
766 }
767 }
768
769 #[inline(always)]
flush(&mut self) -> io::Result<()>770 fn flush(&mut self) -> io::Result<()> {
771 match *self {
772 WriterInner::NoColor(ref mut wtr) => wtr.flush(),
773 WriterInner::Ansi(ref mut wtr) => 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> {
supports_color(&self) -> bool781 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
set_color(&mut self, spec: &ColorSpec) -> io::Result<()>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
reset(&mut self) -> io::Result<()>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
is_synchronous(&self) -> bool816 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> {
write(&mut self, buf: &[u8]) -> io::Result<usize>827 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
828 match *self {
829 WriterInnerLock::Unreachable(_) => unreachable!(),
830 WriterInnerLock::NoColor(ref mut wtr) => wtr.write(buf),
831 WriterInnerLock::Ansi(ref mut wtr) => wtr.write(buf),
832 #[cfg(windows)]
833 WriterInnerLock::Windows { ref mut wtr, .. } => wtr.write(buf),
834 }
835 }
836
flush(&mut self) -> io::Result<()>837 fn flush(&mut self) -> io::Result<()> {
838 match *self {
839 WriterInnerLock::Unreachable(_) => unreachable!(),
840 WriterInnerLock::NoColor(ref mut wtr) => wtr.flush(),
841 WriterInnerLock::Ansi(ref mut wtr) => 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> {
supports_color(&self) -> bool849 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
set_color(&mut self, spec: &ColorSpec) -> io::Result<()>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
reset(&mut self) -> io::Result<()>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
is_synchronous(&self) -> bool886 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))]
create(sty: StandardStreamType, choice: ColorChoice) -> BufferWriter923 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)]
create(sty: StandardStreamType, choice: ColorChoice) -> BufferWriter941 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.
stdout(choice: ColorChoice) -> BufferWriter976 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.
stderr(choice: ColorChoice) -> BufferWriter988 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`.
separator(&mut self, sep: Option<Vec<u8>>)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))]
buffer(&self) -> Buffer1005 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)]
buffer(&self) -> Buffer1014 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.
print(&self, buf: &Buffer) -> io::Result<()>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))]
new(choice: ColorChoice) -> Buffer1086 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)]
new(choice: ColorChoice, console: bool) -> Buffer1102 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.
no_color() -> Buffer1115 pub fn no_color() -> Buffer {
1116 Buffer(BufferInner::NoColor(NoColor(vec![])))
1117 }
1118
1119 /// Create a buffer that uses ANSI escape sequences.
ansi() -> Buffer1120 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)]
console() -> Buffer1126 pub fn console() -> Buffer {
1127 Buffer(BufferInner::Windows(WindowsBuffer::new()))
1128 }
1129
1130 /// Returns true if and only if this buffer is empty.
is_empty(&self) -> bool1131 pub fn is_empty(&self) -> bool {
1132 self.len() == 0
1133 }
1134
1135 /// Returns the length of this buffer in bytes.
len(&self) -> usize1136 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.
clear(&mut self)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.
into_inner(self) -> Vec<u8>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.
as_slice(&self) -> &[u8]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.
as_mut_slice(&mut self) -> &mut [u8]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]
write(&mut self, buf: &[u8]) -> io::Result<usize>1191 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
1192 match self.0 {
1193 BufferInner::NoColor(ref mut w) => w.write(buf),
1194 BufferInner::Ansi(ref mut w) => w.write(buf),
1195 #[cfg(windows)]
1196 BufferInner::Windows(ref mut w) => w.write(buf),
1197 }
1198 }
1199
1200 #[inline]
flush(&mut self) -> io::Result<()>1201 fn flush(&mut self) -> io::Result<()> {
1202 match self.0 {
1203 BufferInner::NoColor(ref mut w) => w.flush(),
1204 BufferInner::Ansi(ref mut w) => w.flush(),
1205 #[cfg(windows)]
1206 BufferInner::Windows(ref mut w) => w.flush(),
1207 }
1208 }
1209 }
1210
1211 impl WriteColor for Buffer {
1212 #[inline]
supports_color(&self) -> bool1213 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]
set_color(&mut self, spec: &ColorSpec) -> io::Result<()>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]
reset(&mut self) -> io::Result<()>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]
is_synchronous(&self) -> bool1243 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.
new(wtr: W) -> NoColor<W>1255 pub fn new(wtr: W) -> NoColor<W> {
1256 NoColor(wtr)
1257 }
1258
1259 /// Consume this `NoColor` value and return the inner writer.
into_inner(self) -> W1260 pub fn into_inner(self) -> W {
1261 self.0
1262 }
1263
1264 /// Return a reference to the inner writer.
get_ref(&self) -> &W1265 pub fn get_ref(&self) -> &W {
1266 &self.0
1267 }
1268
1269 /// Return a mutable reference to the inner writer.
get_mut(&mut self) -> &mut W1270 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]
write(&mut self, buf: &[u8]) -> io::Result<usize>1277 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
1278 self.0.write(buf)
1279 }
1280
1281 #[inline]
flush(&mut self) -> io::Result<()>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]
supports_color(&self) -> bool1289 fn supports_color(&self) -> bool {
1290 false
1291 }
1292
1293 #[inline]
set_color(&mut self, _: &ColorSpec) -> io::Result<()>1294 fn set_color(&mut self, _: &ColorSpec) -> io::Result<()> {
1295 Ok(())
1296 }
1297
1298 #[inline]
reset(&mut self) -> io::Result<()>1299 fn reset(&mut self) -> io::Result<()> {
1300 Ok(())
1301 }
1302
1303 #[inline]
is_synchronous(&self) -> bool1304 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.
new(wtr: W) -> Ansi<W>1316 pub fn new(wtr: W) -> Ansi<W> {
1317 Ansi(wtr)
1318 }
1319
1320 /// Consume this `Ansi` value and return the inner writer.
into_inner(self) -> W1321 pub fn into_inner(self) -> W {
1322 self.0
1323 }
1324
1325 /// Return a reference to the inner writer.
get_ref(&self) -> &W1326 pub fn get_ref(&self) -> &W {
1327 &self.0
1328 }
1329
1330 /// Return a mutable reference to the inner writer.
get_mut(&mut self) -> &mut W1331 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]
write(&mut self, buf: &[u8]) -> io::Result<usize>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]
write_all(&mut self, buf: &[u8]) -> io::Result<()>1349 fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
1350 self.0.write_all(buf)
1351 }
1352
1353 #[inline]
flush(&mut self) -> io::Result<()>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]
supports_color(&self) -> bool1361 fn supports_color(&self) -> bool {
1362 true
1363 }
1364
1365 #[inline]
set_color(&mut self, spec: &ColorSpec) -> io::Result<()>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]
reset(&mut self) -> io::Result<()>1395 fn reset(&mut self) -> io::Result<()> {
1396 self.write_str("\x1B[0m")
1397 }
1398
1399 #[inline]
is_synchronous(&self) -> bool1400 fn is_synchronous(&self) -> bool {
1401 false
1402 }
1403 }
1404
1405 impl<W: io::Write> Ansi<W> {
write_str(&mut self, s: &str) -> io::Result<()>1406 fn write_str(&mut self, s: &str) -> io::Result<()> {
1407 self.write_all(s.as_bytes())
1408 }
1409
write_color( &mut self, fg: bool, c: &Color, intense: bool, ) -> io::Result<()>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 {
supports_color(&self) -> bool1521 fn supports_color(&self) -> bool {
1522 false
1523 }
1524
set_color(&mut self, _: &ColorSpec) -> io::Result<()>1525 fn set_color(&mut self, _: &ColorSpec) -> io::Result<()> {
1526 Ok(())
1527 }
1528
reset(&mut self) -> io::Result<()>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.
new() -> WindowsBuffer1565 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.
push(&mut self, spec: Option<ColorSpec>)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.
print( &self, console: &mut wincon::Console, stream: &mut LossyStandardStream<IoStandardStreamLock>, ) -> io::Result<()>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.
clear(&mut self)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]
write(&mut self, buf: &[u8]) -> io::Result<usize>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]
flush(&mut self) -> io::Result<()>1615 fn flush(&mut self) -> io::Result<()> {
1616 Ok(())
1617 }
1618 }
1619
1620 #[cfg(windows)]
1621 impl WriteColor for WindowsBuffer {
1622 #[inline]
supports_color(&self) -> bool1623 fn supports_color(&self) -> bool {
1624 true
1625 }
1626
1627 #[inline]
set_color(&mut self, spec: &ColorSpec) -> io::Result<()>1628 fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
1629 self.push(Some(spec.clone()));
1630 Ok(())
1631 }
1632
1633 #[inline]
reset(&mut self) -> io::Result<()>1634 fn reset(&mut self) -> io::Result<()> {
1635 self.push(None);
1636 Ok(())
1637 }
1638
1639 #[inline]
is_synchronous(&self) -> bool1640 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 {
default() -> ColorSpec1660 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.
new() -> ColorSpec1677 pub fn new() -> ColorSpec {
1678 ColorSpec::default()
1679 }
1680
1681 /// Get the foreground color.
fg(&self) -> Option<&Color>1682 pub fn fg(&self) -> Option<&Color> {
1683 self.fg_color.as_ref()
1684 }
1685
1686 /// Set the foreground color.
set_fg(&mut self, color: Option<Color>) -> &mut ColorSpec1687 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.
bg(&self) -> Option<&Color>1693 pub fn bg(&self) -> Option<&Color> {
1694 self.bg_color.as_ref()
1695 }
1696
1697 /// Set the background color.
set_bg(&mut self, color: Option<Color>) -> &mut ColorSpec1698 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.
bold(&self) -> bool1706 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.
set_bold(&mut self, yes: bool) -> &mut ColorSpec1713 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.
dimmed(&self) -> bool1721 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.
set_dimmed(&mut self, yes: bool) -> &mut ColorSpec1728 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.
italic(&self) -> bool1736 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.
set_italic(&mut self, yes: bool) -> &mut ColorSpec1743 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.
underline(&self) -> bool1751 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.
set_underline(&mut self, yes: bool) -> &mut ColorSpec1758 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.
strikethrough(&self) -> bool1766 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.
set_strikethrough(&mut self, yes: bool) -> &mut ColorSpec1773 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.
reset(&self) -> bool1785 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.
set_reset(&mut self, yes: bool) -> &mut ColorSpec1800 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.
intense(&self) -> bool1813 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.
set_intense(&mut self, yes: bool) -> &mut ColorSpec1825 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.
is_none(&self) -> bool1831 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.
clear(&mut self)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)]
write_console(&self, console: &mut wincon::Console) -> io::Result<()>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)]
to_windows( self, intense: bool, ) -> Option<(wincon::Intense, wincon::Color)>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.
from_str_numeric(s: &str) -> Result<Color, ParseColorError>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.
invalid(&self) -> &str2026 pub fn invalid(&self) -> &str {
2027 &self.given
2028 }
2029 }
2030
2031 impl error::Error for ParseColorError {
description(&self) -> &str2032 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 {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result2043 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
from_str(s: &str) -> Result<Color, ParseColorError>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))]
new(wtr: W) -> LossyStandardStream<W>2097 fn new(wtr: W) -> LossyStandardStream<W> {
2098 LossyStandardStream { wtr }
2099 }
2100
2101 #[cfg(windows)]
new(wtr: W) -> LossyStandardStream<W>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))]
wrap<Q: io::Write>(&self, wtr: Q) -> LossyStandardStream<Q>2109 fn wrap<Q: io::Write>(&self, wtr: Q) -> LossyStandardStream<Q> {
2110 LossyStandardStream::new(wtr)
2111 }
2112
2113 #[cfg(windows)]
wrap<Q: io::Write>(&self, wtr: Q) -> LossyStandardStream<Q>2114 fn wrap<Q: io::Write>(&self, wtr: Q) -> LossyStandardStream<Q> {
2115 LossyStandardStream { wtr, is_console: self.is_console }
2116 }
2117
get_ref(&self) -> &W2118 fn get_ref(&self) -> &W {
2119 &self.wtr
2120 }
2121 }
2122
2123 impl<W: WriteColor> WriteColor for LossyStandardStream<W> {
supports_color(&self) -> bool2124 fn supports_color(&self) -> bool {
2125 self.wtr.supports_color()
2126 }
set_color(&mut self, spec: &ColorSpec) -> io::Result<()>2127 fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
2128 self.wtr.set_color(spec)
2129 }
reset(&mut self) -> io::Result<()>2130 fn reset(&mut self) -> io::Result<()> {
2131 self.wtr.reset()
2132 }
is_synchronous(&self) -> bool2133 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))]
write(&mut self, buf: &[u8]) -> io::Result<usize>2140 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
2141 self.wtr.write(buf)
2142 }
2143
2144 #[cfg(windows)]
write(&mut self, buf: &[u8]) -> io::Result<usize>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
flush(&mut self) -> io::Result<()>2153 fn flush(&mut self) -> io::Result<()> {
2154 self.wtr.flush()
2155 }
2156 }
2157
2158 #[cfg(windows)]
write_lossy_utf8<W: io::Write>(mut w: W, buf: &[u8]) -> io::Result<usize>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
assert_is_send<T: Send>()2177 fn assert_is_send<T: Send>() {}
2178
2179 #[test]
standard_stream_is_send()2180 fn standard_stream_is_send() {
2181 assert_is_send::<StandardStream>();
2182 }
2183
2184 #[test]
test_simple_parse_ok()2185 fn test_simple_parse_ok() {
2186 let color = "green".parse::<Color>();
2187 assert_eq!(color, Ok(Color::Green));
2188 }
2189
2190 #[test]
test_256_parse_ok()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]
test_256_parse_err_out_of_range()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]
test_rgb_parse_ok()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]
test_rgb_parse_err_out_of_range()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]
test_rgb_parse_err_bad_format()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]
test_var_ansi_write_rgb()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]
test_reset()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]
test_no_reset()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]
test_var_ansi_write_256()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
all_attributes() -> Vec<ColorSpec>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]
test_is_none()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]
test_clear()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