binparse 0.1.0

A colorful, user-friendly alternative to readelf for analyzing ELF binaries
//! Display helpers for colorful terminal output.
//!
//! Provides consistent formatting and coloring for binary analysis output
//! using the `colored` crate.

use colored::Colorize;

/// Print a main title (e.g., "ELF Header")
/// Displays in bold, underlined blue
pub fn print_title(title: &str) {
    println!("\n{}", title.bold().underline().blue());
}

/// Print a subsection header (e.g., "e_ident")
/// Displays in cyan with leading spaces
pub fn print_section(name: &str) {
    println!("  {}", name.cyan().bold());
}

/// Print a field with name and value
/// Name in yellow, value in default color
pub fn print_field(name: &str, value: &str) {
    println!("    {}: {}", name.yellow(), value);
}

/// Print a field with name, human-readable value, and raw value
/// Format: "    Name: Human Readable (0xRAW)"
pub fn print_field_with_raw(name: &str, human: &str, raw: &str) {
    println!(
        "    {}: {} ({})",
        name.yellow(),
        human.green(),
        raw.dimmed()
    );
}

/// Print a field with extra description
pub fn print_field_desc(name: &str, value: &str, desc: &str) {
    println!(
        "    {}: {} - {}",
        name.yellow(),
        value.green(),
        desc.dimmed()
    );
}

/// Print an address field formatted as hex
pub fn print_address(name: &str, addr: u64) {
    println!("    {}: {:#018X}", name.yellow(), addr);
}

/// Print a size field with optional human-readable size
pub fn print_size(name: &str, size: u64) {
    let human = humanize_size(size);
    println!(
        "    {}: {} ({})",
        name.yellow(),
        format!("{}", size).cyan(),
        human.dimmed()
    );
}

/// Print file metadata header
pub fn print_file_info(path: &str, size: usize, format_desc: &str) {
    println!("{}: {}", "File".bold(), path.white().bold());
    println!("{}: {} bytes", "Size".bold(), size.to_string().cyan());
    println!("{}: {}", "Format".bold(), format_desc.green());
}

/// Print a separator line
pub fn print_separator() {
    println!("{}", "".repeat(60).dimmed());
}

/// Print a table header row for program/section headers
pub fn print_table_header(columns: &[(&str, usize)]) {
    let mut line = String::from("    ");
    for (name, width) in columns {
        line.push_str(&format!("{:width$} ", name, width = width));
    }
    println!("{}", line.cyan().bold());

    let mut underline = String::from("    ");
    for (_, width) in columns {
        underline.push_str(&format!("{} ", "".repeat(*width)));
    }
    println!("{}", underline.dimmed());
}

/// Print a table row
pub fn print_table_row(values: &[(&str, usize)]) {
    let mut line = String::from("    ");
    for (value, width) in values {
        line.push_str(&format!("{:width$} ", value, width = width));
    }
    println!("{}", line);
}

/// Print an indexed item header (e.g., "[0] .text")
pub fn print_indexed_item(index: usize, name: &str) {
    println!(
        "  {} {}",
        format!("[{:3}]", index).cyan(),
        name.white().bold()
    );
}

/// Print an error message
pub fn print_error(message: &str) {
    eprintln!("{}: {}", "Error".red().bold(), message);
}

/// Print a warning message
pub fn print_warning(message: &str) {
    eprintln!("{}: {}", "Warning".yellow().bold(), message);
}

/// Convert bytes to human-readable size
fn humanize_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!("{} bytes", bytes)
    }
}

/// Format flags as a string (e.g., "RWX")
pub fn format_flags(read: bool, write: bool, exec: bool) -> String {
    let mut flags = String::with_capacity(3);
    flags.push(if read { 'R' } else { '-' });
    flags.push(if write { 'W' } else { '-' });
    flags.push(if exec { 'X' } else { '-' });
    flags
}