rototo 0.1.0-alpha.2

Control plane for runtime configuration in application code.
Documentation
use std::io::IsTerminal;

#[derive(Clone, Copy)]
pub(crate) enum Role {
    Fg,
    Dim,
    Clay,
    Info,
    Ok,
    Error,
}

#[derive(Clone, Copy)]
enum Stream {
    Stdout,
    Stderr,
}

pub(crate) fn stdout(role: Role, text: impl AsRef<str>) -> String {
    style(Stream::Stdout, role, text.as_ref(), false)
}

pub(crate) fn stdout_bold(role: Role, text: impl AsRef<str>) -> String {
    style(Stream::Stdout, role, text.as_ref(), true)
}

pub(crate) fn stderr(role: Role, text: impl AsRef<str>) -> String {
    style(Stream::Stderr, role, text.as_ref(), false)
}

pub(crate) fn stderr_bold(role: Role, text: impl AsRef<str>) -> String {
    style(Stream::Stderr, role, text.as_ref(), true)
}

fn style(stream: Stream, role: Role, text: &str, bold: bool) -> String {
    if !color_enabled(stream) {
        return text.to_owned();
    }
    if bold {
        format!("\x1b[1;{}m{}\x1b[0m", ansi_code(role), text)
    } else {
        format!("\x1b[{}m{}\x1b[0m", ansi_code(role), text)
    }
}

fn color_enabled(stream: Stream) -> bool {
    if std::env::var_os("NO_COLOR").is_some() {
        return false;
    }

    if let Some(force) = std::env::var_os("CLICOLOR_FORCE") {
        return force.to_string_lossy() != "0";
    }

    if std::env::var_os("CLICOLOR").as_deref() == Some(std::ffi::OsStr::new("0")) {
        return false;
    }

    match stream {
        Stream::Stdout => std::io::stdout().is_terminal(),
        Stream::Stderr => std::io::stderr().is_terminal(),
    }
}

fn ansi_code(role: Role) -> &'static str {
    match role {
        Role::Fg => "38;2;227;223;216",
        Role::Dim => "38;2;144;139;132",
        Role::Clay => "38;2;232;135;97",
        Role::Info => "38;2;94;182;230",
        Role::Ok => "38;2;97;203;124",
        Role::Error => "38;2;246;109;100",
    }
}