weathr 1.2.3

A terminal-based ASCII weather application with animated scenes driven by real-time weather data
Documentation
use crossterm::style::Color;
use std::env;
use std::io::IsTerminal;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ColorSupport {
    None,
    Basic,
    Ansi256,
    TrueColor,
}

#[derive(Debug, Clone)]
pub struct TerminalCapabilities {
    pub color_support: ColorSupport,
    #[allow(dead_code)]
    pub is_tty: bool,
}

impl TerminalCapabilities {
    pub fn detect() -> Self {
        let is_tty = std::io::stdout().is_terminal();

        if env::var("NO_COLOR").is_ok() {
            return Self {
                color_support: ColorSupport::None,
                is_tty,
            };
        }

        if env::var("TERM").is_ok_and(|term| term == "dumb") {
            return Self {
                color_support: ColorSupport::None,
                is_tty,
            };
        }

        if !is_tty {
            return Self {
                color_support: ColorSupport::None,
                is_tty,
            };
        }

        let color_support = if let Ok(colorterm) = env::var("COLORTERM") {
            if colorterm == "truecolor" || colorterm == "24bit" {
                ColorSupport::TrueColor
            } else {
                check_term_for_256()
            }
        } else {
            check_term_for_256()
        };

        Self {
            color_support,
            is_tty,
        }
    }

    pub fn adjust_color(&self, color: Color) -> Color {
        if self.color_support == ColorSupport::None {
            return Color::Reset;
        }

        match self.color_support {
            ColorSupport::None => Color::Reset,
            ColorSupport::Basic => match color {
                Color::Rgb { .. } => Color::White,
                _ => color,
            },
            ColorSupport::Ansi256 => color,
            ColorSupport::TrueColor => color,
        }
    }
}

fn check_term_for_256() -> ColorSupport {
    if env::var("TERM").is_ok_and(|term| term.contains("256color")) {
        return ColorSupport::Ansi256;
    }
    ColorSupport::Basic
}

#[cfg(test)]
mod tests {
    use super::*;
    use crossterm::style::Color;

    #[test]
    fn test_adjust_color_none() {
        let caps = TerminalCapabilities {
            color_support: ColorSupport::None,
            is_tty: true,
        };
        assert_eq!(caps.adjust_color(Color::Red), Color::Reset);
        assert_eq!(
            caps.adjust_color(Color::Rgb { r: 255, g: 0, b: 0 }),
            Color::Reset
        );
    }

    #[test]
    fn test_adjust_color_basic() {
        let caps = TerminalCapabilities {
            color_support: ColorSupport::Basic,
            is_tty: true,
        };
        assert_eq!(caps.adjust_color(Color::Red), Color::Red);
        assert_eq!(
            caps.adjust_color(Color::Rgb { r: 255, g: 0, b: 0 }),
            Color::White
        );
    }

    #[test]
    fn test_adjust_color_ansi256() {
        let caps = TerminalCapabilities {
            color_support: ColorSupport::Ansi256,
            is_tty: true,
        };
        assert_eq!(caps.adjust_color(Color::Red), Color::Red);
        let rgb = Color::Rgb { r: 255, g: 0, b: 0 };
        assert_eq!(caps.adjust_color(rgb), rgb);
    }

    #[test]
    fn test_adjust_color_truecolor() {
        let caps = TerminalCapabilities {
            color_support: ColorSupport::TrueColor,
            is_tty: true,
        };
        assert_eq!(caps.adjust_color(Color::Red), Color::Red);
        let rgb = Color::Rgb { r: 255, g: 0, b: 0 };
        assert_eq!(caps.adjust_color(rgb), rgb);
    }
}