Add colored output

Colored output can be controlled on the command line with
`--use-color auto|always|never`. The default is `auto`, which enables
color if `imdl` detects that it is printing to a terminal.

Color can be disabled entirely by setting the `NO_COLOR` environment
variable.

type: added
This commit is contained in:
Casey Rodarmor 2020-01-15 23:37:12 -08:00
parent b334fa49b2
commit d1f8f24d8e
No known key found for this signature in database
GPG Key ID: 556186B153EC6FE0
12 changed files with 269 additions and 40 deletions

View File

@ -29,7 +29,10 @@ jobs:
run: cargo build --verbose
- name: Test
run: cargo test --verbose
- name: Lint
- name: Clippy
run: cargo clippy
- name: Format
run: cargo fmt -- --check
- name: Lint
if: matrix.os != 'windows-latest'
run: "! grep --color -REn 'FIXME|TODO|XXX' src"

66
Cargo.lock generated
View File

@ -16,6 +16,14 @@ dependencies = [
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ansi_term"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "atty"
version = "0.2.14"
@ -63,6 +71,18 @@ name = "doc-comment"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "env_logger"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
"humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "getrandom"
version = "0.1.14"
@ -89,6 +109,14 @@ dependencies = [
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "humantime"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "idna"
version = "0.2.0"
@ -103,6 +131,9 @@ dependencies = [
name = "imdl"
version = "0.0.0"
dependencies = [
"ansi_term 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
"atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"md5 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -128,6 +159,14 @@ name = "libc"
version = "0.2.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "log"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "matches"
version = "0.1.8"
@ -185,6 +224,11 @@ dependencies = [
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "quick-error"
version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "quote"
version = "1.0.2"
@ -274,7 +318,7 @@ name = "same-file"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -413,6 +457,14 @@ dependencies = [
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "termcolor"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "textwrap"
version = "0.11.0"
@ -482,7 +534,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -506,7 +558,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi-util"
version = "0.1.2"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
@ -520,18 +572,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d"
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
"checksum ansi_term 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
"checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
"checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb"
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
"checksum doc-comment 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "923dea538cea0aa3025e8685b20d6ee21ef99c4f77e954a30febbaac5ec73a97"
"checksum env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
"checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
"checksum hermit-abi 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "eff2656d88f158ce120947499e971d743c05dbcbed62e5bd2f38f1698bbc3772"
"checksum humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
"checksum idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9"
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
"checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558"
"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
"checksum md5 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e"
@ -540,6 +596,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum proc-macro-error 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "53c98547ceaea14eeb26fcadf51dc70d01a2479a7839170eae133721105e4428"
"checksum proc-macro-error-attr 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c2bf5d493cf5d3e296beccfd61794e445e830dfc8070a9c248ad3ee071392c6c"
"checksum proc-macro2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "0319972dcae462681daf4da1adeeaa066e3ebd29c69be96c6abb1259d2ee2bcc"
"checksum quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
"checksum rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412"
"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853"
@ -567,6 +624,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1e4ff033220a41d1a57d8125eab57bf5263783dfdcc18688b1dacc6ce9651ef8"
"checksum syn-mid 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9fd3937748a7eccff61ba5b90af1a20dbf610858923a9192ea0ecb0cb77db1d0"
"checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
"checksum termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f"
"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
@ -580,5 +638,5 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9"
"checksum winapi-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4ccfbf554c6ad11084fb7517daca16cfdcaccbdadba4fc336f032a8b12c2ad80"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

View File

@ -12,18 +12,21 @@ repository = "https://github.com/casey/intermodal"
edition = "2018"
[dependencies]
libc = "0.2"
md5 = "0.7"
regex = "1"
serde_bencode = "0.2"
serde_bytes = "0.11"
sha1 = "0.6"
snafu = "0.6"
structopt = "0.3"
tempfile = "3"
url = "2"
walkdir = "2"
static_assertions = "1.1.0"
ansi_term = "0.12"
atty = "0.2"
env_logger = "0.7"
libc = "0.2"
md5 = "0.7"
regex = "1"
serde_bencode = "0.2"
serde_bytes = "0.11"
sha1 = "0.6"
snafu = "0.6"
static_assertions = "1"
structopt = "0.3"
tempfile = "3"
url = "2"
walkdir = "2"
[dependencies.serde]
version = "1"

View File

@ -1 +1,36 @@
# intermodal: a 40' shipping container for the Internet
## Colored Output
Intermodal features colored help, error, and informational output. Colored
output is disabled if Intermodal detects that it is not printing to a TTY.
To disable colored output, set the `NO_COLOR` environment variable to any
valu or pass `--use-color never` on the command line.
To force colored output, pass `--use-color always` on the command line.
## Semantic Versioning and Unstable Features
Intermodal follows [semantic versioning](https://semver.org/).
In particular:
- v0.0.X: Breaking changes may be introduced at any time.
- v0.X.Y: Breaking changes may only be introduced with a minor version number
bump.
- vX.Y.Z: Breaking changes may only be introduced with a major version number
bump
To avoid premature stabilization and excessive version churn, unstable features
are unavailable unless the `--unstable` / `-u` flag is passed. Unstable
features may be changed or removed at any time.
```
$ imdl torrent stats --input tmp
error: Feature `torrent stats subcommand` cannot be used without passing the `--unstable` flag
$ imdl --unstable torrent stats tmp
Torrents processed: 0
Read failed: 0
Decode failed: 0
```

View File

@ -3,7 +3,7 @@ pub(crate) use std::{
borrow::Cow,
cmp::{Ordering, Reverse},
collections::{BTreeMap, HashMap},
convert::TryInto,
convert::{Infallible, TryInto},
env,
ffi::{OsStr, OsString},
fmt::{self, Display, Formatter},
@ -11,7 +11,8 @@ pub(crate) use std::{
hash::Hash,
io::{self, Read, Write},
path::{Path, PathBuf},
process, str,
process,
str::{self, FromStr},
time::{SystemTime, SystemTimeError},
usize,
};
@ -28,7 +29,7 @@ pub(crate) use url::Url;
pub(crate) use walkdir::WalkDir;
// modules
pub(crate) use crate::{bencode, consts, error, torrent};
pub(crate) use crate::{bencode, consts, error, torrent, use_color};
// traits
pub(crate) use crate::{
@ -38,7 +39,8 @@ pub(crate) use crate::{
// structs and enums
pub(crate) use crate::{
env::Env, error::Error, file_info::FileInfo, hasher::Hasher, info::Info, metainfo::Metainfo,
mode::Mode, opt::Opt, subcommand::Subcommand, torrent::Torrent,
mode::Mode, opt::Opt, style::Style, subcommand::Subcommand, torrent::Torrent,
use_color::UseColor,
};
// test modules

View File

@ -4,7 +4,8 @@ pub(crate) struct Env {
args: Vec<String>,
dir: Box<dyn AsRef<Path>>,
pub(crate) err: Box<dyn Write>,
pub(crate) _out: Box<dyn Write>,
pub(crate) out: Box<dyn Write>,
err_style: Style,
}
impl Env {
@ -14,14 +15,42 @@ impl Env {
Err(error) => panic!("Failed to get current directory: {}", error),
};
Self::new(dir, io::stdout(), io::stderr(), env::args())
let err_style = if env::var_os("NO_COLOR").is_some()
|| env::var_os("TERM").as_deref() == Some(OsStr::new("dumb"))
|| !atty::is(atty::Stream::Stderr)
{
Style::inactive()
} else {
Style::active()
};
Self::new(dir, io::stdout(), io::stderr(), err_style, env::args())
}
pub(crate) fn run(&mut self) -> Result<(), Error> {
Opt::from_iter_safe(&self.args)?.run(self)
#[cfg(windows)]
ansi_term::enable_ansi_support().ok();
#[cfg(not(test))]
env_logger::Builder::from_env(
env_logger::Env::new()
.filter("JUST_LOG")
.write_style("JUST_LOG_STYLE"),
)
.init();
let opt = Opt::from_iter_safe(&self.args)?;
match opt.use_color {
UseColor::Always => self.err_style = Style::active(),
UseColor::Auto => {}
UseColor::Never => self.err_style = Style::inactive(),
}
opt.run(self)
}
pub(crate) fn new<D, O, E, S, I>(dir: D, out: O, err: E, args: I) -> Self
pub(crate) fn new<D, O, E, S, I>(dir: D, out: O, err: E, err_style: Style, args: I) -> Self
where
D: AsRef<Path> + 'static,
O: Write + 'static,
@ -33,21 +62,35 @@ impl Env {
args: args.into_iter().map(Into::into).collect(),
dir: Box::new(dir),
err: Box::new(err),
_out: Box::new(out),
out: Box::new(out),
err_style,
}
}
pub(crate) fn status(&mut self) -> Result<(), i32> {
use structopt::clap::ErrorKind;
if let Err(error) = self.run() {
if let Error::Clap { source } = error {
write!(&mut self.err, "{}", source).ok();
if source.use_stderr() {
write!(&mut self.err, "{}", source).ok();
} else {
write!(&mut self.out, "{}", source).ok();
}
match source.kind {
ErrorKind::VersionDisplayed | ErrorKind::HelpDisplayed => Ok(()),
_ => Err(EXIT_FAILURE),
}
} else {
write!(&mut self.err, "error: {}", error).ok();
writeln!(
&mut self.err,
"{}{}: {}{}",
self.err_style.error().paint("error"),
self.err_style.message().prefix(),
error,
self.err_style.message().suffix(),
)
.ok();
Err(EXIT_FAILURE)
}
} else {

View File

@ -10,6 +10,7 @@
clippy::option_unwrap_used,
clippy::result_expect_used,
clippy::result_unwrap_used,
clippy::unreachable,
clippy::wildcard_enum_match_arm
)]
@ -53,8 +54,10 @@ mod mode;
mod opt;
mod path_ext;
mod reckoner;
mod style;
mod subcommand;
mod torrent;
mod use_color;
fn main() {
if let Err(code) = Env::main().status() {

View File

@ -1,18 +1,25 @@
use crate::common::*;
use structopt::clap::AppSettings;
use structopt::clap::{AppSettings, ArgSettings};
#[derive(StructOpt)]
#[structopt(
about(consts::ABOUT),
version(consts::VERSION),
author(consts::AUTHOR),
setting(AppSettings::ColoredHelp),
setting(AppSettings::ColorAuto)
global_setting(AppSettings::ColoredHelp),
global_setting(AppSettings::ColorAuto)
)]
pub(crate) struct Opt {
#[structopt(long = "unstable", short = "u")]
unstable: bool,
#[structopt(
long = "color",
default_value = use_color::AUTO,
set = ArgSettings::CaseInsensitive,
possible_values = use_color::VALUES,
)]
pub(crate) use_color: UseColor,
#[structopt(subcommand)]
subcommand: Subcommand,
}

30
src/style.rs Normal file
View File

@ -0,0 +1,30 @@
#[derive(Clone, Copy, Debug)]
pub(crate) struct Style {
active: bool,
}
impl Style {
pub(crate) fn active() -> Self {
Self { active: true }
}
pub(crate) fn inactive() -> Self {
Self { active: false }
}
pub(crate) fn message(self) -> ansi_term::Style {
if self.active {
ansi_term::Style::new().bold()
} else {
ansi_term::Style::new()
}
}
pub(crate) fn error(self) -> ansi_term::Style {
if self.active {
ansi_term::Style::new().fg(ansi_term::Color::Red).bold()
} else {
ansi_term::Style::new()
}
}
}

View File

@ -15,6 +15,7 @@ impl TestEnv {
tempfile::tempdir().unwrap(),
out.clone(),
err.clone(),
Style::inactive(),
iter::once(String::from("imdl")).chain(iter.into_iter().map(|item| item.into())),
);

View File

@ -45,17 +45,19 @@ impl Stats {
let max = paths.iter().map(|(_, count)| *count).max().unwrap_or(0);
let width = max.to_string().len();
errln!(env, "Keys:");
for (key, count) in &paths {
if key.starts_with("info/files") {
continue;
}
errln!(env, "{:<width$} - {}", count, key, width = width);
}
for (key, count) in paths {
if key.starts_with("info/files") {
if !paths.is_empty() {
errln!(env, "Keys:");
for (key, count) in &paths {
if key.starts_with("info/files") {
continue;
}
errln!(env, "{:<width$} - {}", count, key, width = width);
}
for (key, count) in paths {
if key.starts_with("info/files") {
errln!(env, "{:<width$} - {}", count, key, width = width);
}
}
}
if !extractor.values.is_empty() {

42
src/use_color.rs Normal file
View File

@ -0,0 +1,42 @@
use crate::common::*;
pub(crate) const AUTO: &str = "auto";
pub(crate) const ALWAYS: &str = "always";
pub(crate) const NEVER: &str = "never";
pub(crate) const VALUES: &[&str] = &[AUTO, ALWAYS, NEVER];
#[derive(Copy, Clone, Debug, PartialEq)]
pub(crate) enum UseColor {
Auto,
Always,
Never,
}
impl FromStr for UseColor {
type Err = Infallible;
fn from_str(text: &str) -> Result<Self, Self::Err> {
match text.to_lowercase().as_str() {
AUTO => Ok(Self::Auto),
ALWAYS => Ok(Self::Always),
NEVER => Ok(Self::Never),
_ => unreachable!(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn from_str() {
assert_eq!(UseColor::Auto, AUTO.parse().unwrap());
assert_eq!(UseColor::Always, ALWAYS.parse().unwrap());
assert_eq!(UseColor::Never, NEVER.parse().unwrap());
assert_eq!(UseColor::Auto, AUTO.to_uppercase().parse().unwrap());
assert_eq!(UseColor::Always, ALWAYS.to_uppercase().parse().unwrap());
assert_eq!(UseColor::Never, NEVER.to_uppercase().parse().unwrap());
}
}