synor/apps/synord/src/cli.rs
2026-01-08 05:22:24 +05:30

162 lines
4.6 KiB
Rust

//! 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<u64> {
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);
}
}