use crate::options::parser::MatchedFlags;
use crate::options::{Vars, flags, vars};
use crate::theme::{Definitions, GradientFlags, Options, UseColours};
impl Options {
pub fn deduce<V: Vars>(matches: &MatchedFlags, vars: &V) -> Self {
let use_colours = UseColours::deduce(matches, vars);
let gradient = GradientFlags::deduce(matches);
let definitions = if use_colours == UseColours::Never {
Definitions::default()
} else {
Definitions::deduce(vars)
};
let theme_override = matches.get(flags::THEME).map(String::from);
Self {
use_colours,
gradient,
definitions,
theme_override,
}
}
}
impl UseColours {
fn deduce<V: Vars>(matches: &MatchedFlags, vars: &V) -> Self {
let default_value = match vars.get(vars::NO_COLOR) {
Some(_) => Self::Never,
None => Self::Automatic,
};
let Some(word) = matches.get(flags::COLOR) else {
return default_value;
};
match word {
"always" => Self::Always,
"auto" | "automatic" => Self::Automatic,
"never" => Self::Never,
_ => unreachable!("Clap rejects invalid --colour values"),
}
}
}
impl GradientFlags {
fn deduce(matches: &MatchedFlags) -> Self {
let mut flags = Self::ALL;
if let Some(s) = matches.get(flags::GRADIENT) {
flags = parse_gradient_value(s);
}
if matches.has(flags::NO_GRADIENT) {
flags = Self::NONE;
}
if matches.has(flags::SMOOTH) {
flags.smooth = true;
}
if matches.has(flags::NO_SMOOTH) {
flags.smooth = false;
}
flags
}
}
fn parse_gradient_value(s: &str) -> GradientFlags {
let mut flags = GradientFlags::NONE;
for tok in s.split(',') {
match tok.trim() {
"" => {} "none" => return GradientFlags::NONE,
"all" => return GradientFlags::ALL,
"size" | "filesize" => flags.size = true,
"date" | "timestamp" => {
flags.modified = true;
flags.accessed = true;
flags.changed = true;
flags.created = true;
}
"modified" => flags.modified = true,
"accessed" => flags.accessed = true,
"changed" => flags.changed = true,
"created" => flags.created = true,
_ => {}
}
}
flags
}
impl Definitions {
fn deduce<V: Vars>(vars: &V) -> Self {
let ls = vars
.get(vars::LS_COLORS)
.map(|e| e.to_string_lossy().to_string());
Self { ls }
}
}
#[cfg(test)]
mod terminal_test {
use super::*;
use std::ffi::OsString;
use crate::options::test::parse_for_test;
macro_rules! test {
($name:ident: $type:ident <- $inputs:expr; $result:expr) => {
#[test]
fn $name() {
for result in parse_for_test($inputs.as_ref(), |mf| $type::deduce(mf)) {
assert_eq!(result, $result);
}
}
};
($name:ident: $type:ident <- $inputs:expr, $env:expr; $result:expr) => {
#[test]
fn $name() {
let env = $env;
for result in parse_for_test($inputs.as_ref(), |mf| $type::deduce(mf, &env)) {
assert_eq!(result, $result);
}
}
};
}
struct MockVars {
ls: &'static str,
no_color: &'static str,
}
impl MockVars {
fn empty() -> MockVars {
MockVars {
ls: "",
no_color: "",
}
}
fn with_no_color() -> MockVars {
MockVars {
ls: "",
no_color: "true",
}
}
}
impl Vars for MockVars {
fn get(&self, name: &'static str) -> Option<OsString> {
if name == vars::LS_COLORS && !self.ls.is_empty() {
Some(OsString::from(self.ls))
} else if name == vars::NO_COLOR && !self.no_color.is_empty() {
Some(OsString::from(self.no_color))
} else {
None
}
}
}
test!(empty: UseColours <- [], MockVars::empty(); UseColours::Automatic);
test!(empty_with_no_color: UseColours <- [], MockVars::with_no_color(); UseColours::Never);
test!(u_always: UseColours <- ["--colour=always"], MockVars::empty(); UseColours::Always);
test!(u_auto: UseColours <- ["--colour", "auto"], MockVars::empty(); UseColours::Automatic);
test!(u_never: UseColours <- ["--colour=never"], MockVars::empty(); UseColours::Never);
test!(no_u_always: UseColours <- ["--color", "always"], MockVars::empty(); UseColours::Always);
test!(no_u_auto: UseColours <- ["--color=auto"], MockVars::empty(); UseColours::Automatic);
test!(no_u_never: UseColours <- ["--color", "never"], MockVars::empty(); UseColours::Never);
#[test]
fn no_u_error() {
let cmd = crate::options::parser::build_command();
assert!(
cmd.try_get_matches_from(["lx", "--color=upstream"])
.is_err()
);
}
#[test]
fn u_error() {
let cmd = crate::options::parser::build_command();
assert!(cmd.try_get_matches_from(["lx", "--colour=lovers"]).is_err());
}
test!(overridden_1: UseColours <- ["--colour=auto", "--colour=never"], MockVars::empty(); UseColours::Never);
test!(overridden_2: UseColours <- ["--color=auto", "--colour=never"], MockVars::empty(); UseColours::Never);
test!(overridden_3: UseColours <- ["--colour=auto", "--color=never"], MockVars::empty(); UseColours::Never);
test!(overridden_4: UseColours <- ["--color=auto", "--color=never"], MockVars::empty(); UseColours::Never);
test!(gf_default: GradientFlags <- []; GradientFlags::ALL);
test!(gf_no_gradient: GradientFlags <- ["--no-gradient"]; GradientFlags::NONE);
test!(gf_size_only: GradientFlags <- ["--gradient=size"];
GradientFlags { size: true, modified: false, accessed: false, changed: false, created: false, smooth: false });
test!(gf_smooth: GradientFlags <- ["--smooth"]; GradientFlags::ALL);
test!(gf_no_smooth_alone: GradientFlags <- ["--no-smooth"];
GradientFlags { smooth: false, ..GradientFlags::ALL });
test!(gf_smooth_then_no: GradientFlags <- ["--smooth", "--no-smooth"];
GradientFlags { smooth: false, ..GradientFlags::ALL });
test!(gf_smooth_with_gradient_size: GradientFlags <- ["--gradient=size", "--smooth"];
GradientFlags { size: true, modified: false, accessed: false, changed: false, created: false, smooth: true });
test!(gf_no_gradient_then_smooth: GradientFlags <- ["--no-gradient", "--smooth"];
GradientFlags { smooth: true, ..GradientFlags::NONE });
}