Skip to main content

jwt_hack/utils/
mod.rs

1use colored::Colorize;
2use std::fmt::Display;
3
4pub mod compression;
5
6/// Displays a success message with a green checkmark prefix
7pub fn log_success<T: Display>(message: T) {
8    eprintln!("{} {}", "✓".green(), message);
9}
10
11/// Displays an information message with a cyan arrow prefix
12pub fn log_info<T: Display>(message: T) {
13    eprintln!("{} {}", "▸".cyan(), message);
14}
15
16/// Displays a warning message with a yellow warning symbol prefix
17pub fn log_warning<T: Display>(message: T) {
18    eprintln!("{} {}", "⚠".yellow(), message);
19}
20
21/// Displays an error message with a red cross prefix
22pub fn log_error<T: Display>(message: T) {
23    eprintln!("{} {}", "✗".red(), message);
24}
25
26/// Displays a debug message with a dimmed dot prefix for development purposes
27#[allow(dead_code)]
28pub fn log_debug<T: Display>(message: T) {
29    eprintln!("{} {}", "●".dimmed(), message);
30}
31
32/// Returns a value formatted with color based on success status (green for success, red for failure)
33#[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
42/// Colorizes JWT token components for better visual distinction (header=cyan, payload=default, signature=yellow)
43pub 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        // Header and payload only
52        return format!("{}.{}", parts[0].cyan(), parts[1]);
53    }
54
55    // Full JWT with signature
56    format!("{}.{}.{}", parts[0].cyan(), parts[1], parts[2].yellow())
57}
58
59/// Creates an animated spinner with a custom color to indicate ongoing operations
60pub 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
74/// Creates an animated spinner to indicate ongoing operations with the specified message
75pub fn start_progress(message: &str) -> indicatif::ProgressBar {
76    start_progress_with_color(message, "blue")
77}
78
79/// Converts a duration into human-readable format (hours, minutes, seconds)
80#[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
101/// Formats a base64 encoded string for display with preview (shows first/last chars with length)
102pub 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(); // is_success = true
193        assert_eq!(format_value(123, true), expected);
194
195        let expected_fail = "456".red(); // is_success = false
196        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}