rush_sync_server/ui/
color.rs1use crate::core::prelude::*;
2use log::Level;
3use once_cell::sync::Lazy;
4use std::collections::HashMap;
5
6#[derive(Debug, Clone, Copy, PartialEq)]
7pub struct AppColor(Color);
8
9static COLOR_MAP: Lazy<HashMap<&'static str, Color>> = Lazy::new(|| {
10 let mut map = HashMap::new();
11
12 map.insert("black", Color::Black);
14 map.insert("red", Color::Red);
15 map.insert("green", Color::Green);
16 map.insert("yellow", Color::Yellow);
17 map.insert("blue", Color::Blue);
18 map.insert("magenta", Color::Magenta);
19 map.insert("cyan", Color::Cyan);
20 map.insert("gray", Color::Gray);
21 map.insert("darkgray", Color::DarkGray);
22 map.insert("lightred", Color::LightRed);
23 map.insert("lightgreen", Color::LightGreen);
24 map.insert("lightyellow", Color::LightYellow);
25 map.insert("lightblue", Color::LightBlue);
26 map.insert("lightmagenta", Color::LightMagenta);
27 map.insert("lightcyan", Color::LightCyan);
28 map.insert("white", Color::White);
29
30 map.insert("error", Color::Red);
32 map.insert("warning", Color::Yellow);
33 map.insert("warn", Color::Yellow);
34 map.insert("info", Color::Green);
35 map.insert("debug", Color::Blue);
36 map.insert("trace", Color::White);
37 map.insert("lang", Color::Cyan);
38 map.insert("version", Color::LightBlue);
39 map.insert("startup", Color::Magenta);
40
41 map
42});
43
44impl AppColor {
45 pub fn new(color: Color) -> Self {
46 Self(color)
47 }
48
49 pub fn from_any<T: Into<String>>(source: T) -> Self {
55 let key = source.into().to_lowercase();
56
57 if let Some(&color) = COLOR_MAP.get(key.as_str()) {
59 log::debug!("✅ Direct color lookup: '{}' → {:?}", key, color);
60 return Self(color);
61 }
62
63 log::debug!("⚠️ Using i18n category mapping for: '{}'", key);
65 let mapped_category = crate::i18n::get_color_category_for_display(&key);
66 let fallback_color = COLOR_MAP
67 .get(mapped_category.as_str())
68 .unwrap_or(&Color::Gray);
69
70 log::debug!(
71 "📍 Category mapping: '{}' → '{}' → {:?}",
72 key,
73 mapped_category,
74 fallback_color
75 );
76 Self(*fallback_color)
77 }
78
79 pub fn from_log_level(level: Level) -> Self {
80 Self::from_any(level.to_string())
81 }
82
83 pub fn from_string(color_str: &str) -> crate::core::error::Result<Self> {
84 let normalized = color_str.trim().to_lowercase();
85
86 log::debug!(
88 "🎨 AppColor::from_string: '{}' → normalized: '{}'",
89 color_str,
90 normalized
91 );
92
93 let color = COLOR_MAP.get(normalized.as_str()).copied().ok_or_else(|| {
95 log::error!("❌ Color '{}' not found in COLOR_MAP", normalized);
96 log::debug!(
97 "📋 Available colors: {:?}",
98 COLOR_MAP.keys().collect::<Vec<_>>()
99 );
100 AppError::Validation(format!("Invalid color: {}", color_str))
101 })?;
102
103 let app_color = Self(color);
104
105 log::debug!(
107 "✅ Color '{}' → '{}' → RGB({:?})",
108 color_str,
109 app_color.to_name(),
110 color
111 );
112
113 Ok(app_color)
114 }
115
116 pub fn format_message(&self, level: &str, message: &str) -> String {
117 if level.is_empty() {
118 format!("\x1B[{}m{}\x1B[0m", self.to_ansi_code(), message)
119 } else {
120 format!(
121 "\x1B[{}m[{}] {}\x1B[0m",
122 self.to_ansi_code(),
123 level,
124 message
125 )
126 }
127 }
128
129 pub fn to_ansi_code(&self) -> u8 {
130 match self.0 {
131 Color::Black => 30,
132 Color::Red => 31,
133 Color::Green => 32,
134 Color::Yellow => 33,
135 Color::Blue => 34,
136 Color::Magenta => 35,
137 Color::Cyan => 36,
138 Color::Gray => 37,
139 Color::DarkGray => 90,
140 Color::LightRed => 91,
141 Color::LightGreen => 92,
142 Color::LightYellow => 93,
143 Color::LightBlue => 94,
144 Color::LightMagenta => 95,
145 Color::LightCyan => 96,
146 Color::White => 97,
147 _ => 37,
148 }
149 }
150
151 pub fn to_name(&self) -> &'static str {
152 COLOR_MAP
153 .iter()
154 .find(|(_, &v)| v == self.0)
155 .map(|(k, _)| *k)
156 .unwrap_or("gray")
157 }
158}
159
160impl From<AppColor> for Color {
161 fn from(app_color: AppColor) -> Self {
162 app_color.0
163 }
164}
165
166impl From<&AppColor> for Color {
167 fn from(app_color: &AppColor) -> Self {
168 app_color.0
169 }
170}
171
172impl Default for AppColor {
173 fn default() -> Self {
174 Self(Color::Gray)
175 }
176}