1use crate::theme::is_light_theme;
5use owo_colors::{OwoColorize, XtermColors};
6use std::env;
7use std::path::Path;
8
9pub use owo_colors as owo;
10pub use owo_colors::Style as OwoStyle;
11
12pub enum Color {
14 White = 15,
15 Black = 16,
16 Teal = 36,
17 Cyan = 38,
18 Blue = 39,
19 Green = 41,
20 Purple = 111,
21 Lime = 112,
22 Lavender = 147,
23 Red = 161,
24 Brown = 172,
25 Pink = 183,
26 Yellow = 185,
27 Orange = 208,
28 Gray = 239,
29 GrayLight = 246,
30}
31
32pub type DarkColor = Color;
34
35pub enum LightColor {
37 White = 15,
38 Black = 16,
39 Teal = 29,
40 Cyan = 30,
41 Blue = 25,
42 Green = 28,
43 Purple = 93,
44 Lime = 101,
45 Lavender = 135,
46 Red = 160,
47 Brown = 94,
48 Pink = 170,
49 Yellow = 178,
50 Orange = 202,
51 Gray = 238,
52 GrayLight = 241,
53}
54
55#[derive(Clone, Debug, PartialEq)]
57pub enum Style {
58 Tag(String),
59
60 Caution,
62 Failure,
63 Invalid,
64 Muted,
65 MutedLight,
66 Success,
67
68 File, Hash, Id, Label, Path, Property, Shell, Symbol, Url, }
79
80impl Style {
81 pub fn ansi_color(&self) -> u8 {
83 if is_light_theme() {
84 self.light_color() as u8
85 } else {
86 self.dark_color() as u8
87 }
88 }
89
90 pub fn color(&self) -> Color {
92 self.dark_color()
93 }
94
95 pub fn dark_color(&self) -> DarkColor {
97 match self {
98 Style::Caution => DarkColor::Orange,
99 Style::Failure => DarkColor::Red,
100 Style::Invalid => DarkColor::Yellow,
101 Style::Muted => DarkColor::Gray,
102 Style::MutedLight => DarkColor::GrayLight,
103 Style::Success => DarkColor::Green,
104 Style::File => DarkColor::Teal,
105 Style::Hash => DarkColor::Green,
106 Style::Id => DarkColor::Purple,
107 Style::Label => DarkColor::Blue,
108 Style::Path => DarkColor::Cyan,
109 Style::Property => DarkColor::Lavender,
110 Style::Shell => DarkColor::Pink,
111 Style::Symbol => DarkColor::Lime,
112 Style::Url => DarkColor::Blue,
113 Style::Tag(_) => DarkColor::White,
114 }
115 }
116
117 pub fn light_color(&self) -> LightColor {
119 match self {
120 Style::Caution => LightColor::Orange,
121 Style::Failure => LightColor::Red,
122 Style::Invalid => LightColor::Yellow,
123 Style::Muted => LightColor::Gray,
124 Style::MutedLight => LightColor::GrayLight,
125 Style::Success => LightColor::Green,
126 Style::File => LightColor::Teal,
127 Style::Hash => LightColor::Green,
128 Style::Id => LightColor::Purple,
129 Style::Label => LightColor::Blue,
130 Style::Path => LightColor::Cyan,
131 Style::Property => LightColor::Lavender,
132 Style::Shell => LightColor::Pink,
133 Style::Symbol => LightColor::Lime,
134 Style::Url => LightColor::Blue,
135 Style::Tag(_) => LightColor::Black,
136 }
137 }
138}
139
140pub fn create_style(color: u8) -> OwoStyle {
142 OwoStyle::new().color(XtermColors::from(color))
143}
144
145pub fn paint<T: AsRef<str>>(color: u8, value: T) -> String {
148 if no_color() {
149 value.as_ref().to_string()
150 } else {
151 value.as_ref().style(create_style(color)).to_string()
152 }
153}
154
155pub fn paint_style<T: AsRef<str>>(style: Style, value: T) -> String {
157 if matches!(style, Style::File | Style::Path | Style::Shell) {
158 paint(style.ansi_color(), clean_path(value.as_ref()))
159 } else {
160 paint(style.ansi_color(), value)
161 }
162}
163
164pub fn caution<T: AsRef<str>>(value: T) -> String {
168 paint_style(Style::Caution, value)
169}
170
171pub fn failure<T: AsRef<str>>(value: T) -> String {
173 paint_style(Style::Failure, value)
174}
175
176pub fn invalid<T: AsRef<str>>(value: T) -> String {
178 paint_style(Style::Invalid, value)
179}
180
181pub fn muted<T: AsRef<str>>(value: T) -> String {
183 paint_style(Style::Muted, value)
184}
185
186pub fn muted_light<T: AsRef<str>>(value: T) -> String {
188 paint_style(Style::MutedLight, value)
189}
190
191pub fn success<T: AsRef<str>>(value: T) -> String {
193 paint_style(Style::Success, value)
194}
195
196pub fn file<T: AsRef<str>>(path: T) -> String {
200 paint_style(Style::File, path)
201}
202
203pub fn hash<T: AsRef<str>>(value: T) -> String {
205 paint_style(Style::Hash, value)
206}
207
208pub fn id<T: AsRef<str>>(value: T) -> String {
210 paint_style(Style::Id, value)
211}
212
213pub fn label<T: AsRef<str>>(value: T) -> String {
215 paint_style(Style::Label, value)
216}
217
218pub fn path<T: AsRef<Path>>(path: T) -> String {
220 paint_style(Style::Path, path.as_ref().to_str().unwrap_or("<unknown>"))
221}
222
223#[cfg(feature = "relative-path")]
225pub fn rel_path<T: AsRef<relative_path::RelativePath>>(path: T) -> String {
226 paint_style(Style::Path, path.as_ref().as_str())
227}
228
229pub fn property<T: AsRef<str>>(value: T) -> String {
231 paint_style(Style::Property, value)
232}
233
234pub fn shell<T: AsRef<str>>(cmd: T) -> String {
236 paint_style(Style::Shell, cmd)
237}
238
239pub fn symbol<T: AsRef<str>>(value: T) -> String {
241 paint_style(Style::Symbol, value)
242}
243
244pub fn url<T: AsRef<str>>(url: T) -> String {
246 paint_style(Style::Url, url)
247}
248
249pub fn clean_path<T: AsRef<str>>(path: T) -> String {
253 let path = path.as_ref();
254
255 #[cfg(not(target_arch = "wasm32"))]
256 if let Some(home) = dirs::home_dir() {
257 return path.replace(home.to_str().unwrap_or_default(), "~");
258 }
259
260 path.to_string()
261}
262
263pub fn log_target<T: AsRef<str>>(value: T) -> String {
266 let value = value.as_ref();
267 let mut hash: u32 = 0;
268
269 for b in value.bytes() {
270 hash = (hash << 5).wrapping_sub(hash) + b as u32;
271 }
272
273 if supports_color() >= 2 {
275 let mut list = vec![];
276
277 if is_light_theme() {
278 list.extend(COLOR_LIST_LIGHT);
279 } else {
280 list.extend(COLOR_LIST_DARK);
281 };
282
283 let index = i32::abs(hash as i32) as usize % list.len();
284
285 return paint(list[index], value);
286 }
287
288 let index = i32::abs(hash as i32) as usize % COLOR_LIST_UNSUPPORTED.len();
289
290 paint(COLOR_LIST_UNSUPPORTED[index], value)
291}
292
293#[cfg(not(target_arch = "wasm32"))]
295pub fn no_color() -> bool {
296 env::var("NO_COLOR").is_ok() || supports_color::on(supports_color::Stream::Stderr).is_none()
297}
298
299#[cfg(target_arch = "wasm32")]
300pub fn no_color() -> bool {
301 true
302}
303
304pub fn supports_color() -> u8 {
307 if no_color() {
308 return 0;
309 }
310
311 if let Some(support) = supports_color::on(supports_color::Stream::Stderr) {
312 if support.has_16m {
313 return 3;
314 } else if support.has_256 {
315 return 2;
316 } else if support.has_basic {
317 return 1;
318 }
319 }
320
321 1
322}
323
324pub(crate) const COLOR_LIST_DARK: [u8; 76] = [
325 20, 21, 26, 27, 32, 33, 38, 39, 40, 41, 42, 43, 44, 45, 56, 57, 62, 63, 68, 69, 74, 75, 76, 77,
326 78, 79, 80, 81, 92, 93, 98, 99, 112, 113, 128, 129, 134, 135, 148, 149, 160, 161, 162, 163,
327 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 178, 179, 184, 185, 196, 197, 198, 199, 200,
328 201, 202, 203, 204, 205, 206, 207, 208, 209, 214, 215, 220, 221,
329];
330
331pub(crate) const COLOR_LIST_LIGHT: [u8; 72] = [
332 20, 21, 26, 27, 32, 33, 38, 39, 40, 41, 42, 43, 44, 45, 56, 57, 62, 63, 68, 69, 74, 75, 76, 77,
333 78, 79, 80, 81, 92, 93, 98, 99, 112, 113, 128, 129, 127, 126, 142, 143, 160, 161, 162, 163,
334 164, 165, 166, 167, 168, 169, 172, 173, 178, 179, 196, 197, 198, 199, 200, 201, 202, 203, 204,
335 205, 206, 207, 208, 209, 214, 215, 220, 221,
336];
337
338pub(crate) const COLOR_LIST_UNSUPPORTED: [u8; 6] = [6, 2, 3, 4, 5, 1];