use colored::*;
use std::env;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ColorScheme {
Dark,
Light,
}
#[derive(Debug, Clone)]
pub struct ColorAdapter {
scheme: ColorScheme,
}
impl ColorAdapter {
pub fn new() -> Self {
Self {
scheme: Self::detect_terminal_background(),
}
}
pub fn with_scheme(scheme: ColorScheme) -> Self {
Self { scheme }
}
fn detect_terminal_background() -> ColorScheme {
if let Ok(colorfgbg) = env::var("COLORFGBG")
&& let Some(bg_str) = colorfgbg.split(';').nth(1)
&& let Ok(bg_code) = bg_str.parse::<u8>()
{
return if bg_code >= 7 {
ColorScheme::Light
} else {
ColorScheme::Dark
};
}
if let Ok(term_program) = env::var("TERM_PROGRAM") {
match term_program.as_str() {
"Apple_Terminal" => {
if let Ok(_term_session_id) = env::var("TERM_SESSION_ID") {
return ColorScheme::Light;
}
return ColorScheme::Light; }
"iTerm.app" => {
if let Ok(_iterm_session_id) = env::var("ITERM_SESSION_ID") {
return ColorScheme::Dark;
}
}
"vscode" | "code" => {
return ColorScheme::Light;
}
_ => {}
}
}
if let Ok(term) = env::var("TERM") {
match term.as_str() {
term if term.contains("light") => return ColorScheme::Light,
term if term.contains("256color") => {
if env::var("TERM_PROGRAM")
.unwrap_or_default()
.contains("Terminal")
{
return ColorScheme::Light; }
}
_ => {}
}
}
if env::var("SSH_CONNECTION").is_ok() || env::var("SSH_CLIENT").is_ok() {
return ColorScheme::Dark;
}
if let Ok(bg_hint) = env::var("BACKGROUND") {
match bg_hint.to_lowercase().as_str() {
"light" | "white" => return ColorScheme::Light,
"dark" | "black" => return ColorScheme::Dark,
_ => {}
}
}
if let Ok(desktop) = env::var("XDG_CURRENT_DESKTOP") {
match desktop.to_lowercase().as_str() {
"gnome" | "kde" | "xfce" => {
return ColorScheme::Light;
}
_ => {}
}
}
if env::var("DISPLAY").is_ok() || env::var("WAYLAND_DISPLAY").is_ok() {
if env::var("TERM_PROGRAM").is_err() {
return ColorScheme::Light;
}
}
#[cfg(target_os = "macos")]
{
ColorScheme::Light }
#[cfg(not(target_os = "macos"))]
{
ColorScheme::Dark }
}
pub fn scheme(&self) -> ColorScheme {
self.scheme
}
pub fn header_text(&self, text: &str) -> ColoredString {
match self.scheme {
ColorScheme::Dark => text.bright_white().bold(),
ColorScheme::Light => text.black().bold(),
}
}
pub fn border(&self, text: &str) -> ColoredString {
match self.scheme {
ColorScheme::Dark => text.bright_blue(),
ColorScheme::Light => text.blue(),
}
}
pub fn primary(&self, text: &str) -> ColoredString {
match self.scheme {
ColorScheme::Dark => text.yellow(),
ColorScheme::Light => text.red().bold(),
}
}
pub fn secondary(&self, text: &str) -> ColoredString {
match self.scheme {
ColorScheme::Dark => text.green(),
ColorScheme::Light => text.green().bold(),
}
}
pub fn language(&self, text: &str) -> ColoredString {
match self.scheme {
ColorScheme::Dark => text.blue(),
ColorScheme::Light => text.blue().bold(),
}
}
pub fn framework(&self, text: &str) -> ColoredString {
match self.scheme {
ColorScheme::Dark => text.magenta(),
ColorScheme::Light => text.magenta().bold(),
}
}
pub fn database(&self, text: &str) -> ColoredString {
match self.scheme {
ColorScheme::Dark => text.cyan(),
ColorScheme::Light => text.cyan().bold(),
}
}
pub fn technology(&self, text: &str) -> ColoredString {
match self.scheme {
ColorScheme::Dark => text.magenta(),
ColorScheme::Light => text.purple().bold(),
}
}
pub fn info(&self, text: &str) -> ColoredString {
match self.scheme {
ColorScheme::Dark => text.cyan(),
ColorScheme::Light => text.blue().bold(),
}
}
pub fn success(&self, text: &str) -> ColoredString {
match self.scheme {
ColorScheme::Dark => text.green(),
ColorScheme::Light => text.green().bold(),
}
}
pub fn warning(&self, text: &str) -> ColoredString {
match self.scheme {
ColorScheme::Dark => text.yellow(),
ColorScheme::Light => text.red(),
}
}
pub fn error(&self, text: &str) -> ColoredString {
match self.scheme {
ColorScheme::Dark => text.red(),
ColorScheme::Light => text.red().bold(),
}
}
pub fn label(&self, text: &str) -> ColoredString {
match self.scheme {
ColorScheme::Dark => text.bright_white(),
ColorScheme::Light => text.black().bold(),
}
}
pub fn value(&self, text: &str) -> ColoredString {
match self.scheme {
ColorScheme::Dark => text.white(),
ColorScheme::Light => text.black(),
}
}
pub fn dimmed(&self, text: &str) -> ColoredString {
match self.scheme {
ColorScheme::Dark => text.dimmed(),
ColorScheme::Light => text.dimmed(),
}
}
pub fn architecture_pattern(&self, text: &str) -> ColoredString {
match self.scheme {
ColorScheme::Dark => text.green(),
ColorScheme::Light => text.green().bold(),
}
}
pub fn project_type(&self, text: &str) -> ColoredString {
match self.scheme {
ColorScheme::Dark => text.yellow(),
ColorScheme::Light => text.red().bold(),
}
}
pub fn metric(&self, text: &str) -> ColoredString {
match self.scheme {
ColorScheme::Dark => text.cyan(),
ColorScheme::Light => text.blue().bold(),
}
}
pub fn path(&self, text: &str) -> ColoredString {
match self.scheme {
ColorScheme::Dark => text.cyan().bold(),
ColorScheme::Light => text.blue().bold(),
}
}
pub fn confidence_high(&self, text: &str) -> ColoredString {
match self.scheme {
ColorScheme::Dark => text.green(),
ColorScheme::Light => text.green().bold(),
}
}
pub fn confidence_medium(&self, text: &str) -> ColoredString {
match self.scheme {
ColorScheme::Dark => text.yellow(),
ColorScheme::Light => text.red(),
}
}
pub fn confidence_low(&self, text: &str) -> ColoredString {
match self.scheme {
ColorScheme::Dark => text.red(),
ColorScheme::Light => text.red().bold(),
}
}
}
impl Default for ColorAdapter {
fn default() -> Self {
Self::new()
}
}
static COLOR_ADAPTER: std::sync::OnceLock<ColorAdapter> = std::sync::OnceLock::new();
pub fn get_color_adapter() -> &'static ColorAdapter {
COLOR_ADAPTER.get_or_init(ColorAdapter::new)
}
pub fn init_color_adapter(scheme: ColorScheme) {
let _ = COLOR_ADAPTER.set(ColorAdapter::with_scheme(scheme));
}
#[macro_export]
macro_rules! color {
($method:ident, $text:expr) => {
$crate::analyzer::display::color_adapter::get_color_adapter().$method($text)
};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_color_adapter_creation() {
let adapter = ColorAdapter::new();
assert!(matches!(
adapter.scheme(),
ColorScheme::Dark | ColorScheme::Light
));
}
#[test]
#[ignore] fn test_color_scheme_specific() {
let dark_adapter = ColorAdapter::with_scheme(ColorScheme::Dark);
let light_adapter = ColorAdapter::with_scheme(ColorScheme::Light);
assert_eq!(dark_adapter.scheme(), ColorScheme::Dark);
assert_eq!(light_adapter.scheme(), ColorScheme::Light);
let test_text = "test";
let dark_result = dark_adapter.header_text(test_text).to_string();
let light_result = light_adapter.header_text(test_text).to_string();
assert_ne!(dark_result, light_result);
}
#[test]
fn test_color_methods() {
let adapter = ColorAdapter::with_scheme(ColorScheme::Dark);
let text = "test";
let _ = adapter.header_text(text);
let _ = adapter.border(text);
let _ = adapter.primary(text);
let _ = adapter.secondary(text);
let _ = adapter.language(text);
let _ = adapter.framework(text);
let _ = adapter.database(text);
let _ = adapter.technology(text);
let _ = adapter.info(text);
let _ = adapter.success(text);
let _ = adapter.warning(text);
let _ = adapter.error(text);
let _ = adapter.label(text);
let _ = adapter.value(text);
let _ = adapter.dimmed(text);
let _ = adapter.architecture_pattern(text);
let _ = adapter.project_type(text);
let _ = adapter.metric(text);
let _ = adapter.path(text);
let _ = adapter.confidence_high(text);
let _ = adapter.confidence_medium(text);
let _ = adapter.confidence_low(text);
}
}