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