]> git.proxmox.com Git - cargo.git/blob - vendor/termcolor/src/lib.rs
New upstream version 0.47.0
[cargo.git] / vendor / termcolor / src / lib.rs
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(test)]
123 use doc_comment::doctest;
124 #[cfg(test)]
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.
142 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.
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.
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.
170 fn is_synchronous(&self) -> bool {
171 false
172 }
173 }
174
175 impl<'a, T: ?Sized + WriteColor> WriteColor for &'a mut T {
176 fn supports_color(&self) -> bool {
177 (&**self).supports_color()
178 }
179 fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
180 (&mut **self).set_color(spec)
181 }
182 fn reset(&mut self) -> io::Result<()> {
183 (&mut **self).reset()
184 }
185 fn is_synchronous(&self) -> bool {
186 (&**self).is_synchronous()
187 }
188 }
189
190 impl<T: ?Sized + WriteColor> WriteColor for Box<T> {
191 fn supports_color(&self) -> bool {
192 (&**self).supports_color()
193 }
194 fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
195 (&mut **self).set_color(spec)
196 }
197 fn reset(&mut self) -> io::Result<()> {
198 (&mut **self).reset()
199 }
200 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.
224 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))]
234 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)]
254 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)]
276 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 {
313 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
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)]
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)]
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)]
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)]
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.
458 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.
471 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.
483 pub fn lock(&self) -> StandardStreamLock<'_> {
484 StandardStreamLock::from_stream(self)
485 }
486 }
487
488 impl<'a> StandardStreamLock<'a> {
489 #[cfg(not(windows))]
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)]
503 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.
532 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.
546 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))]
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)]
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]
607 fn write(&mut self, b: &[u8]) -> io::Result<usize> {
608 self.wtr.write(b)
609 }
610
611 #[inline]
612 fn flush(&mut self) -> io::Result<()> {
613 self.wtr.flush()
614 }
615 }
616
617 impl WriteColor for StandardStream {
618 #[inline]
619 fn supports_color(&self) -> bool {
620 self.wtr.supports_color()
621 }
622
623 #[inline]
624 fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
625 self.wtr.set_color(spec)
626 }
627
628 #[inline]
629 fn reset(&mut self) -> io::Result<()> {
630 self.wtr.reset()
631 }
632
633 #[inline]
634 fn is_synchronous(&self) -> bool {
635 self.wtr.is_synchronous()
636 }
637 }
638
639 impl<'a> io::Write for StandardStreamLock<'a> {
640 #[inline]
641 fn write(&mut self, b: &[u8]) -> io::Result<usize> {
642 self.wtr.write(b)
643 }
644
645 #[inline]
646 fn flush(&mut self) -> io::Result<()> {
647 self.wtr.flush()
648 }
649 }
650
651 impl<'a> WriteColor for StandardStreamLock<'a> {
652 #[inline]
653 fn supports_color(&self) -> bool {
654 self.wtr.supports_color()
655 }
656
657 #[inline]
658 fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
659 self.wtr.set_color(spec)
660 }
661
662 #[inline]
663 fn reset(&mut self) -> io::Result<()> {
664 self.wtr.reset()
665 }
666
667 #[inline]
668 fn is_synchronous(&self) -> bool {
669 self.wtr.is_synchronous()
670 }
671 }
672
673 impl io::Write for BufferedStandardStream {
674 #[inline]
675 fn write(&mut self, b: &[u8]) -> io::Result<usize> {
676 self.wtr.write(b)
677 }
678
679 #[inline]
680 fn flush(&mut self) -> io::Result<()> {
681 self.wtr.flush()
682 }
683 }
684
685 impl WriteColor for BufferedStandardStream {
686 #[inline]
687 fn supports_color(&self) -> bool {
688 self.wtr.supports_color()
689 }
690
691 #[inline]
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]
700 fn reset(&mut self) -> io::Result<()> {
701 self.wtr.reset()
702 }
703
704 #[inline]
705 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)]
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)]
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> {
733 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
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
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
768 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> {
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
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> {
801 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
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
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
838 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))]
874 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)]
892 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.
927 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.
939 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`.
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))]
956 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)]
965 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.
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))]
1035 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)]
1051 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.
1064 pub fn no_color() -> Buffer {
1065 Buffer(BufferInner::NoColor(NoColor(vec![])))
1066 }
1067
1068 /// Create a buffer that uses ANSI escape sequences.
1069 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)]
1075 pub fn console() -> Buffer {
1076 Buffer(BufferInner::Windows(WindowsBuffer::new()))
1077 }
1078
1079 /// Returns true if and only if this buffer is empty.
1080 pub fn is_empty(&self) -> bool {
1081 self.len() == 0
1082 }
1083
1084 /// Returns the length of this buffer in bytes.
1085 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.
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.
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.
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.
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]
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]
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]
1162 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]
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]
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]
1192 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.
1203 pub fn new(wtr: W) -> NoColor<W> {
1204 NoColor(wtr)
1205 }
1206
1207 /// Consume this `NoColor` value and return the inner writer.
1208 pub fn into_inner(self) -> W {
1209 self.0
1210 }
1211
1212 /// Return a reference to the inner writer.
1213 pub fn get_ref(&self) -> &W {
1214 &self.0
1215 }
1216
1217 /// Return a mutable reference to the inner writer.
1218 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]
1225 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
1226 self.0.write(buf)
1227 }
1228
1229 #[inline]
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]
1237 fn supports_color(&self) -> bool {
1238 false
1239 }
1240
1241 #[inline]
1242 fn set_color(&mut self, _: &ColorSpec) -> io::Result<()> {
1243 Ok(())
1244 }
1245
1246 #[inline]
1247 fn reset(&mut self) -> io::Result<()> {
1248 Ok(())
1249 }
1250
1251 #[inline]
1252 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.
1263 pub fn new(wtr: W) -> Ansi<W> {
1264 Ansi(wtr)
1265 }
1266
1267 /// Consume this `Ansi` value and return the inner writer.
1268 pub fn into_inner(self) -> W {
1269 self.0
1270 }
1271
1272 /// Return a reference to the inner writer.
1273 pub fn get_ref(&self) -> &W {
1274 &self.0
1275 }
1276
1277 /// Return a mutable reference to the inner writer.
1278 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]
1285 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
1286 self.0.write(buf)
1287 }
1288
1289 #[inline]
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]
1297 fn supports_color(&self) -> bool {
1298 true
1299 }
1300
1301 #[inline]
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.italic {
1310 self.write_str("\x1B[3m")?;
1311 }
1312 if spec.underline {
1313 self.write_str("\x1B[4m")?;
1314 }
1315 if let Some(ref c) = spec.fg_color {
1316 self.write_color(true, c, spec.intense)?;
1317 }
1318 if let Some(ref c) = spec.bg_color {
1319 self.write_color(false, c, spec.intense)?;
1320 }
1321 Ok(())
1322 }
1323
1324 #[inline]
1325 fn reset(&mut self) -> io::Result<()> {
1326 self.write_str("\x1B[0m")
1327 }
1328
1329 #[inline]
1330 fn is_synchronous(&self) -> bool {
1331 false
1332 }
1333 }
1334
1335 impl<W: io::Write> Ansi<W> {
1336 fn write_str(&mut self, s: &str) -> io::Result<()> {
1337 self.write_all(s.as_bytes())
1338 }
1339
1340 fn write_color(
1341 &mut self,
1342 fg: bool,
1343 c: &Color,
1344 intense: bool,
1345 ) -> io::Result<()> {
1346 macro_rules! write_intense {
1347 ($clr:expr) => {
1348 if fg {
1349 self.write_str(concat!("\x1B[38;5;", $clr, "m"))
1350 } else {
1351 self.write_str(concat!("\x1B[48;5;", $clr, "m"))
1352 }
1353 };
1354 }
1355 macro_rules! write_normal {
1356 ($clr:expr) => {
1357 if fg {
1358 self.write_str(concat!("\x1B[3", $clr, "m"))
1359 } else {
1360 self.write_str(concat!("\x1B[4", $clr, "m"))
1361 }
1362 };
1363 }
1364 macro_rules! write_var_ansi_code {
1365 ($pre:expr, $($code:expr),+) => {{
1366 // The loop generates at worst a literal of the form
1367 // '255,255,255m' which is 12-bytes.
1368 // The largest `pre` expression we currently use is 7 bytes.
1369 // This gives us the maximum of 19-bytes for our work buffer.
1370 let pre_len = $pre.len();
1371 assert!(pre_len <= 7);
1372 let mut fmt = [0u8; 19];
1373 fmt[..pre_len].copy_from_slice($pre);
1374 let mut i = pre_len - 1;
1375 $(
1376 let c1: u8 = ($code / 100) % 10;
1377 let c2: u8 = ($code / 10) % 10;
1378 let c3: u8 = $code % 10;
1379 let mut printed = false;
1380
1381 if c1 != 0 {
1382 printed = true;
1383 i += 1;
1384 fmt[i] = b'0' + c1;
1385 }
1386 if c2 != 0 || printed {
1387 i += 1;
1388 fmt[i] = b'0' + c2;
1389 }
1390 // If we received a zero value we must still print a value.
1391 i += 1;
1392 fmt[i] = b'0' + c3;
1393 i += 1;
1394 fmt[i] = b';';
1395 )+
1396
1397 fmt[i] = b'm';
1398 self.write_all(&fmt[0..i+1])
1399 }}
1400 }
1401 macro_rules! write_custom {
1402 ($ansi256:expr) => {
1403 if fg {
1404 write_var_ansi_code!(b"\x1B[38;5;", $ansi256)
1405 } else {
1406 write_var_ansi_code!(b"\x1B[48;5;", $ansi256)
1407 }
1408 };
1409
1410 ($r:expr, $g:expr, $b:expr) => {{
1411 if fg {
1412 write_var_ansi_code!(b"\x1B[38;2;", $r, $g, $b)
1413 } else {
1414 write_var_ansi_code!(b"\x1B[48;2;", $r, $g, $b)
1415 }
1416 }};
1417 }
1418 if intense {
1419 match *c {
1420 Color::Black => write_intense!("8"),
1421 Color::Blue => write_intense!("12"),
1422 Color::Green => write_intense!("10"),
1423 Color::Red => write_intense!("9"),
1424 Color::Cyan => write_intense!("14"),
1425 Color::Magenta => write_intense!("13"),
1426 Color::Yellow => write_intense!("11"),
1427 Color::White => write_intense!("15"),
1428 Color::Ansi256(c) => write_custom!(c),
1429 Color::Rgb(r, g, b) => write_custom!(r, g, b),
1430 Color::__Nonexhaustive => unreachable!(),
1431 }
1432 } else {
1433 match *c {
1434 Color::Black => write_normal!("0"),
1435 Color::Blue => write_normal!("4"),
1436 Color::Green => write_normal!("2"),
1437 Color::Red => write_normal!("1"),
1438 Color::Cyan => write_normal!("6"),
1439 Color::Magenta => write_normal!("5"),
1440 Color::Yellow => write_normal!("3"),
1441 Color::White => write_normal!("7"),
1442 Color::Ansi256(c) => write_custom!(c),
1443 Color::Rgb(r, g, b) => write_custom!(r, g, b),
1444 Color::__Nonexhaustive => unreachable!(),
1445 }
1446 }
1447 }
1448 }
1449
1450 /// An in-memory buffer that provides Windows console coloring.
1451 ///
1452 /// This doesn't actually communicate with the Windows console. Instead, it
1453 /// acts like a normal buffer but also saves the color information associated
1454 /// with positions in the buffer. It is only when the buffer is written to the
1455 /// console that coloring is actually applied.
1456 ///
1457 /// This is roughly isomorphic to the ANSI based approach (i.e.,
1458 /// `Ansi<Vec<u8>>`), except with ANSI, the color information is embedded
1459 /// directly into the buffer.
1460 ///
1461 /// Note that there is no way to write something generic like
1462 /// `WindowsConsole<W: io::Write>` since coloring on Windows is tied
1463 /// specifically to the console APIs, and therefore can't work on arbitrary
1464 /// writers.
1465 #[cfg(windows)]
1466 #[derive(Clone, Debug)]
1467 struct WindowsBuffer {
1468 /// The actual content that should be printed.
1469 buf: Vec<u8>,
1470 /// A sequence of position oriented color specifications. Namely, each
1471 /// element is a position and a color spec, where the color spec should
1472 /// be applied at the position inside of `buf`.
1473 ///
1474 /// A missing color spec implies the underlying console should be reset.
1475 colors: Vec<(usize, Option<ColorSpec>)>,
1476 }
1477
1478 #[cfg(windows)]
1479 impl WindowsBuffer {
1480 /// Create a new empty buffer for Windows console coloring.
1481 fn new() -> WindowsBuffer {
1482 WindowsBuffer { buf: vec![], colors: vec![] }
1483 }
1484
1485 /// Push the given color specification into this buffer.
1486 ///
1487 /// This has the effect of setting the given color information at the
1488 /// current position in the buffer.
1489 fn push(&mut self, spec: Option<ColorSpec>) {
1490 let pos = self.buf.len();
1491 self.colors.push((pos, spec));
1492 }
1493
1494 /// Print the contents to the given stream handle, and use the console
1495 /// for coloring.
1496 fn print(
1497 &self,
1498 console: &mut wincon::Console,
1499 stream: &mut LossyStandardStream<IoStandardStreamLock>,
1500 ) -> io::Result<()> {
1501 let mut last = 0;
1502 for &(pos, ref spec) in &self.colors {
1503 stream.write_all(&self.buf[last..pos])?;
1504 stream.flush()?;
1505 last = pos;
1506 match *spec {
1507 None => console.reset()?,
1508 Some(ref spec) => spec.write_console(console)?,
1509 }
1510 }
1511 stream.write_all(&self.buf[last..])?;
1512 stream.flush()
1513 }
1514
1515 /// Clear the buffer.
1516 fn clear(&mut self) {
1517 self.buf.clear();
1518 self.colors.clear();
1519 }
1520 }
1521
1522 #[cfg(windows)]
1523 impl io::Write for WindowsBuffer {
1524 #[inline]
1525 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
1526 self.buf.extend_from_slice(buf);
1527 Ok(buf.len())
1528 }
1529
1530 #[inline]
1531 fn flush(&mut self) -> io::Result<()> {
1532 Ok(())
1533 }
1534 }
1535
1536 #[cfg(windows)]
1537 impl WriteColor for WindowsBuffer {
1538 #[inline]
1539 fn supports_color(&self) -> bool {
1540 true
1541 }
1542
1543 #[inline]
1544 fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
1545 self.push(Some(spec.clone()));
1546 Ok(())
1547 }
1548
1549 #[inline]
1550 fn reset(&mut self) -> io::Result<()> {
1551 self.push(None);
1552 Ok(())
1553 }
1554
1555 #[inline]
1556 fn is_synchronous(&self) -> bool {
1557 false
1558 }
1559 }
1560
1561 /// A color specification.
1562 #[derive(Clone, Debug, Eq, PartialEq)]
1563 pub struct ColorSpec {
1564 fg_color: Option<Color>,
1565 bg_color: Option<Color>,
1566 bold: bool,
1567 intense: bool,
1568 underline: bool,
1569 italic: bool,
1570 reset: bool,
1571 }
1572
1573 impl Default for ColorSpec {
1574 fn default() -> ColorSpec {
1575 ColorSpec {
1576 fg_color: None,
1577 bg_color: None,
1578 bold: false,
1579 intense: false,
1580 underline: false,
1581 italic: false,
1582 reset: true,
1583 }
1584 }
1585 }
1586
1587 impl ColorSpec {
1588 /// Create a new color specification that has no colors or styles.
1589 pub fn new() -> ColorSpec {
1590 ColorSpec::default()
1591 }
1592
1593 /// Get the foreground color.
1594 pub fn fg(&self) -> Option<&Color> {
1595 self.fg_color.as_ref()
1596 }
1597
1598 /// Set the foreground color.
1599 pub fn set_fg(&mut self, color: Option<Color>) -> &mut ColorSpec {
1600 self.fg_color = color;
1601 self
1602 }
1603
1604 /// Get the background color.
1605 pub fn bg(&self) -> Option<&Color> {
1606 self.bg_color.as_ref()
1607 }
1608
1609 /// Set the background color.
1610 pub fn set_bg(&mut self, color: Option<Color>) -> &mut ColorSpec {
1611 self.bg_color = color;
1612 self
1613 }
1614
1615 /// Get whether this is bold or not.
1616 ///
1617 /// Note that the bold setting has no effect in a Windows console.
1618 pub fn bold(&self) -> bool {
1619 self.bold
1620 }
1621
1622 /// Set whether the text is bolded or not.
1623 ///
1624 /// Note that the bold setting has no effect in a Windows console.
1625 pub fn set_bold(&mut self, yes: bool) -> &mut ColorSpec {
1626 self.bold = yes;
1627 self
1628 }
1629
1630 /// Get whether this is italic or not.
1631 ///
1632 /// Note that the italic setting has no effect in a Windows console.
1633 pub fn italic(&self) -> bool {
1634 self.italic
1635 }
1636
1637 /// Set whether the text is italicized or not.
1638 ///
1639 /// Note that the italic setting has no effect in a Windows console.
1640 pub fn set_italic(&mut self, yes: bool) -> &mut ColorSpec {
1641 self.italic = yes;
1642 self
1643 }
1644
1645 /// Get whether this is underline or not.
1646 ///
1647 /// Note that the underline setting has no effect in a Windows console.
1648 pub fn underline(&self) -> bool {
1649 self.underline
1650 }
1651
1652 /// Set whether the text is underlined or not.
1653 ///
1654 /// Note that the underline setting has no effect in a Windows console.
1655 pub fn set_underline(&mut self, yes: bool) -> &mut ColorSpec {
1656 self.underline = yes;
1657 self
1658 }
1659
1660 /// Get whether reset is enabled or not.
1661 ///
1662 /// reset is enabled by default. When disabled and using ANSI escape
1663 /// sequences, a "reset" code will be emitted every time a `ColorSpec`'s
1664 /// settings are applied.
1665 ///
1666 /// Note that the reset setting has no effect in a Windows console.
1667 pub fn reset(&self) -> bool {
1668 self.reset
1669 }
1670
1671 /// Set whether to reset the terminal whenever color settings are applied.
1672 ///
1673 /// reset is enabled by default. When disabled and using ANSI escape
1674 /// sequences, a "reset" code will be emitted every time a `ColorSpec`'s
1675 /// settings are applied.
1676 ///
1677 /// Typically this is useful if callers have a requirement to more
1678 /// scrupulously manage the exact sequence of escape codes that are emitted
1679 /// when using ANSI for colors.
1680 ///
1681 /// Note that the reset setting has no effect in a Windows console.
1682 pub fn set_reset(&mut self, yes: bool) -> &mut ColorSpec {
1683 self.reset = yes;
1684 self
1685 }
1686
1687 /// Get whether this is intense or not.
1688 ///
1689 /// On Unix-like systems, this will output the ANSI escape sequence
1690 /// that will print a high-intensity version of the color
1691 /// specified.
1692 ///
1693 /// On Windows systems, this will output the ANSI escape sequence
1694 /// that will print a brighter version of the color specified.
1695 pub fn intense(&self) -> bool {
1696 self.intense
1697 }
1698
1699 /// Set whether the text is intense or not.
1700 ///
1701 /// On Unix-like systems, this will output the ANSI escape sequence
1702 /// that will print a high-intensity version of the color
1703 /// specified.
1704 ///
1705 /// On Windows systems, this will output the ANSI escape sequence
1706 /// that will print a brighter version of the color specified.
1707 pub fn set_intense(&mut self, yes: bool) -> &mut ColorSpec {
1708 self.intense = yes;
1709 self
1710 }
1711
1712 /// Returns true if this color specification has no colors or styles.
1713 pub fn is_none(&self) -> bool {
1714 self.fg_color.is_none()
1715 && self.bg_color.is_none()
1716 && !self.bold
1717 && !self.underline
1718 && !self.italic
1719 && !self.intense
1720 }
1721
1722 /// Clears this color specification so that it has no color/style settings.
1723 pub fn clear(&mut self) {
1724 self.fg_color = None;
1725 self.bg_color = None;
1726 self.bold = false;
1727 self.underline = false;
1728 self.intense = false;
1729 self.italic = false;
1730 }
1731
1732 /// Writes this color spec to the given Windows console.
1733 #[cfg(windows)]
1734 fn write_console(&self, console: &mut wincon::Console) -> io::Result<()> {
1735 let fg_color = self.fg_color.and_then(|c| c.to_windows(self.intense));
1736 if let Some((intense, color)) = fg_color {
1737 console.fg(intense, color)?;
1738 }
1739 let bg_color = self.bg_color.and_then(|c| c.to_windows(self.intense));
1740 if let Some((intense, color)) = bg_color {
1741 console.bg(intense, color)?;
1742 }
1743 Ok(())
1744 }
1745 }
1746
1747 /// The set of available colors for the terminal foreground/background.
1748 ///
1749 /// The `Ansi256` and `Rgb` colors will only output the correct codes when
1750 /// paired with the `Ansi` `WriteColor` implementation.
1751 ///
1752 /// The `Ansi256` and `Rgb` color types are not supported when writing colors
1753 /// on Windows using the console. If they are used on Windows, then they are
1754 /// silently ignored and no colors will be emitted.
1755 ///
1756 /// This set may expand over time.
1757 ///
1758 /// This type has a `FromStr` impl that can parse colors from their human
1759 /// readable form. The format is as follows:
1760 ///
1761 /// 1. Any of the explicitly listed colors in English. They are matched
1762 /// case insensitively.
1763 /// 2. A single 8-bit integer, in either decimal or hexadecimal format.
1764 /// 3. A triple of 8-bit integers separated by a comma, where each integer is
1765 /// in decimal or hexadecimal format.
1766 ///
1767 /// Hexadecimal numbers are written with a `0x` prefix.
1768 #[allow(missing_docs)]
1769 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
1770 pub enum Color {
1771 Black,
1772 Blue,
1773 Green,
1774 Red,
1775 Cyan,
1776 Magenta,
1777 Yellow,
1778 White,
1779 Ansi256(u8),
1780 Rgb(u8, u8, u8),
1781 #[doc(hidden)]
1782 __Nonexhaustive,
1783 }
1784
1785 impl Color {
1786 /// Translate this color to a wincon::Color.
1787 #[cfg(windows)]
1788 fn to_windows(
1789 self,
1790 intense: bool,
1791 ) -> Option<(wincon::Intense, wincon::Color)> {
1792 use wincon::Intense::{No, Yes};
1793
1794 let color = match self {
1795 Color::Black => wincon::Color::Black,
1796 Color::Blue => wincon::Color::Blue,
1797 Color::Green => wincon::Color::Green,
1798 Color::Red => wincon::Color::Red,
1799 Color::Cyan => wincon::Color::Cyan,
1800 Color::Magenta => wincon::Color::Magenta,
1801 Color::Yellow => wincon::Color::Yellow,
1802 Color::White => wincon::Color::White,
1803 Color::Ansi256(0) => return Some((No, wincon::Color::Black)),
1804 Color::Ansi256(1) => return Some((No, wincon::Color::Red)),
1805 Color::Ansi256(2) => return Some((No, wincon::Color::Green)),
1806 Color::Ansi256(3) => return Some((No, wincon::Color::Yellow)),
1807 Color::Ansi256(4) => return Some((No, wincon::Color::Blue)),
1808 Color::Ansi256(5) => return Some((No, wincon::Color::Magenta)),
1809 Color::Ansi256(6) => return Some((No, wincon::Color::Cyan)),
1810 Color::Ansi256(7) => return Some((No, wincon::Color::White)),
1811 Color::Ansi256(8) => return Some((Yes, wincon::Color::Black)),
1812 Color::Ansi256(9) => return Some((Yes, wincon::Color::Red)),
1813 Color::Ansi256(10) => return Some((Yes, wincon::Color::Green)),
1814 Color::Ansi256(11) => return Some((Yes, wincon::Color::Yellow)),
1815 Color::Ansi256(12) => return Some((Yes, wincon::Color::Blue)),
1816 Color::Ansi256(13) => return Some((Yes, wincon::Color::Magenta)),
1817 Color::Ansi256(14) => return Some((Yes, wincon::Color::Cyan)),
1818 Color::Ansi256(15) => return Some((Yes, wincon::Color::White)),
1819 Color::Ansi256(_) => return None,
1820 Color::Rgb(_, _, _) => return None,
1821 Color::__Nonexhaustive => unreachable!(),
1822 };
1823 let intense = if intense { Yes } else { No };
1824 Some((intense, color))
1825 }
1826
1827 /// Parses a numeric color string, either ANSI or RGB.
1828 fn from_str_numeric(s: &str) -> Result<Color, ParseColorError> {
1829 // The "ansi256" format is a single number (decimal or hex)
1830 // corresponding to one of 256 colors.
1831 //
1832 // The "rgb" format is a triple of numbers (decimal or hex) delimited
1833 // by a comma corresponding to one of 256^3 colors.
1834
1835 fn parse_number(s: &str) -> Option<u8> {
1836 use std::u8;
1837
1838 if s.starts_with("0x") {
1839 u8::from_str_radix(&s[2..], 16).ok()
1840 } else {
1841 u8::from_str_radix(s, 10).ok()
1842 }
1843 }
1844
1845 let codes: Vec<&str> = s.split(',').collect();
1846 if codes.len() == 1 {
1847 if let Some(n) = parse_number(&codes[0]) {
1848 Ok(Color::Ansi256(n))
1849 } else {
1850 if s.chars().all(|c| c.is_digit(16)) {
1851 Err(ParseColorError {
1852 kind: ParseColorErrorKind::InvalidAnsi256,
1853 given: s.to_string(),
1854 })
1855 } else {
1856 Err(ParseColorError {
1857 kind: ParseColorErrorKind::InvalidName,
1858 given: s.to_string(),
1859 })
1860 }
1861 }
1862 } else if codes.len() == 3 {
1863 let mut v = vec![];
1864 for code in codes {
1865 let n = parse_number(code).ok_or_else(|| ParseColorError {
1866 kind: ParseColorErrorKind::InvalidRgb,
1867 given: s.to_string(),
1868 })?;
1869 v.push(n);
1870 }
1871 Ok(Color::Rgb(v[0], v[1], v[2]))
1872 } else {
1873 Err(if s.contains(",") {
1874 ParseColorError {
1875 kind: ParseColorErrorKind::InvalidRgb,
1876 given: s.to_string(),
1877 }
1878 } else {
1879 ParseColorError {
1880 kind: ParseColorErrorKind::InvalidName,
1881 given: s.to_string(),
1882 }
1883 })
1884 }
1885 }
1886 }
1887
1888 /// An error from parsing an invalid color specification.
1889 #[derive(Clone, Debug, Eq, PartialEq)]
1890 pub struct ParseColorError {
1891 kind: ParseColorErrorKind,
1892 given: String,
1893 }
1894
1895 #[derive(Clone, Debug, Eq, PartialEq)]
1896 enum ParseColorErrorKind {
1897 InvalidName,
1898 InvalidAnsi256,
1899 InvalidRgb,
1900 }
1901
1902 impl ParseColorError {
1903 /// Return the string that couldn't be parsed as a valid color.
1904 pub fn invalid(&self) -> &str {
1905 &self.given
1906 }
1907 }
1908
1909 impl error::Error for ParseColorError {
1910 fn description(&self) -> &str {
1911 use self::ParseColorErrorKind::*;
1912 match self.kind {
1913 InvalidName => "unrecognized color name",
1914 InvalidAnsi256 => "invalid ansi256 color number",
1915 InvalidRgb => "invalid RGB color triple",
1916 }
1917 }
1918 }
1919
1920 impl fmt::Display for ParseColorError {
1921 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1922 use self::ParseColorErrorKind::*;
1923 match self.kind {
1924 InvalidName => write!(
1925 f,
1926 "unrecognized color name '{}'. Choose from: \
1927 black, blue, green, red, cyan, magenta, yellow, \
1928 white",
1929 self.given
1930 ),
1931 InvalidAnsi256 => write!(
1932 f,
1933 "unrecognized ansi256 color number, \
1934 should be '[0-255]' (or a hex number), but is '{}'",
1935 self.given
1936 ),
1937 InvalidRgb => write!(
1938 f,
1939 "unrecognized RGB color triple, \
1940 should be '[0-255],[0-255],[0-255]' (or a hex \
1941 triple), but is '{}'",
1942 self.given
1943 ),
1944 }
1945 }
1946 }
1947
1948 impl FromStr for Color {
1949 type Err = ParseColorError;
1950
1951 fn from_str(s: &str) -> Result<Color, ParseColorError> {
1952 match &*s.to_lowercase() {
1953 "black" => Ok(Color::Black),
1954 "blue" => Ok(Color::Blue),
1955 "green" => Ok(Color::Green),
1956 "red" => Ok(Color::Red),
1957 "cyan" => Ok(Color::Cyan),
1958 "magenta" => Ok(Color::Magenta),
1959 "yellow" => Ok(Color::Yellow),
1960 "white" => Ok(Color::White),
1961 _ => Color::from_str_numeric(s),
1962 }
1963 }
1964 }
1965
1966 struct LossyStandardStream<W> {
1967 wtr: W,
1968 #[cfg(windows)]
1969 is_console: bool,
1970 }
1971
1972 impl<W: io::Write> LossyStandardStream<W> {
1973 #[cfg(not(windows))]
1974 fn new(wtr: W) -> LossyStandardStream<W> {
1975 LossyStandardStream { wtr: wtr }
1976 }
1977
1978 #[cfg(windows)]
1979 fn new(wtr: W) -> LossyStandardStream<W> {
1980 let is_console = wincon::Console::stdout().is_ok()
1981 || wincon::Console::stderr().is_ok();
1982 LossyStandardStream { wtr: wtr, is_console: is_console }
1983 }
1984
1985 #[cfg(not(windows))]
1986 fn wrap<Q: io::Write>(&self, wtr: Q) -> LossyStandardStream<Q> {
1987 LossyStandardStream::new(wtr)
1988 }
1989
1990 #[cfg(windows)]
1991 fn wrap<Q: io::Write>(&self, wtr: Q) -> LossyStandardStream<Q> {
1992 LossyStandardStream { wtr: wtr, is_console: self.is_console }
1993 }
1994
1995 fn get_ref(&self) -> &W {
1996 &self.wtr
1997 }
1998 }
1999
2000 impl<W: WriteColor> WriteColor for LossyStandardStream<W> {
2001 fn supports_color(&self) -> bool {
2002 self.wtr.supports_color()
2003 }
2004 fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
2005 self.wtr.set_color(spec)
2006 }
2007 fn reset(&mut self) -> io::Result<()> {
2008 self.wtr.reset()
2009 }
2010 fn is_synchronous(&self) -> bool {
2011 self.wtr.is_synchronous()
2012 }
2013 }
2014
2015 impl<W: io::Write> io::Write for LossyStandardStream<W> {
2016 #[cfg(not(windows))]
2017 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
2018 self.wtr.write(buf)
2019 }
2020
2021 #[cfg(windows)]
2022 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
2023 if self.is_console {
2024 write_lossy_utf8(&mut self.wtr, buf)
2025 } else {
2026 self.wtr.write(buf)
2027 }
2028 }
2029
2030 fn flush(&mut self) -> io::Result<()> {
2031 self.wtr.flush()
2032 }
2033 }
2034
2035 #[cfg(windows)]
2036 fn write_lossy_utf8<W: io::Write>(mut w: W, buf: &[u8]) -> io::Result<usize> {
2037 match ::std::str::from_utf8(buf) {
2038 Ok(s) => w.write(s.as_bytes()),
2039 Err(ref e) if e.valid_up_to() == 0 => {
2040 w.write(b"\xEF\xBF\xBD")?;
2041 Ok(1)
2042 }
2043 Err(e) => w.write(&buf[..e.valid_up_to()]),
2044 }
2045 }
2046
2047 #[cfg(test)]
2048 mod tests {
2049 use super::{
2050 Ansi, Color, ColorSpec, ParseColorError, ParseColorErrorKind,
2051 StandardStream, WriteColor,
2052 };
2053
2054 fn assert_is_send<T: Send>() {}
2055
2056 #[test]
2057 fn standard_stream_is_send() {
2058 assert_is_send::<StandardStream>();
2059 }
2060
2061 #[test]
2062 fn test_simple_parse_ok() {
2063 let color = "green".parse::<Color>();
2064 assert_eq!(color, Ok(Color::Green));
2065 }
2066
2067 #[test]
2068 fn test_256_parse_ok() {
2069 let color = "7".parse::<Color>();
2070 assert_eq!(color, Ok(Color::Ansi256(7)));
2071
2072 let color = "32".parse::<Color>();
2073 assert_eq!(color, Ok(Color::Ansi256(32)));
2074
2075 let color = "0xFF".parse::<Color>();
2076 assert_eq!(color, Ok(Color::Ansi256(0xFF)));
2077 }
2078
2079 #[test]
2080 fn test_256_parse_err_out_of_range() {
2081 let color = "256".parse::<Color>();
2082 assert_eq!(
2083 color,
2084 Err(ParseColorError {
2085 kind: ParseColorErrorKind::InvalidAnsi256,
2086 given: "256".to_string(),
2087 })
2088 );
2089 }
2090
2091 #[test]
2092 fn test_rgb_parse_ok() {
2093 let color = "0,0,0".parse::<Color>();
2094 assert_eq!(color, Ok(Color::Rgb(0, 0, 0)));
2095
2096 let color = "0,128,255".parse::<Color>();
2097 assert_eq!(color, Ok(Color::Rgb(0, 128, 255)));
2098
2099 let color = "0x0,0x0,0x0".parse::<Color>();
2100 assert_eq!(color, Ok(Color::Rgb(0, 0, 0)));
2101
2102 let color = "0x33,0x66,0xFF".parse::<Color>();
2103 assert_eq!(color, Ok(Color::Rgb(0x33, 0x66, 0xFF)));
2104 }
2105
2106 #[test]
2107 fn test_rgb_parse_err_out_of_range() {
2108 let color = "0,0,256".parse::<Color>();
2109 assert_eq!(
2110 color,
2111 Err(ParseColorError {
2112 kind: ParseColorErrorKind::InvalidRgb,
2113 given: "0,0,256".to_string(),
2114 })
2115 );
2116 }
2117
2118 #[test]
2119 fn test_rgb_parse_err_bad_format() {
2120 let color = "0,0".parse::<Color>();
2121 assert_eq!(
2122 color,
2123 Err(ParseColorError {
2124 kind: ParseColorErrorKind::InvalidRgb,
2125 given: "0,0".to_string(),
2126 })
2127 );
2128
2129 let color = "not_a_color".parse::<Color>();
2130 assert_eq!(
2131 color,
2132 Err(ParseColorError {
2133 kind: ParseColorErrorKind::InvalidName,
2134 given: "not_a_color".to_string(),
2135 })
2136 );
2137 }
2138
2139 #[test]
2140 fn test_var_ansi_write_rgb() {
2141 let mut buf = Ansi::new(vec![]);
2142 let _ = buf.write_color(true, &Color::Rgb(254, 253, 255), false);
2143 assert_eq!(buf.0, b"\x1B[38;2;254;253;255m");
2144 }
2145
2146 #[test]
2147 fn test_reset() {
2148 let spec = ColorSpec::new();
2149 let mut buf = Ansi::new(vec![]);
2150 buf.set_color(&spec).unwrap();
2151 assert_eq!(buf.0, b"\x1B[0m");
2152 }
2153
2154 #[test]
2155 fn test_no_reset() {
2156 let mut spec = ColorSpec::new();
2157 spec.set_reset(false);
2158
2159 let mut buf = Ansi::new(vec![]);
2160 buf.set_color(&spec).unwrap();
2161 assert_eq!(buf.0, b"");
2162 }
2163
2164 #[test]
2165 fn test_var_ansi_write_256() {
2166 let mut buf = Ansi::new(vec![]);
2167 let _ = buf.write_color(false, &Color::Ansi256(7), false);
2168 assert_eq!(buf.0, b"\x1B[48;5;7m");
2169
2170 let mut buf = Ansi::new(vec![]);
2171 let _ = buf.write_color(false, &Color::Ansi256(208), false);
2172 assert_eq!(buf.0, b"\x1B[48;5;208m");
2173 }
2174
2175 fn all_attributes() -> Vec<ColorSpec> {
2176 let mut result = vec![];
2177 for fg in vec![None, Some(Color::Red)] {
2178 for bg in vec![None, Some(Color::Red)] {
2179 for bold in vec![false, true] {
2180 for underline in vec![false, true] {
2181 for intense in vec![false, true] {
2182 for italic in vec![false, true] {
2183 let mut color = ColorSpec::new();
2184 color.set_fg(fg);
2185 color.set_bg(bg);
2186 color.set_bold(bold);
2187 color.set_underline(underline);
2188 color.set_intense(intense);
2189 color.set_italic(italic);
2190 result.push(color);
2191 }
2192 }
2193 }
2194 }
2195 }
2196 }
2197 result
2198 }
2199
2200 #[test]
2201 fn test_is_none() {
2202 for (i, color) in all_attributes().iter().enumerate() {
2203 assert_eq!(
2204 i == 0,
2205 color.is_none(),
2206 "{:?} => {}",
2207 color,
2208 color.is_none()
2209 )
2210 }
2211 }
2212
2213 #[test]
2214 fn test_clear() {
2215 for color in all_attributes() {
2216 let mut color1 = color.clone();
2217 color1.clear();
2218 assert!(color1.is_none(), "{:?} => {:?}", color, color1);
2219 }
2220 }
2221 }