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