use std::io::IsTerminal;
use std::path::PathBuf;
#[derive(Debug, Clone, Copy)]
#[allow(
clippy::struct_excessive_bools,
reason = "independent yes/no facts about the environment"
)]
pub struct Env {
pub ci: bool,
pub no_color: bool,
pub stdin_tty: bool,
pub stdout_tty: bool,
pub github_actions: bool,
}
impl Env {
pub fn detect() -> Self {
let ci = std::env::var_os("CI").map(|value| value.to_string_lossy().into_owned());
let no_color = std::env::var_os("NO_COLOR").is_some_and(|value| !value.is_empty());
Self {
ci: is_truthy(ci.as_deref()),
no_color,
stdin_tty: std::io::stdin().is_terminal(),
stdout_tty: std::io::stdout().is_terminal(),
github_actions: std::env::var_os("GITHUB_ACTIONS").is_some_and(|value| value == "true"),
}
}
}
const TIMINGS_VAR: &str = "SKIF_TIMINGS";
pub fn enable_timings() {
if std::env::var_os(TIMINGS_VAR).is_some_and(|value| !value.is_empty() && value != "0") {
let _ = skif_core::timing::enable_stderr();
}
}
pub fn github_workspace() -> Option<PathBuf> {
std::env::var_os("GITHUB_WORKSPACE")
.filter(|value| !value.is_empty())
.map(PathBuf::from)
}
pub fn is_truthy(value: Option<&str>) -> bool {
match value.map(str::trim) {
None | Some("") => false,
Some(text) => !["0", "false", "no", "off"]
.iter()
.any(|falsy| text.eq_ignore_ascii_case(falsy)),
}
}
#[cfg(test)]
mod tests {
use super::is_truthy;
#[test]
fn ci_truthiness_table() {
let cases = [
(None, false),
(Some(""), false),
(Some(" "), false),
(Some("0"), false),
(Some("false"), false),
(Some("FALSE"), false),
(Some("No"), false),
(Some("off"), false),
(Some("1"), true),
(Some("true"), true),
(Some("yes"), true),
(Some("anything"), true),
];
for (value, expected) in cases {
assert_eq!(is_truthy(value), expected, "{value:?}");
}
}
}