A complete blockchain implementation featuring: - synord: Full node with GHOSTDAG consensus - explorer-web: Modern React blockchain explorer with 3D DAG visualization - CLI wallet and tools - Smart contract SDK and example contracts (DEX, NFT, token) - WASM crypto library for browser/mobile
178 lines
4.6 KiB
Rust
178 lines
4.6 KiB
Rust
//! Output formatting.
|
||
|
||
use console::style;
|
||
use serde::Serialize;
|
||
use tabled::{builder::Builder, settings::Style as TableStyle};
|
||
|
||
/// Output format.
|
||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||
pub enum OutputFormat {
|
||
Text,
|
||
Json,
|
||
}
|
||
|
||
impl OutputFormat {
|
||
pub fn from_str(s: &str) -> Self {
|
||
match s.to_lowercase().as_str() {
|
||
"json" => OutputFormat::Json,
|
||
_ => OutputFormat::Text,
|
||
}
|
||
}
|
||
}
|
||
|
||
/// Prints a value in the specified format.
|
||
pub fn print_value<T: Serialize + std::fmt::Debug>(value: &T, format: OutputFormat) {
|
||
match format {
|
||
OutputFormat::Json => {
|
||
println!("{}", serde_json::to_string_pretty(value).unwrap());
|
||
}
|
||
OutputFormat::Text => {
|
||
println!("{:#?}", value);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// Prints a success message.
|
||
pub fn print_success(message: &str) {
|
||
println!("{} {}", style("✓").green().bold(), message);
|
||
}
|
||
|
||
/// Prints an error message.
|
||
pub fn print_error(message: &str) {
|
||
eprintln!("{} {}", style("✗").red().bold(), message);
|
||
}
|
||
|
||
/// Prints a warning message.
|
||
pub fn print_warning(message: &str) {
|
||
println!("{} {}", style("⚠").yellow().bold(), message);
|
||
}
|
||
|
||
/// Prints info message.
|
||
pub fn print_info(message: &str) {
|
||
println!("{} {}", style("ℹ").blue().bold(), message);
|
||
}
|
||
|
||
/// Prints a key-value pair.
|
||
pub fn print_kv(key: &str, value: &str) {
|
||
println!("{}: {}", style(key).bold(), value);
|
||
}
|
||
|
||
/// Prints a header.
|
||
pub fn print_header(title: &str) {
|
||
println!();
|
||
println!("{}", style(title).bold().underlined());
|
||
println!();
|
||
}
|
||
|
||
/// Prints a table.
|
||
pub fn print_table(headers: Vec<&str>, rows: Vec<Vec<String>>) {
|
||
let mut builder = Builder::default();
|
||
builder.push_record(headers);
|
||
|
||
for row in rows {
|
||
builder.push_record(row);
|
||
}
|
||
|
||
let mut table = builder.build();
|
||
let styled = table.with(TableStyle::rounded());
|
||
println!("{}", styled);
|
||
}
|
||
|
||
/// Formats SYNOR amount.
|
||
pub fn format_synor(sompi: u64) -> String {
|
||
let synor = sompi as f64 / 100_000_000.0;
|
||
format!("{:.8} SYNOR", synor)
|
||
}
|
||
|
||
/// Formats a hash for display.
|
||
pub fn format_hash(hash: &str) -> String {
|
||
if hash.len() > 16 {
|
||
format!("{}...{}", &hash[..8], &hash[hash.len() - 8..])
|
||
} else {
|
||
hash.to_string()
|
||
}
|
||
}
|
||
|
||
/// Formats a timestamp.
|
||
pub fn format_timestamp(ts: u64) -> String {
|
||
use chrono::{DateTime, Utc};
|
||
|
||
let dt = DateTime::<Utc>::from_timestamp_millis(ts as i64)
|
||
.unwrap_or_else(|| DateTime::<Utc>::from_timestamp(0, 0).unwrap());
|
||
dt.format("%Y-%m-%d %H:%M:%S UTC").to_string()
|
||
}
|
||
|
||
/// Formats duration.
|
||
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", seconds / 3600, (seconds % 3600) / 60)
|
||
} else {
|
||
format!("{}d {}h", seconds / 86400, (seconds % 86400) / 3600)
|
||
}
|
||
}
|
||
|
||
/// Formats 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;
|
||
|
||
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 bytes size.
|
||
pub fn format_size(bytes: u64) -> String {
|
||
const KB: u64 = 1024;
|
||
const MB: u64 = KB * 1024;
|
||
const GB: u64 = MB * 1024;
|
||
|
||
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)
|
||
}
|
||
}
|
||
|
||
/// Progress bar for long operations.
|
||
pub fn create_progress_bar(len: u64, message: &str) -> indicatif::ProgressBar {
|
||
let pb = indicatif::ProgressBar::new(len);
|
||
pb.set_style(
|
||
indicatif::ProgressStyle::default_bar()
|
||
.template("{msg} [{bar:40.cyan/blue}] {pos}/{len} ({eta})")
|
||
.unwrap()
|
||
.progress_chars("=>-"),
|
||
);
|
||
pb.set_message(message.to_string());
|
||
pb
|
||
}
|
||
|
||
/// Spinner for indeterminate operations.
|
||
pub fn create_spinner(message: &str) -> indicatif::ProgressBar {
|
||
let sp = indicatif::ProgressBar::new_spinner();
|
||
sp.set_style(
|
||
indicatif::ProgressStyle::default_spinner()
|
||
.template("{spinner:.green} {msg}")
|
||
.unwrap(),
|
||
);
|
||
sp.set_message(message.to_string());
|
||
sp
|
||
}
|