1use colored::Colorize;
2use std::fmt::Display;
3
4pub mod compression;
5
6pub fn log_success<T: Display>(message: T) {
8 eprintln!("{} {}", "✓".green(), message);
9}
10
11pub fn log_info<T: Display>(message: T) {
13 eprintln!("{} {}", "▸".cyan(), message);
14}
15
16pub fn log_warning<T: Display>(message: T) {
18 eprintln!("{} {}", "⚠".yellow(), message);
19}
20
21pub fn log_error<T: Display>(message: T) {
23 eprintln!("{} {}", "✗".red(), message);
24}
25
26#[allow(dead_code)]
28pub fn log_debug<T: Display>(message: T) {
29 eprintln!("{} {}", "●".dimmed(), message);
30}
31
32#[allow(dead_code)]
34pub fn format_value<T: Display>(value: T, is_success: bool) -> colored::ColoredString {
35 if is_success {
36 format!("{value}").green()
37 } else {
38 format!("{value}").red()
39 }
40}
41
42pub fn format_jwt_token(token: &str) -> String {
44 let parts: Vec<&str> = token.split('.').collect();
45
46 if parts.len() < 2 {
47 return token.to_string();
48 }
49
50 if parts.len() == 2 {
51 return format!("{}.{}", parts[0].cyan(), parts[1]);
53 }
54
55 format!("{}.{}.{}", parts[0].cyan(), parts[1], parts[2].yellow())
57}
58
59pub fn start_progress_with_color(message: &str, color: &str) -> indicatif::ProgressBar {
61 let pb = indicatif::ProgressBar::new_spinner();
62 let template = format!("{{spinner:.{color}}} {{msg}}");
63 pb.set_style(
64 indicatif::ProgressStyle::default_spinner()
65 .template(&template)
66 .unwrap()
67 .tick_strings(&["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]),
68 );
69 pb.set_message(message.to_string());
70 pb.enable_steady_tick(std::time::Duration::from_millis(100));
71 pb
72}
73
74pub fn start_progress(message: &str) -> indicatif::ProgressBar {
76 start_progress_with_color(message, "blue")
77}
78
79#[allow(dead_code)]
81pub fn format_duration(duration: std::time::Duration) -> String {
82 let seconds = duration.as_secs();
83
84 if seconds < 60 {
85 return format!("{seconds}s");
86 }
87
88 let minutes = seconds / 60;
89 let remain_seconds = seconds % 60;
90
91 if minutes < 60 {
92 return format!("{minutes}m {remain_seconds}s");
93 }
94
95 let hours = minutes / 60;
96 let remain_minutes = minutes % 60;
97
98 format!("{hours}h {remain_minutes}m {remain_seconds}s")
99}
100
101pub fn format_base64_preview(base64_str: &str) -> String {
103 const PREVIEW_LEN: usize = 8;
104
105 if base64_str.len() <= PREVIEW_LEN * 2 {
106 return base64_str.to_string();
107 }
108
109 let start = &base64_str[..PREVIEW_LEN];
110 let end = &base64_str[base64_str.len() - PREVIEW_LEN..];
111 let length = base64_str.len();
112
113 format!("{}...{} ({} chars)", start, end, length)
114}
115
116#[cfg(test)]
117mod tests {
118 use super::*;
119 use colored::Colorize;
120 use std::time::Duration;
121
122 #[test]
123 fn test_format_jwt_token_full() {
124 let token = "header.payload.signature";
125 let expected = format!("{}.{}.{}", "header".cyan(), "payload", "signature".yellow());
126 assert_eq!(format_jwt_token(token), expected);
127 }
128
129 #[test]
130 fn test_format_jwt_token_no_signature() {
131 let token = "header.payload";
132 let expected = format!("{}.{}", "header".cyan(), "payload");
133 assert_eq!(format_jwt_token(token), expected);
134 }
135
136 #[test]
137 fn test_format_jwt_token_invalid_format() {
138 let token = "invalidtoken";
139 assert_eq!(format_jwt_token(token), "invalidtoken");
140 }
141
142 #[test]
143 fn test_format_jwt_token_empty_string() {
144 let token = "";
145 assert_eq!(format_jwt_token(token), "");
146 }
147
148 #[test]
149 fn test_format_duration_util_seconds() {
150 assert_eq!(format_duration(Duration::from_secs(5)), "5s");
151 }
152
153 #[test]
154 fn test_format_duration_util_minutes_seconds() {
155 assert_eq!(format_duration(Duration::from_secs(125)), "2m 5s");
156 }
157
158 #[test]
159 fn test_format_duration_util_hours_minutes_seconds() {
160 assert_eq!(format_duration(Duration::from_secs(3723)), "1h 2m 3s");
161 }
162
163 #[test]
164 fn test_format_duration_util_exact_minute() {
165 assert_eq!(format_duration(Duration::from_secs(60)), "1m 0s");
166 }
167
168 #[test]
169 fn test_format_duration_util_exact_hour() {
170 assert_eq!(format_duration(Duration::from_secs(3600)), "1h 0m 0s");
171 }
172
173 #[test]
174 fn test_format_duration_util_zero() {
175 assert_eq!(format_duration(Duration::ZERO), "0s");
176 }
177
178 #[test]
179 fn test_format_value_success() {
180 let expected = "success_text".green();
181 assert_eq!(format_value("success_text", true), expected);
182 }
183
184 #[test]
185 fn test_format_value_failure() {
186 let expected = "failure_text".red();
187 assert_eq!(format_value("failure_text", false), expected);
188 }
189
190 #[test]
191 fn test_format_value_integer() {
192 let expected = "123".green(); assert_eq!(format_value(123, true), expected);
194
195 let expected_fail = "456".red(); assert_eq!(format_value(456, false), expected_fail);
197 }
198
199 #[test]
200 fn test_format_base64_preview_empty() {
201 assert_eq!(format_base64_preview(""), "");
202 }
203
204 #[test]
205 fn test_format_base64_preview_short() {
206 let input = "SGVsbG8=";
207 assert_eq!(format_base64_preview(input), input);
208 }
209
210 #[test]
211 fn test_format_base64_preview_threshold() {
212 let input = "1234567812345678";
213 assert_eq!(format_base64_preview(input), input);
214 }
215
216 #[test]
217 fn test_format_base64_preview_long() {
218 let input = "1234567890abcdefg";
219 let expected = "12345678...0abcdefg (17 chars)";
220 assert_eq!(format_base64_preview(input), expected);
221 }
222
223 #[test]
224 fn test_format_base64_preview_very_long() {
225 let input = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
226 let expected = "ABCDEFGH...456789+/ (64 chars)";
227 assert_eq!(format_base64_preview(input), expected);
228 }
229}