]> git.proxmox.com Git - rustc.git/blame - vendor/termcolor/src/lib.rs
New upstream version 1.68.2+dfsg1
[rustc.git] / vendor / termcolor / src / lib.rs
CommitLineData
2c00a5a8
XL
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
13# Organization
14
15The `WriteColor` trait extends the `io::Write` trait with methods for setting
16colors or resetting them.
17
18`StandardStream` and `StandardStreamLock` both satisfy `WriteColor` and are
19analogous to `std::io::Stdout` and `std::io::StdoutLock`, or `std::io::Stderr`
20and `std::io::StderrLock`.
21
22`Buffer` is an in memory buffer that supports colored text. In a parallel
23program, each thread might write to its own buffer. A buffer can be printed to
24using a `BufferWriter`. The advantage of this design is that each thread can
25work in parallel on a buffer without having to synchronize access to global
26resources such as the Windows console. Moreover, this design also prevents
27interleaving 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
31analogous type for the Windows console is not provided since it cannot exist.
32
33# Example: using `StandardStream`
34
35The `StandardStream` type in this crate works similarly to `std::io::Stdout`,
36except it is augmented with methods for coloring by the `WriteColor` trait.
37For example, to write some green text:
38
39```rust,no_run
40# fn test() -> Result<(), Box<::std::error::Error>> {
41use std::io::Write;
42use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
43
44let mut stdout = StandardStream::stdout(ColorChoice::Always);
0531ce1d
XL
45stdout.set_color(ColorSpec::new().set_fg(Some(Color::Green)))?;
46writeln!(&mut stdout, "green text!")?;
2c00a5a8
XL
47# Ok(()) }
48```
49
3dfed10e
XL
50Note that any text written to the terminal now will be colored
51green when using ANSI escape sequences, even if it is written via
52stderr, and even if stderr had previously been set to `Color::Red`.
53Users will need to manage any color changes themselves by calling
54[`WriteColor::set_color`](trait.WriteColor.html#tymethod.set_color), and this
55may include calling [`WriteColor::reset`](trait.WriteColor.html#tymethod.reset)
56before the program exits to a shell.
57
2c00a5a8
XL
58# Example: using `BufferWriter`
59
60A `BufferWriter` can create buffers and write buffers to stdout or stderr. It
61does *not* implement `io::Write` or `WriteColor` itself. Instead, `Buffer`
62implements `io::Write` and `io::WriteColor`.
63
64This example shows how to print some green text to stderr.
65
66```rust,no_run
67# fn test() -> Result<(), Box<::std::error::Error>> {
68use std::io::Write;
69use termcolor::{BufferWriter, Color, ColorChoice, ColorSpec, WriteColor};
70
71let mut bufwtr = BufferWriter::stderr(ColorChoice::Always);
72let mut buffer = bufwtr.buffer();
0531ce1d
XL
73buffer.set_color(ColorSpec::new().set_fg(Some(Color::Green)))?;
74writeln!(&mut buffer, "green text!")?;
75bufwtr.print(&buffer)?;
2c00a5a8
XL
76# Ok(()) }
77```
3dfed10e
XL
78
79# Detecting presence of a terminal
80
81In many scenarios when using color, one often wants to enable colors
82automatically when writing to a terminal and disable colors automatically when
83writing to anything else. The typical way to achieve this in Unix environments
84is via libc's
6522a427 85[`isatty`](https://man7.org/linux/man-pages/man3/isatty.3.html)
3dfed10e
XL
86function.
87Unfortunately, this notoriously does not work well in Windows environments. To
88work around that, the currently recommended solution is to use the
89[`atty`](https://crates.io/crates/atty)
90crate, which goes out of its way to get this as right as possible in Windows
91environments.
92
93For example, in a command line application that exposes a `--color` flag,
94your logic for how to enable colors might look like this:
95
96```rust,ignore
97use atty;
98use termcolor::{ColorChoice, StandardStream};
99
100let preference = argv.get_flag("color").unwrap_or("auto");
101let 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};
113let stdout = StandardStream::stdout(choice);
114// ... write to stdout
115```
116
117Currently, `termcolor` does not provide anything to do this for you.
2c00a5a8
XL
118*/
119
120#![deny(missing_docs)]
121
5869c6ff
XL
122// #[cfg(doctest)]
123// use doc_comment::doctest;
124// #[cfg(doctest)]
125// doctest!("../README.md");
2c00a5a8
XL
126
127use std::env;
128use std::error;
129use std::fmt;
130use std::io::{self, Write};
131use std::str::FromStr;
3dfed10e 132use std::sync::atomic::{AtomicBool, Ordering};
2c00a5a8
XL
133#[cfg(windows)]
134use std::sync::{Mutex, MutexGuard};
3dfed10e
XL
135
136#[cfg(windows)]
137use winapi_util::console as wincon;
2c00a5a8
XL
138
139/// This trait describes the behavior of writers that support colored output.
140pub 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<()>;
b7449926
XL
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 }
2c00a5a8
XL
173}
174
0531ce1d 175impl<'a, T: ?Sized + WriteColor> WriteColor for &'a mut T {
3dfed10e
XL
176 fn supports_color(&self) -> bool {
177 (&**self).supports_color()
178 }
2c00a5a8
XL
179 fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
180 (&mut **self).set_color(spec)
181 }
3dfed10e
XL
182 fn reset(&mut self) -> io::Result<()> {
183 (&mut **self).reset()
184 }
185 fn is_synchronous(&self) -> bool {
186 (&**self).is_synchronous()
187 }
b7449926
XL
188}
189
190impl<T: ?Sized + WriteColor> WriteColor for Box<T> {
3dfed10e
XL
191 fn supports_color(&self) -> bool {
192 (&**self).supports_color()
193 }
b7449926
XL
194 fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
195 (&mut **self).set_color(spec)
196 }
3dfed10e
XL
197 fn reset(&mut self) -> io::Result<()> {
198 (&mut **self).reset()
199 }
200 fn is_synchronous(&self) -> bool {
201 (&**self).is_synchronous()
202 }
2c00a5a8
XL
203}
204
205/// ColorChoice represents the color preferences of an end user.
206#[derive(Clone, Copy, Debug, Eq, PartialEq)]
207pub 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
3dfed10e
XL
215 /// available on Windows, or if TERM=dumb, or if `NO_COLOR` is defined, for
216 /// example, then don't use colors.
2c00a5a8
XL
217 Auto,
218 /// Never emit colors.
219 Never,
220}
221
222impl ColorChoice {
223 /// Returns true if we should attempt to write colored output.
2c00a5a8
XL
224 fn should_attempt_color(&self) -> bool {
225 match *self {
226 ColorChoice::Always => true,
227 ColorChoice::AlwaysAnsi => true,
228 ColorChoice::Never => false,
3dfed10e
XL
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;
2c00a5a8
XL
242 }
243 }
244 }
3dfed10e
XL
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
2c00a5a8
XL
251 }
252
2c00a5a8 253 #[cfg(windows)]
3dfed10e
XL
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;
2c00a5a8
XL
261 }
262 }
3dfed10e
XL
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
2c00a5a8
XL
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
298enum StandardStreamType {
299 Stdout,
300 Stderr,
b7449926
XL
301 StdoutBuffered,
302 StderrBuffered,
2c00a5a8
XL
303}
304
305enum IoStandardStream {
306 Stdout(io::Stdout),
307 Stderr(io::Stderr),
b7449926
XL
308 StdoutBuffered(io::BufWriter<io::Stdout>),
309 StderrBuffered(io::BufWriter<io::Stderr>),
2c00a5a8
XL
310}
311
312impl 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 }
b7449926
XL
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 }
2c00a5a8
XL
329 }
330 }
331
3dfed10e 332 fn lock(&self) -> IoStandardStreamLock<'_> {
2c00a5a8
XL
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 }
b7449926
XL
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 }
2c00a5a8
XL
346 }
347 }
348}
349
350impl io::Write for IoStandardStream {
0731742a 351 #[inline(always)]
2c00a5a8
XL
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),
b7449926
XL
356 IoStandardStream::StdoutBuffered(ref mut s) => s.write(b),
357 IoStandardStream::StderrBuffered(ref mut s) => s.write(b),
2c00a5a8
XL
358 }
359 }
360
0731742a 361 #[inline(always)]
2c00a5a8
XL
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(),
b7449926
XL
366 IoStandardStream::StdoutBuffered(ref mut s) => s.flush(),
367 IoStandardStream::StderrBuffered(ref mut s) => s.flush(),
2c00a5a8
XL
368 }
369 }
370}
371
0531ce1d 372// Same rigmarole for the locked variants of the standard streams.
2c00a5a8
XL
373
374enum IoStandardStreamLock<'a> {
375 StdoutLock(io::StdoutLock<'a>),
376 StderrLock(io::StderrLock<'a>),
377}
378
379impl<'a> io::Write for IoStandardStreamLock<'a> {
0731742a 380 #[inline(always)]
2c00a5a8
XL
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
0731742a 388 #[inline(always)]
2c00a5a8
XL
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.
399pub 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`.
410pub struct StandardStreamLock<'a> {
411 wtr: LossyStandardStream<WriterInnerLock<'a, IoStandardStreamLock<'a>>>,
412}
413
b7449926
XL
414/// Like `StandardStream`, but does buffered writing.
415pub struct BufferedStandardStream {
416 wtr: LossyStandardStream<WriterInner<IoStandardStream>>,
417}
418
2c00a5a8
XL
419/// WriterInner is a (limited) generic representation of a writer. It is
420/// limited because W should only ever be stdout/stderr on Windows.
421enum WriterInner<W> {
422 NoColor(NoColor<W>),
423 Ansi(Ansi<W>),
424 #[cfg(windows)]
3dfed10e
XL
425 Windows {
426 wtr: W,
427 console: Mutex<wincon::Console>,
428 },
2c00a5a8
XL
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.
433enum 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
0531ce1d 438 /// on Unix, which makes the `'a` unused. To satisfy the compiler, we need
2c00a5a8
XL
439 /// a PhantomData.
440 #[allow(dead_code)]
441 Unreachable(::std::marker::PhantomData<&'a ()>),
442 #[cfg(windows)]
3dfed10e
XL
443 Windows {
444 wtr: W,
445 console: MutexGuard<'a, wincon::Console>,
446 },
2c00a5a8
XL
447}
448
449impl StandardStream {
2c00a5a8
XL
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 {
b7449926
XL
459 let wtr = WriterInner::create(StandardStreamType::Stdout, choice);
460 StandardStream { wtr: LossyStandardStream::new(wtr) }
2c00a5a8
XL
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 {
b7449926
XL
472 let wtr = WriterInner::create(StandardStreamType::Stderr, choice);
473 StandardStream { wtr: LossyStandardStream::new(wtr) }
2c00a5a8
XL
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.
3dfed10e 483 pub fn lock(&self) -> StandardStreamLock<'_> {
2c00a5a8
XL
484 StandardStreamLock::from_stream(self)
485 }
486}
487
488impl<'a> StandardStreamLock<'a> {
489 #[cfg(not(windows))]
3dfed10e 490 fn from_stream(stream: &StandardStream) -> StandardStreamLock<'_> {
2c00a5a8
XL
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
b7449926
XL
523impl 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 {
3dfed10e
XL
533 let wtr =
534 WriterInner::create(StandardStreamType::StdoutBuffered, choice);
b7449926
XL
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 {
3dfed10e
XL
547 let wtr =
548 WriterInner::create(StandardStreamType::StderrBuffered, choice);
b7449926
XL
549 BufferedStandardStream { wtr: LossyStandardStream::new(wtr) }
550 }
551}
552
553impl 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 {
3dfed10e
XL
579 StandardStreamType::Stdout => wincon::Console::stdout(),
580 StandardStreamType::Stderr => wincon::Console::stderr(),
581 StandardStreamType::StdoutBuffered => wincon::Console::stdout(),
582 StandardStreamType::StderrBuffered => wincon::Console::stderr(),
b7449926 583 };
3dfed10e
XL
584 let is_console_virtual = con
585 .as_mut()
586 .map(|con| con.set_virtual_terminal_processing(true).is_ok())
587 .unwrap_or(false);
b7449926
XL
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
2c00a5a8 605impl io::Write for StandardStream {
0731742a 606 #[inline]
3dfed10e
XL
607 fn write(&mut self, b: &[u8]) -> io::Result<usize> {
608 self.wtr.write(b)
609 }
0731742a
XL
610
611 #[inline]
3dfed10e
XL
612 fn flush(&mut self) -> io::Result<()> {
613 self.wtr.flush()
614 }
2c00a5a8
XL
615}
616
617impl WriteColor for StandardStream {
0731742a 618 #[inline]
3dfed10e
XL
619 fn supports_color(&self) -> bool {
620 self.wtr.supports_color()
621 }
0731742a
XL
622
623 #[inline]
2c00a5a8
XL
624 fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
625 self.wtr.set_color(spec)
626 }
0731742a
XL
627
628 #[inline]
3dfed10e
XL
629 fn reset(&mut self) -> io::Result<()> {
630 self.wtr.reset()
631 }
0731742a
XL
632
633 #[inline]
3dfed10e
XL
634 fn is_synchronous(&self) -> bool {
635 self.wtr.is_synchronous()
636 }
2c00a5a8
XL
637}
638
639impl<'a> io::Write for StandardStreamLock<'a> {
0731742a 640 #[inline]
3dfed10e
XL
641 fn write(&mut self, b: &[u8]) -> io::Result<usize> {
642 self.wtr.write(b)
643 }
0731742a
XL
644
645 #[inline]
3dfed10e
XL
646 fn flush(&mut self) -> io::Result<()> {
647 self.wtr.flush()
648 }
2c00a5a8
XL
649}
650
651impl<'a> WriteColor for StandardStreamLock<'a> {
0731742a 652 #[inline]
3dfed10e
XL
653 fn supports_color(&self) -> bool {
654 self.wtr.supports_color()
655 }
0731742a
XL
656
657 #[inline]
2c00a5a8
XL
658 fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
659 self.wtr.set_color(spec)
660 }
0731742a
XL
661
662 #[inline]
3dfed10e
XL
663 fn reset(&mut self) -> io::Result<()> {
664 self.wtr.reset()
665 }
0731742a
XL
666
667 #[inline]
3dfed10e
XL
668 fn is_synchronous(&self) -> bool {
669 self.wtr.is_synchronous()
670 }
b7449926
XL
671}
672
673impl io::Write for BufferedStandardStream {
0731742a 674 #[inline]
3dfed10e
XL
675 fn write(&mut self, b: &[u8]) -> io::Result<usize> {
676 self.wtr.write(b)
677 }
0731742a
XL
678
679 #[inline]
3dfed10e
XL
680 fn flush(&mut self) -> io::Result<()> {
681 self.wtr.flush()
682 }
b7449926
XL
683}
684
685impl WriteColor for BufferedStandardStream {
0731742a 686 #[inline]
3dfed10e
XL
687 fn supports_color(&self) -> bool {
688 self.wtr.supports_color()
689 }
0731742a
XL
690
691 #[inline]
b7449926
XL
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 }
0731742a
XL
698
699 #[inline]
3dfed10e
XL
700 fn reset(&mut self) -> io::Result<()> {
701 self.wtr.reset()
702 }
0731742a
XL
703
704 #[inline]
3dfed10e
XL
705 fn is_synchronous(&self) -> bool {
706 self.wtr.is_synchronous()
707 }
2c00a5a8
XL
708}
709
710impl<W: io::Write> io::Write for WriterInner<W> {
0731742a 711 #[inline(always)]
2c00a5a8
XL
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
0731742a 721 #[inline(always)]
2c00a5a8
XL
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
732impl<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 } => {
0531ce1d 748 wtr.flush()?;
2c00a5a8
XL
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 } => {
0531ce1d
XL
761 wtr.flush()?;
762 console.lock().unwrap().reset()?;
2c00a5a8
XL
763 Ok(())
764 }
765 }
766 }
b7449926
XL
767
768 fn is_synchronous(&self) -> bool {
769 match *self {
770 WriterInner::NoColor(_) => false,
771 WriterInner::Ansi(_) => false,
772 #[cfg(windows)]
3dfed10e 773 WriterInner::Windows { .. } => true,
b7449926
XL
774 }
775 }
2c00a5a8
XL
776}
777
778impl<'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
800impl<'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 } => {
0531ce1d 818 wtr.flush()?;
2c00a5a8
XL
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 } => {
0531ce1d
XL
831 wtr.flush()?;
832 console.reset()?;
2c00a5a8
XL
833 Ok(())
834 }
835 }
836 }
b7449926
XL
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)]
3dfed10e 844 WriterInnerLock::Windows { .. } => true,
b7449926
XL
845 }
846 }
2c00a5a8
XL
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.
858pub struct BufferWriter {
859 stream: LossyStandardStream<IoStandardStream>,
860 printed: AtomicBool,
861 separator: Option<Vec<u8>>,
862 color_choice: ColorChoice,
863 #[cfg(windows)]
3dfed10e 864 console: Option<Mutex<wincon::Console>>,
2c00a5a8
XL
865}
866
867impl 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 {
0531ce1d 893 let mut con = match sty {
3dfed10e
XL
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);
0531ce1d
XL
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 }
b7449926 909 let stream = LossyStandardStream::new(IoStandardStream::new(sty));
2c00a5a8
XL
910 BufferWriter {
911 stream: stream,
912 printed: AtomicBool::new(false),
913 separator: None,
914 color_choice: choice,
0531ce1d 915 console: con.map(Mutex::new),
2c00a5a8
XL
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) {
0531ce1d
XL
981 stream.write_all(sep)?;
982 stream.write_all(b"\n")?;
2c00a5a8
XL
983 }
984 }
985 match buf.0 {
0531ce1d
XL
986 BufferInner::NoColor(ref b) => stream.write_all(&b.0)?,
987 BufferInner::Ansi(ref b) => stream.write_all(&b.0)?,
2c00a5a8
XL
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.
3dfed10e
XL
992 let console_mutex = self
993 .console
994 .as_ref()
2c00a5a8
XL
995 .expect("got Windows buffer but have no Console");
996 let mut console = console_mutex.lock().unwrap();
0531ce1d 997 b.print(&mut *console, &mut stream)?;
2c00a5a8
XL
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).
1016pub struct Buffer(BufferInner);
1017
1018/// BufferInner is an enumeration of different buffer types.
1019enum 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
1032impl 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
1138impl io::Write for Buffer {
0731742a 1139 #[inline]
2c00a5a8
XL
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
0731742a 1149 #[inline]
2c00a5a8
XL
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
1160impl WriteColor for Buffer {
0731742a 1161 #[inline]
2c00a5a8
XL
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
0731742a 1171 #[inline]
2c00a5a8
XL
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
0731742a 1181 #[inline]
2c00a5a8
XL
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 }
b7449926 1190
0731742a 1191 #[inline]
b7449926
XL
1192 fn is_synchronous(&self) -> bool {
1193 false
1194 }
2c00a5a8
XL
1195}
1196
1197/// Satisfies `WriteColor` but ignores all color options.
1198pub struct NoColor<W>(W);
1199
1200impl<W: Write> NoColor<W> {
1201 /// Create a new writer that satisfies `WriteColor` but drops all color
1202 /// information.
3dfed10e
XL
1203 pub fn new(wtr: W) -> NoColor<W> {
1204 NoColor(wtr)
1205 }
2c00a5a8
XL
1206
1207 /// Consume this `NoColor` value and return the inner writer.
3dfed10e
XL
1208 pub fn into_inner(self) -> W {
1209 self.0
1210 }
2c00a5a8
XL
1211
1212 /// Return a reference to the inner writer.
3dfed10e
XL
1213 pub fn get_ref(&self) -> &W {
1214 &self.0
1215 }
2c00a5a8
XL
1216
1217 /// Return a mutable reference to the inner writer.
3dfed10e
XL
1218 pub fn get_mut(&mut self) -> &mut W {
1219 &mut self.0
1220 }
2c00a5a8
XL
1221}
1222
1223impl<W: io::Write> io::Write for NoColor<W> {
0731742a 1224 #[inline]
2c00a5a8
XL
1225 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
1226 self.0.write(buf)
1227 }
1228
0731742a 1229 #[inline]
2c00a5a8
XL
1230 fn flush(&mut self) -> io::Result<()> {
1231 self.0.flush()
1232 }
1233}
1234
1235impl<W: io::Write> WriteColor for NoColor<W> {
0731742a 1236 #[inline]
3dfed10e
XL
1237 fn supports_color(&self) -> bool {
1238 false
1239 }
0731742a
XL
1240
1241 #[inline]
3dfed10e
XL
1242 fn set_color(&mut self, _: &ColorSpec) -> io::Result<()> {
1243 Ok(())
1244 }
0731742a
XL
1245
1246 #[inline]
3dfed10e
XL
1247 fn reset(&mut self) -> io::Result<()> {
1248 Ok(())
1249 }
0731742a
XL
1250
1251 #[inline]
3dfed10e
XL
1252 fn is_synchronous(&self) -> bool {
1253 false
1254 }
2c00a5a8
XL
1255}
1256
1257/// Satisfies `WriteColor` using standard ANSI escape sequences.
1258pub struct Ansi<W>(W);
1259
1260impl<W: Write> Ansi<W> {
1261 /// Create a new writer that satisfies `WriteColor` using standard ANSI
1262 /// escape sequences.
3dfed10e
XL
1263 pub fn new(wtr: W) -> Ansi<W> {
1264 Ansi(wtr)
1265 }
2c00a5a8
XL
1266
1267 /// Consume this `Ansi` value and return the inner writer.
3dfed10e
XL
1268 pub fn into_inner(self) -> W {
1269 self.0
1270 }
2c00a5a8
XL
1271
1272 /// Return a reference to the inner writer.
3dfed10e
XL
1273 pub fn get_ref(&self) -> &W {
1274 &self.0
1275 }
2c00a5a8
XL
1276
1277 /// Return a mutable reference to the inner writer.
3dfed10e
XL
1278 pub fn get_mut(&mut self) -> &mut W {
1279 &mut self.0
1280 }
2c00a5a8
XL
1281}
1282
1283impl<W: io::Write> io::Write for Ansi<W> {
0731742a 1284 #[inline]
2c00a5a8
XL
1285 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
1286 self.0.write(buf)
1287 }
1288
0731742a 1289 #[inline]
2c00a5a8
XL
1290 fn flush(&mut self) -> io::Result<()> {
1291 self.0.flush()
1292 }
1293}
1294
1295impl<W: io::Write> WriteColor for Ansi<W> {
0731742a 1296 #[inline]
3dfed10e
XL
1297 fn supports_color(&self) -> bool {
1298 true
1299 }
2c00a5a8 1300
0731742a 1301 #[inline]
2c00a5a8 1302 fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
3dfed10e
XL
1303 if spec.reset {
1304 self.reset()?;
1305 }
0531ce1d
XL
1306 if spec.bold {
1307 self.write_str("\x1B[1m")?;
1308 }
5869c6ff
XL
1309 if spec.dimmed {
1310 self.write_str("\x1B[2m")?;
1311 }
3dfed10e
XL
1312 if spec.italic {
1313 self.write_str("\x1B[3m")?;
1314 }
0531ce1d
XL
1315 if spec.underline {
1316 self.write_str("\x1B[4m")?;
1317 }
2c00a5a8 1318 if let Some(ref c) = spec.fg_color {
0531ce1d 1319 self.write_color(true, c, spec.intense)?;
2c00a5a8
XL
1320 }
1321 if let Some(ref c) = spec.bg_color {
0531ce1d 1322 self.write_color(false, c, spec.intense)?;
2c00a5a8
XL
1323 }
1324 Ok(())
1325 }
1326
0731742a 1327 #[inline]
2c00a5a8 1328 fn reset(&mut self) -> io::Result<()> {
0531ce1d 1329 self.write_str("\x1B[0m")
2c00a5a8 1330 }
b7449926 1331
0731742a 1332 #[inline]
3dfed10e
XL
1333 fn is_synchronous(&self) -> bool {
1334 false
1335 }
2c00a5a8
XL
1336}
1337
1338impl<W: io::Write> Ansi<W> {
1339 fn write_str(&mut self, s: &str) -> io::Result<()> {
1340 self.write_all(s.as_bytes())
1341 }
1342
1343 fn write_color(
1344 &mut self,
1345 fg: bool,
1346 c: &Color,
1347 intense: bool,
1348 ) -> io::Result<()> {
1349 macro_rules! write_intense {
1350 ($clr:expr) => {
1351 if fg {
1352 self.write_str(concat!("\x1B[38;5;", $clr, "m"))
1353 } else {
1354 self.write_str(concat!("\x1B[48;5;", $clr, "m"))
1355 }
3dfed10e 1356 };
2c00a5a8
XL
1357 }
1358 macro_rules! write_normal {
1359 ($clr:expr) => {
1360 if fg {
1361 self.write_str(concat!("\x1B[3", $clr, "m"))
1362 } else {
1363 self.write_str(concat!("\x1B[4", $clr, "m"))
1364 }
3dfed10e 1365 };
2c00a5a8 1366 }
0531ce1d
XL
1367 macro_rules! write_var_ansi_code {
1368 ($pre:expr, $($code:expr),+) => {{
1369 // The loop generates at worst a literal of the form
1370 // '255,255,255m' which is 12-bytes.
1371 // The largest `pre` expression we currently use is 7 bytes.
1372 // This gives us the maximum of 19-bytes for our work buffer.
1373 let pre_len = $pre.len();
1374 assert!(pre_len <= 7);
1375 let mut fmt = [0u8; 19];
1376 fmt[..pre_len].copy_from_slice($pre);
1377 let mut i = pre_len - 1;
1378 $(
1379 let c1: u8 = ($code / 100) % 10;
1380 let c2: u8 = ($code / 10) % 10;
1381 let c3: u8 = $code % 10;
1382 let mut printed = false;
1383
1384 if c1 != 0 {
1385 printed = true;
1386 i += 1;
1387 fmt[i] = b'0' + c1;
1388 }
1389 if c2 != 0 || printed {
1390 i += 1;
1391 fmt[i] = b'0' + c2;
1392 }
1393 // If we received a zero value we must still print a value.
1394 i += 1;
1395 fmt[i] = b'0' + c3;
1396 i += 1;
1397 fmt[i] = b';';
1398 )+
1399
1400 fmt[i] = b'm';
1401 self.write_all(&fmt[0..i+1])
1402 }}
1403 }
1404 macro_rules! write_custom {
1405 ($ansi256:expr) => {
1406 if fg {
1407 write_var_ansi_code!(b"\x1B[38;5;", $ansi256)
1408 } else {
1409 write_var_ansi_code!(b"\x1B[48;5;", $ansi256)
1410 }
1411 };
1412
1413 ($r:expr, $g:expr, $b:expr) => {{
1414 if fg {
1415 write_var_ansi_code!(b"\x1B[38;2;", $r, $g, $b)
1416 } else {
1417 write_var_ansi_code!(b"\x1B[48;2;", $r, $g, $b)
1418 }
1419 }};
1420 }
2c00a5a8
XL
1421 if intense {
1422 match *c {
1423 Color::Black => write_intense!("8"),
1424 Color::Blue => write_intense!("12"),
1425 Color::Green => write_intense!("10"),
1426 Color::Red => write_intense!("9"),
1427 Color::Cyan => write_intense!("14"),
1428 Color::Magenta => write_intense!("13"),
1429 Color::Yellow => write_intense!("11"),
1430 Color::White => write_intense!("15"),
0531ce1d
XL
1431 Color::Ansi256(c) => write_custom!(c),
1432 Color::Rgb(r, g, b) => write_custom!(r, g, b),
2c00a5a8
XL
1433 Color::__Nonexhaustive => unreachable!(),
1434 }
1435 } else {
1436 match *c {
1437 Color::Black => write_normal!("0"),
1438 Color::Blue => write_normal!("4"),
1439 Color::Green => write_normal!("2"),
1440 Color::Red => write_normal!("1"),
1441 Color::Cyan => write_normal!("6"),
1442 Color::Magenta => write_normal!("5"),
1443 Color::Yellow => write_normal!("3"),
1444 Color::White => write_normal!("7"),
0531ce1d
XL
1445 Color::Ansi256(c) => write_custom!(c),
1446 Color::Rgb(r, g, b) => write_custom!(r, g, b),
2c00a5a8
XL
1447 Color::__Nonexhaustive => unreachable!(),
1448 }
1449 }
1450 }
1451}
1452
6522a427
EL
1453impl WriteColor for io::Sink {
1454 fn supports_color(&self) -> bool {
1455 false
1456 }
1457
1458 fn set_color(&mut self, _: &ColorSpec) -> io::Result<()> {
1459 Ok(())
1460 }
1461
1462 fn reset(&mut self) -> io::Result<()> {
1463 Ok(())
1464 }
1465}
1466
2c00a5a8
XL
1467/// An in-memory buffer that provides Windows console coloring.
1468///
1469/// This doesn't actually communicate with the Windows console. Instead, it
1470/// acts like a normal buffer but also saves the color information associated
1471/// with positions in the buffer. It is only when the buffer is written to the
1472/// console that coloring is actually applied.
1473///
1474/// This is roughly isomorphic to the ANSI based approach (i.e.,
1475/// `Ansi<Vec<u8>>`), except with ANSI, the color information is embedded
1476/// directly into the buffer.
1477///
1478/// Note that there is no way to write something generic like
1479/// `WindowsConsole<W: io::Write>` since coloring on Windows is tied
1480/// specifically to the console APIs, and therefore can't work on arbitrary
1481/// writers.
1482#[cfg(windows)]
1483#[derive(Clone, Debug)]
1484struct WindowsBuffer {
1485 /// The actual content that should be printed.
1486 buf: Vec<u8>,
1487 /// A sequence of position oriented color specifications. Namely, each
1488 /// element is a position and a color spec, where the color spec should
1489 /// be applied at the position inside of `buf`.
1490 ///
1491 /// A missing color spec implies the underlying console should be reset.
1492 colors: Vec<(usize, Option<ColorSpec>)>,
1493}
1494
1495#[cfg(windows)]
1496impl WindowsBuffer {
1497 /// Create a new empty buffer for Windows console coloring.
1498 fn new() -> WindowsBuffer {
3dfed10e 1499 WindowsBuffer { buf: vec![], colors: vec![] }
2c00a5a8
XL
1500 }
1501
1502 /// Push the given color specification into this buffer.
1503 ///
1504 /// This has the effect of setting the given color information at the
1505 /// current position in the buffer.
1506 fn push(&mut self, spec: Option<ColorSpec>) {
1507 let pos = self.buf.len();
1508 self.colors.push((pos, spec));
1509 }
1510
1511 /// Print the contents to the given stream handle, and use the console
1512 /// for coloring.
1513 fn print(
1514 &self,
3dfed10e 1515 console: &mut wincon::Console,
2c00a5a8
XL
1516 stream: &mut LossyStandardStream<IoStandardStreamLock>,
1517 ) -> io::Result<()> {
1518 let mut last = 0;
1519 for &(pos, ref spec) in &self.colors {
0531ce1d
XL
1520 stream.write_all(&self.buf[last..pos])?;
1521 stream.flush()?;
2c00a5a8
XL
1522 last = pos;
1523 match *spec {
0531ce1d
XL
1524 None => console.reset()?,
1525 Some(ref spec) => spec.write_console(console)?,
2c00a5a8
XL
1526 }
1527 }
0531ce1d 1528 stream.write_all(&self.buf[last..])?;
2c00a5a8
XL
1529 stream.flush()
1530 }
1531
1532 /// Clear the buffer.
1533 fn clear(&mut self) {
1534 self.buf.clear();
1535 self.colors.clear();
1536 }
1537}
1538
1539#[cfg(windows)]
1540impl io::Write for WindowsBuffer {
0731742a 1541 #[inline]
2c00a5a8
XL
1542 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
1543 self.buf.extend_from_slice(buf);
1544 Ok(buf.len())
1545 }
1546
0731742a 1547 #[inline]
2c00a5a8
XL
1548 fn flush(&mut self) -> io::Result<()> {
1549 Ok(())
1550 }
1551}
1552
1553#[cfg(windows)]
1554impl WriteColor for WindowsBuffer {
0731742a 1555 #[inline]
3dfed10e
XL
1556 fn supports_color(&self) -> bool {
1557 true
1558 }
2c00a5a8 1559
0731742a 1560 #[inline]
2c00a5a8
XL
1561 fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
1562 self.push(Some(spec.clone()));
1563 Ok(())
1564 }
1565
0731742a 1566 #[inline]
2c00a5a8
XL
1567 fn reset(&mut self) -> io::Result<()> {
1568 self.push(None);
1569 Ok(())
1570 }
b7449926 1571
0731742a 1572 #[inline]
b7449926
XL
1573 fn is_synchronous(&self) -> bool {
1574 false
1575 }
2c00a5a8
XL
1576}
1577
1578/// A color specification.
3dfed10e 1579#[derive(Clone, Debug, Eq, PartialEq)]
2c00a5a8
XL
1580pub struct ColorSpec {
1581 fg_color: Option<Color>,
1582 bg_color: Option<Color>,
1583 bold: bool,
1584 intense: bool,
0531ce1d 1585 underline: bool,
5869c6ff 1586 dimmed: bool,
3dfed10e
XL
1587 italic: bool,
1588 reset: bool,
1589}
1590
1591impl Default for ColorSpec {
1592 fn default() -> ColorSpec {
1593 ColorSpec {
1594 fg_color: None,
1595 bg_color: None,
1596 bold: false,
1597 intense: false,
1598 underline: false,
5869c6ff 1599 dimmed: false,
3dfed10e
XL
1600 italic: false,
1601 reset: true,
1602 }
1603 }
2c00a5a8
XL
1604}
1605
1606impl ColorSpec {
1607 /// Create a new color specification that has no colors or styles.
1608 pub fn new() -> ColorSpec {
1609 ColorSpec::default()
1610 }
1611
1612 /// Get the foreground color.
3dfed10e
XL
1613 pub fn fg(&self) -> Option<&Color> {
1614 self.fg_color.as_ref()
1615 }
2c00a5a8
XL
1616
1617 /// Set the foreground color.
1618 pub fn set_fg(&mut self, color: Option<Color>) -> &mut ColorSpec {
1619 self.fg_color = color;
1620 self
1621 }
1622
1623 /// Get the background color.
3dfed10e
XL
1624 pub fn bg(&self) -> Option<&Color> {
1625 self.bg_color.as_ref()
1626 }
2c00a5a8
XL
1627
1628 /// Set the background color.
1629 pub fn set_bg(&mut self, color: Option<Color>) -> &mut ColorSpec {
1630 self.bg_color = color;
1631 self
1632 }
1633
1634 /// Get whether this is bold or not.
1635 ///
1636 /// Note that the bold setting has no effect in a Windows console.
3dfed10e
XL
1637 pub fn bold(&self) -> bool {
1638 self.bold
1639 }
2c00a5a8
XL
1640
1641 /// Set whether the text is bolded or not.
1642 ///
1643 /// Note that the bold setting has no effect in a Windows console.
1644 pub fn set_bold(&mut self, yes: bool) -> &mut ColorSpec {
1645 self.bold = yes;
1646 self
1647 }
1648
5869c6ff
XL
1649 /// Get whether this is dimmed or not.
1650 ///
1651 /// Note that the dimmed setting has no effect in a Windows console.
1652 pub fn dimmed(&self) -> bool {
1653 self.dimmed
1654 }
1655
1656 /// Set whether the text is dimmed or not.
1657 ///
1658 /// Note that the dimmed setting has no effect in a Windows console.
1659 pub fn set_dimmed(&mut self, yes: bool) -> &mut ColorSpec {
1660 self.dimmed = yes;
1661 self
1662 }
1663
3dfed10e
XL
1664 /// Get whether this is italic or not.
1665 ///
1666 /// Note that the italic setting has no effect in a Windows console.
1667 pub fn italic(&self) -> bool {
1668 self.italic
1669 }
1670
1671 /// Set whether the text is italicized or not.
1672 ///
1673 /// Note that the italic setting has no effect in a Windows console.
1674 pub fn set_italic(&mut self, yes: bool) -> &mut ColorSpec {
1675 self.italic = yes;
1676 self
1677 }
1678
0531ce1d
XL
1679 /// Get whether this is underline or not.
1680 ///
1681 /// Note that the underline setting has no effect in a Windows console.
3dfed10e
XL
1682 pub fn underline(&self) -> bool {
1683 self.underline
1684 }
0531ce1d
XL
1685
1686 /// Set whether the text is underlined or not.
1687 ///
1688 /// Note that the underline setting has no effect in a Windows console.
1689 pub fn set_underline(&mut self, yes: bool) -> &mut ColorSpec {
1690 self.underline = yes;
1691 self
1692 }
1693
3dfed10e
XL
1694 /// Get whether reset is enabled or not.
1695 ///
1696 /// reset is enabled by default. When disabled and using ANSI escape
1697 /// sequences, a "reset" code will be emitted every time a `ColorSpec`'s
1698 /// settings are applied.
1699 ///
1700 /// Note that the reset setting has no effect in a Windows console.
1701 pub fn reset(&self) -> bool {
1702 self.reset
1703 }
1704
1705 /// Set whether to reset the terminal whenever color settings are applied.
1706 ///
1707 /// reset is enabled by default. When disabled and using ANSI escape
1708 /// sequences, a "reset" code will be emitted every time a `ColorSpec`'s
1709 /// settings are applied.
1710 ///
1711 /// Typically this is useful if callers have a requirement to more
1712 /// scrupulously manage the exact sequence of escape codes that are emitted
1713 /// when using ANSI for colors.
1714 ///
1715 /// Note that the reset setting has no effect in a Windows console.
1716 pub fn set_reset(&mut self, yes: bool) -> &mut ColorSpec {
1717 self.reset = yes;
1718 self
1719 }
1720
2c00a5a8 1721 /// Get whether this is intense or not.
0531ce1d
XL
1722 ///
1723 /// On Unix-like systems, this will output the ANSI escape sequence
1724 /// that will print a high-intensity version of the color
1725 /// specified.
1726 ///
1727 /// On Windows systems, this will output the ANSI escape sequence
1728 /// that will print a brighter version of the color specified.
3dfed10e
XL
1729 pub fn intense(&self) -> bool {
1730 self.intense
1731 }
2c00a5a8
XL
1732
1733 /// Set whether the text is intense or not.
0531ce1d
XL
1734 ///
1735 /// On Unix-like systems, this will output the ANSI escape sequence
1736 /// that will print a high-intensity version of the color
1737 /// specified.
1738 ///
1739 /// On Windows systems, this will output the ANSI escape sequence
1740 /// that will print a brighter version of the color specified.
2c00a5a8
XL
1741 pub fn set_intense(&mut self, yes: bool) -> &mut ColorSpec {
1742 self.intense = yes;
1743 self
1744 }
1745
1746 /// Returns true if this color specification has no colors or styles.
1747 pub fn is_none(&self) -> bool {
3dfed10e
XL
1748 self.fg_color.is_none()
1749 && self.bg_color.is_none()
1750 && !self.bold
1751 && !self.underline
5869c6ff 1752 && !self.dimmed
3dfed10e
XL
1753 && !self.italic
1754 && !self.intense
2c00a5a8
XL
1755 }
1756
1757 /// Clears this color specification so that it has no color/style settings.
1758 pub fn clear(&mut self) {
1759 self.fg_color = None;
1760 self.bg_color = None;
1761 self.bold = false;
0531ce1d 1762 self.underline = false;
3dfed10e 1763 self.intense = false;
5869c6ff 1764 self.dimmed = false;
3dfed10e 1765 self.italic = false;
2c00a5a8
XL
1766 }
1767
1768 /// Writes this color spec to the given Windows console.
1769 #[cfg(windows)]
3dfed10e
XL
1770 fn write_console(&self, console: &mut wincon::Console) -> io::Result<()> {
1771 let fg_color = self.fg_color.and_then(|c| c.to_windows(self.intense));
1772 if let Some((intense, color)) = fg_color {
0531ce1d 1773 console.fg(intense, color)?;
2c00a5a8 1774 }
3dfed10e
XL
1775 let bg_color = self.bg_color.and_then(|c| c.to_windows(self.intense));
1776 if let Some((intense, color)) = bg_color {
0531ce1d 1777 console.bg(intense, color)?;
2c00a5a8
XL
1778 }
1779 Ok(())
1780 }
1781}
1782
0531ce1d 1783/// The set of available colors for the terminal foreground/background.
2c00a5a8 1784///
0531ce1d
XL
1785/// The `Ansi256` and `Rgb` colors will only output the correct codes when
1786/// paired with the `Ansi` `WriteColor` implementation.
1787///
1788/// The `Ansi256` and `Rgb` color types are not supported when writing colors
1789/// on Windows using the console. If they are used on Windows, then they are
1790/// silently ignored and no colors will be emitted.
1791///
1792/// This set may expand over time.
1793///
1794/// This type has a `FromStr` impl that can parse colors from their human
1795/// readable form. The format is as follows:
1796///
1797/// 1. Any of the explicitly listed colors in English. They are matched
1798/// case insensitively.
1799/// 2. A single 8-bit integer, in either decimal or hexadecimal format.
1800/// 3. A triple of 8-bit integers separated by a comma, where each integer is
1801/// in decimal or hexadecimal format.
1802///
1803/// Hexadecimal numbers are written with a `0x` prefix.
2c00a5a8 1804#[allow(missing_docs)]
0731742a 1805#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2c00a5a8
XL
1806pub enum Color {
1807 Black,
1808 Blue,
1809 Green,
1810 Red,
1811 Cyan,
1812 Magenta,
1813 Yellow,
1814 White,
0531ce1d
XL
1815 Ansi256(u8),
1816 Rgb(u8, u8, u8),
2c00a5a8
XL
1817 #[doc(hidden)]
1818 __Nonexhaustive,
1819}
1820
2c00a5a8 1821impl Color {
3dfed10e 1822 /// Translate this color to a wincon::Color.
0531ce1d 1823 #[cfg(windows)]
3dfed10e
XL
1824 fn to_windows(
1825 self,
1826 intense: bool,
1827 ) -> Option<(wincon::Intense, wincon::Color)> {
1828 use wincon::Intense::{No, Yes};
1829
1830 let color = match self {
1831 Color::Black => wincon::Color::Black,
1832 Color::Blue => wincon::Color::Blue,
1833 Color::Green => wincon::Color::Green,
1834 Color::Red => wincon::Color::Red,
1835 Color::Cyan => wincon::Color::Cyan,
1836 Color::Magenta => wincon::Color::Magenta,
1837 Color::Yellow => wincon::Color::Yellow,
1838 Color::White => wincon::Color::White,
1839 Color::Ansi256(0) => return Some((No, wincon::Color::Black)),
1840 Color::Ansi256(1) => return Some((No, wincon::Color::Red)),
1841 Color::Ansi256(2) => return Some((No, wincon::Color::Green)),
1842 Color::Ansi256(3) => return Some((No, wincon::Color::Yellow)),
1843 Color::Ansi256(4) => return Some((No, wincon::Color::Blue)),
1844 Color::Ansi256(5) => return Some((No, wincon::Color::Magenta)),
1845 Color::Ansi256(6) => return Some((No, wincon::Color::Cyan)),
1846 Color::Ansi256(7) => return Some((No, wincon::Color::White)),
1847 Color::Ansi256(8) => return Some((Yes, wincon::Color::Black)),
1848 Color::Ansi256(9) => return Some((Yes, wincon::Color::Red)),
1849 Color::Ansi256(10) => return Some((Yes, wincon::Color::Green)),
1850 Color::Ansi256(11) => return Some((Yes, wincon::Color::Yellow)),
1851 Color::Ansi256(12) => return Some((Yes, wincon::Color::Blue)),
1852 Color::Ansi256(13) => return Some((Yes, wincon::Color::Magenta)),
1853 Color::Ansi256(14) => return Some((Yes, wincon::Color::Cyan)),
1854 Color::Ansi256(15) => return Some((Yes, wincon::Color::White)),
1855 Color::Ansi256(_) => return None,
1856 Color::Rgb(_, _, _) => return None,
2c00a5a8 1857 Color::__Nonexhaustive => unreachable!(),
3dfed10e
XL
1858 };
1859 let intense = if intense { Yes } else { No };
1860 Some((intense, color))
2c00a5a8 1861 }
0531ce1d
XL
1862
1863 /// Parses a numeric color string, either ANSI or RGB.
1864 fn from_str_numeric(s: &str) -> Result<Color, ParseColorError> {
1865 // The "ansi256" format is a single number (decimal or hex)
1866 // corresponding to one of 256 colors.
1867 //
1868 // The "rgb" format is a triple of numbers (decimal or hex) delimited
1869 // by a comma corresponding to one of 256^3 colors.
1870
1871 fn parse_number(s: &str) -> Option<u8> {
1872 use std::u8;
1873
1874 if s.starts_with("0x") {
1875 u8::from_str_radix(&s[2..], 16).ok()
1876 } else {
1877 u8::from_str_radix(s, 10).ok()
1878 }
1879 }
1880
1881 let codes: Vec<&str> = s.split(',').collect();
1882 if codes.len() == 1 {
1883 if let Some(n) = parse_number(&codes[0]) {
1884 Ok(Color::Ansi256(n))
1885 } else {
1886 if s.chars().all(|c| c.is_digit(16)) {
1887 Err(ParseColorError {
1888 kind: ParseColorErrorKind::InvalidAnsi256,
1889 given: s.to_string(),
1890 })
1891 } else {
1892 Err(ParseColorError {
1893 kind: ParseColorErrorKind::InvalidName,
1894 given: s.to_string(),
1895 })
1896 }
1897 }
1898 } else if codes.len() == 3 {
1899 let mut v = vec![];
1900 for code in codes {
3dfed10e
XL
1901 let n = parse_number(code).ok_or_else(|| ParseColorError {
1902 kind: ParseColorErrorKind::InvalidRgb,
1903 given: s.to_string(),
0531ce1d
XL
1904 })?;
1905 v.push(n);
1906 }
1907 Ok(Color::Rgb(v[0], v[1], v[2]))
1908 } else {
1909 Err(if s.contains(",") {
1910 ParseColorError {
1911 kind: ParseColorErrorKind::InvalidRgb,
1912 given: s.to_string(),
1913 }
1914 } else {
1915 ParseColorError {
1916 kind: ParseColorErrorKind::InvalidName,
1917 given: s.to_string(),
1918 }
1919 })
1920 }
1921 }
1922}
1923
1924/// An error from parsing an invalid color specification.
1925#[derive(Clone, Debug, Eq, PartialEq)]
1926pub struct ParseColorError {
1927 kind: ParseColorErrorKind,
1928 given: String,
2c00a5a8
XL
1929}
1930
2c00a5a8 1931#[derive(Clone, Debug, Eq, PartialEq)]
0531ce1d
XL
1932enum ParseColorErrorKind {
1933 InvalidName,
1934 InvalidAnsi256,
1935 InvalidRgb,
1936}
2c00a5a8
XL
1937
1938impl ParseColorError {
1939 /// Return the string that couldn't be parsed as a valid color.
3dfed10e
XL
1940 pub fn invalid(&self) -> &str {
1941 &self.given
1942 }
2c00a5a8
XL
1943}
1944
1945impl error::Error for ParseColorError {
0531ce1d
XL
1946 fn description(&self) -> &str {
1947 use self::ParseColorErrorKind::*;
1948 match self.kind {
1949 InvalidName => "unrecognized color name",
1950 InvalidAnsi256 => "invalid ansi256 color number",
1951 InvalidRgb => "invalid RGB color triple",
1952 }
1953 }
2c00a5a8
XL
1954}
1955
1956impl fmt::Display for ParseColorError {
3dfed10e 1957 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
0531ce1d
XL
1958 use self::ParseColorErrorKind::*;
1959 match self.kind {
3dfed10e
XL
1960 InvalidName => write!(
1961 f,
1962 "unrecognized color name '{}'. Choose from: \
1963 black, blue, green, red, cyan, magenta, yellow, \
1964 white",
1965 self.given
1966 ),
1967 InvalidAnsi256 => write!(
1968 f,
1969 "unrecognized ansi256 color number, \
1970 should be '[0-255]' (or a hex number), but is '{}'",
1971 self.given
1972 ),
1973 InvalidRgb => write!(
1974 f,
1975 "unrecognized RGB color triple, \
1976 should be '[0-255],[0-255],[0-255]' (or a hex \
1977 triple), but is '{}'",
1978 self.given
1979 ),
0531ce1d 1980 }
2c00a5a8
XL
1981 }
1982}
1983
1984impl FromStr for Color {
1985 type Err = ParseColorError;
1986
1987 fn from_str(s: &str) -> Result<Color, ParseColorError> {
1988 match &*s.to_lowercase() {
1989 "black" => Ok(Color::Black),
1990 "blue" => Ok(Color::Blue),
1991 "green" => Ok(Color::Green),
1992 "red" => Ok(Color::Red),
1993 "cyan" => Ok(Color::Cyan),
1994 "magenta" => Ok(Color::Magenta),
1995 "yellow" => Ok(Color::Yellow),
1996 "white" => Ok(Color::White),
0531ce1d 1997 _ => Color::from_str_numeric(s),
2c00a5a8
XL
1998 }
1999 }
2000}
2001
2002struct LossyStandardStream<W> {
2003 wtr: W,
2004 #[cfg(windows)]
2005 is_console: bool,
2006}
2007
2008impl<W: io::Write> LossyStandardStream<W> {
2009 #[cfg(not(windows))]
2010 fn new(wtr: W) -> LossyStandardStream<W> {
2011 LossyStandardStream { wtr: wtr }
2012 }
2013
2014 #[cfg(windows)]
2015 fn new(wtr: W) -> LossyStandardStream<W> {
3dfed10e
XL
2016 let is_console = wincon::Console::stdout().is_ok()
2017 || wincon::Console::stderr().is_ok();
b7449926 2018 LossyStandardStream { wtr: wtr, is_console: is_console }
2c00a5a8
XL
2019 }
2020
2021 #[cfg(not(windows))]
2022 fn wrap<Q: io::Write>(&self, wtr: Q) -> LossyStandardStream<Q> {
2023 LossyStandardStream::new(wtr)
2024 }
2025
2026 #[cfg(windows)]
2027 fn wrap<Q: io::Write>(&self, wtr: Q) -> LossyStandardStream<Q> {
b7449926 2028 LossyStandardStream { wtr: wtr, is_console: self.is_console }
2c00a5a8
XL
2029 }
2030
2031 fn get_ref(&self) -> &W {
2032 &self.wtr
2033 }
2034}
2035
2036impl<W: WriteColor> WriteColor for LossyStandardStream<W> {
3dfed10e
XL
2037 fn supports_color(&self) -> bool {
2038 self.wtr.supports_color()
2039 }
2c00a5a8
XL
2040 fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
2041 self.wtr.set_color(spec)
2042 }
3dfed10e
XL
2043 fn reset(&mut self) -> io::Result<()> {
2044 self.wtr.reset()
2045 }
2046 fn is_synchronous(&self) -> bool {
2047 self.wtr.is_synchronous()
2048 }
2c00a5a8
XL
2049}
2050
2051impl<W: io::Write> io::Write for LossyStandardStream<W> {
2052 #[cfg(not(windows))]
2053 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
2054 self.wtr.write(buf)
2055 }
2056
2057 #[cfg(windows)]
2058 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
2059 if self.is_console {
2060 write_lossy_utf8(&mut self.wtr, buf)
2061 } else {
2062 self.wtr.write(buf)
2063 }
2064 }
2065
2066 fn flush(&mut self) -> io::Result<()> {
2067 self.wtr.flush()
2068 }
2069}
2070
2071#[cfg(windows)]
2072fn write_lossy_utf8<W: io::Write>(mut w: W, buf: &[u8]) -> io::Result<usize> {
2073 match ::std::str::from_utf8(buf) {
2074 Ok(s) => w.write(s.as_bytes()),
2075 Err(ref e) if e.valid_up_to() == 0 => {
0531ce1d 2076 w.write(b"\xEF\xBF\xBD")?;
2c00a5a8
XL
2077 Ok(1)
2078 }
2079 Err(e) => w.write(&buf[..e.valid_up_to()]),
2080 }
2081}
2082
2083#[cfg(test)]
2084mod tests {
0531ce1d 2085 use super::{
3dfed10e
XL
2086 Ansi, Color, ColorSpec, ParseColorError, ParseColorErrorKind,
2087 StandardStream, WriteColor,
0531ce1d 2088 };
2c00a5a8
XL
2089
2090 fn assert_is_send<T: Send>() {}
2091
2092 #[test]
2093 fn standard_stream_is_send() {
2094 assert_is_send::<StandardStream>();
2095 }
0531ce1d
XL
2096
2097 #[test]
2098 fn test_simple_parse_ok() {
2099 let color = "green".parse::<Color>();
2100 assert_eq!(color, Ok(Color::Green));
2101 }
2102
2103 #[test]
2104 fn test_256_parse_ok() {
2105 let color = "7".parse::<Color>();
2106 assert_eq!(color, Ok(Color::Ansi256(7)));
2107
2108 let color = "32".parse::<Color>();
2109 assert_eq!(color, Ok(Color::Ansi256(32)));
2110
2111 let color = "0xFF".parse::<Color>();
2112 assert_eq!(color, Ok(Color::Ansi256(0xFF)));
2113 }
2114
2115 #[test]
2116 fn test_256_parse_err_out_of_range() {
2117 let color = "256".parse::<Color>();
3dfed10e
XL
2118 assert_eq!(
2119 color,
2120 Err(ParseColorError {
2121 kind: ParseColorErrorKind::InvalidAnsi256,
2122 given: "256".to_string(),
2123 })
2124 );
0531ce1d
XL
2125 }
2126
2127 #[test]
2128 fn test_rgb_parse_ok() {
2129 let color = "0,0,0".parse::<Color>();
2130 assert_eq!(color, Ok(Color::Rgb(0, 0, 0)));
2131
2132 let color = "0,128,255".parse::<Color>();
2133 assert_eq!(color, Ok(Color::Rgb(0, 128, 255)));
2134
2135 let color = "0x0,0x0,0x0".parse::<Color>();
2136 assert_eq!(color, Ok(Color::Rgb(0, 0, 0)));
2137
2138 let color = "0x33,0x66,0xFF".parse::<Color>();
2139 assert_eq!(color, Ok(Color::Rgb(0x33, 0x66, 0xFF)));
2140 }
2141
2142 #[test]
2143 fn test_rgb_parse_err_out_of_range() {
2144 let color = "0,0,256".parse::<Color>();
3dfed10e
XL
2145 assert_eq!(
2146 color,
2147 Err(ParseColorError {
2148 kind: ParseColorErrorKind::InvalidRgb,
2149 given: "0,0,256".to_string(),
2150 })
2151 );
0531ce1d
XL
2152 }
2153
2154 #[test]
2155 fn test_rgb_parse_err_bad_format() {
2156 let color = "0,0".parse::<Color>();
3dfed10e
XL
2157 assert_eq!(
2158 color,
2159 Err(ParseColorError {
2160 kind: ParseColorErrorKind::InvalidRgb,
2161 given: "0,0".to_string(),
2162 })
2163 );
0531ce1d
XL
2164
2165 let color = "not_a_color".parse::<Color>();
3dfed10e
XL
2166 assert_eq!(
2167 color,
2168 Err(ParseColorError {
2169 kind: ParseColorErrorKind::InvalidName,
2170 given: "not_a_color".to_string(),
2171 })
2172 );
0531ce1d
XL
2173 }
2174
2175 #[test]
2176 fn test_var_ansi_write_rgb() {
2177 let mut buf = Ansi::new(vec![]);
2178 let _ = buf.write_color(true, &Color::Rgb(254, 253, 255), false);
2179 assert_eq!(buf.0, b"\x1B[38;2;254;253;255m");
2180 }
2181
3dfed10e
XL
2182 #[test]
2183 fn test_reset() {
2184 let spec = ColorSpec::new();
2185 let mut buf = Ansi::new(vec![]);
2186 buf.set_color(&spec).unwrap();
2187 assert_eq!(buf.0, b"\x1B[0m");
2188 }
2189
2190 #[test]
2191 fn test_no_reset() {
2192 let mut spec = ColorSpec::new();
2193 spec.set_reset(false);
2194
2195 let mut buf = Ansi::new(vec![]);
2196 buf.set_color(&spec).unwrap();
2197 assert_eq!(buf.0, b"");
2198 }
2199
0531ce1d
XL
2200 #[test]
2201 fn test_var_ansi_write_256() {
2202 let mut buf = Ansi::new(vec![]);
2203 let _ = buf.write_color(false, &Color::Ansi256(7), false);
2204 assert_eq!(buf.0, b"\x1B[48;5;7m");
2205
2206 let mut buf = Ansi::new(vec![]);
2207 let _ = buf.write_color(false, &Color::Ansi256(208), false);
2208 assert_eq!(buf.0, b"\x1B[48;5;208m");
2209 }
3dfed10e
XL
2210
2211 fn all_attributes() -> Vec<ColorSpec> {
2212 let mut result = vec![];
2213 for fg in vec![None, Some(Color::Red)] {
2214 for bg in vec![None, Some(Color::Red)] {
2215 for bold in vec![false, true] {
2216 for underline in vec![false, true] {
2217 for intense in vec![false, true] {
2218 for italic in vec![false, true] {
5869c6ff
XL
2219 for dimmed in vec![false, true] {
2220 let mut color = ColorSpec::new();
2221 color.set_fg(fg);
2222 color.set_bg(bg);
2223 color.set_bold(bold);
2224 color.set_underline(underline);
2225 color.set_intense(intense);
2226 color.set_dimmed(dimmed);
2227 color.set_italic(italic);
2228 result.push(color);
2229 }
3dfed10e
XL
2230 }
2231 }
2232 }
2233 }
2234 }
2235 }
2236 result
2237 }
2238
2239 #[test]
2240 fn test_is_none() {
2241 for (i, color) in all_attributes().iter().enumerate() {
2242 assert_eq!(
2243 i == 0,
2244 color.is_none(),
2245 "{:?} => {}",
2246 color,
2247 color.is_none()
2248 )
2249 }
2250 }
2251
2252 #[test]
2253 fn test_clear() {
2254 for color in all_attributes() {
2255 let mut color1 = color.clone();
2256 color1.clear();
2257 assert!(color1.is_none(), "{:?} => {:?}", color, color1);
2258 }
2259 }
2c00a5a8 2260}