//! CLI utilities. #![allow(dead_code)] use std::io::{self, Write}; /// Prints a banner. pub fn print_banner() { println!( r#" ███████╗██╗ ██╗███╗ ██╗ ██████╗ ██████╗ ██╔════╝╚██╗ ██╔╝████╗ ██║██╔═══██╗██╔══██╗ ███████╗ ╚████╔╝ ██╔██╗ ██║██║ ██║██████╔╝ ╚════██║ ╚██╔╝ ██║╚██╗██║██║ ██║██╔══██╗ ███████║ ██║ ██║ ╚████║╚██████╔╝██║ ██║ ╚══════╝ ╚═╝ ╚═╝ ╚═══╝ ╚═════╝ ╚═╝ ╚═╝ Synor Blockchain Node v{} BlockDAG with GHOSTDAG Consensus "#, env!("CARGO_PKG_VERSION") ); } /// Prompts for confirmation. pub fn confirm(prompt: &str) -> bool { print!("{} [y/N]: ", prompt); io::stdout().flush().unwrap(); let mut input = String::new(); io::stdin().read_line(&mut input).unwrap(); matches!(input.trim().to_lowercase().as_str(), "y" | "yes") } /// Formats a hash for display. pub fn format_hash(hash: &[u8]) -> String { if hash.len() >= 8 { format!( "{}...{}", hex::encode(&hash[..4]), hex::encode(&hash[hash.len() - 4..]) ) } else { hex::encode(hash) } } /// Formats bytes as human-readable size. pub fn format_size(bytes: u64) -> String { const KB: u64 = 1024; const MB: u64 = KB * 1024; const GB: u64 = MB * 1024; const TB: u64 = GB * 1024; if bytes >= TB { format!("{:.2} TB", bytes as f64 / TB as f64) } else if bytes >= GB { format!("{:.2} GB", bytes as f64 / GB as f64) } else if bytes >= MB { format!("{:.2} MB", bytes as f64 / MB as f64) } else if bytes >= KB { format!("{:.2} KB", bytes as f64 / KB as f64) } else { format!("{} B", bytes) } } /// Formats a hashrate. pub fn format_hashrate(hps: f64) -> String { const K: f64 = 1000.0; const M: f64 = K * 1000.0; const G: f64 = M * 1000.0; const T: f64 = G * 1000.0; const P: f64 = T * 1000.0; if hps >= P { format!("{:.2} PH/s", hps / P) } else if hps >= T { format!("{:.2} TH/s", hps / T) } else if hps >= G { format!("{:.2} GH/s", hps / G) } else if hps >= M { format!("{:.2} MH/s", hps / M) } else if hps >= K { format!("{:.2} KH/s", hps / K) } else { format!("{:.2} H/s", hps) } } /// Formats duration in seconds. pub fn format_duration(seconds: u64) -> String { if seconds < 60 { format!("{}s", seconds) } else if seconds < 3600 { format!("{}m {}s", seconds / 60, seconds % 60) } else if seconds < 86400 { format!( "{}h {}m {}s", seconds / 3600, (seconds % 3600) / 60, seconds % 60 ) } else { format!( "{}d {}h {}m", seconds / 86400, (seconds % 86400) / 3600, (seconds % 3600) / 60 ) } } /// Formats SYNOR amount. pub fn format_synor(sompi: u64) -> String { let synor = sompi as f64 / 100_000_000.0; format!("{:.8} SYNOR", synor) } /// Parses SYNOR amount. pub fn parse_synor(s: &str) -> anyhow::Result { let s = s.trim().to_uppercase(); let s = s.strip_suffix("SYNOR").unwrap_or(&s).trim(); let synor: f64 = s.parse()?; let sompi = (synor * 100_000_000.0) as u64; Ok(sompi) } #[cfg(test)] mod tests { use super::*; #[test] fn test_format_size() { assert_eq!(format_size(500), "500 B"); assert_eq!(format_size(1024), "1.00 KB"); assert_eq!(format_size(1024 * 1024), "1.00 MB"); assert_eq!(format_size(1024 * 1024 * 1024), "1.00 GB"); } #[test] fn test_format_hashrate() { assert_eq!(format_hashrate(500.0), "500.00 H/s"); assert_eq!(format_hashrate(1500.0), "1.50 KH/s"); assert_eq!(format_hashrate(1_500_000.0), "1.50 MH/s"); } #[test] fn test_format_synor() { assert_eq!(format_synor(100_000_000), "1.00000000 SYNOR"); assert_eq!(format_synor(50_000_000), "0.50000000 SYNOR"); } #[test] fn test_parse_synor() { assert_eq!(parse_synor("1").unwrap(), 100_000_000); assert_eq!(parse_synor("1.5 SYNOR").unwrap(), 150_000_000); assert_eq!(parse_synor("0.5").unwrap(), 50_000_000); } }