use owo_colors::{OwoColorize, Rgb};
#[derive(Debug, Clone)]
pub enum OutputEvent {
Found { hash: String, password: String, username: Option<String> },
Progress { pct: f64, rate: f64, elapsed: f64, remaining: f64 },
Start { mode: String, hash_type: String, num_hashes: usize, keyspace: u64 },
Done { found: usize, elapsed: f64 },
}
pub fn emit_json(event: &OutputEvent) {
match event {
OutputEvent::Found { hash, password, username } => {
let user = username.as_deref().unwrap_or("");
println!("{{\"event\":\"found\",\"hash\":{},\"password\":{},\"username\":{}}}",
serde_json::to_string(hash).unwrap_or_default(),
serde_json::to_string(password).unwrap_or_default(),
serde_json::to_string(user).unwrap_or_default());
}
OutputEvent::Start { mode, hash_type, num_hashes, keyspace } => {
println!("{{\"event\":\"start\",\"mode\":{},\"hash_type\":{},\"num_hashes\":{},\"keyspace\":{}}}",
serde_json::to_string(mode).unwrap_or_default(),
serde_json::to_string(hash_type).unwrap_or_default(),
num_hashes, keyspace);
}
OutputEvent::Done { found, elapsed } => {
println!("{{\"event\":\"done\",\"found\":{},\"elapsed_secs\":{:.3}}}", found, elapsed);
}
OutputEvent::Progress { pct, rate, elapsed, remaining } => {
println!("{{\"event\":\"progress\",\"pct\":{:.3},\"rate_hs\":{:.0},\"elapsed_secs\":{:.3},\"remaining_secs\":{:.3}}}",
pct, rate, elapsed, remaining);
}
}
}
pub fn print_banner() {
let cyan = Rgb(0, 200, 255);
let green = Rgb(0, 255, 100);
let yellow = Rgb(255, 200, 0);
let dim = Rgb(100, 100, 120);
println!();
println!("{}", " ╔══════════════════════════════════════════╗".color(cyan));
println!("{}", " ║ ║".color(cyan));
println!(" ║ {} {} ║",
"HASH CRACKER".bold().color(green),
"v0.5-beta".color(yellow));
println!("{}", " ║ GPU-Accelerated Password Recovery ║".color(cyan));
println!(" ║ {} hash types · Rust + Vulkan ║",
"35".color(dim));
println!("{}", " ╚══════════════════════════════════════════╝".color(cyan));
println!();
}
pub fn print_found_entry(hash: &str, password: &str, hex_mode: bool) {
let display_pwd = if hex_mode { hex::encode(password.as_bytes()) } else { password.to_string() };
let green = Rgb(0, 255, 100);
let white = Rgb(255, 255, 255);
println!(" {} {:<64} : {}",
"✅".color(green),
hash.color(white),
display_pwd.color(green).bold());
}
pub fn print_summary(results: &[(String, String, bool)], total_time: std::time::Duration, total_hashes: usize) {
let green = Rgb(0, 255, 100);
let red = Rgb(255, 50, 50);
let cyan = Rgb(0, 200, 255);
let yellow = Rgb(255, 200, 0);
let found_count = results.iter().filter(|r| r.2).count();
println!();
println!("{}", " ┌──────────────────────────────────────┐".color(cyan));
println!("{}", " │ Results Summary │".color(cyan));
println!("{}", " ├──────────────────────────────────────┤".color(cyan));
if results.len() <= 8 {
for (hash, password, found) in results {
let status = if *found { "✓".color(green) } else { "✗".color(red) };
let display = if password.len() > 30 {
format!("{}...", &password[..27])
} else {
password.clone()
};
println!(" │ {} {} │", status, format!("{} → {}", hash, display).color(yellow));
}
if !results.is_empty() {
println!("{}", " ├──────────────────────────────────────┤".color(cyan));
}
}
let secs = total_time.as_secs_f64();
let time_str = if secs > 60.0 {
format!("{:.0}m {:.0}s", secs / 60.0, secs % 60.0)
} else {
format!("{:.1}s", secs)
};
println!(" │ {} {} │", "Time:".color(cyan), format!("{:>23}", time_str).color(green));
println!(" │ {} {} │", "Found:".color(cyan), format!("{:>21}", found_count).color(green));
if total_hashes > 0 {
println!(" │ {} {} │", "Total:".color(cyan), format!("{:>21}", total_hashes).color(green));
}
println!("{}", " └──────────────────────────────────────┘".color(cyan));
println!();
}
pub fn print_bench_header(bench_duration: std::time::Duration) {
let cyan = Rgb(0, 200, 255);
let green = Rgb(0, 255, 100);
let yellow = Rgb(255, 200, 0);
println!("{}", " ╔════════════════════════════════════════════╗".color(cyan));
println!(" ║ {} ║",
"HashCracker — GPU Benchmark".bold().color(green));
println!("{}", " ╠════════════════════════════════════════════╣".color(cyan));
println!(" ║ {} {} ║",
"Password length: 3".color(yellow),
"Keyspace: 238,328".color(yellow));
println!(" ║ {} {} s ║",
"Sample time:".color(yellow),
format!("{}", bench_duration.as_secs()).color(yellow));
println!("{}", " ╚════════════════════════════════════════════╝".color(cyan));
}
pub fn print_bench_row(name: &str, hps: f64) {
let green = Rgb(0, 255, 100);
let rate_str = if hps >= 1_000_000.0 {
format!("{:.2} MH/s", hps / 1_000_000.0)
} else if hps >= 1_000.0 {
format!("{:.2} KH/s", hps / 1_000.0)
} else {
format!("{:.0} H/s", hps)
};
println!(" ║ {:<8} {:>20} ║", name.color(green), rate_str.color(green));
}
pub fn print_bench_footer(results: &[(&str, f64)]) {
let cyan = Rgb(0, 200, 255);
let green = Rgb(0, 255, 100);
let yellow = Rgb(255, 200, 0);
println!("{}", " ╠════════════════════════════════════════════╣".color(cyan));
for (name, hps) in results {
let rate_str = if *hps >= 1_000_000.0 {
format!("{:.2} MH/s", hps / 1_000_000.0)
} else if *hps >= 1_000.0 {
format!("{:.2} KH/s", hps / 1_000.0)
} else {
format!("{:.0} H/s", hps)
};
println!(" ║ {:<8} {:>20} ║", name.color(yellow), rate_str.color(green));
}
println!("{}", " ╚════════════════════════════════════════════╝".color(cyan));
}
pub fn print_config(hash_type: &str, mode_desc: &str, hash_info: &str, salt_display: Option<&str>, num_passwords: u64, num_targets: usize) {
let cyan = Rgb(0, 200, 255);
let green = Rgb(0, 255, 100);
let yellow = Rgb(255, 200, 0);
println!("{}", " ╔══════════════════════════════════════════╗".color(cyan));
println!(" ║ {} ║",
"Configuration".bold().color(green));
println!("{}", " ╠══════════════════════════════════════════╣".color(cyan));
println!(" ║ {} {:<31} ║", "Hash:".color(yellow), hash_type.bold());
println!(" ║ {} {:<31} ║", "Mode:".color(yellow), mode_desc);
println!(" ║ {} {:<31} ║", "Target:".color(yellow), hash_info);
if num_targets > 1 {
println!(" ║ {} {:<31} ║", "Targets:".color(yellow), format!("{} hashes", num_targets));
}
if let Some(salt) = salt_display {
println!(" ║ {} {:<31} ║", "Salt:".color(yellow), salt);
}
println!(" ║ {} {:<31} ║", "Space:".color(yellow), format!("{} passwords", num_passwords));
println!("{}", " ╚══════════════════════════════════════════╝".color(cyan));
}