sara-tasks 2.6.0

Sara — folder-aware task manager
use anyhow::{Result, bail};
use ratatui::style::Color;
use std::io::IsTerminal;

use crate::infrastructure::config::{self, Config};
use crate::infrastructure::tui::theme::{self, Palette};

pub fn run(cfg: &mut Config, chosen: Option<&str>) -> Result<String> {
    let Some(name) = chosen else {
        return Ok(status(cfg, std::io::stdout().is_terminal()));
    };
    if Palette::parse(name).is_none() {
        bail!(
            "unknown theme `{name}` (use one of: {})",
            Palette::NAMES.join(", ")
        );
    }
    cfg.tui.theme = name.trim().to_ascii_lowercase();
    config::save(cfg)?;
    Ok(format!("Theme set to {}.{}", cfg.tui.theme, env_note()))
}

pub(super) fn status(cfg: &Config, paint: bool) -> String {
    let saved = Palette::parse(&cfg.tui.theme).unwrap_or_default();
    let mark = |p: Palette| if p == saved { "▸" } else { " " };
    let rows: Vec<String> = Palette::ALL
        .into_iter()
        .map(|p| match theme::swatch(p).filter(|_| paint) {
            Some(colors) => format!("{} {:<16}{}", mark(p), p.name(), blocks(&colors)),
            None => format!("{} {}", mark(p), p.name()),
        })
        .collect();
    format!("{}{}", rows.join("\n"), env_note())
}

fn blocks(colors: &[Color]) -> String {
    colors
        .iter()
        .map(|c| format!("\x1b[{}m██\x1b[0m", sgr(*c)))
        .collect::<Vec<_>>()
        .join(" ")
}

fn sgr(c: Color) -> String {
    match c {
        Color::Rgb(r, g, b) => format!("38;2;{r};{g};{b}"),
        Color::Indexed(n) => format!("38;5;{n}"),
        named => {
            let code = match named {
                Color::Black => 30,
                Color::Red => 31,
                Color::Green => 32,
                Color::Yellow => 33,
                Color::Blue => 34,
                Color::Magenta => 35,
                Color::Cyan => 36,
                Color::Gray => 37,
                Color::DarkGray => 90,
                Color::LightRed => 91,
                Color::LightGreen => 92,
                Color::LightYellow => 93,
                Color::LightBlue => 94,
                Color::LightMagenta => 95,
                Color::LightCyan => 96,
                Color::White => 97,
                _ => 39,
            };
            code.to_string()
        }
    }
}

fn env_note() -> String {
    match std::env::var("SARA_THEME") {
        Ok(v) if Palette::parse(&v).is_some() => {
            format!("\nNote: SARA_THEME={v} overrides this in the current shell.")
        }
        _ => String::new(),
    }
}

#[cfg(test)]
#[path = "../../../tests/unit/commands/theme/mod.rs"]
mod tests;