#![deny(missing_docs)]
use std::env;
use std::error;
use std::fmt;
use std::io::{self, Write};
use std::str::FromStr;
use std::sync::atomic::{AtomicBool, Ordering};
#[cfg(windows)]
use std::sync::{Mutex, MutexGuard};
#[cfg(windows)]
use winapi_util::console as wincon;
pub trait WriteColor: io::Write {
fn supports_color(&self) -> bool;
fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()>;
fn reset(&mut self) -> io::Result<()>;
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<T: ?Sized + WriteColor> WriteColor for Box<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()
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ColorChoice {
Always,
AlwaysAnsi,
Auto,
Never,
}
impl ColorChoice {
fn should_attempt_color(&self) -> bool {
match *self {
ColorChoice::Always => true,
ColorChoice::AlwaysAnsi => true,
ColorChoice::Never => false,
ColorChoice::Auto => self.env_allows_color(),
}
}
#[cfg(not(windows))]
fn env_allows_color(&self) -> bool {
match env::var_os("TERM") {
None => return false,
Some(k) => {
if k == "dumb" {
return false;
}
}
}
if env::var_os("NO_COLOR").is_some() {
return false;
}
true
}
#[cfg(windows)]
fn env_allows_color(&self) -> bool {
if let Some(k) = env::var_os("TERM") {
if k == "dumb" {
return false;
}
}
if env::var_os("NO_COLOR").is_some() {
return false;
}
true
}
#[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,
Ok(k) => k != "dumb" && k != "cygwin",
}
}
}
}
}
enum StandardStreamType {
Stdout,
Stderr,
StdoutBuffered,
StderrBuffered,
}
enum IoStandardStream {
Stdout(io::Stdout),
Stderr(io::Stderr),
StdoutBuffered(io::BufWriter<io::Stdout>),
StderrBuffered(io::BufWriter<io::Stderr>),
}
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(_) => {
panic!("cannot lock a buffered standard stream")
}
}
}
}
impl io::Write for IoStandardStream {
#[inline(always)]
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),
IoStandardStream::StdoutBuffered(ref mut s) => s.write(b),
IoStandardStream::StderrBuffered(ref mut s) => s.write(b),
}
}
#[inline(always)]
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(),
}
}
}
enum IoStandardStreamLock<'a> {
StdoutLock(io::StdoutLock<'a>),
StderrLock(io::StderrLock<'a>),
}
impl<'a> io::Write for IoStandardStreamLock<'a> {
#[inline(always)]
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),
}
}
#[inline(always)]
fn flush(&mut self) -> io::Result<()> {
match *self {
IoStandardStreamLock::StdoutLock(ref mut s) => s.flush(),
IoStandardStreamLock::StderrLock(ref mut s) => s.flush(),
}
}
}
pub struct StandardStream {
wtr: LossyStandardStream<WriterInner<IoStandardStream>>,
}
pub struct StandardStreamLock<'a> {
wtr: LossyStandardStream<WriterInnerLock<'a, IoStandardStreamLock<'a>>>,
}
pub struct BufferedStandardStream {
wtr: LossyStandardStream<WriterInner<IoStandardStream>>,
}
enum WriterInner<W> {
NoColor(NoColor<W>),
Ansi(Ansi<W>),
#[cfg(windows)]
Windows {
wtr: W,
console: Mutex<wincon::Console>,
},
}
enum WriterInnerLock<'a, W> {
NoColor(NoColor<W>),
Ansi(Ansi<W>),
#[allow(dead_code)]
Unreachable(::std::marker::PhantomData<&'a ()>),
#[cfg(windows)]
Windows {
wtr: W,
console: MutexGuard<'a, wincon::Console>,
},
}
impl StandardStream {
pub fn stdout(choice: ColorChoice) -> StandardStream {
let wtr = WriterInner::create(StandardStreamType::Stdout, choice);
StandardStream { wtr: LossyStandardStream::new(wtr) }
}
pub fn stderr(choice: ColorChoice) -> StandardStream {
let wtr = WriterInner::create(StandardStreamType::Stderr, choice);
StandardStream { wtr: LossyStandardStream::new(wtr) }
}
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 {
pub fn stdout(choice: ColorChoice) -> BufferedStandardStream {
let wtr =
WriterInner::create(StandardStreamType::StdoutBuffered, choice);
BufferedStandardStream { wtr: LossyStandardStream::new(wtr) }
}
pub fn stderr(choice: ColorChoice) -> BufferedStandardStream {
let wtr =
WriterInner::create(StandardStreamType::StderrBuffered, choice);
BufferedStandardStream { wtr: LossyStandardStream::new(wtr) }
}
}
impl WriterInner<IoStandardStream> {
#[cfg(not(windows))]
fn create(
sty: StandardStreamType,
choice: ColorChoice,
) -> WriterInner<IoStandardStream> {
if choice.should_attempt_color() {
WriterInner::Ansi(Ansi(IoStandardStream::new(sty)))
} else {
WriterInner::NoColor(NoColor(IoStandardStream::new(sty)))
}
}
#[cfg(windows)]
fn create(
sty: StandardStreamType,
choice: ColorChoice,
) -> WriterInner<IoStandardStream> {
let mut con = match sty {
StandardStreamType::Stdout => wincon::Console::stdout(),
StandardStreamType::Stderr => wincon::Console::stderr(),
StandardStreamType::StdoutBuffered => wincon::Console::stdout(),
StandardStreamType::StderrBuffered => wincon::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 {
#[inline]
fn write(&mut self, b: &[u8]) -> io::Result<usize> {
self.wtr.write(b)
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
self.wtr.flush()
}
}
impl WriteColor for StandardStream {
#[inline]
fn supports_color(&self) -> bool {
self.wtr.supports_color()
}
#[inline]
fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
self.wtr.set_color(spec)
}
#[inline]
fn reset(&mut self) -> io::Result<()> {
self.wtr.reset()
}
#[inline]
fn is_synchronous(&self) -> bool {
self.wtr.is_synchronous()
}
}
impl<'a> io::Write for StandardStreamLock<'a> {
#[inline]
fn write(&mut self, b: &[u8]) -> io::Result<usize> {
self.wtr.write(b)
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
self.wtr.flush()
}
}
impl<'a> WriteColor for StandardStreamLock<'a> {
#[inline]
fn supports_color(&self) -> bool {
self.wtr.supports_color()
}
#[inline]
fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
self.wtr.set_color(spec)
}
#[inline]
fn reset(&mut self) -> io::Result<()> {
self.wtr.reset()
}
#[inline]
fn is_synchronous(&self) -> bool {
self.wtr.is_synchronous()
}
}
impl io::Write for BufferedStandardStream {
#[inline]
fn write(&mut self, b: &[u8]) -> io::Result<usize> {
self.wtr.write(b)
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
self.wtr.flush()
}
}
impl WriteColor for BufferedStandardStream {
#[inline]
fn supports_color(&self) -> bool {
self.wtr.supports_color()
}
#[inline]
fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
if self.is_synchronous() {
self.wtr.flush()?;
}
self.wtr.set_color(spec)
}
#[inline]
fn reset(&mut self) -> io::Result<()> {
self.wtr.reset()
}
#[inline]
fn is_synchronous(&self) -> bool {
self.wtr.is_synchronous()
}
}
impl<W: io::Write> io::Write for WriterInner<W> {
#[inline(always)]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
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),
}
}
#[inline(always)]
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<W: io::Write> WriteColor for WriterInner<W> {
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<usize> {
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,
}
}
}
pub struct BufferWriter {
stream: LossyStandardStream<IoStandardStream>,
printed: AtomicBool,
separator: Option<Vec<u8>>,
color_choice: ColorChoice,
#[cfg(windows)]
console: Option<Mutex<wincon::Console>>,
}
impl BufferWriter {
#[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,
}
}
#[cfg(windows)]
fn create(sty: StandardStreamType, choice: ColorChoice) -> BufferWriter {
let mut con = match sty {
StandardStreamType::Stdout => wincon::Console::stdout(),
StandardStreamType::Stderr => wincon::Console::stderr(),
StandardStreamType::StdoutBuffered => wincon::Console::stdout(),
StandardStreamType::StderrBuffered => wincon::Console::stderr(),
}
.ok();
let is_console_virtual = con
.as_mut()
.map(|con| con.set_virtual_terminal_processing(true).is_ok())
.unwrap_or(false);
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),
}
}
pub fn stdout(choice: ColorChoice) -> BufferWriter {
BufferWriter::create(StandardStreamType::Stdout, choice)
}
pub fn stderr(choice: ColorChoice) -> BufferWriter {
BufferWriter::create(StandardStreamType::Stderr, choice)
}
pub fn separator(&mut self, sep: Option<Vec<u8>>) {
self.separator = sep;
}
#[cfg(not(windows))]
pub fn buffer(&self) -> Buffer {
Buffer::new(self.color_choice)
}
#[cfg(windows)]
pub fn buffer(&self) -> Buffer {
Buffer::new(self.color_choice, self.console.is_some())
}
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) => {
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(())
}
}
pub struct Buffer(BufferInner);
enum BufferInner {
NoColor(NoColor<Vec<u8>>),
Ansi(Ansi<Vec<u8>>),
#[cfg(windows)]
Windows(WindowsBuffer),
}
impl Buffer {
#[cfg(not(windows))]
fn new(choice: ColorChoice) -> Buffer {
if choice.should_attempt_color() {
Buffer::ansi()
} else {
Buffer::no_color()
}
}
#[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()
}
}
pub fn no_color() -> Buffer {
Buffer(BufferInner::NoColor(NoColor(vec![])))
}
pub fn ansi() -> Buffer {
Buffer(BufferInner::Ansi(Ansi(vec![])))
}
#[cfg(windows)]
pub fn console() -> Buffer {
Buffer(BufferInner::Windows(WindowsBuffer::new()))
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
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(),
}
}
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(),
}
}
pub fn into_inner(self) -> Vec<u8> {
match self.0 {
BufferInner::NoColor(b) => b.0,
BufferInner::Ansi(b) => b.0,
#[cfg(windows)]
BufferInner::Windows(b) => b.buf,
}
}
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,
}
}
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 {
#[inline]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
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),
}
}
#[inline]
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 {
#[inline]
fn supports_color(&self) -> bool {
match self.0 {
BufferInner::NoColor(_) => false,
BufferInner::Ansi(_) => true,
#[cfg(windows)]
BufferInner::Windows(_) => true,
}
}
#[inline]
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),
}
}
#[inline]
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(),
}
}
#[inline]
fn is_synchronous(&self) -> bool {
false
}
}
pub struct NoColor<W>(W);
impl<W: Write> NoColor<W> {
pub fn new(wtr: W) -> NoColor<W> {
NoColor(wtr)
}
pub fn into_inner(self) -> W {
self.0
}
pub fn get_ref(&self) -> &W {
&self.0
}
pub fn get_mut(&mut self) -> &mut W {
&mut self.0
}
}
impl<W: io::Write> io::Write for NoColor<W> {
#[inline]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.0.write(buf)
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
self.0.flush()
}
}
impl<W: io::Write> WriteColor for NoColor<W> {
#[inline]
fn supports_color(&self) -> bool {
false
}
#[inline]
fn set_color(&mut self, _: &ColorSpec) -> io::Result<()> {
Ok(())
}
#[inline]
fn reset(&mut self) -> io::Result<()> {
Ok(())
}
#[inline]
fn is_synchronous(&self) -> bool {
false
}
}
pub struct Ansi<W>(W);
impl<W: Write> Ansi<W> {
pub fn new(wtr: W) -> Ansi<W> {
Ansi(wtr)
}
pub fn into_inner(self) -> W {
self.0
}
pub fn get_ref(&self) -> &W {
&self.0
}
pub fn get_mut(&mut self) -> &mut W {
&mut self.0
}
}
impl<W: io::Write> io::Write for Ansi<W> {
#[inline]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.0.write(buf)
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
self.0.flush()
}
}
impl<W: io::Write> WriteColor for Ansi<W> {
#[inline]
fn supports_color(&self) -> bool {
true
}
#[inline]
fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
if spec.reset {
self.reset()?;
}
if spec.bold {
self.write_str("\x1B[1m")?;
}
if spec.dimmed {
self.write_str("\x1B[2m")?;
}
if spec.italic {
self.write_str("\x1B[3m")?;
}
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(())
}
#[inline]
fn reset(&mut self) -> io::Result<()> {
self.write_str("\x1B[0m")
}
#[inline]
fn is_synchronous(&self) -> bool {
false
}
}
impl<W: io::Write> Ansi<W> {
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),+) => {{
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;
}
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!(),
}
}
}
}
#[cfg(windows)]
#[derive(Clone, Debug)]
struct WindowsBuffer {
buf: Vec<u8>,
colors: Vec<(usize, Option<ColorSpec>)>,
}
#[cfg(windows)]
impl WindowsBuffer {
fn new() -> WindowsBuffer {
WindowsBuffer { buf: vec![], colors: vec![] }
}
fn push(&mut self, spec: Option<ColorSpec>) {
let pos = self.buf.len();
self.colors.push((pos, spec));
}
fn print(
&self,
console: &mut wincon::Console,
stream: &mut LossyStandardStream<IoStandardStreamLock>,
) -> 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()
}
fn clear(&mut self) {
self.buf.clear();
self.colors.clear();
}
}
#[cfg(windows)]
impl io::Write for WindowsBuffer {
#[inline]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.buf.extend_from_slice(buf);
Ok(buf.len())
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
#[cfg(windows)]
impl WriteColor for WindowsBuffer {
#[inline]
fn supports_color(&self) -> bool {
true
}
#[inline]
fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
self.push(Some(spec.clone()));
Ok(())
}
#[inline]
fn reset(&mut self) -> io::Result<()> {
self.push(None);
Ok(())
}
#[inline]
fn is_synchronous(&self) -> bool {
false
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ColorSpec {
fg_color: Option<Color>,
bg_color: Option<Color>,
bold: bool,
intense: bool,
underline: bool,
dimmed: bool,
italic: bool,
reset: bool,
}
impl Default for ColorSpec {
fn default() -> ColorSpec {
ColorSpec {
fg_color: None,
bg_color: None,
bold: false,
intense: false,
underline: false,
dimmed: false,
italic: false,
reset: true,
}
}
}
impl ColorSpec {
pub fn new() -> ColorSpec {
ColorSpec::default()
}
pub fn fg(&self) -> Option<&Color> {
self.fg_color.as_ref()
}
pub fn set_fg(&mut self, color: Option<Color>) -> &mut ColorSpec {
self.fg_color = color;
self
}
pub fn bg(&self) -> Option<&Color> {
self.bg_color.as_ref()
}
pub fn set_bg(&mut self, color: Option<Color>) -> &mut ColorSpec {
self.bg_color = color;
self
}
pub fn bold(&self) -> bool {
self.bold
}
pub fn set_bold(&mut self, yes: bool) -> &mut ColorSpec {
self.bold = yes;
self
}
pub fn dimmed(&self) -> bool {
self.dimmed
}
pub fn set_dimmed(&mut self, yes: bool) -> &mut ColorSpec {
self.dimmed = yes;
self
}
pub fn italic(&self) -> bool {
self.italic
}
pub fn set_italic(&mut self, yes: bool) -> &mut ColorSpec {
self.italic = yes;
self
}
pub fn underline(&self) -> bool {
self.underline
}
pub fn set_underline(&mut self, yes: bool) -> &mut ColorSpec {
self.underline = yes;
self
}
pub fn reset(&self) -> bool {
self.reset
}
pub fn set_reset(&mut self, yes: bool) -> &mut ColorSpec {
self.reset = yes;
self
}
pub fn intense(&self) -> bool {
self.intense
}
pub fn set_intense(&mut self, yes: bool) -> &mut ColorSpec {
self.intense = yes;
self
}
pub fn is_none(&self) -> bool {
self.fg_color.is_none()
&& self.bg_color.is_none()
&& !self.bold
&& !self.underline
&& !self.dimmed
&& !self.italic
&& !self.intense
}
pub fn clear(&mut self) {
self.fg_color = None;
self.bg_color = None;
self.bold = false;
self.underline = false;
self.intense = false;
self.dimmed = false;
self.italic = false;
}
#[cfg(windows)]
fn write_console(&self, console: &mut wincon::Console) -> io::Result<()> {
let fg_color = self.fg_color.and_then(|c| c.to_windows(self.intense));
if let Some((intense, color)) = fg_color {
console.fg(intense, color)?;
}
let bg_color = self.bg_color.and_then(|c| c.to_windows(self.intense));
if let Some((intense, color)) = bg_color {
console.bg(intense, color)?;
}
Ok(())
}
}
#[allow(missing_docs)]
#[derive(Clone, Copy, 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 {
#[cfg(windows)]
fn to_windows(
self,
intense: bool,
) -> Option<(wincon::Intense, wincon::Color)> {
use wincon::Intense::{No, Yes};
let color = match self {
Color::Black => wincon::Color::Black,
Color::Blue => wincon::Color::Blue,
Color::Green => wincon::Color::Green,
Color::Red => wincon::Color::Red,
Color::Cyan => wincon::Color::Cyan,
Color::Magenta => wincon::Color::Magenta,
Color::Yellow => wincon::Color::Yellow,
Color::White => wincon::Color::White,
Color::Ansi256(0) => return Some((No, wincon::Color::Black)),
Color::Ansi256(1) => return Some((No, wincon::Color::Red)),
Color::Ansi256(2) => return Some((No, wincon::Color::Green)),
Color::Ansi256(3) => return Some((No, wincon::Color::Yellow)),
Color::Ansi256(4) => return Some((No, wincon::Color::Blue)),
Color::Ansi256(5) => return Some((No, wincon::Color::Magenta)),
Color::Ansi256(6) => return Some((No, wincon::Color::Cyan)),
Color::Ansi256(7) => return Some((No, wincon::Color::White)),
Color::Ansi256(8) => return Some((Yes, wincon::Color::Black)),
Color::Ansi256(9) => return Some((Yes, wincon::Color::Red)),
Color::Ansi256(10) => return Some((Yes, wincon::Color::Green)),
Color::Ansi256(11) => return Some((Yes, wincon::Color::Yellow)),
Color::Ansi256(12) => return Some((Yes, wincon::Color::Blue)),
Color::Ansi256(13) => return Some((Yes, wincon::Color::Magenta)),
Color::Ansi256(14) => return Some((Yes, wincon::Color::Cyan)),
Color::Ansi256(15) => return Some((Yes, wincon::Color::White)),
Color::Ansi256(_) => return None,
Color::Rgb(_, _, _) => return None,
Color::__Nonexhaustive => unreachable!(),
};
let intense = if intense { Yes } else { No };
Some((intense, color))
}
fn from_str_numeric(s: &str) -> Result<Color, ParseColorError> {
fn parse_number(s: &str) -> Option<u8> {
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(),
}
})
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ParseColorError {
kind: ParseColorErrorKind,
given: String,
}
#[derive(Clone, Debug, Eq, PartialEq)]
enum ParseColorErrorKind {
InvalidName,
InvalidAnsi256,
InvalidRgb,
}
impl ParseColorError {
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<Color, ParseColorError> {
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<W> {
wtr: W,
#[cfg(windows)]
is_console: bool,
}
impl<W: io::Write> LossyStandardStream<W> {
#[cfg(not(windows))]
fn new(wtr: W) -> LossyStandardStream<W> {
LossyStandardStream { wtr: wtr }
}
#[cfg(windows)]
fn new(wtr: W) -> LossyStandardStream<W> {
let is_console = wincon::Console::stdout().is_ok()
|| wincon::Console::stderr().is_ok();
LossyStandardStream { wtr: wtr, is_console: is_console }
}
#[cfg(not(windows))]
fn wrap<Q: io::Write>(&self, wtr: Q) -> LossyStandardStream<Q> {
LossyStandardStream::new(wtr)
}
#[cfg(windows)]
fn wrap<Q: io::Write>(&self, wtr: Q) -> LossyStandardStream<Q> {
LossyStandardStream { wtr: wtr, is_console: self.is_console }
}
fn get_ref(&self) -> &W {
&self.wtr
}
}
impl<W: WriteColor> WriteColor for LossyStandardStream<W> {
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<W: io::Write> io::Write for LossyStandardStream<W> {
#[cfg(not(windows))]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.wtr.write(buf)
}
#[cfg(windows)]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
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<W: io::Write>(mut w: W, buf: &[u8]) -> io::Result<usize> {
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, ColorSpec, ParseColorError, ParseColorErrorKind,
StandardStream, WriteColor,
};
fn assert_is_send<T: Send>() {}
#[test]
fn standard_stream_is_send() {
assert_is_send::<StandardStream>();
}
#[test]
fn test_simple_parse_ok() {
let color = "green".parse::<Color>();
assert_eq!(color, Ok(Color::Green));
}
#[test]
fn test_256_parse_ok() {
let color = "7".parse::<Color>();
assert_eq!(color, Ok(Color::Ansi256(7)));
let color = "32".parse::<Color>();
assert_eq!(color, Ok(Color::Ansi256(32)));
let color = "0xFF".parse::<Color>();
assert_eq!(color, Ok(Color::Ansi256(0xFF)));
}
#[test]
fn test_256_parse_err_out_of_range() {
let color = "256".parse::<Color>();
assert_eq!(
color,
Err(ParseColorError {
kind: ParseColorErrorKind::InvalidAnsi256,
given: "256".to_string(),
})
);
}
#[test]
fn test_rgb_parse_ok() {
let color = "0,0,0".parse::<Color>();
assert_eq!(color, Ok(Color::Rgb(0, 0, 0)));
let color = "0,128,255".parse::<Color>();
assert_eq!(color, Ok(Color::Rgb(0, 128, 255)));
let color = "0x0,0x0,0x0".parse::<Color>();
assert_eq!(color, Ok(Color::Rgb(0, 0, 0)));
let color = "0x33,0x66,0xFF".parse::<Color>();
assert_eq!(color, Ok(Color::Rgb(0x33, 0x66, 0xFF)));
}
#[test]
fn test_rgb_parse_err_out_of_range() {
let color = "0,0,256".parse::<Color>();
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::<Color>();
assert_eq!(
color,
Err(ParseColorError {
kind: ParseColorErrorKind::InvalidRgb,
given: "0,0".to_string(),
})
);
let color = "not_a_color".parse::<Color>();
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_reset() {
let spec = ColorSpec::new();
let mut buf = Ansi::new(vec![]);
buf.set_color(&spec).unwrap();
assert_eq!(buf.0, b"\x1B[0m");
}
#[test]
fn test_no_reset() {
let mut spec = ColorSpec::new();
spec.set_reset(false);
let mut buf = Ansi::new(vec![]);
buf.set_color(&spec).unwrap();
assert_eq!(buf.0, b"");
}
#[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");
}
fn all_attributes() -> Vec<ColorSpec> {
let mut result = vec![];
for fg in vec![None, Some(Color::Red)] {
for bg in vec![None, Some(Color::Red)] {
for bold in vec![false, true] {
for underline in vec![false, true] {
for intense in vec![false, true] {
for italic in vec![false, true] {
for dimmed in vec![false, true] {
let mut color = ColorSpec::new();
color.set_fg(fg);
color.set_bg(bg);
color.set_bold(bold);
color.set_underline(underline);
color.set_intense(intense);
color.set_dimmed(dimmed);
color.set_italic(italic);
result.push(color);
}
}
}
}
}
}
}
result
}
#[test]
fn test_is_none() {
for (i, color) in all_attributes().iter().enumerate() {
assert_eq!(
i == 0,
color.is_none(),
"{:?} => {}",
color,
color.is_none()
)
}
}
#[test]
fn test_clear() {
for color in all_attributes() {
let mut color1 = color.clone();
color1.clear();
assert!(color1.is_none(), "{:?} => {:?}", color, color1);
}
}
}