error_forge/
console_theme.rs1pub struct ConsoleTheme {
8 error_color: String,
9 warning_color: String,
10 info_color: String,
11 success_color: String,
12 caption_color: String,
13 reset: String,
14 bold: String,
15 dim: String,
16}
17
18impl Default for ConsoleTheme {
19 fn default() -> Self {
20 Self {
21 error_color: "\x1b[31m".to_string(), warning_color: "\x1b[33m".to_string(), info_color: "\x1b[34m".to_string(), success_color: "\x1b[32m".to_string(), caption_color: "\x1b[36m".to_string(), reset: "\x1b[0m".to_string(),
27 bold: "\x1b[1m".to_string(),
28 dim: "\x1b[2m".to_string(),
29 }
30 }
31}
32
33impl ConsoleTheme {
34 pub fn new() -> Self {
36 Self::default()
37 }
38
39 pub fn plain() -> Self {
41 Self {
42 error_color: "".to_string(),
43 warning_color: "".to_string(),
44 info_color: "".to_string(),
45 success_color: "".to_string(),
46 caption_color: "".to_string(),
47 reset: "".to_string(),
48 bold: "".to_string(),
49 dim: "".to_string(),
50 }
51 }
52
53 pub fn error(&self, text: &str) -> String {
55 format!("{}{}{}", self.error_color, text, self.reset)
56 }
57
58 pub fn warning(&self, text: &str) -> String {
60 format!("{}{}{}", self.warning_color, text, self.reset)
61 }
62
63 pub fn info(&self, text: &str) -> String {
65 format!("{}{}{}", self.info_color, text, self.reset)
66 }
67
68 pub fn success(&self, text: &str) -> String {
70 format!("{}{}{}", self.success_color, text, self.reset)
71 }
72
73 pub fn caption(&self, text: &str) -> String {
75 format!("{}{}{}", self.caption_color, text, self.reset)
76 }
77
78 pub fn bold(&self, text: &str) -> String {
80 format!("{}{}{}", self.bold, text, self.reset)
81 }
82
83 pub fn dim(&self, text: &str) -> String {
85 format!("{}{}{}", self.dim, text, self.reset)
86 }
87
88 pub fn format_error<E: crate::error::ForgeError>(&self, err: &E) -> String {
90 let mut result = String::new();
91
92 result.push_str(&format!("{}\n", self.caption(&format!("⚠️ {}", err.caption()))));
94
95 result.push_str(&format!("{}\n", self.error(&err.to_string())));
97
98 if err.is_retryable() {
100 result.push_str(&format!("{}Retryable: {}{}\n",
101 self.dim,
102 self.success("Yes"),
103 self.reset
104 ));
105 } else {
106 result.push_str(&format!("{}Retryable: {}{}\n",
107 self.dim,
108 self.error("No"),
109 self.reset
110 ));
111 }
112
113 if let Some(source) = err.source() {
115 result.push_str(&format!("{}Caused by: {}{}\n",
116 self.dim,
117 self.error(&source.to_string()),
118 self.reset
119 ));
120 }
121
122 result
123 }
124}
125
126pub fn print_error<E: crate::error::ForgeError>(err: &E) {
128 let theme = ConsoleTheme::default();
129 eprintln!("{}", theme.format_error(err));
130}
131
132pub fn install_panic_hook() {
134 let theme = ConsoleTheme::default();
135 std::panic::set_hook(Box::new(move |panic_info| {
136 let message = match panic_info.payload().downcast_ref::<&str>() {
137 Some(s) => *s,
138 None => match panic_info.payload().downcast_ref::<String>() {
139 Some(s) => s.as_str(),
140 None => "Unknown panic",
141 },
142 };
143
144 let location = if let Some(location) = panic_info.location() {
145 format!("at {}:{}", location.file(), location.line())
146 } else {
147 "at unknown location".to_string()
148 };
149
150 eprintln!("{}", theme.caption("💥 PANIC"));
151 eprintln!("{}", theme.error(&format!("{} {}", message, theme.dim(&location))));
152 }));
153}