use blockchain::env; use blockchain::fs; use blockchain::tilde; use rustyline::completion::FilenameCompleter; use rustyline::error::ReadlineError; use rustyline::{history::DefaultHistory, CompletionType, Config, Editor}; use rustyline_derive::Completer; use rustyline_derive::Helper as RustyHelper; use rustyline_derive::Highlighter as RustyHighlighter; use rustyline_derive::Hinter as RustyHinter; use rustyline_derive::Validator as RustyValidator; fn format_balance(balance: u64) -> String { // Balance files store atomic units; display as whole coins with 8 decimals. let whole = balance / 100_000_000; let fractional = balance % 100_000_000; format!("{whole}.{fractional:08}") } #[derive(RustyHelper, Completer, RustyHinter, RustyHighlighter, RustyValidator)] struct PathHelper { #[rustyline(Completer)] completer: FilenameCompleter, } fn prompt_for_path(prompt: &str) -> Result { // Rustyline provides path completion for interactive balance-file lookup. let config = Config::builder() .completion_type(CompletionType::List) .build(); let mut editor = Editor::::with_config(config).map_err(|e| e.to_string())?; editor.set_helper(Some(PathHelper { completer: FilenameCompleter::new(), })); match editor.readline(prompt) { Ok(line) => Ok(line.trim().to_string()), Err(ReadlineError::Interrupted) | Err(ReadlineError::Eof) => { Err("Input cancelled".to_string()) } Err(err) => Err(format!("Failed to read balance file path: {err}")), } } #[tokio::main] async fn main() { // The tool accepts a balance file path as an arg or prompts with completion. let args: Vec = env::args().collect(); if args.len() > 1 && args.len() != 2 { eprintln!("Usage: ./lookup_local_balance "); return; } let balance_file_path = if args.len() == 2 { args[1].clone() } else { match prompt_for_path("Please enter the path to the balance file: ") { Ok(path) => path, Err(err) => { eprintln!("{err}"); return; } } }; // Allow users to type paths with ~ and read the balance bytes from disk. let expanded_path = tilde(&balance_file_path).to_string(); let bytes = match fs::read(&expanded_path) { Ok(bytes) => bytes, Err(err) => { eprintln!("Error reading file: {err}"); return; } }; if bytes.len() < 8 { eprintln!("Error: File should have at least 8 bytes of data"); return; } // The first 8 bytes are the little-endian u64 balance value. let mut buffer = [0u8; 8]; buffer.copy_from_slice(&bytes[..8]); let value = u64::from_le_bytes(buffer); println!("{}", format_balance(value)); }