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