rush_sync_server/ui/
color.rs1use crate::core::prelude::*;
6use log::Level;
7use once_cell::sync::Lazy;
8use std::collections::HashMap;
9
10#[derive(Debug, Clone, Copy, PartialEq)]
11pub struct AppColor(Color);
12
13static COLOR_MAP: Lazy<HashMap<&'static str, Color>> = Lazy::new(|| {
15 let mut map = HashMap::new();
16
17 map.insert("black", Color::Black);
19 map.insert("red", Color::Red);
20 map.insert("green", Color::Green);
21 map.insert("yellow", Color::Yellow);
22 map.insert("blue", Color::Blue);
23 map.insert("magenta", Color::Magenta);
24 map.insert("cyan", Color::Cyan);
25 map.insert("gray", Color::Gray);
26 map.insert("darkgray", Color::DarkGray);
27 map.insert("lightred", Color::LightRed);
28 map.insert("lightgreen", Color::LightGreen);
29 map.insert("lightyellow", Color::LightYellow);
30 map.insert("lightblue", Color::LightBlue);
31 map.insert("lightmagenta", Color::LightMagenta);
32 map.insert("lightcyan", Color::LightCyan);
33 map.insert("white", Color::White);
34
35 map.insert("error", Color::Red);
37 map.insert("warning", Color::Yellow);
38 map.insert("warn", Color::Yellow);
39 map.insert("info", Color::Green);
40 map.insert("debug", Color::Blue);
41 map.insert("trace", Color::White);
42 map.insert("lang", Color::Cyan);
43 map.insert("version", Color::LightBlue);
44 map.insert("startup", Color::Magenta);
45 map.insert("theme", Color::LightMagenta);
46
47 map
48});
49
50static DISPLAY_COLOR_MAP: Lazy<HashMap<&'static str, Color>> = Lazy::new(|| {
52 let mut map = HashMap::new();
53
54 map.insert("CONFIRM", Color::Yellow);
58 map.insert("BESTÄTIGEN", Color::Yellow);
59
60 map.insert("ERROR", Color::Red);
62 map.insert("FEHLER", Color::Red);
63 map.insert("RENDER", Color::Red);
64
65 map.insert("WARN", Color::Yellow);
67 map.insert("WARNING", Color::Yellow);
68 map.insert("TERMINAL", Color::Yellow);
69
70 map.insert("INFO", Color::Green);
72 map.insert("CLIPBOARD", Color::Green);
73 map.insert("HISTORY", Color::Green);
74 map.insert("HISTORIE", Color::Green);
75 map.insert("LOG_LEVEL", Color::Green);
76 map.insert("SYSTEM", Color::Green);
77
78 map.insert("DEBUG", Color::Blue);
80
81 map.insert("TRACE", Color::White);
83
84 map.insert("THEME", Color::LightMagenta); map.insert("LANG", Color::Cyan); map.insert("SPRACHE", Color::Cyan); map.insert("VERSION", Color::LightBlue); map.insert("READY", Color::Magenta); map.insert("BEREIT", Color::Magenta); map
93});
94
95impl AppColor {
96 pub fn new(color: Color) -> Self {
97 Self(color)
98 }
99
100 pub fn from_display_text(display_text: &str) -> Self {
102 let normalized = display_text.trim().to_uppercase();
103
104 let color = DISPLAY_COLOR_MAP
106 .get(normalized.as_str())
107 .copied()
108 .unwrap_or(Color::Green); Self(color)
111 }
112
113 pub fn from_category(category: &str) -> Self {
115 let normalized = category.trim().to_lowercase();
116 let color = COLOR_MAP
117 .get(normalized.as_str())
118 .copied()
119 .unwrap_or(Color::Green);
120 Self(color)
121 }
122
123 pub fn from_any<T: Into<String>>(source: T) -> Self {
125 let key = source.into().to_lowercase();
126 let color = COLOR_MAP.get(key.as_str()).copied().unwrap_or(Color::Green);
127 Self(color)
128 }
129
130 pub fn from_log_level(level: Level) -> Self {
131 Self::from_category(&level.to_string())
132 }
133
134 pub fn from_string(color_str: &str) -> crate::core::error::Result<Self> {
135 let normalized = color_str.trim().to_lowercase();
136 let color = COLOR_MAP
137 .get(normalized.as_str())
138 .copied()
139 .ok_or_else(|| AppError::Validation(format!("Invalid color: {}", color_str)))?;
140 Ok(Self(color))
141 }
142
143 pub fn from_display_text_with_timing(display_text: &str) -> (Self, std::time::Duration) {
145 let start = std::time::Instant::now();
146 let color = Self::from_display_text(display_text);
147 let duration = start.elapsed();
148 (color, duration)
149 }
150
151 pub fn available_display_texts() -> Vec<&'static str> {
153 DISPLAY_COLOR_MAP.keys().copied().collect()
154 }
155
156 pub fn available_categories() -> Vec<&'static str> {
157 COLOR_MAP.keys().copied().collect()
158 }
159
160 pub fn format_message(&self, level: &str, message: &str) -> String {
162 if level.is_empty() {
163 format!("\x1B[{}m{}\x1B[0m", self.to_ansi_code(), message)
164 } else {
165 format!(
166 "\x1B[{}m[{}] {}\x1B[0m",
167 self.to_ansi_code(),
168 level,
169 message
170 )
171 }
172 }
173
174 pub fn to_ansi_code(&self) -> u8 {
175 match self.0 {
176 Color::Black => 30,
177 Color::Red => 31,
178 Color::Green => 32,
179 Color::Yellow => 33,
180 Color::Blue => 34,
181 Color::Magenta => 35,
182 Color::Cyan => 36,
183 Color::Gray => 37,
184 Color::DarkGray => 90,
185 Color::LightRed => 91,
186 Color::LightGreen => 92,
187 Color::LightYellow => 93,
188 Color::LightBlue => 94,
189 Color::LightMagenta => 95,
190 Color::LightCyan => 96,
191 Color::White => 97,
192 _ => 37,
193 }
194 }
195
196 pub fn to_name(&self) -> &'static str {
197 COLOR_MAP
198 .iter()
199 .find(|(_, &v)| v == self.0)
200 .map(|(k, _)| *k)
201 .unwrap_or("gray")
202 }
203}
204
205impl From<AppColor> for Color {
207 fn from(app_color: AppColor) -> Self {
208 app_color.0
209 }
210}
211
212impl From<&AppColor> for Color {
213 fn from(app_color: &AppColor) -> Self {
214 app_color.0
215 }
216}
217
218impl Default for AppColor {
219 fn default() -> Self {
220 Self(Color::Gray)
221 }
222}