zarust 0.2.1

Rust implementation of the ZArchive format
Documentation
//! The `--help` and `--version` screens.
//!
//! Kept out of the dispatcher because this is pure terminal layout, and out of
//! `ui` because it describes the command set rather than how to draw things.

use crate::cli::{
    Outcome,
    ui::{Palette, Reporter},
};

/// Width of the left column in the command and option tables.
const COLUMN: usize = 26;

const COMMANDS: [(&str, &str, &str); 5] = [
    (
        "pack",
        "<dir> [archive]",
        "Create an archive from a directory",
    ),
    (
        "extract",
        "<archive> [dir]",
        "Extract an archive into a directory",
    ),
    ("list", "<archive> [path]", "List the entries in an archive"),
    ("info", "<archive>", "Show sizes and the compression ratio"),
    ("verify", "<archive>", "Check the stored integrity hash"),
];

const OPTIONS: [(&str, &str); 10] = [
    ("-o, --output <path>", "Output path, instead of positional"),
    ("-f, --force", "Overwrite an existing output path"),
    ("-v, --verbose", "Print every entry as it is processed"),
    ("-q, --quiet", "Print errors only"),
    ("    --tree", "Render `list` as a tree"),
    ("    --check", "Verify integrity before extracting"),
    ("    --no-progress", "Never draw the progress bar"),
    (
        "    --color <when>",
        "auto, always, or never (default auto)",
    ),
    ("-h, --help", "Show this help"),
    ("-V, --version", "Show the version"),
];

const TRAILER: [&str; 3] = [
    "Given a bare path, zarust packs a directory or extracts an archive, matching",
    "the original ZArchive tool: `zarust game/` makes game.zar, and `zarust",
    "game.zar` unpacks it into game_extracted/.",
];

pub fn version(reporter: &mut Reporter) -> Outcome {
    reporter.line(format_args!(
        "{} {}",
        env!("CARGO_PKG_NAME"),
        env!("CARGO_PKG_VERSION")
    ))
}

pub fn help(reporter: &mut Reporter) -> Outcome {
    let p = reporter.palette();

    reporter.line(format_args!(
        "{bold}{cyan}zarust{reset} {dim}{}{reset}  —  read and write ZArchive {dim}(.zar / .wua){reset} files",
        env!("CARGO_PKG_VERSION"),
        bold = p.bold,
        cyan = p.cyan,
        dim = p.dim,
        reset = p.reset,
    ))?;

    section(reporter, "USAGE")?;
    reporter.line(format_args!(
        "  {}zarust{} {}<command>{} {}[options]{}",
        p.magenta, p.reset, p.bold, p.reset, p.dim, p.reset
    ))?;

    section(reporter, "COMMANDS")?;
    for (name, arguments, description) in COMMANDS {
        // Pad by visible width; the escapes around the name have none.
        let pad = COLUMN.saturating_sub(name.len() + 1 + arguments.len());
        reporter.line(format_args!(
            "  {m}{b}{name}{r} {d}{arguments}{r}{:pad$}{description}",
            "",
            m = p.magenta,
            b = p.bold,
            d = p.dim,
            r = p.reset,
        ))?;
    }

    section(reporter, "OPTIONS")?;
    for (flag, description) in OPTIONS {
        reporter.line(format_args!(
            "  {c}{flag:<COLUMN$}{r}{description}",
            c = p.cyan,
            r = p.reset
        ))?;
    }

    reporter.line(format_args!(""))?;
    for line in TRAILER {
        reporter.line(format_args!("{d}{line}{r}", d = p.dim, r = p.reset))?;
    }
    Ok(())
}

fn section(reporter: &mut Reporter, title: &str) -> Outcome {
    let Palette {
        yellow,
        bold,
        reset,
        ..
    } = reporter.palette();
    reporter.line(format_args!("\n{yellow}{bold}{title}{reset}"))
}