locc 1.1.0

CLI for counting lines of code per language
pub const BAR_CHAR: &str = "";
pub const LIST_CHAR: &str = "";
pub const SEP_CHAR: &str = "";
pub const BAR_WIDTH: usize = 50;

pub struct Mode {
    pub show_bar: bool,
    pub show_header: bool,
    pub show_lines: bool,
    pub show_pct: bool,
}

fn ansi_fg(r: u8, g: u8, b: u8) -> String {
    format!("\x1b[38;2;{};{};{}m", r, g, b)
}

const RESET: &str = "\x1b[0m";

fn colored(text: &str, r: u8, g: u8, b: u8) -> String {
    format!("{}{}{}", ansi_fg(r, g, b), text, RESET)
}

pub fn display(entries: &[(&str, usize, (u8, u8, u8))], total: usize, mode: &Mode) {
    if mode.show_bar {
        print_bar(entries, total);
    }

    if !mode.show_lines && !mode.show_pct {
        return;
    }

    let max_name = entries
        .iter()
        .map(|(n, _, _)| n.len())
        .max()
        .unwrap_or(0)
        .max("Language".len());
    let line_width = if mode.show_lines {
        entries
            .iter()
            .map(|(_, l, _)| l)
            .max()
            .unwrap_or(&0)
            .to_string()
            .len()
            .max("Lines".len())
    } else {
        0
    };
    let pct_width = if mode.show_pct {
        entries
            .iter()
            .map(|(_, l, _)| format!("{:.1}%", *l as f64 / total as f64).len())
            .max()
            .unwrap_or(0)
            .max("Percentage".len())
    } else {
        0
    };

    if mode.show_header {
        print_header(max_name, line_width, pct_width, mode);
    }

    for &(name, lines, (r, g, b)) in entries {
        let pct = lines as f64 / total as f64 * 100.0;
        print_row(
            name, lines, pct, max_name, line_width, pct_width, mode, r, g, b,
        );
    }
}

fn print_header(name_w: usize, line_w: usize, pct_w: usize, mode: &Mode) {
    let mut cols = vec![format!("{:name_w$}", "Language", name_w = name_w)];
    if mode.show_lines {
        cols.push(format!("{:line_w$}", "Lines", line_w = line_w));
    }
    if mode.show_pct {
        cols.push(format!("{:pct_w$}", "Percentage", pct_w = pct_w));
    }
    if mode.show_bar {
        println!("   {}", cols.join("  "));
    } else {
        println!("{}", cols.join("  "));
    }
}

fn print_row(
    name: &str,
    lines: usize,
    pct: f64,
    name_w: usize,
    line_w: usize,
    pct_w: usize,
    mode: &Mode,
    r: u8,
    g: u8,
    b: u8,
) {
    let mut cols = vec![format!("{:name_w$}", name, name_w = name_w)];
    if mode.show_lines {
        cols.push(format!("{:<line_w$}", lines, line_w = line_w));
    }
    if mode.show_pct {
        cols.push(format!("{:pct_w$}", format!("{:.1}%", pct), pct_w = pct_w));
    }
    if mode.show_bar {
        println!("{}  {}", colored(LIST_CHAR, r, g, b), cols.join("  "));
    } else {
        println!("{}", cols.join("  "));
    }
}

fn print_bar(entries: &[(&str, usize, (u8, u8, u8))], total: usize) {
    for &(_, lines, (r, g, b)) in entries {
        let count = (lines as f64 / total as f64 * BAR_WIDTH as f64).round() as usize;
        for _ in 0..count {
            print!("{}", colored(BAR_CHAR, r, g, b));
        }
    }
    println!();
    println!("{}", SEP_CHAR.repeat(BAR_WIDTH));
}