diff --git a/termcolor/COPYING b/termcolor/COPYING deleted file mode 100644 index bb9c20a0..00000000 --- a/termcolor/COPYING +++ /dev/null @@ -1,3 +0,0 @@ -This project is dual-licensed under the Unlicense and MIT licenses. - -You may use this code under the terms of either license. diff --git a/termcolor/Cargo.toml b/termcolor/Cargo.toml deleted file mode 100644 index d0a0ff4c..00000000 --- a/termcolor/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "termcolor" -version = "0.3.6" #:version -authors = ["Andrew Gallant "] -description = """ -A simple cross platform library for writing colored text to a terminal. -""" -documentation = "https://docs.rs/termcolor" -homepage = "https://github.com/BurntSushi/ripgrep/tree/master/termcolor" -repository = "https://github.com/BurntSushi/ripgrep/tree/master/termcolor" -readme = "README.md" -keywords = ["windows", "win", "color", "ansi", "console"] -license = "Unlicense/MIT" - -[lib] -name = "termcolor" -bench = false - -[target.'cfg(windows)'.dependencies] -wincolor = { version = "0.1.6", path = "../wincolor" } diff --git a/termcolor/LICENSE-MIT b/termcolor/LICENSE-MIT deleted file mode 100644 index 3b0a5dc0..00000000 --- a/termcolor/LICENSE-MIT +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 Andrew Gallant - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/termcolor/README.md b/termcolor/README.md index 7317d431..f09ea149 100644 --- a/termcolor/README.md +++ b/termcolor/README.md @@ -1,86 +1,2 @@ -termcolor -========= -A simple cross platform library for writing colored text to a terminal. This -library writes colored text either using standard ANSI escape sequences or -by interacting with the Windows console. Several convenient abstractions -are provided for use in single-threaded or multi-threaded command line -applications. - -[![Linux build status](https://api.travis-ci.org/BurntSushi/ripgrep.png)](https://travis-ci.org/BurntSushi/ripgrep) -[![Windows build status](https://ci.appveyor.com/api/projects/status/github/BurntSushi/ripgrep?svg=true)](https://ci.appveyor.com/project/BurntSushi/ripgrep) -[![](https://img.shields.io/crates/v/termcolor.svg)](https://crates.io/crates/termcolor) - -Dual-licensed under MIT or the [UNLICENSE](http://unlicense.org). - -### Documentation - -[https://docs.rs/termcolor](https://docs.rs/termcolor) - -### Usage - -Add this to your `Cargo.toml`: - -```toml -[dependencies] -termcolor = "0.3" -``` - -and this to your crate root: - -```rust -extern crate termcolor; -``` - -### Organization - -The `WriteColor` trait extends the `io::Write` trait with methods for setting -colors or resetting them. - -`StandardStream` and `StandardStreamLock` both satisfy `WriteColor` and are -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 -program, each thread might write to its own buffer. A buffer can be printed to -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 -access to global resources such as the Windows console. Moreover, this design -also prevents interleaving of buffer output. - -`Ansi` and `NoColor` both satisfy `WriteColor` for arbitrary implementors of -`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. - -### Example: using `StandardStream` - -The `StandardStream` type in this crate works similarly to `std::io::Stdout`, -except it is augmented with methods for coloring by the `WriteColor` trait. -For example, to write some green text: - -```rust -use std::io::Write; -use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; - -let mut stdout = StandardStream::stdout(ColorChoice::Always); -stdout.set_color(ColorSpec::new().set_fg(Some(Color::Green)))?; -writeln!(&mut stdout, "green text!")?; -``` - -### Example: using `BufferWriter` - -A `BufferWriter` can create buffers and write buffers to stdout or stderr. It -does *not* implement `io::Write` or `WriteColor` itself. Instead, `Buffer` -implements `io::Write` and `termcolor::WriteColor`. - -This example shows how to print some green text to stderr. - -```rust -use std::io::Write; -use termcolor::{BufferWriter, Color, ColorChoice, ColorSpec, WriteColor}; - -let mut bufwtr = BufferWriter::stderr(ColorChoice::Always); -let mut buffer = bufwtr.buffer(); -buffer.set_color(ColorSpec::new().set_fg(Some(Color::Green)))?; -writeln!(&mut buffer, "green text!")?; -bufwtr.print(&buffer)?; -``` +termcolor has moved to its own repository: +https://github.com/BurntSushi/termcolor diff --git a/termcolor/UNLICENSE b/termcolor/UNLICENSE deleted file mode 100644 index 68a49daa..00000000 --- a/termcolor/UNLICENSE +++ /dev/null @@ -1,24 +0,0 @@ -This is free and unencumbered software released into the public domain. - -Anyone is free to copy, modify, publish, use, compile, sell, or -distribute this software, either in source code form or as a compiled -binary, for any purpose, commercial or non-commercial, and by any -means. - -In jurisdictions that recognize copyright laws, the author or authors -of this software dedicate any and all copyright interest in the -software to the public domain. We make this dedication for the benefit -of the public at large and to the detriment of our heirs and -successors. We intend this dedication to be an overt act of -relinquishment in perpetuity of all present and future rights to this -software under copyright law. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR -OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. - -For more information, please refer to diff --git a/termcolor/src/lib.rs b/termcolor/src/lib.rs deleted file mode 100644 index 7d904917..00000000 --- a/termcolor/src/lib.rs +++ /dev/null @@ -1,1832 +0,0 @@ -/*! -This crate provides a cross platform abstraction for writing colored text to -a terminal. Colors are written using either ANSI escape sequences or by -communicating with a Windows console. Much of this API was motivated by use -inside command line applications, where colors or styles can be configured -by the end user and/or the environment. - -This crate also provides platform independent support for writing colored text -to an in memory buffer. While this is easy to do with ANSI escape sequences -(because they are in the buffer themselves), it is trickier to do with the -Windows console API, which requires synchronous communication. - -# Organization - -The `WriteColor` trait extends the `io::Write` trait with methods for setting -colors or resetting them. - -`StandardStream` and `StandardStreamLock` both satisfy `WriteColor` and are -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 -program, each thread might write to its own buffer. A buffer can be printed to -using a `BufferWriter`. The advantage of this design is that 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 also prevents -interleaving of buffer output. - -`Ansi` and `NoColor` both satisfy `WriteColor` for arbitrary implementors of -`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. - -# Example: using `StandardStream` - -The `StandardStream` type in this crate works similarly to `std::io::Stdout`, -except it is augmented with methods for coloring by the `WriteColor` trait. -For example, to write some green text: - -```rust,no_run -# fn test() -> Result<(), Box<::std::error::Error>> { -use std::io::Write; -use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; - -let mut stdout = StandardStream::stdout(ColorChoice::Always); -stdout.set_color(ColorSpec::new().set_fg(Some(Color::Green)))?; -writeln!(&mut stdout, "green text!")?; -# Ok(()) } -``` - -# Example: using `BufferWriter` - -A `BufferWriter` can create buffers and write buffers to stdout or stderr. It -does *not* implement `io::Write` or `WriteColor` itself. Instead, `Buffer` -implements `io::Write` and `io::WriteColor`. - -This example shows how to print some green text to stderr. - -```rust,no_run -# fn test() -> Result<(), Box<::std::error::Error>> { -use std::io::Write; -use termcolor::{BufferWriter, Color, ColorChoice, ColorSpec, WriteColor}; - -let mut bufwtr = BufferWriter::stderr(ColorChoice::Always); -let mut buffer = bufwtr.buffer(); -buffer.set_color(ColorSpec::new().set_fg(Some(Color::Green)))?; -writeln!(&mut buffer, "green text!")?; -bufwtr.print(&buffer)?; -# Ok(()) } -``` -*/ - -#![deny(missing_docs)] - -#[cfg(windows)] -extern crate wincolor; - -use std::env; -use std::error; -use std::fmt; -use std::io::{self, Write}; -use std::str::FromStr; -#[cfg(windows)] -use std::sync::{Mutex, MutexGuard}; -use std::sync::atomic::{AtomicBool, Ordering}; - -/// This trait describes the behavior of writers that support colored output. -pub trait WriteColor: io::Write { - /// Returns true if and only if the underlying writer supports colors. - fn supports_color(&self) -> bool; - - /// Set the color settings of the writer. - /// - /// Subsequent writes to this writer will use these settings until either - /// `reset` is called or new color settings are set. - /// - /// If there was a problem setting the color settings, then an error is - /// returned. - fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()>; - - /// Reset the current color settings to their original settings. - /// - /// If there was a problem resetting the color settings, then an error is - /// returned. - fn reset(&mut self) -> io::Result<()>; - - /// Returns true if and only if the underlying writer must synchronously - /// interact with an end user's device in order to control colors. By - /// default, this always returns `false`. - /// - /// In practice, this should return `true` if the underlying writer is - /// manipulating colors using the Windows console APIs. - /// - /// This is useful for writing generic code (such as a buffered writer) - /// that can perform certain optimizations when the underlying writer - /// doesn't rely on synchronous APIs. For example, ANSI escape sequences - /// can be passed through to the end user's device as is. - fn is_synchronous(&self) -> bool { - false - } -} - -impl<'a, T: ?Sized + WriteColor> WriteColor for &'a mut T { - fn supports_color(&self) -> bool { (&**self).supports_color() } - fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> { - (&mut **self).set_color(spec) - } - fn reset(&mut self) -> io::Result<()> { (&mut **self).reset() } - fn is_synchronous(&self) -> bool { (&**self).is_synchronous() } -} - -impl WriteColor for Box { - fn supports_color(&self) -> bool { (&**self).supports_color() } - fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> { - (&mut **self).set_color(spec) - } - fn reset(&mut self) -> io::Result<()> { (&mut **self).reset() } - fn is_synchronous(&self) -> bool { (&**self).is_synchronous() } -} - -/// ColorChoice represents the color preferences of an end user. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum ColorChoice { - /// Try very hard to emit colors. This includes emitting ANSI colors - /// on Windows if the console API is unavailable. - Always, - /// AlwaysAnsi is like Always, except it never tries to use anything other - /// than emitting ANSI color codes. - AlwaysAnsi, - /// Try to use colors, but don't force the issue. If the console isn't - /// available on Windows, or if TERM=dumb, for example, then don't use - /// colors. - Auto, - /// Never emit colors. - Never, -} - -impl ColorChoice { - /// Returns true if we should attempt to write colored output. - #[cfg(not(windows))] - fn should_attempt_color(&self) -> bool { - match *self { - ColorChoice::Always => true, - ColorChoice::AlwaysAnsi => true, - ColorChoice::Never => false, - ColorChoice::Auto => { - match env::var("TERM") { - Err(_) => false, - Ok(k) => k != "dumb", - } - } - } - } - - /// Returns true if we should attempt to write colored output. - #[cfg(windows)] - fn should_attempt_color(&self) -> bool { - match *self { - ColorChoice::Always => true, - ColorChoice::AlwaysAnsi => true, - ColorChoice::Never => false, - ColorChoice::Auto => { - match env::var("TERM") { - Err(_) => true, - Ok(k) => k != "dumb", - } - } - } - } - - /// Returns true if this choice should forcefully use ANSI color codes. - /// - /// It's possible that ANSI is still the correct choice even if this - /// returns false. - #[cfg(windows)] - fn should_ansi(&self) -> bool { - match *self { - ColorChoice::Always => false, - ColorChoice::AlwaysAnsi => true, - ColorChoice::Never => false, - ColorChoice::Auto => { - match env::var("TERM") { - Err(_) => false, - // cygwin doesn't seem to support ANSI escape sequences - // and instead has its own variety. However, the Windows - // console API may be available. - Ok(k) => k != "dumb" && k != "cygwin", - } - } - } - } -} - -/// `std::io` implements `Stdout` and `Stderr` (and their `Lock` variants) as -/// separate types, which makes it difficult to abstract over them. We use -/// some simple internal enum types to work around this. - -enum StandardStreamType { - Stdout, - Stderr, - StdoutBuffered, - StderrBuffered, -} - -enum IoStandardStream { - Stdout(io::Stdout), - Stderr(io::Stderr), - StdoutBuffered(io::BufWriter), - StderrBuffered(io::BufWriter), -} - -impl IoStandardStream { - fn new(sty: StandardStreamType) -> IoStandardStream { - match sty { - StandardStreamType::Stdout => { - IoStandardStream::Stdout(io::stdout()) - } - StandardStreamType::Stderr => { - IoStandardStream::Stderr(io::stderr()) - } - StandardStreamType::StdoutBuffered => { - let wtr = io::BufWriter::new(io::stdout()); - IoStandardStream::StdoutBuffered(wtr) - } - StandardStreamType::StderrBuffered => { - let wtr = io::BufWriter::new(io::stderr()); - IoStandardStream::StderrBuffered(wtr) - } - } - } - - fn lock(&self) -> IoStandardStreamLock { - match *self { - IoStandardStream::Stdout(ref s) => { - IoStandardStreamLock::StdoutLock(s.lock()) - } - IoStandardStream::Stderr(ref s) => { - IoStandardStreamLock::StderrLock(s.lock()) - } - IoStandardStream::StdoutBuffered(_) - | IoStandardStream::StderrBuffered(_) => { - // We don't permit this case to ever occur in the public API, - // so it's OK to panic. - panic!("cannot lock a buffered standard stream") - } - } - } -} - -impl io::Write for IoStandardStream { - fn write(&mut self, b: &[u8]) -> io::Result { - match *self { - IoStandardStream::Stdout(ref mut s) => s.write(b), - IoStandardStream::Stderr(ref mut s) => s.write(b), - IoStandardStream::StdoutBuffered(ref mut s) => s.write(b), - IoStandardStream::StderrBuffered(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(), - IoStandardStream::StdoutBuffered(ref mut s) => s.flush(), - IoStandardStream::StderrBuffered(ref mut s) => s.flush(), - } - } -} - -// Same rigmarole 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 { - 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>, -} - -/// `StandardStreamLock` is a locked reference to a `StandardStream`. -/// -/// This implements the `io::Write` and `WriteColor` traits, and is constructed -/// via the `Write::lock` method. -/// -/// The lifetime `'a` refers to the lifetime of the corresponding -/// `StandardStream`. -pub struct StandardStreamLock<'a> { - wtr: LossyStandardStream>>, -} - -/// Like `StandardStream`, but does buffered writing. -pub struct BufferedStandardStream { - wtr: LossyStandardStream>, -} - -/// WriterInner is a (limited) generic representation of a writer. It is -/// limited because W should only ever be stdout/stderr on Windows. -enum WriterInner { - NoColor(NoColor), - Ansi(Ansi), - #[cfg(windows)] - Windows { wtr: W, console: Mutex }, -} - -/// WriterInnerLock is a (limited) generic representation of a writer. It is -/// limited because W should only ever be stdout/stderr on Windows. -enum WriterInnerLock<'a, W> { - NoColor(NoColor), - Ansi(Ansi), - /// What a gross hack. On Windows, we need to specify a lifetime for the - /// console when in a locked state, but obviously don't need to do that - /// on Unix, which makes the `'a` unused. To satisfy the compiler, we need - /// a PhantomData. - #[allow(dead_code)] - Unreachable(::std::marker::PhantomData<&'a ()>), - #[cfg(windows)] - Windows { wtr: W, console: MutexGuard<'a, wincolor::Console> }, -} - -impl StandardStream { - /// 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 { - let wtr = WriterInner::create(StandardStreamType::Stdout, choice); - StandardStream { wtr: LossyStandardStream::new(wtr) } - } - - /// 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 { - let wtr = WriterInner::create(StandardStreamType::Stderr, choice); - StandardStream { wtr: LossyStandardStream::new(wtr) } - } - - /// Lock the underlying writer. - /// - /// The lock guard returned also satisfies `io::Write` and - /// `WriteColor`. - /// - /// This method is **not reentrant**. It may panic if `lock` is called - /// while a `StandardStreamLock` is still alive. - pub fn lock(&self) -> StandardStreamLock { - StandardStreamLock::from_stream(self) - } -} - -impl<'a> StandardStreamLock<'a> { - #[cfg(not(windows))] - fn from_stream(stream: &StandardStream) -> StandardStreamLock { - let locked = match *stream.wtr.get_ref() { - WriterInner::NoColor(ref w) => { - WriterInnerLock::NoColor(NoColor(w.0.lock())) - } - WriterInner::Ansi(ref w) => { - WriterInnerLock::Ansi(Ansi(w.0.lock())) - } - }; - StandardStreamLock { wtr: stream.wtr.wrap(locked) } - } - - #[cfg(windows)] - fn from_stream(stream: &StandardStream) -> StandardStreamLock { - let locked = match *stream.wtr.get_ref() { - WriterInner::NoColor(ref w) => { - WriterInnerLock::NoColor(NoColor(w.0.lock())) - } - WriterInner::Ansi(ref w) => { - WriterInnerLock::Ansi(Ansi(w.0.lock())) - } - #[cfg(windows)] - WriterInner::Windows { ref wtr, ref console } => { - WriterInnerLock::Windows { - wtr: wtr.lock(), - console: console.lock().unwrap(), - } - } - }; - StandardStreamLock { wtr: stream.wtr.wrap(locked) } - } -} - -impl BufferedStandardStream { - /// Create a new `BufferedStandardStream` with the given color preferences - /// that writes to standard output via a buffered writer. - /// - /// 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) -> BufferedStandardStream { - let wtr = WriterInner::create( - StandardStreamType::StdoutBuffered, - choice, - ); - BufferedStandardStream { wtr: LossyStandardStream::new(wtr) } - } - - /// Create a new `BufferedStandardStream` with the given color preferences - /// that writes to standard error via a buffered writer. - /// - /// 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) -> BufferedStandardStream { - let wtr = WriterInner::create( - StandardStreamType::StderrBuffered, - choice, - ); - BufferedStandardStream { wtr: LossyStandardStream::new(wtr) } - } -} - -impl WriterInner { - /// Create a new inner writer for a standard stream with the given color - /// preferences. - #[cfg(not(windows))] - fn create( - sty: StandardStreamType, - choice: ColorChoice, - ) -> WriterInner { - if choice.should_attempt_color() { - WriterInner::Ansi(Ansi(IoStandardStream::new(sty))) - } else { - WriterInner::NoColor(NoColor(IoStandardStream::new(sty))) - } - } - - /// Create a new inner writer for a standard stream with the given color - /// preferences. - /// - /// If coloring is desired and a Windows console could not be found, then - /// ANSI escape sequences are used instead. - #[cfg(windows)] - fn create( - sty: StandardStreamType, - choice: ColorChoice, - ) -> WriterInner { - let mut con = match sty { - StandardStreamType::Stdout => wincolor::Console::stdout(), - StandardStreamType::Stderr => wincolor::Console::stderr(), - StandardStreamType::StdoutBuffered => wincolor::Console::stdout(), - StandardStreamType::StderrBuffered => wincolor::Console::stderr(), - }; - let is_console_virtual = con.as_mut().map(|con| { - con.set_virtual_terminal_processing(true).is_ok() - }).unwrap_or(false); - if choice.should_attempt_color() { - if choice.should_ansi() || is_console_virtual { - WriterInner::Ansi(Ansi(IoStandardStream::new(sty))) - } else if let Ok(console) = con { - WriterInner::Windows { - wtr: IoStandardStream::new(sty), - console: Mutex::new(console), - } - } else { - WriterInner::Ansi(Ansi(IoStandardStream::new(sty))) - } - } else { - WriterInner::NoColor(NoColor(IoStandardStream::new(sty))) - } - } -} - -impl io::Write for StandardStream { - fn write(&mut self, b: &[u8]) -> io::Result { self.wtr.write(b) } - fn flush(&mut self) -> io::Result<()> { self.wtr.flush() } -} - -impl WriteColor for StandardStream { - fn supports_color(&self) -> bool { self.wtr.supports_color() } - fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> { - self.wtr.set_color(spec) - } - fn reset(&mut self) -> io::Result<()> { self.wtr.reset() } - fn is_synchronous(&self) -> bool { self.wtr.is_synchronous() } -} - -impl<'a> io::Write for StandardStreamLock<'a> { - fn write(&mut self, b: &[u8]) -> io::Result { self.wtr.write(b) } - fn flush(&mut self) -> io::Result<()> { self.wtr.flush() } -} - -impl<'a> WriteColor for StandardStreamLock<'a> { - fn supports_color(&self) -> bool { self.wtr.supports_color() } - fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> { - self.wtr.set_color(spec) - } - fn reset(&mut self) -> io::Result<()> { self.wtr.reset() } - fn is_synchronous(&self) -> bool { self.wtr.is_synchronous() } -} - -impl io::Write for BufferedStandardStream { - fn write(&mut self, b: &[u8]) -> io::Result { self.wtr.write(b) } - fn flush(&mut self) -> io::Result<()> { self.wtr.flush() } -} - -impl WriteColor for BufferedStandardStream { - fn supports_color(&self) -> bool { self.wtr.supports_color() } - fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> { - if self.is_synchronous() { - self.wtr.flush()?; - } - self.wtr.set_color(spec) - } - fn reset(&mut self) -> io::Result<()> { self.wtr.reset() } - fn is_synchronous(&self) -> bool { self.wtr.is_synchronous() } -} - -impl io::Write for WriterInner { - fn write(&mut self, buf: &[u8]) -> io::Result { - match *self { - WriterInner::NoColor(ref mut wtr) => wtr.write(buf), - WriterInner::Ansi(ref mut wtr) => wtr.write(buf), - #[cfg(windows)] - WriterInner::Windows { ref mut wtr, .. } => wtr.write(buf), - } - } - - fn flush(&mut self) -> io::Result<()> { - match *self { - WriterInner::NoColor(ref mut wtr) => wtr.flush(), - WriterInner::Ansi(ref mut wtr) => wtr.flush(), - #[cfg(windows)] - WriterInner::Windows { ref mut wtr, .. } => wtr.flush(), - } - } -} - -impl WriteColor for WriterInner { - fn supports_color(&self) -> bool { - match *self { - WriterInner::NoColor(_) => false, - WriterInner::Ansi(_) => true, - #[cfg(windows)] - WriterInner::Windows { .. } => true, - } - } - - fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> { - match *self { - WriterInner::NoColor(ref mut wtr) => wtr.set_color(spec), - WriterInner::Ansi(ref mut wtr) => wtr.set_color(spec), - #[cfg(windows)] - WriterInner::Windows { ref mut wtr, ref console } => { - wtr.flush()?; - let mut console = console.lock().unwrap(); - spec.write_console(&mut *console) - } - } - } - - fn reset(&mut self) -> io::Result<()> { - match *self { - WriterInner::NoColor(ref mut wtr) => wtr.reset(), - WriterInner::Ansi(ref mut wtr) => wtr.reset(), - #[cfg(windows)] - WriterInner::Windows { ref mut wtr, ref mut console } => { - wtr.flush()?; - console.lock().unwrap().reset()?; - Ok(()) - } - } - } - - fn is_synchronous(&self) -> bool { - match *self { - WriterInner::NoColor(_) => false, - WriterInner::Ansi(_) => false, - #[cfg(windows)] - WriterInner::Windows {..} => true, - } - } -} - -impl<'a, W: io::Write> io::Write for WriterInnerLock<'a, W> { - fn write(&mut self, buf: &[u8]) -> io::Result { - match *self { - WriterInnerLock::Unreachable(_) => unreachable!(), - WriterInnerLock::NoColor(ref mut wtr) => wtr.write(buf), - WriterInnerLock::Ansi(ref mut wtr) => wtr.write(buf), - #[cfg(windows)] - WriterInnerLock::Windows { ref mut wtr, .. } => wtr.write(buf), - } - } - - fn flush(&mut self) -> io::Result<()> { - match *self { - WriterInnerLock::Unreachable(_) => unreachable!(), - WriterInnerLock::NoColor(ref mut wtr) => wtr.flush(), - WriterInnerLock::Ansi(ref mut wtr) => wtr.flush(), - #[cfg(windows)] - WriterInnerLock::Windows { ref mut wtr, .. } => wtr.flush(), - } - } -} - -impl<'a, W: io::Write> WriteColor for WriterInnerLock<'a, W> { - fn supports_color(&self) -> bool { - match *self { - WriterInnerLock::Unreachable(_) => unreachable!(), - WriterInnerLock::NoColor(_) => false, - WriterInnerLock::Ansi(_) => true, - #[cfg(windows)] - WriterInnerLock::Windows { .. } => true, - } - } - - fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> { - match *self { - WriterInnerLock::Unreachable(_) => unreachable!(), - WriterInnerLock::NoColor(ref mut wtr) => wtr.set_color(spec), - WriterInnerLock::Ansi(ref mut wtr) => wtr.set_color(spec), - #[cfg(windows)] - WriterInnerLock::Windows { ref mut wtr, ref mut console } => { - wtr.flush()?; - spec.write_console(console) - } - } - } - - fn reset(&mut self) -> io::Result<()> { - match *self { - WriterInnerLock::Unreachable(_) => unreachable!(), - WriterInnerLock::NoColor(ref mut wtr) => wtr.reset(), - WriterInnerLock::Ansi(ref mut wtr) => wtr.reset(), - #[cfg(windows)] - WriterInnerLock::Windows { ref mut wtr, ref mut console } => { - wtr.flush()?; - console.reset()?; - Ok(()) - } - } - } - - fn is_synchronous(&self) -> bool { - match *self { - WriterInnerLock::Unreachable(_) => unreachable!(), - WriterInnerLock::NoColor(_) => false, - WriterInnerLock::Ansi(_) => false, - #[cfg(windows)] - WriterInnerLock::Windows {..} => true, - } - } -} - -/// Writes colored buffers to stdout or stderr. -/// -/// Writable buffers can be obtained by calling `buffer` on a `BufferWriter`. -/// -/// This writer works with terminals that support ANSI escape sequences or -/// with a Windows console. -/// -/// It is intended for a `BufferWriter` to be put in an `Arc` and written to -/// from multiple threads simultaneously. -pub struct BufferWriter { - stream: LossyStandardStream, - printed: AtomicBool, - separator: Option>, - color_choice: ColorChoice, - #[cfg(windows)] - console: Option>, -} - -impl BufferWriter { - /// Create a new `BufferWriter` that writes to a standard stream with the - /// given color preferences. - /// - /// The specific color/style settings can be configured when writing to - /// the buffers themselves. - #[cfg(not(windows))] - fn create(sty: StandardStreamType, choice: ColorChoice) -> BufferWriter { - BufferWriter { - stream: LossyStandardStream::new(IoStandardStream::new(sty)), - printed: AtomicBool::new(false), - separator: None, - color_choice: choice, - } - } - - /// Create a new `BufferWriter` that writes to a standard stream with the - /// given color preferences. - /// - /// 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. - #[cfg(windows)] - fn create(sty: StandardStreamType, choice: ColorChoice) -> BufferWriter { - let mut con = match sty { - StandardStreamType::Stdout => wincolor::Console::stdout(), - StandardStreamType::Stderr => wincolor::Console::stderr(), - StandardStreamType::StdoutBuffered => wincolor::Console::stdout(), - StandardStreamType::StderrBuffered => wincolor::Console::stderr(), - }.ok(); - let is_console_virtual = con.as_mut().map(|con| { - con.set_virtual_terminal_processing(true).is_ok() - }).unwrap_or(false); - // If we can enable ANSI on Windows, then we don't need the console - // anymore. - if is_console_virtual { - con = None; - } - let stream = LossyStandardStream::new(IoStandardStream::new(sty)); - BufferWriter { - stream: stream, - printed: AtomicBool::new(false), - separator: None, - color_choice: choice, - console: con.map(Mutex::new), - } - } - - /// 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 - /// separator is printed. - /// - /// The default value is `None`. - pub fn separator(&mut self, sep: Option>) { - self.separator = sep; - } - - /// Creates a new `Buffer` with the current color preferences. - /// - /// A `Buffer` satisfies both `io::Write` and `WriteColor`. A `Buffer` can - /// be printed using the `print` method. - #[cfg(not(windows))] - pub fn buffer(&self) -> Buffer { - Buffer::new(self.color_choice) - } - - /// Creates a new `Buffer` with the current color preferences. - /// - /// A `Buffer` satisfies both `io::Write` and `WriteColor`. A `Buffer` can - /// be printed using the `print` method. - #[cfg(windows)] - pub fn buffer(&self) -> Buffer { - Buffer::new(self.color_choice, self.console.is_some()) - } - - /// Prints the contents of the given buffer. - /// - /// It is safe to call this from multiple threads simultaneously. In - /// particular, all buffers are written atomically. No interleaving will - /// occur. - pub fn print(&self, buf: &Buffer) -> io::Result<()> { - if buf.is_empty() { - return Ok(()); - } - let mut stream = self.stream.wrap(self.stream.get_ref().lock()); - if let Some(ref sep) = self.separator { - if self.printed.load(Ordering::SeqCst) { - stream.write_all(sep)?; - stream.write_all(b"\n")?; - } - } - match buf.0 { - BufferInner::NoColor(ref b) => stream.write_all(&b.0)?, - BufferInner::Ansi(ref b) => stream.write_all(&b.0)?, - #[cfg(windows)] - BufferInner::Windows(ref b) => { - // We guarantee by construction that we have a console here. - // Namely, a BufferWriter is the only way to produce a Buffer. - let console_mutex = self.console.as_ref() - .expect("got Windows buffer but have no Console"); - let mut console = console_mutex.lock().unwrap(); - b.print(&mut *console, &mut stream)?; - } - } - self.printed.store(true, Ordering::SeqCst); - Ok(()) - } -} - -/// Write colored text to memory. -/// -/// `Buffer` is a platform independent abstraction for printing colored text to -/// an in memory buffer. When the buffer is printed using a `BufferWriter`, the -/// color information will be applied to the output device (a tty on Unix and a -/// console on Windows). -/// -/// A `Buffer` is typically created by calling the `BufferWriter.buffer` -/// method, which will take color preferences and the environment into -/// account. However, buffers can also be manually created using `no_color`, -/// `ansi` or `console` (on Windows). -pub struct Buffer(BufferInner); - -/// BufferInner is an enumeration of different buffer types. -enum BufferInner { - /// No coloring information should be applied. This ignores all coloring - /// directives. - NoColor(NoColor>), - /// Apply coloring using ANSI escape sequences embedded into the buffer. - Ansi(Ansi>), - /// Apply coloring using the Windows console APIs. This buffer saves - /// color information in memory and only interacts with the console when - /// the buffer is printed. - #[cfg(windows)] - Windows(WindowsBuffer), -} - -impl Buffer { - /// Create a new buffer with the given color settings. - #[cfg(not(windows))] - fn new(choice: ColorChoice) -> Buffer { - if choice.should_attempt_color() { - Buffer::ansi() - } else { - Buffer::no_color() - } - } - - /// Create a new buffer with the given color settings. - /// - /// On Windows, one can elect to create a buffer capable of being written - /// to a console. Only enable it if a console is available. - /// - /// If coloring is desired and `console` is false, then ANSI escape - /// sequences are used instead. - #[cfg(windows)] - fn new(choice: ColorChoice, console: bool) -> Buffer { - if choice.should_attempt_color() { - if !console || choice.should_ansi() { - Buffer::ansi() - } else { - Buffer::console() - } - } else { - Buffer::no_color() - } - } - - /// Create a buffer that drops all color information. - pub fn no_color() -> Buffer { - Buffer(BufferInner::NoColor(NoColor(vec![]))) - } - - /// Create a buffer that uses ANSI escape sequences. - pub fn ansi() -> Buffer { - Buffer(BufferInner::Ansi(Ansi(vec![]))) - } - - /// Create a buffer that can be written to a Windows console. - #[cfg(windows)] - pub fn console() -> Buffer { - Buffer(BufferInner::Windows(WindowsBuffer::new())) - } - - /// Returns true if and only if this buffer is empty. - pub fn is_empty(&self) -> bool { - self.len() == 0 - } - - /// Returns the length of this buffer in bytes. - pub fn len(&self) -> usize { - match self.0 { - BufferInner::NoColor(ref b) => b.0.len(), - BufferInner::Ansi(ref b) => b.0.len(), - #[cfg(windows)] - BufferInner::Windows(ref b) => b.buf.len(), - } - } - - /// Clears this buffer. - pub fn clear(&mut self) { - match self.0 { - BufferInner::NoColor(ref mut b) => b.0.clear(), - BufferInner::Ansi(ref mut b) => b.0.clear(), - #[cfg(windows)] - BufferInner::Windows(ref mut b) => b.clear(), - } - } - - /// Consume this buffer and return the underlying raw data. - /// - /// On Windows, this unrecoverably drops all color information associated - /// with the buffer. - pub fn into_inner(self) -> Vec { - match self.0 { - BufferInner::NoColor(b) => b.0, - BufferInner::Ansi(b) => b.0, - #[cfg(windows)] - BufferInner::Windows(b) => b.buf, - } - } - - /// Return the underlying data of the buffer. - pub fn as_slice(&self) -> &[u8] { - match self.0 { - BufferInner::NoColor(ref b) => &b.0, - BufferInner::Ansi(ref b) => &b.0, - #[cfg(windows)] - BufferInner::Windows(ref b) => &b.buf, - } - } - - /// Return the underlying data of the buffer as a mutable slice. - pub fn as_mut_slice(&mut self) -> &mut [u8] { - match self.0 { - BufferInner::NoColor(ref mut b) => &mut b.0, - BufferInner::Ansi(ref mut b) => &mut b.0, - #[cfg(windows)] - BufferInner::Windows(ref mut b) => &mut b.buf, - } - } -} - -impl io::Write for Buffer { - fn write(&mut self, buf: &[u8]) -> io::Result { - match self.0 { - BufferInner::NoColor(ref mut w) => w.write(buf), - BufferInner::Ansi(ref mut w) => w.write(buf), - #[cfg(windows)] - BufferInner::Windows(ref mut w) => w.write(buf), - } - } - - fn flush(&mut self) -> io::Result<()> { - match self.0 { - BufferInner::NoColor(ref mut w) => w.flush(), - BufferInner::Ansi(ref mut w) => w.flush(), - #[cfg(windows)] - BufferInner::Windows(ref mut w) => w.flush(), - } - } -} - -impl WriteColor for Buffer { - fn supports_color(&self) -> bool { - match self.0 { - BufferInner::NoColor(_) => false, - BufferInner::Ansi(_) => true, - #[cfg(windows)] - BufferInner::Windows(_) => true, - } - } - - fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> { - match self.0 { - BufferInner::NoColor(ref mut w) => w.set_color(spec), - BufferInner::Ansi(ref mut w) => w.set_color(spec), - #[cfg(windows)] - BufferInner::Windows(ref mut w) => w.set_color(spec), - } - } - - fn reset(&mut self) -> io::Result<()> { - match self.0 { - BufferInner::NoColor(ref mut w) => w.reset(), - BufferInner::Ansi(ref mut w) => w.reset(), - #[cfg(windows)] - BufferInner::Windows(ref mut w) => w.reset(), - } - } - - fn is_synchronous(&self) -> bool { - false - } -} - -/// Satisfies `WriteColor` but ignores all color options. -pub struct NoColor(W); - -impl NoColor { - /// Create a new writer that satisfies `WriteColor` but drops all color - /// information. - pub fn new(wtr: W) -> NoColor { NoColor(wtr) } - - /// Consume this `NoColor` value and return the inner writer. - pub fn into_inner(self) -> W { self.0 } - - /// Return a reference to the inner writer. - pub fn get_ref(&self) -> &W { &self.0 } - - /// Return a mutable reference to the inner writer. - pub fn get_mut(&mut self) -> &mut W { &mut self.0 } -} - -impl io::Write for NoColor { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.0.write(buf) - } - - fn flush(&mut self) -> io::Result<()> { - self.0.flush() - } -} - -impl WriteColor for NoColor { - fn supports_color(&self) -> bool { false } - fn set_color(&mut self, _: &ColorSpec) -> io::Result<()> { Ok(()) } - fn reset(&mut self) -> io::Result<()> { Ok(()) } - fn is_synchronous(&self) -> bool { false } -} - -/// Satisfies `WriteColor` using standard ANSI escape sequences. -pub struct Ansi(W); - -impl Ansi { - /// Create a new writer that satisfies `WriteColor` using standard ANSI - /// escape sequences. - pub fn new(wtr: W) -> Ansi { Ansi(wtr) } - - /// Consume this `Ansi` value and return the inner writer. - pub fn into_inner(self) -> W { self.0 } - - /// Return a reference to the inner writer. - pub fn get_ref(&self) -> &W { &self.0 } - - /// Return a mutable reference to the inner writer. - pub fn get_mut(&mut self) -> &mut W { &mut self.0 } -} - -impl io::Write for Ansi { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.0.write(buf) - } - - fn flush(&mut self) -> io::Result<()> { - self.0.flush() - } -} - -impl WriteColor for Ansi { - fn supports_color(&self) -> bool { true } - - fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> { - self.reset()?; - if spec.bold { - self.write_str("\x1B[1m")?; - } - if spec.underline { - self.write_str("\x1B[4m")?; - } - if let Some(ref c) = spec.fg_color { - self.write_color(true, c, spec.intense)?; - } - if let Some(ref c) = spec.bg_color { - self.write_color(false, c, spec.intense)?; - } - Ok(()) - } - - fn reset(&mut self) -> io::Result<()> { - self.write_str("\x1B[0m") - } - - fn is_synchronous(&self) -> bool { false } -} - -impl Ansi { - fn write_str(&mut self, s: &str) -> io::Result<()> { - self.write_all(s.as_bytes()) - } - - fn write_color( - &mut self, - fg: bool, - c: &Color, - intense: bool, - ) -> io::Result<()> { - macro_rules! write_intense { - ($clr:expr) => { - if fg { - self.write_str(concat!("\x1B[38;5;", $clr, "m")) - } else { - self.write_str(concat!("\x1B[48;5;", $clr, "m")) - } - } - } - macro_rules! write_normal { - ($clr:expr) => { - if fg { - self.write_str(concat!("\x1B[3", $clr, "m")) - } else { - self.write_str(concat!("\x1B[4", $clr, "m")) - } - } - } - macro_rules! write_var_ansi_code { - ($pre:expr, $($code:expr),+) => {{ - // The loop generates at worst a literal of the form - // '255,255,255m' which is 12-bytes. - // The largest `pre` expression we currently use is 7 bytes. - // This gives us the maximum of 19-bytes for our work buffer. - let pre_len = $pre.len(); - assert!(pre_len <= 7); - let mut fmt = [0u8; 19]; - fmt[..pre_len].copy_from_slice($pre); - let mut i = pre_len - 1; - $( - let c1: u8 = ($code / 100) % 10; - let c2: u8 = ($code / 10) % 10; - let c3: u8 = $code % 10; - let mut printed = false; - - if c1 != 0 { - printed = true; - i += 1; - fmt[i] = b'0' + c1; - } - if c2 != 0 || printed { - i += 1; - fmt[i] = b'0' + c2; - } - // If we received a zero value we must still print a value. - i += 1; - fmt[i] = b'0' + c3; - i += 1; - fmt[i] = b';'; - )+ - - fmt[i] = b'm'; - self.write_all(&fmt[0..i+1]) - }} - } - macro_rules! write_custom { - ($ansi256:expr) => { - if fg { - write_var_ansi_code!(b"\x1B[38;5;", $ansi256) - } else { - write_var_ansi_code!(b"\x1B[48;5;", $ansi256) - } - }; - - ($r:expr, $g:expr, $b:expr) => {{ - if fg { - write_var_ansi_code!(b"\x1B[38;2;", $r, $g, $b) - } else { - write_var_ansi_code!(b"\x1B[48;2;", $r, $g, $b) - } - }}; - } - if intense { - match *c { - Color::Black => write_intense!("8"), - Color::Blue => write_intense!("12"), - Color::Green => write_intense!("10"), - Color::Red => write_intense!("9"), - Color::Cyan => write_intense!("14"), - Color::Magenta => write_intense!("13"), - Color::Yellow => write_intense!("11"), - Color::White => write_intense!("15"), - Color::Ansi256(c) => write_custom!(c), - Color::Rgb(r, g, b) => write_custom!(r, g, b), - Color::__Nonexhaustive => unreachable!(), - } - } else { - match *c { - Color::Black => write_normal!("0"), - Color::Blue => write_normal!("4"), - Color::Green => write_normal!("2"), - Color::Red => write_normal!("1"), - Color::Cyan => write_normal!("6"), - Color::Magenta => write_normal!("5"), - Color::Yellow => write_normal!("3"), - Color::White => write_normal!("7"), - Color::Ansi256(c) => write_custom!(c), - Color::Rgb(r, g, b) => write_custom!(r, g, b), - Color::__Nonexhaustive => unreachable!(), - } - } - } -} - -/// An in-memory buffer that provides Windows console coloring. -/// -/// This doesn't actually communicate with the Windows console. Instead, it -/// acts like a normal buffer but also saves the color information associated -/// with positions in the buffer. It is only when the buffer is written to the -/// console that coloring is actually applied. -/// -/// This is roughly isomorphic to the ANSI based approach (i.e., -/// `Ansi>`), except with ANSI, the color information is embedded -/// directly into the buffer. -/// -/// Note that there is no way to write something generic like -/// `WindowsConsole` since coloring on Windows is tied -/// specifically to the console APIs, and therefore can't work on arbitrary -/// writers. -#[cfg(windows)] -#[derive(Clone, Debug)] -struct WindowsBuffer { - /// The actual content that should be printed. - buf: Vec, - /// A sequence of position oriented color specifications. Namely, each - /// element is a position and a color spec, where the color spec should - /// be applied at the position inside of `buf`. - /// - /// A missing color spec implies the underlying console should be reset. - colors: Vec<(usize, Option)>, -} - -#[cfg(windows)] -impl WindowsBuffer { - /// Create a new empty buffer for Windows console coloring. - fn new() -> WindowsBuffer { - WindowsBuffer { - buf: vec![], - colors: vec![], - } - } - - /// Push the given color specification into this buffer. - /// - /// This has the effect of setting the given color information at the - /// current position in the buffer. - fn push(&mut self, spec: Option) { - let pos = self.buf.len(); - self.colors.push((pos, spec)); - } - - /// Print the contents to the given stream handle, and use the console - /// for coloring. - fn print( - &self, - console: &mut wincolor::Console, - stream: &mut LossyStandardStream, - ) -> io::Result<()> { - let mut last = 0; - for &(pos, ref spec) in &self.colors { - stream.write_all(&self.buf[last..pos])?; - stream.flush()?; - last = pos; - match *spec { - None => console.reset()?, - Some(ref spec) => spec.write_console(console)?, - } - } - stream.write_all(&self.buf[last..])?; - stream.flush() - } - - /// Clear the buffer. - fn clear(&mut self) { - self.buf.clear(); - self.colors.clear(); - } -} - -#[cfg(windows)] -impl io::Write for WindowsBuffer { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.buf.extend_from_slice(buf); - Ok(buf.len()) - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -#[cfg(windows)] -impl WriteColor for WindowsBuffer { - fn supports_color(&self) -> bool { true } - - fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> { - self.push(Some(spec.clone())); - Ok(()) - } - - fn reset(&mut self) -> io::Result<()> { - self.push(None); - Ok(()) - } - - fn is_synchronous(&self) -> bool { - false - } -} - -/// A color specification. -#[derive(Clone, Debug, Default, Eq, PartialEq)] -pub struct ColorSpec { - fg_color: Option, - bg_color: Option, - bold: bool, - intense: bool, - underline: bool, -} - -impl ColorSpec { - /// Create a new color specification that has no colors or styles. - pub fn new() -> ColorSpec { - ColorSpec::default() - } - - /// Get the foreground color. - pub fn fg(&self) -> Option<&Color> { self.fg_color.as_ref() } - - /// Set the foreground color. - pub fn set_fg(&mut self, color: Option) -> &mut ColorSpec { - self.fg_color = color; - self - } - - /// Get the background color. - pub fn bg(&self) -> Option<&Color> { self.bg_color.as_ref() } - - /// Set the background color. - pub fn set_bg(&mut self, color: Option) -> &mut ColorSpec { - self.bg_color = color; - self - } - - /// Get whether this is bold or not. - /// - /// Note that the bold setting has no effect in a Windows console. - pub fn bold(&self) -> bool { self.bold } - - /// Set whether the text is bolded or not. - /// - /// Note that the bold setting has no effect in a Windows console. - pub fn set_bold(&mut self, yes: bool) -> &mut ColorSpec { - self.bold = yes; - self - } - - /// Get whether this is underline or not. - /// - /// Note that the underline setting has no effect in a Windows console. - pub fn underline(&self) -> bool { self.underline } - - /// Set whether the text is underlined or not. - /// - /// Note that the underline setting has no effect in a Windows console. - pub fn set_underline(&mut self, yes: bool) -> &mut ColorSpec { - self.underline = yes; - self - } - - /// Get whether this is intense or not. - /// - /// On Unix-like systems, this will output the ANSI escape sequence - /// that will print a high-intensity version of the color - /// specified. - /// - /// On Windows systems, this will output the ANSI escape sequence - /// that will print a brighter version of the color specified. - pub fn intense(&self) -> bool { self.intense } - - /// Set whether the text is intense or not. - /// - /// On Unix-like systems, this will output the ANSI escape sequence - /// that will print a high-intensity version of the color - /// specified. - /// - /// On Windows systems, this will output the ANSI escape sequence - /// that will print a brighter version of the color specified. - pub fn set_intense(&mut self, yes: bool) -> &mut ColorSpec { - self.intense = yes; - self - } - - /// Returns true if this color specification has no colors or styles. - pub fn is_none(&self) -> bool { - self.fg_color.is_none() && self.bg_color.is_none() - && !self.bold && !self.underline - } - - /// Clears this color specification so that it has no color/style settings. - pub fn clear(&mut self) { - self.fg_color = None; - self.bg_color = None; - self.bold = false; - self.underline = false; - } - - /// Writes this color spec to the given Windows console. - #[cfg(windows)] - fn write_console( - &self, - console: &mut wincolor::Console, - ) -> io::Result<()> { - use wincolor::Intense; - - let intense = if self.intense { Intense::Yes } else { Intense::No }; - - let fg_color = self.fg_color.as_ref().and_then(|c| c.to_windows()); - if let Some(color) = fg_color { - console.fg(intense, color)?; - } - - let bg_color = self.bg_color.as_ref().and_then(|c| c.to_windows()); - if let Some(color) = bg_color { - console.bg(intense, color)?; - } - Ok(()) - } -} - -/// The set of available colors for the terminal foreground/background. -/// -/// The `Ansi256` and `Rgb` colors will only output the correct codes when -/// paired with the `Ansi` `WriteColor` implementation. -/// -/// The `Ansi256` and `Rgb` color types are not supported when writing colors -/// on Windows using the console. If they are used on Windows, then they are -/// silently ignored and no colors will be emitted. -/// -/// This set may expand over time. -/// -/// This type has a `FromStr` impl that can parse colors from their human -/// readable form. The format is as follows: -/// -/// 1. Any of the explicitly listed colors in English. They are matched -/// case insensitively. -/// 2. A single 8-bit integer, in either decimal or hexadecimal format. -/// 3. A triple of 8-bit integers separated by a comma, where each integer is -/// in decimal or hexadecimal format. -/// -/// Hexadecimal numbers are written with a `0x` prefix. -#[allow(missing_docs)] -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum Color { - Black, - Blue, - Green, - Red, - Cyan, - Magenta, - Yellow, - White, - Ansi256(u8), - Rgb(u8, u8, u8), - #[doc(hidden)] - __Nonexhaustive, -} - -impl Color { - /// Translate this color to a wincolor::Color. - #[cfg(windows)] - fn to_windows(&self) -> Option { - match *self { - Color::Black => Some(wincolor::Color::Black), - Color::Blue => Some(wincolor::Color::Blue), - Color::Green => Some(wincolor::Color::Green), - Color::Red => Some(wincolor::Color::Red), - Color::Cyan => Some(wincolor::Color::Cyan), - Color::Magenta => Some(wincolor::Color::Magenta), - Color::Yellow => Some(wincolor::Color::Yellow), - Color::White => Some(wincolor::Color::White), - Color::Ansi256(_) => None, - Color::Rgb(_, _, _) => None, - Color::__Nonexhaustive => unreachable!(), - } - } - - /// Parses a numeric color string, either ANSI or RGB. - fn from_str_numeric(s: &str) -> Result { - // The "ansi256" format is a single number (decimal or hex) - // corresponding to one of 256 colors. - // - // The "rgb" format is a triple of numbers (decimal or hex) delimited - // by a comma corresponding to one of 256^3 colors. - - fn parse_number(s: &str) -> Option { - use std::u8; - - if s.starts_with("0x") { - u8::from_str_radix(&s[2..], 16).ok() - } else { - u8::from_str_radix(s, 10).ok() - } - } - - let codes: Vec<&str> = s.split(',').collect(); - if codes.len() == 1 { - if let Some(n) = parse_number(&codes[0]) { - Ok(Color::Ansi256(n)) - } else { - if s.chars().all(|c| c.is_digit(16)) { - Err(ParseColorError { - kind: ParseColorErrorKind::InvalidAnsi256, - given: s.to_string(), - }) - } else { - Err(ParseColorError { - kind: ParseColorErrorKind::InvalidName, - given: s.to_string(), - }) - } - } - } else if codes.len() == 3 { - let mut v = vec![]; - for code in codes { - let n = parse_number(code).ok_or_else(|| { - ParseColorError { - kind: ParseColorErrorKind::InvalidRgb, - given: s.to_string(), - } - })?; - v.push(n); - } - Ok(Color::Rgb(v[0], v[1], v[2])) - } else { - Err(if s.contains(",") { - ParseColorError { - kind: ParseColorErrorKind::InvalidRgb, - given: s.to_string(), - } - } else { - ParseColorError { - kind: ParseColorErrorKind::InvalidName, - given: s.to_string(), - } - }) - } - } -} - -/// An error from parsing an invalid color specification. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct ParseColorError { - kind: ParseColorErrorKind, - given: String, -} - -#[derive(Clone, Debug, Eq, PartialEq)] -enum ParseColorErrorKind { - InvalidName, - InvalidAnsi256, - InvalidRgb, -} - -impl ParseColorError { - /// Return the string that couldn't be parsed as a valid color. - pub fn invalid(&self) -> &str { &self.given } -} - -impl error::Error for ParseColorError { - fn description(&self) -> &str { - use self::ParseColorErrorKind::*; - match self.kind { - InvalidName => "unrecognized color name", - InvalidAnsi256 => "invalid ansi256 color number", - InvalidRgb => "invalid RGB color triple", - } - } -} - -impl fmt::Display for ParseColorError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use self::ParseColorErrorKind::*; - match self.kind { - InvalidName => { - write!(f, "unrecognized color name '{}'. Choose from: \ - black, blue, green, red, cyan, magenta, yellow, \ - white", - self.given) - } - InvalidAnsi256 => { - write!(f, "unrecognized ansi256 color number, \ - should be '[0-255]' (or a hex number), but is '{}'", - self.given) - } - InvalidRgb => { - write!(f, "unrecognized RGB color triple, \ - should be '[0-255],[0-255],[0-255]' (or a hex \ - triple), but is '{}'", self.given) - } - } - } -} - -impl FromStr for Color { - type Err = ParseColorError; - - fn from_str(s: &str) -> Result { - match &*s.to_lowercase() { - "black" => Ok(Color::Black), - "blue" => Ok(Color::Blue), - "green" => Ok(Color::Green), - "red" => Ok(Color::Red), - "cyan" => Ok(Color::Cyan), - "magenta" => Ok(Color::Magenta), - "yellow" => Ok(Color::Yellow), - "white" => Ok(Color::White), - _ => Color::from_str_numeric(s), - } - } -} - -struct LossyStandardStream { - wtr: W, - #[cfg(windows)] - is_console: bool, -} - -impl LossyStandardStream { - #[cfg(not(windows))] - fn new(wtr: W) -> LossyStandardStream { - LossyStandardStream { wtr: wtr } - } - - #[cfg(windows)] - fn new(wtr: W) -> LossyStandardStream { - let is_console = - wincolor::Console::stdout().is_ok() - || wincolor::Console::stderr().is_ok(); - LossyStandardStream { wtr: wtr, is_console: is_console } - } - - #[cfg(not(windows))] - fn wrap(&self, wtr: Q) -> LossyStandardStream { - LossyStandardStream::new(wtr) - } - - #[cfg(windows)] - fn wrap(&self, wtr: Q) -> LossyStandardStream { - LossyStandardStream { wtr: wtr, is_console: self.is_console } - } - - fn get_ref(&self) -> &W { - &self.wtr - } -} - -impl WriteColor for LossyStandardStream { - fn supports_color(&self) -> bool { self.wtr.supports_color() } - fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> { - self.wtr.set_color(spec) - } - fn reset(&mut self) -> io::Result<()> { self.wtr.reset() } - fn is_synchronous(&self) -> bool { self.wtr.is_synchronous() } -} - -impl io::Write for LossyStandardStream { - #[cfg(not(windows))] - fn write(&mut self, buf: &[u8]) -> io::Result { - self.wtr.write(buf) - } - - #[cfg(windows)] - fn write(&mut self, buf: &[u8]) -> io::Result { - if self.is_console { - write_lossy_utf8(&mut self.wtr, buf) - } else { - self.wtr.write(buf) - } - } - - fn flush(&mut self) -> io::Result<()> { - self.wtr.flush() - } -} - -#[cfg(windows)] -fn write_lossy_utf8(mut w: W, buf: &[u8]) -> io::Result { - match ::std::str::from_utf8(buf) { - Ok(s) => w.write(s.as_bytes()), - Err(ref e) if e.valid_up_to() == 0 => { - w.write(b"\xEF\xBF\xBD")?; - Ok(1) - } - Err(e) => w.write(&buf[..e.valid_up_to()]), - } -} - -#[cfg(test)] -mod tests { - use super::{ - Ansi, Color, ParseColorError, ParseColorErrorKind, StandardStream, - }; - - fn assert_is_send() {} - - #[test] - fn standard_stream_is_send() { - assert_is_send::(); - } - - #[test] - fn test_simple_parse_ok() { - let color = "green".parse::(); - assert_eq!(color, Ok(Color::Green)); - } - - #[test] - fn test_256_parse_ok() { - let color = "7".parse::(); - assert_eq!(color, Ok(Color::Ansi256(7))); - - let color = "32".parse::(); - assert_eq!(color, Ok(Color::Ansi256(32))); - - let color = "0xFF".parse::(); - assert_eq!(color, Ok(Color::Ansi256(0xFF))); - } - - #[test] - fn test_256_parse_err_out_of_range() { - let color = "256".parse::(); - assert_eq!(color, Err(ParseColorError { - kind: ParseColorErrorKind::InvalidAnsi256, - given: "256".to_string(), - })); - } - - #[test] - fn test_rgb_parse_ok() { - let color = "0,0,0".parse::(); - assert_eq!(color, Ok(Color::Rgb(0, 0, 0))); - - let color = "0,128,255".parse::(); - assert_eq!(color, Ok(Color::Rgb(0, 128, 255))); - - let color = "0x0,0x0,0x0".parse::(); - assert_eq!(color, Ok(Color::Rgb(0, 0, 0))); - - let color = "0x33,0x66,0xFF".parse::(); - assert_eq!(color, Ok(Color::Rgb(0x33, 0x66, 0xFF))); - } - - #[test] - fn test_rgb_parse_err_out_of_range() { - let color = "0,0,256".parse::(); - assert_eq!(color, Err(ParseColorError { - kind: ParseColorErrorKind::InvalidRgb, - given: "0,0,256".to_string(), - })); - } - - #[test] - fn test_rgb_parse_err_bad_format() { - let color = "0,0".parse::(); - assert_eq!(color, Err(ParseColorError { - kind: ParseColorErrorKind::InvalidRgb, - given: "0,0".to_string(), - })); - - let color = "not_a_color".parse::(); - assert_eq!(color, Err(ParseColorError { - kind: ParseColorErrorKind::InvalidName, - given: "not_a_color".to_string(), - })); - } - - #[test] - fn test_var_ansi_write_rgb() { - let mut buf = Ansi::new(vec![]); - let _ = buf.write_color(true, &Color::Rgb(254, 253, 255), false); - assert_eq!(buf.0, b"\x1B[38;2;254;253;255m"); - } - - #[test] - fn test_var_ansi_write_256() { - let mut buf = Ansi::new(vec![]); - let _ = buf.write_color(false, &Color::Ansi256(7), false); - assert_eq!(buf.0, b"\x1B[48;5;7m"); - - let mut buf = Ansi::new(vec![]); - let _ = buf.write_color(false, &Color::Ansi256(208), false); - assert_eq!(buf.0, b"\x1B[48;5;208m"); - } -} diff --git a/wincolor/COPYING b/wincolor/COPYING deleted file mode 100644 index bb9c20a0..00000000 --- a/wincolor/COPYING +++ /dev/null @@ -1,3 +0,0 @@ -This project is dual-licensed under the Unlicense and MIT licenses. - -You may use this code under the terms of either license. diff --git a/wincolor/Cargo.toml b/wincolor/Cargo.toml deleted file mode 100644 index 7c811755..00000000 --- a/wincolor/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -name = "wincolor" -version = "0.1.6" #:version -authors = ["Andrew Gallant "] -description = """ -A simple Windows specific API for controlling text color in a Windows console. -""" -documentation = "https://docs.rs/wincolor" -homepage = "https://github.com/BurntSushi/ripgrep/tree/master/wincolor" -repository = "https://github.com/BurntSushi/ripgrep/tree/master/wincolor" -readme = "README.md" -keywords = ["windows", "win", "color", "ansi", "console"] -license = "Unlicense/MIT" - -[lib] -name = "wincolor" -bench = false - -[dependencies.winapi] -version = "0.3" -features = ["consoleapi", "minwindef", "processenv", "winbase", "wincon"] diff --git a/wincolor/LICENSE-MIT b/wincolor/LICENSE-MIT deleted file mode 100644 index 3b0a5dc0..00000000 --- a/wincolor/LICENSE-MIT +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 Andrew Gallant - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/wincolor/README.md b/wincolor/README.md index cc780340..b6c4f40a 100644 --- a/wincolor/README.md +++ b/wincolor/README.md @@ -1,44 +1,2 @@ -wincolor -======== -A simple Windows specific API for controlling text color in a Windows console. -The purpose of this crate is to expose the full inflexibility of the Windows -console without any platform independent abstraction. - -[![Windows build status](https://ci.appveyor.com/api/projects/status/github/BurntSushi/ripgrep?svg=true)](https://ci.appveyor.com/project/BurntSushi/ripgrep) -[![](https://img.shields.io/crates/v/wincolor.svg)](https://crates.io/crates/wincolor) - -Dual-licensed under MIT or the [UNLICENSE](http://unlicense.org). - -### Documentation - -[https://docs.rs/wincolor](https://docs.rs/wincolor) - -### Usage - -Add this to your `Cargo.toml`: - -```toml -[dependencies] -wincolor = "0.1" -``` - -and this to your crate root: - -```rust -extern crate wincolor; -``` - -### Example - -This is a simple example that shows how to write text with a foreground color -of cyan and the intense attribute set: - -```rust -use wincolor::{Console, Color, Intense}; - -let mut con = Console::stdout().unwrap(); -con.fg(Intense::Yes, Color::Cyan).unwrap(); -println!("This text will be intense cyan."); -con.reset().unwrap(); -println!("This text will be normal."); -``` +wincolor has moved to the termcolor repository: +https://github.com/BurntSushi/termcolor diff --git a/wincolor/UNLICENSE b/wincolor/UNLICENSE deleted file mode 100644 index 68a49daa..00000000 --- a/wincolor/UNLICENSE +++ /dev/null @@ -1,24 +0,0 @@ -This is free and unencumbered software released into the public domain. - -Anyone is free to copy, modify, publish, use, compile, sell, or -distribute this software, either in source code form or as a compiled -binary, for any purpose, commercial or non-commercial, and by any -means. - -In jurisdictions that recognize copyright laws, the author or authors -of this software dedicate any and all copyright interest in the -software to the public domain. We make this dedication for the benefit -of the public at large and to the detriment of our heirs and -successors. We intend this dedication to be an overt act of -relinquishment in perpetuity of all present and future rights to this -software under copyright law. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR -OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. - -For more information, please refer to diff --git a/wincolor/src/lib.rs b/wincolor/src/lib.rs deleted file mode 100644 index 722cbd53..00000000 --- a/wincolor/src/lib.rs +++ /dev/null @@ -1,33 +0,0 @@ -/*! -This crate provides a safe and simple Windows specific API to control -text attributes in the Windows console. Text attributes are limited to -foreground/background colors, as well as whether to make colors intense or not. - -Note that on non-Windows platforms, this crate is empty but will compile. - -# Example - -```no_run -# #[cfg(windows)] -# { -use wincolor::{Console, Color, Intense}; - -let mut con = Console::stdout().unwrap(); -con.fg(Intense::Yes, Color::Cyan).unwrap(); -println!("This text will be intense cyan."); -con.reset().unwrap(); -println!("This text will be normal."); -# } -``` -*/ - -#![deny(missing_docs)] - -#[cfg(windows)] -extern crate winapi; - -#[cfg(windows)] -pub use win::*; - -#[cfg(windows)] -mod win; diff --git a/wincolor/src/win.rs b/wincolor/src/win.rs deleted file mode 100644 index 9542704c..00000000 --- a/wincolor/src/win.rs +++ /dev/null @@ -1,266 +0,0 @@ -use std::io; -use std::mem; - -use winapi::shared::minwindef::{DWORD, WORD}; -use winapi::um::consoleapi; -use winapi::um::processenv; -use winapi::um::winbase::{STD_ERROR_HANDLE, STD_OUTPUT_HANDLE}; -use winapi::um::wincon::{ - self, - FOREGROUND_BLUE as FG_BLUE, - FOREGROUND_GREEN as FG_GREEN, - FOREGROUND_RED as FG_RED, - FOREGROUND_INTENSITY as FG_INTENSITY, -}; - -const FG_CYAN: WORD = FG_BLUE | FG_GREEN; -const FG_MAGENTA: WORD = FG_BLUE | FG_RED; -const FG_YELLOW: WORD = FG_GREEN | FG_RED; -const FG_WHITE: WORD = FG_BLUE | FG_GREEN | FG_RED; - -/// A Windows console. -/// -/// This represents a very limited set of functionality available to a Windows -/// console. In particular, it can only change text attributes such as color -/// and intensity. -/// -/// There is no way to "write" to this console. Simply write to -/// stdout or stderr instead, while interleaving instructions to the console -/// to change text attributes. -/// -/// A common pitfall when using a console is to forget to flush writes to -/// stdout before setting new text attributes. -#[derive(Debug)] -pub struct Console { - handle_id: DWORD, - start_attr: TextAttributes, - cur_attr: TextAttributes, -} - -impl Console { - /// Get a console for a standard I/O stream. - fn create_for_stream(handle_id: DWORD) -> io::Result { - let mut info = unsafe { mem::zeroed() }; - let res = unsafe { - let handle = processenv::GetStdHandle(handle_id); - wincon::GetConsoleScreenBufferInfo(handle, &mut info) - }; - if res == 0 { - return Err(io::Error::last_os_error()); - } - let attr = TextAttributes::from_word(info.wAttributes); - Ok(Console { - handle_id: handle_id, - start_attr: attr, - cur_attr: attr, - }) - } - - /// Create a new Console to stdout. - /// - /// If there was a problem creating the console, then an error is returned. - pub fn stdout() -> io::Result { - Self::create_for_stream(STD_OUTPUT_HANDLE) - } - - /// Create a new Console to stderr. - /// - /// If there was a problem creating the console, then an error is returned. - pub fn stderr() -> io::Result { - Self::create_for_stream(STD_ERROR_HANDLE) - } - - /// Applies the current text attributes. - fn set(&mut self) -> io::Result<()> { - let attr = self.cur_attr.to_word(); - let res = unsafe { - let handle = processenv::GetStdHandle(self.handle_id); - wincon::SetConsoleTextAttribute(handle, attr) - }; - if res == 0 { - return Err(io::Error::last_os_error()); - } - Ok(()) - } - - /// Apply the given intensity and color attributes to the console - /// foreground. - /// - /// If there was a problem setting attributes on the console, then an error - /// is returned. - pub fn fg(&mut self, intense: Intense, color: Color) -> io::Result<()> { - self.cur_attr.fg_color = color; - self.cur_attr.fg_intense = intense; - self.set() - } - - /// Apply the given intensity and color attributes to the console - /// background. - /// - /// If there was a problem setting attributes on the console, then an error - /// is returned. - pub fn bg(&mut self, intense: Intense, color: Color) -> io::Result<()> { - self.cur_attr.bg_color = color; - self.cur_attr.bg_intense = intense; - self.set() - } - - /// Reset the console text attributes to their original settings. - /// - /// The original settings correspond to the text attributes on the console - /// when this `Console` value was created. - /// - /// If there was a problem setting attributes on the console, then an error - /// is returned. - pub fn reset(&mut self) -> io::Result<()> { - self.cur_attr = self.start_attr; - self.set() - } - - /// Toggle virtual terminal processing. - /// - /// This method attempts to toggle virtual terminal processing for this - /// console. If there was a problem toggling it, then an error returned. - /// On success, the caller may assume that toggling it was successful. - /// - /// When virtual terminal processing is enabled, characters emitted to the - /// console are parsed for VT100 and similar control character sequences - /// that control color and other similar operations. - pub fn set_virtual_terminal_processing( - &mut self, - yes: bool, - ) -> io::Result<()> { - let vt = wincon::ENABLE_VIRTUAL_TERMINAL_PROCESSING; - - let mut old_mode = 0; - let handle = unsafe { processenv::GetStdHandle(self.handle_id) }; - if unsafe { consoleapi::GetConsoleMode(handle, &mut old_mode) } == 0 { - return Err(io::Error::last_os_error()); - } - let new_mode = - if yes { - old_mode | vt - } else { - old_mode & !vt - }; - if old_mode == new_mode { - return Ok(()); - } - if unsafe { consoleapi::SetConsoleMode(handle, new_mode) } == 0 { - return Err(io::Error::last_os_error()); - } - Ok(()) - } -} - -/// A representation of text attributes for the Windows console. -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -struct TextAttributes { - fg_color: Color, - fg_intense: Intense, - bg_color: Color, - bg_intense: Intense, -} - -impl TextAttributes { - fn to_word(&self) -> WORD { - let mut w = 0; - w |= self.fg_color.to_fg(); - w |= self.fg_intense.to_fg(); - w |= self.bg_color.to_bg(); - w |= self.bg_intense.to_bg(); - w - } - - fn from_word(word: WORD) -> TextAttributes { - TextAttributes { - fg_color: Color::from_fg(word), - fg_intense: Intense::from_fg(word), - bg_color: Color::from_bg(word), - bg_intense: Intense::from_bg(word), - } - } -} - -/// Whether to use intense colors or not. -#[allow(missing_docs)] -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum Intense { - Yes, - No, -} - -impl Intense { - fn to_bg(&self) -> WORD { - self.to_fg() << 4 - } - - fn from_bg(word: WORD) -> Intense { - Intense::from_fg(word >> 4) - } - - fn to_fg(&self) -> WORD { - match *self { - Intense::No => 0, - Intense::Yes => FG_INTENSITY, - } - } - - fn from_fg(word: WORD) -> Intense { - if word & FG_INTENSITY > 0 { - Intense::Yes - } else { - Intense::No - } - } -} - -/// The set of available colors for use with a Windows console. -#[allow(missing_docs)] -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum Color { - Black, - Blue, - Green, - Red, - Cyan, - Magenta, - Yellow, - White, -} - -impl Color { - fn to_bg(&self) -> WORD { - self.to_fg() << 4 - } - - fn from_bg(word: WORD) -> Color { - Color::from_fg(word >> 4) - } - - fn to_fg(&self) -> WORD { - match *self { - Color::Black => 0, - Color::Blue => FG_BLUE, - Color::Green => FG_GREEN, - Color::Red => FG_RED, - Color::Cyan => FG_CYAN, - Color::Magenta => FG_MAGENTA, - Color::Yellow => FG_YELLOW, - Color::White => FG_WHITE, - } - } - - fn from_fg(word: WORD) -> Color { - match word & 0b111 { - FG_BLUE => Color::Blue, - FG_GREEN => Color::Green, - FG_RED => Color::Red, - FG_CYAN => Color::Cyan, - FG_MAGENTA => Color::Magenta, - FG_YELLOW => Color::Yellow, - FG_WHITE => Color::White, - _ => Color::Black, - } - } -}