mecha10-cli 0.8.0

Mecha10 CLI tool
Documentation
//! Small color helpers for the CLI's non-TUI command output.
//!
//! Built on the existing `console::style` API (already a direct dependency, already used for
//! interactive-prompt theming in [`super::prompts`] and ad hoc in a couple of handlers). `console`
//! auto-detects whether stdout is a tty and respects `NO_COLOR`, so these helpers don't need to
//! do any manual `atty`/env-var checks themselves -- styling is simply a no-op when output isn't
//! a color-capable terminal.
//!
//! The color choices here follow what was already in use ad hoc in
//! `handlers/topology.rs`/`handlers/model.rs`: cyan for headings/section titles, green for
//! success, red for errors, yellow for warnings, dim for secondary/muted text.

use console::{style, StyledObject};

/// Green -- successful operations (pairs well with a `✅`/`✓` marker).
pub fn success(msg: impl std::fmt::Display) -> StyledObject<String> {
    style(msg.to_string()).green()
}

/// Bold red -- errors/failures (pairs well with a `❌`/`✗` marker).
pub fn error(msg: impl std::fmt::Display) -> StyledObject<String> {
    style(msg.to_string()).red().bold()
}

/// Yellow -- warnings (pairs well with a `⚠️` marker).
pub fn warn(msg: impl std::fmt::Display) -> StyledObject<String> {
    style(msg.to_string()).yellow()
}

/// Bold cyan -- section headings/titles.
pub fn heading(msg: impl std::fmt::Display) -> StyledObject<String> {
    style(msg.to_string()).cyan().bold()
}

/// Dim -- secondary/supporting text.
pub fn muted(msg: impl std::fmt::Display) -> StyledObject<String> {
    style(msg.to_string()).dim()
}

/// A dimmed `━`-repeated separator line of the given width, matching the `heading`/`muted`
/// palette used around it.
pub fn separator(width: usize) -> StyledObject<String> {
    muted("━".repeat(width))
}

/// Fixed, deterministic color palette cycled by index -- used to give each log source (or any
/// other small, repeating set of labels) a stable, visually distinct color without randomness.
const PALETTE: [Palette; 5] = [
    Palette::Cyan,
    Palette::Magenta,
    Palette::Green,
    Palette::Yellow,
    Palette::Blue,
];

#[derive(Clone, Copy)]
enum Palette {
    Cyan,
    Magenta,
    Green,
    Yellow,
    Blue,
}

/// Style `msg` with the palette color at `index`, wrapping around for `index >= PALETTE.len()`.
pub fn palette(index: usize, msg: impl std::fmt::Display) -> StyledObject<String> {
    let styled = style(msg.to_string());
    match PALETTE[index % PALETTE.len()] {
        Palette::Cyan => styled.cyan(),
        Palette::Magenta => styled.magenta(),
        Palette::Green => styled.green(),
        Palette::Yellow => styled.yellow(),
        Palette::Blue => styled.blue(),
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn palette_wraps_around() {
        // Index 5 wraps back to index 0's color (cyan); just check it doesn't panic and
        // produces the same rendered text as the base index.
        assert_eq!(format!("{}", palette(0, "x")), format!("{}", palette(5, "x")));
    }
}