use std::env;
use std::io::{self, IsTerminal};
const DEFAULT_WIDTH: u16 = 80;
const DEFAULT_HEIGHT: u16 = 24;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ColorSupport {
None,
Ansi16,
TrueColor,
}
pub fn terminal_size() -> (u16, u16) {
let env_cols = env_dimension("COLUMNS");
let env_rows = env_dimension("LINES");
if let (Some(cols), Some(rows)) = (env_cols, env_rows) {
return (cols, rows);
}
let (cols, rows) =
crossterm::terminal::size().unwrap_or((DEFAULT_WIDTH, DEFAULT_HEIGHT));
(env_cols.unwrap_or(cols), env_rows.unwrap_or(rows))
}
fn env_dimension(key: &str) -> Option<u16> {
let value = env::var(key).ok()?;
value.trim().parse::<u16>().ok().filter(|&n| n > 0)
}
pub fn term_width() -> u16 {
terminal_size().0
}
pub fn term_height() -> u16 {
terminal_size().1
}
fn no_tty_override() -> bool {
matches!(env::var("SPARCLI_NO_TTY"), Ok(value)
if !value.is_empty() && value != "0")
}
pub fn is_output_tty() -> bool {
!no_tty_override() && io::stdout().is_terminal()
}
pub fn is_input_tty() -> bool {
!no_tty_override()
&& io::stdin().is_terminal()
&& io::stdout().is_terminal()
}
fn env_enabled(key: &str) -> bool {
matches!(env::var(key), Ok(value) if !value.is_empty() && value != "0")
}
fn colorterm_truecolor() -> bool {
let Ok(value) = env::var("COLORTERM") else {
return false;
};
let value = value.to_lowercase();
value.contains("truecolor") || value.contains("24bit")
}
pub fn color_support() -> ColorSupport {
if env::var_os("NO_COLOR").is_some() {
return ColorSupport::None;
}
let forced = env_enabled("CLICOLOR_FORCE");
if !forced && !is_output_tty() {
return ColorSupport::None;
}
if colorterm_truecolor() {
ColorSupport::TrueColor
} else {
ColorSupport::Ansi16
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn terminal_size_has_sensible_fallback() {
let (width, height) = terminal_size();
assert!(width >= 1);
assert!(height >= 1);
}
}