Skip to content

Commit

Permalink
api: impl {Default, FromStr} for ColorChoice
Browse files Browse the repository at this point in the history
This allows x.py in rust-lang/rust to remove a wrapper type around
ColorChoice. The defaults here seem reasonable; I chose kebab-case for
parsing `AlwaysAnsi` but I'm happy to switch it.

This also updates the top-level atty example in the crate docs to use
the new color choice parser.

Closes #42
  • Loading branch information
jyn514 authored and BurntSushi committed Jan 15, 2023
1 parent 00f084d commit 5b524b6
Showing 1 changed file with 53 additions and 12 deletions.
65 changes: 53 additions & 12 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,18 +99,10 @@ use atty;
use termcolor::{ColorChoice, StandardStream};
let preference = argv.get_flag("color").unwrap_or("auto");
let choice = match preference {
"always" => ColorChoice::Always,
"ansi" => ColorChoice::AlwaysAnsi,
"auto" => {
if atty::is(atty::Stream::Stdout) {
ColorChoice::Auto
} else {
ColorChoice::Never
}
}
_ => ColorChoice::Never,
};
let mut choice = preference.parse::<ColorChoice>()?;
if choice == ColorChoice::Auto && !atty::is(atty::Stream::Stdout) {
choice = ColorChoice::Never;
}
let stdout = StandardStream::stdout(choice);
// ... write to stdout
```
Expand Down Expand Up @@ -204,6 +196,13 @@ impl<T: ?Sized + WriteColor> WriteColor for Box<T> {
}

/// ColorChoice represents the color preferences of an end user.
///
/// The `Default` implementation for this type will select `Auto`, which tries
/// to do the right thing based on the current environment.
///
/// The `FromStr` implementation for this type converts a lowercase kebab-case
/// string of the variant name to the corresponding variant. Any other string
/// results in an error.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ColorChoice {
/// Try very hard to emit colors. This includes emitting ANSI colors
Expand All @@ -220,6 +219,29 @@ pub enum ColorChoice {
Never,
}

/// The default is `Auto`.
impl Default for ColorChoice {
fn default() -> ColorChoice {
ColorChoice::Auto
}
}

impl FromStr for ColorChoice {
type Err = ColorChoiceParseError;

fn from_str(s: &str) -> Result<ColorChoice, ColorChoiceParseError> {
match s.to_lowercase().as_str() {
"always" => Ok(ColorChoice::Always),
"always-ansi" => Ok(ColorChoice::AlwaysAnsi),
"never" => Ok(ColorChoice::Never),
"auto" => Ok(ColorChoice::Auto),
unknown => Err(ColorChoiceParseError {
unknown_choice: unknown.to_string(),
}),
}
}
}

impl ColorChoice {
/// Returns true if we should attempt to write colored output.
fn should_attempt_color(&self) -> bool {
Expand Down Expand Up @@ -292,6 +314,25 @@ impl ColorChoice {
}
}

/// An error that occurs when parsing a `ColorChoice` fails.
#[derive(Clone, Debug)]
pub struct ColorChoiceParseError {
unknown_choice: String,
}

impl std::error::Error for ColorChoiceParseError {}

impl fmt::Display for ColorChoiceParseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"unrecognized color choice '{}': valid choices are: \
always, always-ansi, never, auto",
self.unknown_choice,
)
}
}

/// `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.
Expand Down

0 comments on commit 5b524b6

Please sign in to comment.