#[cfg(feature = "pyffi")]
use pyo3::prelude::*;
use crate::termco::Colorant;
use crate::util::{Env, Environment};
#[cfg_attr(
feature = "pyffi",
pyclass(eq, eq_int, frozen, hash, module = "prettypretty.color.style")
)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum Layer {
Foreground,
Background,
}
#[cfg_attr(feature = "pyffi", pymethods)]
impl Layer {
pub fn is_foreground(&self) -> bool {
matches!(*self, Self::Foreground)
}
pub fn is_background(&self) -> bool {
matches!(*self, Self::Background)
}
pub fn offset(&self) -> u8 {
match *self {
Self::Foreground => 0,
Self::Background => 10,
}
}
}
#[cfg_attr(
feature = "pyffi",
pyclass(eq, eq_int, frozen, hash, ord, module = "prettypretty.color.style")
)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum Fidelity {
Plain,
NoColor,
Ansi,
EightBit,
TwentyFourBit,
HiRes,
}
#[cfg_attr(feature = "pyffi", pymethods)]
impl Fidelity {
#[cfg(feature = "pyffi")]
#[staticmethod]
pub fn from_color(
#[pyo3(from_py_with = crate::termco::into_colorant)] colorant: Colorant,
) -> Self {
colorant.into()
}
#[cfg(feature = "pyffi")]
#[staticmethod]
pub fn from_environment(has_tty: bool) -> Self {
fidelity_from_environment(&Env::default(), has_tty)
}
#[cfg(not(feature = "pyffi"))]
pub fn from_environment(has_tty: bool) -> Self {
fidelity_from_environment(&Env::default(), has_tty)
}
#[cfg(feature = "pyffi")]
#[pyo3(name = "covers")]
pub fn py_covers(
&self,
#[pyo3(from_py_with = crate::termco::into_colorant)] colorant: Colorant,
) -> bool {
self.covers(colorant)
}
}
impl Fidelity {
pub fn covers<C: Into<Colorant>>(&self, colorant: C) -> bool {
Fidelity::from(colorant.into()) <= *self
}
}
impl From<&Colorant> for Fidelity {
fn from(value: &Colorant) -> Self {
match *value {
Colorant::Default() | Colorant::Ansi(_) => Self::Ansi,
Colorant::Embedded(_) | Colorant::Gray(_) => Self::EightBit,
Colorant::Rgb(_) => Self::TwentyFourBit,
Colorant::HiRes(_) => Self::HiRes,
}
}
}
impl From<Colorant> for Fidelity {
fn from(value: Colorant) -> Self {
Fidelity::from(&value)
}
}
pub(crate) fn fidelity_from_environment(env: &impl Environment, has_tty: bool) -> Fidelity {
if env.is_non_empty("NO_COLOR") {
return Fidelity::NoColor;
} else if env.is_non_empty("FORCE_COLOR") {
return Fidelity::Ansi;
} else if env.is_defined("TF_BUILD") || env.is_defined("AGENT_NAME") {
return Fidelity::Ansi;
} else if !has_tty {
return Fidelity::Plain;
} else if env.has_value("TERM", "dumb") {
return Fidelity::Plain; } else if env.is_defined("CI") {
if env.is_defined("GITHUB_ACTIONS") || env.is_defined("GITEA_ACTIONS") {
return Fidelity::TwentyFourBit;
}
for ci in [
"TRAVIS",
"CIRCLECI",
"APPVEYOR",
"GITLAB_CI",
"BUILDKITE",
"DRONE",
] {
if env.is_defined(ci) {
return Fidelity::Ansi;
}
}
if env.has_value("CI_NAME", "codeship") {
return Fidelity::Ansi;
}
return Fidelity::Plain;
}
if let Ok(teamcity) = env.read("TEAMCITY_VERSION") {
let mut charity = teamcity.chars();
let c1 = charity.next();
let c2 = charity.next();
if c1
.filter(|c| *c == '9')
.and(c2.filter(|c| *c == '.'))
.or_else(|| {
c1.filter(|c| c.is_ascii_digit() && *c != '0')
.and(c2.filter(char::is_ascii_digit))
.and(charity.next().filter(|c| *c == '.'))
})
.is_some()
{
return Fidelity::Ansi;
}
return Fidelity::Plain;
} else if env.has_value("COLORTERM", "truecolor") || env.has_value("TERM", "xterm-kitty") {
return Fidelity::TwentyFourBit;
} else if env.has_value("TERM_PROGRAM", "Apple_Terminal") {
return Fidelity::EightBit;
} else if env.has_value("TERM_PROGRAM", "iTerm.app") {
if let Ok(version) = env.read("TERM_PROGRAM_VERSION") {
let mut charity = version.chars();
if charity
.next()
.filter(|c| *c == '3')
.and(charity.next().filter(|c| *c == '.'))
.is_some()
{
return Fidelity::TwentyFourBit;
}
}
return Fidelity::EightBit;
}
if let Ok(mut term) = env.read("TERM") {
term.make_ascii_lowercase();
if term.ends_with("-256") || term.ends_with("-256color") {
return Fidelity::EightBit;
} else if term.starts_with("screen")
|| term.starts_with("xterm")
|| term.starts_with("vt100")
|| term.starts_with("vt220")
|| term.starts_with("rxvt")
|| term == "color"
|| term == "ansi"
|| term == "cygwin"
|| term == "linux"
{
return Fidelity::Ansi;
}
} else if env.is_defined("COLORTERM") {
return Fidelity::Ansi;
}
Fidelity::Plain
}
#[cfg(test)]
mod test {
use super::{fidelity_from_environment, Fidelity};
use crate::util::FakeEnv;
#[test]
fn test_fidelity() {
let env = &mut FakeEnv::new();
assert_eq!(fidelity_from_environment(env, true), Fidelity::Plain);
env.set("TERM", "cygwin");
assert_eq!(fidelity_from_environment(env, true), Fidelity::Ansi);
env.set("TERM_PROGRAM", "iTerm.app");
assert_eq!(fidelity_from_environment(env, true), Fidelity::EightBit);
env.set("TERM_PROGRAM_VERSION", "3.5");
assert_eq!(
fidelity_from_environment(env, true),
Fidelity::TwentyFourBit
);
env.set("COLORTERM", "truecolor");
assert_eq!(
fidelity_from_environment(env, true),
Fidelity::TwentyFourBit
);
env.set("CI", "");
env.set("APPVEYOR", "");
assert_eq!(fidelity_from_environment(env, true), Fidelity::Ansi);
env.set("TF_BUILD", "");
assert_eq!(fidelity_from_environment(env, true), Fidelity::Ansi);
env.set("NO_COLOR", "");
assert_eq!(fidelity_from_environment(env, true), Fidelity::Ansi);
env.set("NO_COLOR", "1");
assert_eq!(fidelity_from_environment(env, true), Fidelity::NoColor);
}
}