Skip to content

Commit

Permalink
If we cannot acquire a colorful stderr, fallback to no color. Fixes #5.
Browse files Browse the repository at this point in the history
  • Loading branch information
kennytm committed Feb 3, 2017
1 parent 380723c commit 8ebe2ca
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 4 deletions.
6 changes: 4 additions & 2 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ use std::string::FromUtf8Error;
use std::str::Utf8Error;

use term::color::{YELLOW, GREEN, RED, WHITE};
use term::{stderr, Attr};
use term::Attr;
use rustc_serialize::json::BuilderError;

use stderr;

#[derive(Debug)]
pub enum Error {
UnsupportedOS,
Expand Down Expand Up @@ -76,7 +78,7 @@ impl From<BuilderError> for Error {
impl Error {
/// Prints the error message and quit.
pub fn print_error_and_quit(&self) -> ! {
let mut t = stderr().expect("Could not open stderr!");
let mut t = stderr::new();

t.fg(RED).unwrap();
t.attr(Attr::Bold).unwrap();
Expand Down
5 changes: 3 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ extern crate rustc_serialize;
extern crate regex;
#[cfg(test)] extern crate tempdir;

mod stderr;
mod errors;
mod cargo;
mod target_finder;
Expand All @@ -42,7 +43,7 @@ use errors::Error;
use cargo::{cargo, Cmd};
use target_finder::*;
use term::color::GREEN;
use term::{stderr, Attr};
use term::Attr;
use rustc_serialize::json::Json;

fn main() {
Expand Down Expand Up @@ -186,7 +187,7 @@ fn run(matches: &ArgMatches) -> Result<(), Error> {
}

fn write_msg(title: &str, msg: &str) {
let mut t = stderr().unwrap();
let mut t = stderr::new();
t.fg(GREEN).unwrap();
t.attr(Attr::Bold).unwrap();
write!(t, "{:>12}", title).unwrap();
Expand Down
42 changes: 42 additions & 0 deletions src/stderr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//! Wrapper of term::stderr which fallbacks to colorless output if disabled.

use std::io;
use std::fmt::Arguments;

use term::{stderr, Terminal, StderrTerminal, Attr, Result, Error};
use term::color::Color;

/// Creates a new stderr console, which is capable of coloring, and gracefully fallback to colorless
/// output if stderr does not support it.
pub fn new() -> Box<StderrTerminal> {
stderr().unwrap_or_else(|| Box::new(ColorlessWriter(io::stderr())))
}

/// Wraps a writer which implements `term::Terminal` which ignores all styling commands. This
/// structure is used when `term::stderr()` returns None when targeting non-TTY.
struct ColorlessWriter<W: io::Write>(W);

impl<W: io::Write> Terminal for ColorlessWriter<W> {
type Output = W;

fn fg(&mut self, _: Color) -> Result<()> { Ok(()) }
fn bg(&mut self, _: Color) -> Result<()> { Ok(()) }
fn attr(&mut self, _: Attr) -> Result<()> { Ok(()) }
fn supports_attr(&self, _: Attr) -> bool { true }
fn reset(&mut self) -> Result<()> { Ok(()) }
fn supports_reset(&self) -> bool { true }
fn supports_color(&self) -> bool { true }
fn cursor_up(&mut self) -> Result<()> { Err(Error::NotSupported) }
fn delete_line(&mut self) -> Result<()> { Err(Error::NotSupported) }
fn carriage_return(&mut self) -> Result<()> { Err(Error::NotSupported) }
fn get_ref(&self) -> &Self::Output { &self.0 }
fn get_mut(&mut self) -> &mut Self::Output { &mut self.0 }
fn into_inner(self) -> Self::Output { self.0 }
}

impl<W: io::Write> io::Write for ColorlessWriter<W> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> { self.0.write(buf) }
fn flush(&mut self) -> io::Result<()> { self.0.flush() }
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { self.0.write_all(buf) }
fn write_fmt(&mut self, fmt: Arguments) -> io::Result<()> { self.0.write_fmt(fmt) }
}
36 changes: 36 additions & 0 deletions tests/check-colorless-output.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// The MIT License (MIT)
//
// Copyright (c) 2017 Kenny Chan
//
// 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.

#![cfg(not(any(target_os="windows", target_os="macos", target_os="ios")))]

use std::process::Command;

// See issue #5 for detail.
#[test]
fn test_colorless_stderr() {
let status = Command::new("cargo")
.args(&["run", "--", "kcov", "--manifest-path", "/dev/null"])
.env("TERM", "none")
.status()
.expect("finished normally");

// the return code should be "2" to indicate the /dev/null is not a valid Cargo.toml.
// if the code is "101", it means we have panicked.
assert_eq!(status.code(), Some(2));
}

0 comments on commit 8ebe2ca

Please sign in to comment.