1
0
mirror of https://github.com/BurntSushi/ripgrep.git synced 2025-01-19 05:49:14 +02:00

termcolor: add support for output to standard error

This is essentially a rename of the existing `Stdout` type to `StandardStream`
and a change of its constructor from a single `new()` function to have two
`stdout()` and `stderr()` functions.

Under the hood, we add add internal IoStandardStream{,Lock} enums that allow
us to abstract between Stdout and Stderr conveniently. The rest of the needed
changes then fall out fairly naturally.

Fixes #324.

[breaking-change]
This commit is contained in:
Peter Williams 2017-02-05 11:02:54 -05:00 committed by Andrew Gallant
parent e424f87487
commit 22cb644eb6
3 changed files with 233 additions and 108 deletions

View File

@ -182,8 +182,8 @@ impl Args {
} }
/// Create a new writer for single-threaded searching with color support. /// Create a new writer for single-threaded searching with color support.
pub fn stdout(&self) -> termcolor::Stdout { pub fn stdout(&self) -> termcolor::StandardStream {
termcolor::Stdout::new(self.color_choice) termcolor::StandardStream::stdout(self.color_choice)
} }
/// Returns a handle to stdout for filtering search. /// Returns a handle to stdout for filtering search.

View File

@ -39,12 +39,13 @@ extern crate termcolor;
The `WriteColor` trait extends the `io::Write` trait with methods for setting The `WriteColor` trait extends the `io::Write` trait with methods for setting
colors or resetting them. colors or resetting them.
`Stdout` and `StdoutLock` both satisfy `WriteColor` and are analogous to `StandardStream` and `StandardStreamLock` both satisfy `WriteColor` and are
`std::io::Stdout` and `std::io::StdoutLock`. analogous to `std::io::Stdout` and `std::io::StdoutLock`, or `std::io::Stderr`
and `std::io::StderrLock`.
`Buffer` is an in memory buffer that supports colored text. In a parallel `Buffer` is an in memory buffer that supports colored text. In a parallel
program, each thread might write to its own buffer. A buffer can be printed program, each thread might write to its own buffer. A buffer can be printed to
to stdout using a `BufferWriter`. The advantage of this design is that stdout or stderr using a `BufferWriter`. The advantage of this design is that
each thread can work in parallel on a buffer without having to synchronize each thread can work in parallel on a buffer without having to synchronize
access to global resources such as the Windows console. Moreover, this design access to global resources such as the Windows console. Moreover, this design
also prevents interleaving of buffer output. also prevents interleaving of buffer output.
@ -53,34 +54,34 @@ also prevents interleaving of buffer output.
`io::Write`. These types are useful when you know exactly what you need. An `io::Write`. These types are useful when you know exactly what you need. An
analogous type for the Windows console is not provided since it cannot exist. analogous type for the Windows console is not provided since it cannot exist.
### Example: using `Stdout` ### Example: using `StandardStream`
The `Stdout` type in this crate works similarly to `std::io::Stdout`, except The `StandardStream` type in this crate works similarly to `std::io::Stdout`,
it is augmented with methods for coloring by the `WriteColor` trait. For except it is augmented with methods for coloring by the `WriteColor` trait.
example, to write some green text: For example, to write some green text:
```rust ```rust
use std::io::Write; use std::io::Write;
use termcolor::{Color, ColorChoice, ColorSpec, Stdout, WriteColor}; use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
let mut stdout = Stdout::new(ColorChoice::Always); let mut stdout = StandardStream::stdout(ColorChoice::Always);
try!(stdout.set_color(ColorSpec::new().set_fg(Some(Color::Green)))); try!(stdout.set_color(ColorSpec::new().set_fg(Some(Color::Green))));
try!(writeln!(&mut stdout, "green text!")); try!(writeln!(&mut stdout, "green text!"));
``` ```
### Example: using `BufferWriter` ### Example: using `BufferWriter`
A `BufferWriter` can create buffers and write buffers to stdout. It does *not* A `BufferWriter` can create buffers and write buffers to stdout or stderr. It
implement `io::Write` or `WriteColor` itself. Instead, `Buffer` implements does *not* implement `io::Write` or `WriteColor` itself. Instead, `Buffer`
`io::Write` and `io::WriteColor`. implements `io::Write` and `io::WriteColor`.
This example shows how to print some green text to stdout. This example shows how to print some green text to stderr.
```rust ```rust
use std::io::Write; use std::io::Write;
use termcolor::{BufferWriter, Color, ColorChoice, ColorSpec, WriteColor}; use termcolor::{BufferWriter, Color, ColorChoice, ColorSpec, WriteColor};
let mut bufwtr = BufferWriter::stdout(ColorChoice::Always); let mut bufwtr = BufferWriter::stderr(ColorChoice::Always);
let mut buffer = bufwtr.buffer(); let mut buffer = bufwtr.buffer();
try!(buffer.set_color(ColorSpec::new().set_fg(Some(Color::Green)))); try!(buffer.set_color(ColorSpec::new().set_fg(Some(Color::Green))));
try!(writeln!(&mut buffer, "green text!")); try!(writeln!(&mut buffer, "green text!"));

View File

@ -15,32 +15,33 @@ Windows console API, which requires synchronous communication.
The `WriteColor` trait extends the `io::Write` trait with methods for setting The `WriteColor` trait extends the `io::Write` trait with methods for setting
colors or resetting them. colors or resetting them.
`Stdout` and `StdoutLock` both satisfy `WriteColor` and are analogous to `StandardStream` and `StandardStreamLock` both satisfy `WriteColor` and are
`std::io::Stdout` and `std::io::StdoutLock`. analogous to `std::io::Stdout` and `std::io::StdoutLock`, or `std::io::Stderr`
and `std::io::StderrLock`.
`Buffer` is an in memory buffer that supports colored text. In a parallel `Buffer` is an in memory buffer that supports colored text. In a parallel
program, each thread might write to its own buffer. A buffer can be printed program, each thread might write to its own buffer. A buffer can be printed to
to stdout using a `BufferWriter`. The advantage of this design is that using a `BufferWriter`. The advantage of this design is that each thread can
each thread can work in parallel on a buffer without having to synchronize work in parallel on a buffer without having to synchronize access to global
access to global resources such as the Windows console. Moreover, this design resources such as the Windows console. Moreover, this design also prevents
also prevents interleaving of buffer output. interleaving of buffer output.
`Ansi` and `NoColor` both satisfy `WriteColor` for arbitrary implementors of `Ansi` and `NoColor` both satisfy `WriteColor` for arbitrary implementors of
`io::Write`. These types are useful when you know exactly what you need. An `io::Write`. These types are useful when you know exactly what you need. An
analogous type for the Windows console is not provided since it cannot exist. analogous type for the Windows console is not provided since it cannot exist.
# Example: using `Stdout` # Example: using `StandardStream`
The `Stdout` type in this crate works similarly to `std::io::Stdout`, except The `StandardStream` type in this crate works similarly to `std::io::Stdout`,
it is augmented with methods for coloring by the `WriteColor` trait. For except it is augmented with methods for coloring by the `WriteColor` trait.
example, to write some green text: For example, to write some green text:
```rust,no_run ```rust,no_run
# fn test() -> Result<(), Box<::std::error::Error>> { # fn test() -> Result<(), Box<::std::error::Error>> {
use std::io::Write; use std::io::Write;
use termcolor::{Color, ColorChoice, ColorSpec, Stdout, WriteColor}; use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
let mut stdout = Stdout::new(ColorChoice::Always); let mut stdout = StandardStream::stdout(ColorChoice::Always);
try!(stdout.set_color(ColorSpec::new().set_fg(Some(Color::Green)))); try!(stdout.set_color(ColorSpec::new().set_fg(Some(Color::Green))));
try!(writeln!(&mut stdout, "green text!")); try!(writeln!(&mut stdout, "green text!"));
# Ok(()) } # Ok(()) }
@ -48,18 +49,18 @@ try!(writeln!(&mut stdout, "green text!"));
# Example: using `BufferWriter` # Example: using `BufferWriter`
A `BufferWriter` can create buffers and write buffers to stdout. It does *not* A `BufferWriter` can create buffers and write buffers to stdout or stderr. It
implement `io::Write` or `WriteColor` itself. Instead, `Buffer` implements does *not* implement `io::Write` or `WriteColor` itself. Instead, `Buffer`
`io::Write` and `io::WriteColor`. implements `io::Write` and `io::WriteColor`.
This example shows how to print some green text to stdout. This example shows how to print some green text to stderr.
```rust,no_run ```rust,no_run
# fn test() -> Result<(), Box<::std::error::Error>> { # fn test() -> Result<(), Box<::std::error::Error>> {
use std::io::Write; use std::io::Write;
use termcolor::{BufferWriter, Color, ColorChoice, ColorSpec, WriteColor}; use termcolor::{BufferWriter, Color, ColorChoice, ColorSpec, WriteColor};
let mut bufwtr = BufferWriter::stdout(ColorChoice::Always); let mut bufwtr = BufferWriter::stderr(ColorChoice::Always);
let mut buffer = bufwtr.buffer(); let mut buffer = bufwtr.buffer();
try!(buffer.set_color(ColorSpec::new().set_fg(Some(Color::Green)))); try!(buffer.set_color(ColorSpec::new().set_fg(Some(Color::Green))));
try!(writeln!(&mut buffer, "green text!")); try!(writeln!(&mut buffer, "green text!"));
@ -184,20 +185,89 @@ impl ColorChoice {
} }
} }
/// Satisfies `io::Write` and `WriteColor`, and supports optional coloring /// `std::io` implements `Stdout` and `Stderr` (and their `Lock` variants) as
/// to stdout. /// separate types, which makes it difficult to abstract over them. We use
pub struct Stdout { /// some simple internal enum types to work around this.
wtr: LossyStdout<WriterInner<'static, io::Stdout>>,
enum StandardStreamType {
Stdout,
Stderr,
} }
/// `StdoutLock` is a locked reference to a `Stdout`. enum IoStandardStream {
Stdout(io::Stdout),
Stderr(io::Stderr),
}
impl IoStandardStream {
fn new(sty: StandardStreamType) -> IoStandardStream {
match sty {
StandardStreamType::Stdout => IoStandardStream::Stdout(io::stdout()),
StandardStreamType::Stderr => IoStandardStream::Stderr(io::stderr()),
}
}
fn lock(&self) -> IoStandardStreamLock {
match *self {
IoStandardStream::Stdout(ref s) => IoStandardStreamLock::StdoutLock(s.lock()),
IoStandardStream::Stderr(ref s) => IoStandardStreamLock::StderrLock(s.lock()),
}
}
}
impl io::Write for IoStandardStream {
fn write(&mut self, b: &[u8]) -> io::Result<usize> {
match *self {
IoStandardStream::Stdout(ref mut s) => s.write(b),
IoStandardStream::Stderr(ref mut s) => s.write(b),
}
}
fn flush(&mut self) -> io::Result<()> {
match *self {
IoStandardStream::Stdout(ref mut s) => s.flush(),
IoStandardStream::Stderr(ref mut s) => s.flush(),
}
}
}
/// Same rigamorale for the locked variants of the standard streams.
enum IoStandardStreamLock<'a> {
StdoutLock(io::StdoutLock<'a>),
StderrLock(io::StderrLock<'a>),
}
impl<'a> io::Write for IoStandardStreamLock<'a> {
fn write(&mut self, b: &[u8]) -> io::Result<usize> {
match *self {
IoStandardStreamLock::StdoutLock(ref mut s) => s.write(b),
IoStandardStreamLock::StderrLock(ref mut s) => s.write(b),
}
}
fn flush(&mut self) -> io::Result<()> {
match *self {
IoStandardStreamLock::StdoutLock(ref mut s) => s.flush(),
IoStandardStreamLock::StderrLock(ref mut s) => s.flush(),
}
}
}
/// Satisfies `io::Write` and `WriteColor`, and supports optional coloring
/// to either of the standard output streams, stdout and stderr.
pub struct StandardStream {
wtr: LossyStandardStream<WriterInner<'static, IoStandardStream>>,
}
/// `StandardStreamLock` is a locked reference to a `StandardStream`.
/// ///
/// This implements the `io::Write` and `WriteColor` traits, and is constructed /// This implements the `io::Write` and `WriteColor` traits, and is constructed
/// via the `Write::lock` method. /// via the `Write::lock` method.
/// ///
/// The lifetime `'a` refers to the lifetime of the corresponding `Stdout`. /// The lifetime `'a` refers to the lifetime of the corresponding `StandardStream`.
pub struct StdoutLock<'a> { pub struct StandardStreamLock<'a> {
wtr: LossyStdout<WriterInner<'a, io::StdoutLock<'a>>>, wtr: LossyStandardStream<WriterInner<'a, IoStandardStreamLock<'a>>>,
} }
/// WriterInner is a (limited) generic representation of a writer. It is /// WriterInner is a (limited) generic representation of a writer. It is
@ -217,23 +287,23 @@ enum WriterInner<'a, W> {
WindowsLocked { wtr: W, console: MutexGuard<'a, wincolor::Console> }, WindowsLocked { wtr: W, console: MutexGuard<'a, wincolor::Console> },
} }
impl Stdout { impl StandardStream {
/// Create a new `Stdout` with the given color preferences. /// Create a new `StandardStream` with the given color preferences.
/// ///
/// The specific color/style settings can be configured when writing via /// The specific color/style settings can be configured when writing via
/// the `WriteColor` trait. /// the `WriteColor` trait.
#[cfg(not(windows))] #[cfg(not(windows))]
pub fn new(choice: ColorChoice) -> Stdout { fn create(sty: StandardStreamType, choice: ColorChoice) -> StandardStream {
let wtr = let wtr =
if choice.should_attempt_color() { if choice.should_attempt_color() {
WriterInner::Ansi(Ansi(io::stdout())) WriterInner::Ansi(Ansi(IoStandardStream::new(sty)))
} else { } else {
WriterInner::NoColor(NoColor(io::stdout())) WriterInner::NoColor(NoColor(IoStandardStream::new(sty)))
}; };
Stdout { wtr: LossyStdout::new(wtr) } StandardStream { wtr: LossyStandardStream::new(wtr) }
} }
/// Create a new `Stdout` with the given color preferences. /// Create a new `StandardStream` with the given color preferences.
/// ///
/// If coloring is desired and a Windows console could not be found, then /// If coloring is desired and a Windows console could not be found, then
/// ANSI escape sequences are used instead. /// ANSI escape sequences are used instead.
@ -241,25 +311,52 @@ impl Stdout {
/// The specific color/style settings can be configured when writing via /// The specific color/style settings can be configured when writing via
/// the `WriteColor` trait. /// the `WriteColor` trait.
#[cfg(windows)] #[cfg(windows)]
pub fn new(choice: ColorChoice) -> Stdout { fn create(sty: StandardStreamType, choice: ColorChoice) -> StandardStream {
let con = wincolor::Console::stdout(); let con = match sty {
StandardStreamType::Stdout => wincolor::Console::stdout(),
StandardStreamType::Stderr => wincolor::Console::stderr(),
};
let is_win_console = con.is_ok(); let is_win_console = con.is_ok();
let wtr = let wtr =
if choice.should_attempt_color() { if choice.should_attempt_color() {
if choice.should_ansi() { if choice.should_ansi() {
WriterInner::Ansi(Ansi(io::stdout())) WriterInner::Ansi(Ansi(IoStandardStream::new(sty)))
} else if let Ok(console) = con { } else if let Ok(console) = con {
WriterInner::Windows { WriterInner::Windows {
wtr: io::stdout(), wtr: IoStandardStream::new(sty),
console: Mutex::new(console), console: Mutex::new(console),
} }
} else { } else {
WriterInner::Ansi(Ansi(io::stdout())) WriterInner::Ansi(Ansi(IoStandardStream::new(sty)))
} }
} else { } else {
WriterInner::NoColor(NoColor(io::stdout())) WriterInner::NoColor(NoColor(IoStandardStream::new(sty)))
}; };
Stdout { wtr: LossyStdout::new(wtr).is_console(is_win_console) } StandardStream { wtr: LossyStandardStream::new(wtr).is_console(is_win_console) }
}
/// Create a new `StandardStream` with the given color preferences that
/// writes to standard output.
///
/// On Windows, if coloring is desired and a Windows console could not be
/// found, then ANSI escape sequences are used instead.
///
/// The specific color/style settings can be configured when writing via
/// the `WriteColor` trait.
pub fn stdout(choice: ColorChoice) -> StandardStream {
StandardStream::create(StandardStreamType::Stdout, choice)
}
/// Create a new `StandardStream` with the given color preferences that
/// writes to standard error.
///
/// On Windows, if coloring is desired and a Windows console could not be
/// found, then ANSI escape sequences are used instead.
///
/// The specific color/style settings can be configured when writing via
/// the `WriteColor` trait.
pub fn stderr(choice: ColorChoice) -> StandardStream {
StandardStream::create(StandardStreamType::Stderr, choice)
} }
/// Lock the underlying writer. /// Lock the underlying writer.
@ -268,16 +365,16 @@ impl Stdout {
/// `WriteColor`. /// `WriteColor`.
/// ///
/// This method is **not reentrant**. It may panic if `lock` is called /// This method is **not reentrant**. It may panic if `lock` is called
/// while a `StdoutLock` is still alive. /// while a `StandardStreamLock` is still alive.
pub fn lock(&self) -> StdoutLock { pub fn lock(&self) -> StandardStreamLock {
StdoutLock::from_stdout(self) StandardStreamLock::from_stream(self)
} }
} }
impl<'a> StdoutLock<'a> { impl<'a> StandardStreamLock<'a> {
#[cfg(not(windows))] #[cfg(not(windows))]
fn from_stdout(stdout: &Stdout) -> StdoutLock { fn from_stream(stream: &StandardStream) -> StandardStreamLock {
let locked = match *stdout.wtr.get_ref() { let locked = match *stream.wtr.get_ref() {
WriterInner::Unreachable(_) => unreachable!(), WriterInner::Unreachable(_) => unreachable!(),
WriterInner::NoColor(ref w) => { WriterInner::NoColor(ref w) => {
WriterInner::NoColor(NoColor(w.0.lock())) WriterInner::NoColor(NoColor(w.0.lock()))
@ -286,12 +383,12 @@ impl<'a> StdoutLock<'a> {
WriterInner::Ansi(Ansi(w.0.lock())) WriterInner::Ansi(Ansi(w.0.lock()))
} }
}; };
StdoutLock { wtr: stdout.wtr.wrap(locked) } StandardStreamLock { wtr: stream.wtr.wrap(locked) }
} }
#[cfg(windows)] #[cfg(windows)]
fn from_stdout(stdout: &Stdout) -> StdoutLock { fn from_stream(stream: &StandardStream) -> StandardStreamLock {
let locked = match *stdout.wtr.get_ref() { let locked = match *stream.wtr.get_ref() {
WriterInner::Unreachable(_) => unreachable!(), WriterInner::Unreachable(_) => unreachable!(),
WriterInner::NoColor(ref w) => { WriterInner::NoColor(ref w) => {
WriterInner::NoColor(NoColor(w.0.lock())) WriterInner::NoColor(NoColor(w.0.lock()))
@ -308,19 +405,19 @@ impl<'a> StdoutLock<'a> {
} }
#[cfg(windows)] #[cfg(windows)]
WriterInner::WindowsLocked{..} => { WriterInner::WindowsLocked{..} => {
panic!("cannot call Stdout.lock while a StdoutLock is alive"); panic!("cannot call StandardStream.lock while a StandardStreamLock is alive");
} }
}; };
StdoutLock { wtr: stdout.wtr.wrap(locked) } StandardStreamLock { wtr: stream.wtr.wrap(locked) }
} }
} }
impl io::Write for Stdout { impl io::Write for StandardStream {
fn write(&mut self, b: &[u8]) -> io::Result<usize> { self.wtr.write(b) } fn write(&mut self, b: &[u8]) -> io::Result<usize> { self.wtr.write(b) }
fn flush(&mut self) -> io::Result<()> { self.wtr.flush() } fn flush(&mut self) -> io::Result<()> { self.wtr.flush() }
} }
impl WriteColor for Stdout { impl WriteColor for StandardStream {
fn supports_color(&self) -> bool { self.wtr.supports_color() } fn supports_color(&self) -> bool { self.wtr.supports_color() }
fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> { fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
self.wtr.set_color(spec) self.wtr.set_color(spec)
@ -328,12 +425,12 @@ impl WriteColor for Stdout {
fn reset(&mut self) -> io::Result<()> { self.wtr.reset() } fn reset(&mut self) -> io::Result<()> { self.wtr.reset() }
} }
impl<'a> io::Write for StdoutLock<'a> { impl<'a> io::Write for StandardStreamLock<'a> {
fn write(&mut self, b: &[u8]) -> io::Result<usize> { self.wtr.write(b) } fn write(&mut self, b: &[u8]) -> io::Result<usize> { self.wtr.write(b) }
fn flush(&mut self) -> io::Result<()> { self.wtr.flush() } fn flush(&mut self) -> io::Result<()> { self.wtr.flush() }
} }
impl<'a> WriteColor for StdoutLock<'a> { impl<'a> WriteColor for StandardStreamLock<'a> {
fn supports_color(&self) -> bool { self.wtr.supports_color() } fn supports_color(&self) -> bool { self.wtr.supports_color() }
fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> { fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
self.wtr.set_color(spec) self.wtr.set_color(spec)
@ -420,7 +517,7 @@ impl<'a, W: io::Write> WriteColor for WriterInner<'a, W> {
} }
} }
/// Writes colored buffers to stdout. /// Writes colored buffers to stdout or stderr.
/// ///
/// Writable buffers can be obtained by calling `buffer` on a `BufferWriter`. /// Writable buffers can be obtained by calling `buffer` on a `BufferWriter`.
/// ///
@ -430,7 +527,7 @@ impl<'a, W: io::Write> WriteColor for WriterInner<'a, W> {
/// It is intended for a `BufferWriter` to be put in an `Arc` and written to /// It is intended for a `BufferWriter` to be put in an `Arc` and written to
/// from multiple threads simultaneously. /// from multiple threads simultaneously.
pub struct BufferWriter { pub struct BufferWriter {
stdout: LossyStdout<io::Stdout>, stream: LossyStandardStream<IoStandardStream>,
printed: AtomicBool, printed: AtomicBool,
separator: Option<Vec<u8>>, separator: Option<Vec<u8>>,
color_choice: ColorChoice, color_choice: ColorChoice,
@ -439,23 +536,23 @@ pub struct BufferWriter {
} }
impl BufferWriter { impl BufferWriter {
/// Create a new `BufferWriter` that writes to stdout with the given /// Create a new `BufferWriter` that writes to a standard stream with the
/// color preferences. /// given color preferences.
/// ///
/// The specific color/style settings can be configured when writing to /// The specific color/style settings can be configured when writing to
/// the buffers themselves. /// the buffers themselves.
#[cfg(not(windows))] #[cfg(not(windows))]
pub fn stdout(choice: ColorChoice) -> BufferWriter { fn create(sty: StandardStreamType, choice: ColorChoice) -> BufferWriter {
BufferWriter { BufferWriter {
stdout: LossyStdout::new(io::stdout()), stream: LossyStandardStream::new(IoStandardStream::new(sty)),
printed: AtomicBool::new(false), printed: AtomicBool::new(false),
separator: None, separator: None,
color_choice: choice, color_choice: choice,
} }
} }
/// Create a new `BufferWriter` that writes to stdout with the given /// Create a new `BufferWriter` that writes to a standard stream with the
/// color preferences. /// given color preferences.
/// ///
/// If coloring is desired and a Windows console could not be found, then /// If coloring is desired and a Windows console could not be found, then
/// ANSI escape sequences are used instead. /// ANSI escape sequences are used instead.
@ -463,11 +560,14 @@ impl BufferWriter {
/// The specific color/style settings can be configured when writing to /// The specific color/style settings can be configured when writing to
/// the buffers themselves. /// the buffers themselves.
#[cfg(windows)] #[cfg(windows)]
pub fn stdout(choice: ColorChoice) -> BufferWriter { fn create(sty: StandardStreamType, choice: ColorChoice) -> BufferWriter {
let con = wincolor::Console::stdout().ok().map(Mutex::new); let con = match sty {
let stdout = LossyStdout::new(io::stdout()).is_console(con.is_some()); StandardStreamType::Stdout => wincolor::Console::stdout(),
StandardStreamType::Stderr => wincolor::Console::stderr(),
}.ok().map(Mutex::new);
let stream = LossyStandardStream::new(IoStandardStream::new(sty)).is_console(con.is_some());
BufferWriter { BufferWriter {
stdout: stdout, stream: stream,
printed: AtomicBool::new(false), printed: AtomicBool::new(false),
separator: None, separator: None,
color_choice: choice, color_choice: choice,
@ -475,6 +575,30 @@ impl BufferWriter {
} }
} }
/// Create a new `BufferWriter` that writes to stdout with the given
/// color preferences.
///
/// On Windows, if coloring is desired and a Windows console could not be
/// found, then ANSI escape sequences are used instead.
///
/// The specific color/style settings can be configured when writing to
/// the buffers themselves.
pub fn stdout(choice: ColorChoice) -> BufferWriter {
BufferWriter::create(StandardStreamType::Stdout, choice)
}
/// Create a new `BufferWriter` that writes to stderr with the given
/// color preferences.
///
/// On Windows, if coloring is desired and a Windows console could not be
/// found, then ANSI escape sequences are used instead.
///
/// The specific color/style settings can be configured when writing to
/// the buffers themselves.
pub fn stderr(choice: ColorChoice) -> BufferWriter {
BufferWriter::create(StandardStreamType::Stderr, choice)
}
/// If set, the separator given is printed between buffers. By default, no /// If set, the separator given is printed between buffers. By default, no
/// separator is printed. /// separator is printed.
/// ///
@ -510,16 +634,16 @@ impl BufferWriter {
if buf.is_empty() { if buf.is_empty() {
return Ok(()); return Ok(());
} }
let mut stdout = self.stdout.wrap(self.stdout.get_ref().lock()); let mut stream = self.stream.wrap(self.stream.get_ref().lock());
if let Some(ref sep) = self.separator { if let Some(ref sep) = self.separator {
if self.printed.load(Ordering::SeqCst) { if self.printed.load(Ordering::SeqCst) {
try!(stdout.write_all(sep)); try!(stream.write_all(sep));
try!(stdout.write_all(b"\n")); try!(stream.write_all(b"\n"));
} }
} }
match buf.0 { match buf.0 {
BufferInner::NoColor(ref b) => try!(stdout.write_all(&b.0)), BufferInner::NoColor(ref b) => try!(stream.write_all(&b.0)),
BufferInner::Ansi(ref b) => try!(stdout.write_all(&b.0)), BufferInner::Ansi(ref b) => try!(stream.write_all(&b.0)),
#[cfg(windows)] #[cfg(windows)]
BufferInner::Windows(ref b) => { BufferInner::Windows(ref b) => {
// We guarantee by construction that we have a console here. // We guarantee by construction that we have a console here.
@ -527,7 +651,7 @@ impl BufferWriter {
let console_mutex = self.console.as_ref() let console_mutex = self.console.as_ref()
.expect("got Windows buffer but have no Console"); .expect("got Windows buffer but have no Console");
let mut console = console_mutex.lock().unwrap(); let mut console = console_mutex.lock().unwrap();
try!(b.print(&mut *console, &mut stdout)); try!(b.print(&mut *console, &mut stream));
} }
} }
self.printed.store(true, Ordering::SeqCst); self.printed.store(true, Ordering::SeqCst);
@ -905,25 +1029,25 @@ impl WindowsBuffer {
self.colors.push((pos, spec)); self.colors.push((pos, spec));
} }
/// Print the contents to the given stdout handle, and use the console /// Print the contents to the given stream handle, and use the console
/// for coloring. /// for coloring.
fn print( fn print(
&self, &self,
console: &mut wincolor::Console, console: &mut wincolor::Console,
stdout: &mut LossyStdout<io::StdoutLock>, stream: &mut LossyStandardStream<IoStandardStreamLock>,
) -> io::Result<()> { ) -> io::Result<()> {
let mut last = 0; let mut last = 0;
for &(pos, ref spec) in &self.colors { for &(pos, ref spec) in &self.colors {
try!(stdout.write_all(&self.buf[last..pos])); try!(stream.write_all(&self.buf[last..pos]));
try!(stdout.flush()); try!(stream.flush());
last = pos; last = pos;
match *spec { match *spec {
None => try!(console.reset()), None => try!(console.reset()),
Some(ref spec) => try!(spec.write_console(console)), Some(ref spec) => try!(spec.write_console(console)),
} }
} }
try!(stdout.write_all(&self.buf[last..])); try!(stream.write_all(&self.buf[last..]));
stdout.flush() stream.flush()
} }
/// Clear the buffer. /// Clear the buffer.
@ -1121,33 +1245,33 @@ impl FromStr for Color {
} }
} }
struct LossyStdout<W> { struct LossyStandardStream<W> {
wtr: W, wtr: W,
#[cfg(windows)] #[cfg(windows)]
is_console: bool, is_console: bool,
} }
impl<W: io::Write> LossyStdout<W> { impl<W: io::Write> LossyStandardStream<W> {
#[cfg(not(windows))] #[cfg(not(windows))]
fn new(wtr: W) -> LossyStdout<W> { LossyStdout { wtr: wtr } } fn new(wtr: W) -> LossyStandardStream<W> { LossyStandardStream { wtr: wtr } }
#[cfg(windows)] #[cfg(windows)]
fn new(wtr: W) -> LossyStdout<W> { fn new(wtr: W) -> LossyStandardStream<W> {
LossyStdout { wtr: wtr, is_console: false } LossyStandardStream { wtr: wtr, is_console: false }
} }
#[cfg(not(windows))] #[cfg(not(windows))]
fn wrap<Q: io::Write>(&self, wtr: Q) -> LossyStdout<Q> { fn wrap<Q: io::Write>(&self, wtr: Q) -> LossyStandardStream<Q> {
LossyStdout::new(wtr) LossyStandardStream::new(wtr)
} }
#[cfg(windows)] #[cfg(windows)]
fn wrap<Q: io::Write>(&self, wtr: Q) -> LossyStdout<Q> { fn wrap<Q: io::Write>(&self, wtr: Q) -> LossyStandardStream<Q> {
LossyStdout::new(wtr).is_console(self.is_console) LossyStandardStream::new(wtr).is_console(self.is_console)
} }
#[cfg(windows)] #[cfg(windows)]
fn is_console(mut self, yes: bool) -> LossyStdout<W> { fn is_console(mut self, yes: bool) -> LossyStandardStream<W> {
self.is_console = yes; self.is_console = yes;
self self
} }
@ -1157,7 +1281,7 @@ impl<W: io::Write> LossyStdout<W> {
} }
} }
impl<W: WriteColor> WriteColor for LossyStdout<W> { impl<W: WriteColor> WriteColor for LossyStandardStream<W> {
fn supports_color(&self) -> bool { self.wtr.supports_color() } fn supports_color(&self) -> bool { self.wtr.supports_color() }
fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> { fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
self.wtr.set_color(spec) self.wtr.set_color(spec)
@ -1165,7 +1289,7 @@ impl<W: WriteColor> WriteColor for LossyStdout<W> {
fn reset(&mut self) -> io::Result<()> { self.wtr.reset() } fn reset(&mut self) -> io::Result<()> { self.wtr.reset() }
} }
impl<W: io::Write> io::Write for LossyStdout<W> { impl<W: io::Write> io::Write for LossyStandardStream<W> {
#[cfg(not(windows))] #[cfg(not(windows))]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> { fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.wtr.write(buf) self.wtr.write(buf)