2017-08-24 22:38:26 +00:00
|
|
|
|
use output::Colours;
|
|
|
|
|
|
2017-08-25 16:43:36 +00:00
|
|
|
|
use options::{flags, Vars, Misfire};
|
2017-08-24 22:38:26 +00:00
|
|
|
|
use options::parser::MatchedFlags;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Under what circumstances we should display coloured, rather than plain,
|
|
|
|
|
/// output to the terminal.
|
|
|
|
|
///
|
|
|
|
|
/// By default, we want to display the colours when stdout can display them.
|
|
|
|
|
/// Turning them on when output is going to, say, a pipe, would make programs
|
|
|
|
|
/// such as `grep` or `more` not work properly. So the `Automatic` mode does
|
|
|
|
|
/// this check and only displays colours when they can be truly appreciated.
|
|
|
|
|
#[derive(PartialEq, Debug)]
|
|
|
|
|
enum TerminalColours {
|
|
|
|
|
|
|
|
|
|
/// Display them even when output isn’t going to a terminal.
|
|
|
|
|
Always,
|
|
|
|
|
|
|
|
|
|
/// Display them when output is going to a terminal, but not otherwise.
|
|
|
|
|
Automatic,
|
|
|
|
|
|
|
|
|
|
/// Never display them, even when output is going to a terminal.
|
|
|
|
|
Never,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for TerminalColours {
|
|
|
|
|
fn default() -> TerminalColours {
|
|
|
|
|
TerminalColours::Automatic
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const COLOURS: &[&str] = &["always", "auto", "never"];
|
|
|
|
|
|
|
|
|
|
impl TerminalColours {
|
|
|
|
|
|
|
|
|
|
/// Determine which terminal colour conditions to use.
|
|
|
|
|
fn deduce(matches: &MatchedFlags) -> Result<TerminalColours, Misfire> {
|
|
|
|
|
|
|
|
|
|
let word = match matches.get_where(|f| f.matches(&flags::COLOR) || f.matches(&flags::COLOUR))? {
|
|
|
|
|
Some(w) => w,
|
|
|
|
|
None => return Ok(TerminalColours::default()),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if word == "always" {
|
|
|
|
|
Ok(TerminalColours::Always)
|
|
|
|
|
}
|
|
|
|
|
else if word == "auto" || word == "automatic" {
|
|
|
|
|
Ok(TerminalColours::Automatic)
|
|
|
|
|
}
|
|
|
|
|
else if word == "never" {
|
|
|
|
|
Ok(TerminalColours::Never)
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
Err(Misfire::bad_argument(&flags::COLOR, word, COLOURS))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl Colours {
|
2017-08-25 16:43:36 +00:00
|
|
|
|
pub fn deduce<V, TW>(matches: &MatchedFlags, vars: &V, widther: TW) -> Result<Colours, Misfire>
|
|
|
|
|
where TW: Fn() -> Option<usize>, V: Vars {
|
2017-08-24 22:38:26 +00:00
|
|
|
|
use self::TerminalColours::*;
|
2017-08-25 16:43:36 +00:00
|
|
|
|
use output::lsc::LSColors;
|
2017-08-24 22:38:26 +00:00
|
|
|
|
|
|
|
|
|
let tc = TerminalColours::deduce(matches)?;
|
2017-08-25 16:43:36 +00:00
|
|
|
|
if tc == Never || (tc == Automatic && widther().is_none()) {
|
|
|
|
|
return Ok(Colours::plain());
|
2017-08-24 22:38:26 +00:00
|
|
|
|
}
|
2017-08-25 16:43:36 +00:00
|
|
|
|
|
|
|
|
|
let scale = matches.has_where(|f| f.matches(&flags::COLOR_SCALE) || f.matches(&flags::COLOUR_SCALE))?;
|
|
|
|
|
let mut c = Colours::colourful(scale.is_some());
|
|
|
|
|
|
|
|
|
|
if let Some(lsc) = vars.get("LS_COLORS") {
|
|
|
|
|
let lsc = lsc.to_string_lossy();
|
|
|
|
|
let lsc = LSColors::parse(lsc.as_ref());
|
|
|
|
|
|
|
|
|
|
if let Some(dir) = lsc.get("di") {
|
2017-08-25 16:50:22 +00:00
|
|
|
|
c.filekinds.directory = dir;
|
2017-08-25 16:43:36 +00:00
|
|
|
|
}
|
2017-08-24 22:38:26 +00:00
|
|
|
|
}
|
2017-08-25 16:43:36 +00:00
|
|
|
|
|
|
|
|
|
Ok(c)
|
2017-08-24 22:38:26 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
2017-08-25 07:53:35 +00:00
|
|
|
|
mod terminal_test {
|
2017-08-24 22:38:26 +00:00
|
|
|
|
use super::*;
|
|
|
|
|
use std::ffi::OsString;
|
|
|
|
|
use options::flags;
|
|
|
|
|
use options::parser::{Flag, Arg};
|
|
|
|
|
|
|
|
|
|
use options::test::parse_for_test;
|
|
|
|
|
use options::test::Strictnesses::*;
|
|
|
|
|
|
|
|
|
|
pub fn os(input: &'static str) -> OsString {
|
|
|
|
|
let mut os = OsString::new();
|
|
|
|
|
os.push(input);
|
|
|
|
|
os
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-25 07:53:35 +00:00
|
|
|
|
static TEST_ARGS: &[&Arg] = &[ &flags::COLOR, &flags::COLOUR ];
|
2017-08-24 22:38:26 +00:00
|
|
|
|
|
|
|
|
|
macro_rules! test {
|
2017-08-25 08:07:28 +00:00
|
|
|
|
($name:ident: $inputs:expr; $stricts:expr => $result:expr) => {
|
2017-08-24 22:38:26 +00:00
|
|
|
|
#[test]
|
|
|
|
|
fn $name() {
|
2017-08-25 08:07:28 +00:00
|
|
|
|
for result in parse_for_test($inputs.as_ref(), TEST_ARGS, $stricts, |mf| TerminalColours::deduce(mf)) {
|
2017-08-24 22:38:26 +00:00
|
|
|
|
assert_eq!(result, $result);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2017-08-25 08:07:28 +00:00
|
|
|
|
($name:ident: $inputs:expr; $stricts:expr => err $result:expr) => {
|
2017-08-24 22:38:26 +00:00
|
|
|
|
#[test]
|
|
|
|
|
fn $name() {
|
2017-08-25 08:07:28 +00:00
|
|
|
|
for result in parse_for_test($inputs.as_ref(), TEST_ARGS, $stricts, |mf| TerminalColours::deduce(mf)) {
|
2017-08-24 22:38:26 +00:00
|
|
|
|
assert_eq!(result.unwrap_err(), $result);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Default
|
2017-08-25 08:07:28 +00:00
|
|
|
|
test!(empty: []; Both => Ok(TerminalColours::default()));
|
2017-08-24 22:38:26 +00:00
|
|
|
|
|
|
|
|
|
// --colour
|
2017-08-25 08:07:28 +00:00
|
|
|
|
test!(u_always: ["--colour=always"]; Both => Ok(TerminalColours::Always));
|
|
|
|
|
test!(u_auto: ["--colour", "auto"]; Both => Ok(TerminalColours::Automatic));
|
|
|
|
|
test!(u_never: ["--colour=never"]; Both => Ok(TerminalColours::Never));
|
2017-08-24 22:38:26 +00:00
|
|
|
|
|
|
|
|
|
// --color
|
2017-08-25 08:07:28 +00:00
|
|
|
|
test!(no_u_always: ["--color", "always"]; Both => Ok(TerminalColours::Always));
|
|
|
|
|
test!(no_u_auto: ["--color=auto"]; Both => Ok(TerminalColours::Automatic));
|
|
|
|
|
test!(no_u_never: ["--color", "never"]; Both => Ok(TerminalColours::Never));
|
2017-08-24 22:38:26 +00:00
|
|
|
|
|
|
|
|
|
// Errors
|
2017-08-25 08:07:28 +00:00
|
|
|
|
test!(no_u_error: ["--color=upstream"]; Both => err Misfire::bad_argument(&flags::COLOR, &os("upstream"), super::COLOURS)); // the error is for --color
|
|
|
|
|
test!(u_error: ["--colour=lovers"]; Both => err Misfire::bad_argument(&flags::COLOR, &os("lovers"), super::COLOURS)); // and so is this one!
|
2017-08-24 22:38:26 +00:00
|
|
|
|
|
|
|
|
|
// Overriding
|
2017-08-25 08:07:28 +00:00
|
|
|
|
test!(overridden_1: ["--colour=auto", "--colour=never"]; Last => Ok(TerminalColours::Never));
|
|
|
|
|
test!(overridden_2: ["--color=auto", "--colour=never"]; Last => Ok(TerminalColours::Never));
|
|
|
|
|
test!(overridden_3: ["--colour=auto", "--color=never"]; Last => Ok(TerminalColours::Never));
|
|
|
|
|
test!(overridden_4: ["--color=auto", "--color=never"]; Last => Ok(TerminalColours::Never));
|
|
|
|
|
|
|
|
|
|
test!(overridden_5: ["--colour=auto", "--colour=never"]; Complain => err Misfire::Duplicate(Flag::Long("colour"), Flag::Long("colour")));
|
|
|
|
|
test!(overridden_6: ["--color=auto", "--colour=never"]; Complain => err Misfire::Duplicate(Flag::Long("color"), Flag::Long("colour")));
|
|
|
|
|
test!(overridden_7: ["--colour=auto", "--color=never"]; Complain => err Misfire::Duplicate(Flag::Long("colour"), Flag::Long("color")));
|
|
|
|
|
test!(overridden_8: ["--color=auto", "--color=never"]; Complain => err Misfire::Duplicate(Flag::Long("color"), Flag::Long("color")));
|
2017-08-25 07:53:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod colour_test {
|
|
|
|
|
use super::*;
|
|
|
|
|
use options::flags;
|
|
|
|
|
use options::parser::{Flag, Arg};
|
|
|
|
|
|
|
|
|
|
use options::test::parse_for_test;
|
|
|
|
|
use options::test::Strictnesses::*;
|
|
|
|
|
|
|
|
|
|
static TEST_ARGS: &[&Arg] = &[ &flags::COLOR, &flags::COLOUR,
|
|
|
|
|
&flags::COLOR_SCALE, &flags::COLOUR_SCALE ];
|
|
|
|
|
|
|
|
|
|
macro_rules! test {
|
2017-08-25 08:07:28 +00:00
|
|
|
|
($name:ident: $inputs:expr, $widther:expr; $stricts:expr => $result:expr) => {
|
2017-08-25 08:03:47 +00:00
|
|
|
|
#[test]
|
|
|
|
|
fn $name() {
|
2017-08-25 16:43:36 +00:00
|
|
|
|
for result in parse_for_test($inputs.as_ref(), TEST_ARGS, $stricts, |mf| Colours::deduce(mf, &None, &$widther)) {
|
2017-08-25 08:03:47 +00:00
|
|
|
|
assert_eq!(result, $result);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2017-08-25 08:07:28 +00:00
|
|
|
|
($name:ident: $inputs:expr, $widther:expr; $stricts:expr => err $result:expr) => {
|
2017-08-25 07:53:35 +00:00
|
|
|
|
#[test]
|
|
|
|
|
fn $name() {
|
2017-08-25 16:43:36 +00:00
|
|
|
|
for result in parse_for_test($inputs.as_ref(), TEST_ARGS, $stricts, |mf| Colours::deduce(mf, &None, &$widther)) {
|
2017-08-25 07:53:35 +00:00
|
|
|
|
assert_eq!(result.unwrap_err(), $result);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2017-08-25 08:07:28 +00:00
|
|
|
|
($name:ident: $inputs:expr, $widther:expr; $stricts:expr => like $pat:pat) => {
|
2017-08-25 07:53:35 +00:00
|
|
|
|
#[test]
|
|
|
|
|
fn $name() {
|
2017-08-25 16:43:36 +00:00
|
|
|
|
for result in parse_for_test($inputs.as_ref(), TEST_ARGS, $stricts, |mf| Colours::deduce(mf, &None, &$widther)) {
|
2017-08-25 07:53:35 +00:00
|
|
|
|
println!("Testing {:?}", result);
|
|
|
|
|
match result {
|
|
|
|
|
$pat => assert!(true),
|
|
|
|
|
_ => assert!(false),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
2017-08-24 22:38:26 +00:00
|
|
|
|
|
2017-08-25 08:07:28 +00:00
|
|
|
|
test!(width_1: ["--colour", "always"], || Some(80); Both => Ok(Colours::colourful(false)));
|
|
|
|
|
test!(width_2: ["--colour", "always"], || None; Both => Ok(Colours::colourful(false)));
|
|
|
|
|
test!(width_3: ["--colour", "never"], || Some(80); Both => Ok(Colours::plain()));
|
|
|
|
|
test!(width_4: ["--colour", "never"], || None; Both => Ok(Colours::plain()));
|
|
|
|
|
test!(width_5: ["--colour", "automatic"], || Some(80); Both => Ok(Colours::colourful(false)));
|
|
|
|
|
test!(width_6: ["--colour", "automatic"], || None; Both => Ok(Colours::plain()));
|
|
|
|
|
test!(width_7: [], || Some(80); Both => Ok(Colours::colourful(false)));
|
|
|
|
|
test!(width_8: [], || None; Both => Ok(Colours::plain()));
|
|
|
|
|
|
|
|
|
|
test!(scale_1: ["--color=always", "--color-scale", "--colour-scale"], || None; Last => like Ok(Colours { scale: true, .. }));
|
|
|
|
|
test!(scale_2: ["--color=always", "--color-scale", ], || None; Last => like Ok(Colours { scale: true, .. }));
|
|
|
|
|
test!(scale_3: ["--color=always", "--colour-scale"], || None; Last => like Ok(Colours { scale: true, .. }));
|
|
|
|
|
test!(scale_4: ["--color=always", ], || None; Last => like Ok(Colours { scale: false, .. }));
|
|
|
|
|
|
|
|
|
|
test!(scale_5: ["--color=always", "--color-scale", "--colour-scale"], || None; Complain => err Misfire::Duplicate(Flag::Long("color-scale"), Flag::Long("colour-scale")));
|
|
|
|
|
test!(scale_6: ["--color=always", "--color-scale", ], || None; Complain => like Ok(Colours { scale: true, .. }));
|
|
|
|
|
test!(scale_7: ["--color=always", "--colour-scale"], || None; Complain => like Ok(Colours { scale: true, .. }));
|
|
|
|
|
test!(scale_8: ["--color=always", ], || None; Complain => like Ok(Colours { scale: false, .. }));
|
2017-08-24 22:38:26 +00:00
|
|
|
|
}
|
2017-08-25 16:43:36 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod customs_test {
|
|
|
|
|
use std::ffi::OsString;
|
|
|
|
|
|
|
|
|
|
use super::*;
|
|
|
|
|
use options::Vars;
|
|
|
|
|
use options::test::parse_for_test;
|
|
|
|
|
use options::test::Strictnesses::Both;
|
|
|
|
|
|
|
|
|
|
use ansi_term::Colour::*;
|
|
|
|
|
|
|
|
|
|
macro_rules! test {
|
|
|
|
|
($name:ident: ls $ls:expr, exa $exa:expr => $resulter:expr) => {
|
|
|
|
|
#[test]
|
|
|
|
|
fn $name() {
|
|
|
|
|
let mut c = Colours::colourful(false);
|
|
|
|
|
$resulter(&mut c);
|
|
|
|
|
|
|
|
|
|
let vars = MockVars { ls: $ls, exa: $exa };
|
|
|
|
|
|
|
|
|
|
for result in parse_for_test(&[], &[], Both, |mf| Colours::deduce(mf, &vars, || Some(80))) {
|
|
|
|
|
assert_eq!(result, Ok(c));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct MockVars {
|
|
|
|
|
ls: &'static str,
|
|
|
|
|
exa: &'static str,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Test impl that just returns the value it has.
|
|
|
|
|
impl Vars for MockVars {
|
|
|
|
|
fn get(&self, name: &'static str) -> Option<OsString> {
|
|
|
|
|
if name == "LS_COLORS" && !self.ls.is_empty() {
|
|
|
|
|
OsString::from(self.ls.clone()).into()
|
|
|
|
|
}
|
|
|
|
|
else if name == "EXA_COLORS" && !self.exa.is_empty() {
|
|
|
|
|
OsString::from(self.exa.clone()).into()
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-25 16:50:22 +00:00
|
|
|
|
test!(override_1: ls "di=34", exa "" => |c: &mut Colours| { c.filekinds.directory = Blue.normal(); });
|
2017-08-25 16:43:36 +00:00
|
|
|
|
}
|