1pub mod windows;
2
3/// Check [CLICOLOR] status
4///
5/// - When `true`, ANSI colors are supported and should be used when the program isn't piped,
6/// similar to [`term_supports_color`]
7/// - When `false`, don’t output ANSI color escape codes, similar to [`no_color`]
8///
9/// See also:
10/// - [terminfo](https://crates.io/crates/terminfo) or [term](https://crates.io/crates/term) for
11/// checking termcaps
12/// - [termbg](https://crates.io/crates/termbg) for detecting background color
13///
14/// [CLICOLOR]: https://bixense.com/clicolors/
15#[inline]
16pub fn clicolor() -> Option<bool> {
17 let value: OsString = std::env::var_os(key:"CLICOLOR")?;
18 Some(value != "0")
19}
20
21/// Check [CLICOLOR_FORCE] status
22///
23/// ANSI colors should be enabled no matter what.
24///
25/// [CLICOLOR_FORCE]: https://bixense.com/clicolors/
26#[inline]
27pub fn clicolor_force() -> bool {
28 let value: Option = std::env::var_os(key:"CLICOLOR_FORCE");
29 valueOption<&OsStr>
30 .as_deref()
31 .unwrap_or_else(|| std::ffi::OsStr::new("0"))
32 != "0"
33}
34
35/// Check [NO_COLOR] status
36///
37/// When `true`, should prevent the addition of ANSI color.
38///
39/// User-level configuration files and per-instance command-line arguments should override
40/// [NO_COLOR]. A user should be able to export `$NO_COLOR` in their shell configuration file as a
41/// default, but configure a specific program in its configuration file to specifically enable
42/// color.
43///
44/// [NO_COLOR]: https://no-color.org/
45#[inline]
46pub fn no_color() -> bool {
47 let value: Option = std::env::var_os(key:"NO_COLOR");
48 value.as_deref().unwrap_or_else(|| std::ffi::OsStr::new("")) != ""
49}
50
51/// Check `TERM` for color support
52#[inline]
53#[cfg(not(windows))]
54pub fn term_supports_color() -> bool {
55 match std::env::var_os(key:"TERM") {
56 // If TERM isn't set, then we are in a weird environment that
57 // probably doesn't support colors.
58 None => return false,
59 Some(k: OsString) => {
60 if k == "dumb" {
61 return false;
62 }
63 }
64 }
65 true
66}
67
68/// Check `TERM` for color support
69#[inline]
70#[cfg(windows)]
71pub fn term_supports_color() -> bool {
72 // On Windows, if TERM isn't set, then we shouldn't automatically
73 // assume that colors aren't allowed. This is unlike Unix environments
74 // where TERM is more rigorously set.
75 if let Some(k) = std::env::var_os("TERM") {
76 if k == "dumb" {
77 return false;
78 }
79 }
80 true
81}
82
83/// Check `TERM` for ANSI color support
84#[inline]
85#[cfg(not(windows))]
86pub fn term_supports_ansi_color() -> bool {
87 term_supports_color()
88}
89
90/// Check `TERM` for ANSI color support
91#[inline]
92#[cfg(windows)]
93pub fn term_supports_ansi_color() -> bool {
94 match std::env::var_os("TERM") {
95 // If TERM isn't set, then we are in a weird environment that
96 // probably doesn't support ansi.
97 None => return false,
98 Some(k) => {
99 // cygwin doesn't seem to support ANSI escape sequences
100 // and instead has its own variety. However, the Windows
101 // console API may be available.
102 if k == "dumb" || k == "cygwin" {
103 return false;
104 }
105 }
106 }
107 true
108}
109
110/// Check [COLORTERM] for truecolor support
111///
112/// [COLORTERM]: https://github.com/termstandard/colors
113#[inline]
114pub fn truecolor() -> bool {
115 let value: Option = std::env::var_os(key:"COLORTERM");
116 let value: &OsStr = value.as_deref().unwrap_or_default();
117 value == "truecolor" || value == "24bit"
118}
119
120/// Report whether this is running in CI
121///
122/// CI is a common environment where, despite being piped, ansi color codes are supported
123///
124/// This is not as exhaustive as you'd find in a crate like `is_ci` but it should work in enough
125/// cases.
126#[inline]
127pub fn is_ci() -> bool {
128 // Assuming its CI based on presence because who would be setting `CI=false`?
129 //
130 // This makes it easier to all of the potential values when considering our known values:
131 // - Gitlab and Github set it to `true`
132 // - Woodpecker sets it to `woodpecker`
133 std::env::var_os(key:"CI").is_some()
134}
135