use anstyle::{AnsiColor, Color, Style};
#[derive(Debug, Clone, Copy)]
pub struct Palette;
impl Palette {
#[must_use]
pub fn key() -> Style {
Style::new().bold()
}
#[must_use]
pub fn label() -> Style {
Style::new().dimmed()
}
#[must_use]
pub fn ok() -> Style {
Style::new().fg_color(Some(Color::Ansi(AnsiColor::Green)))
}
#[must_use]
pub fn warn() -> Style {
Style::new().fg_color(Some(Color::Ansi(AnsiColor::Yellow)))
}
#[must_use]
pub fn bad() -> Style {
Style::new().fg_color(Some(Color::Ansi(AnsiColor::Red)))
}
#[must_use]
pub fn url() -> Style {
Style::new()
.fg_color(Some(Color::Ansi(AnsiColor::Cyan)))
.underline()
}
#[must_use]
pub fn heading() -> Style {
Style::new().bold().underline()
}
#[must_use]
pub fn untrusted() -> Style {
Style::new().dimmed()
}
}
#[derive(Debug, Clone, Copy)]
pub struct Painter {
enabled: bool,
}
impl Painter {
#[must_use]
pub fn colour() -> Self {
Self { enabled: true }
}
#[must_use]
pub fn plain() -> Self {
Self { enabled: false }
}
#[must_use]
pub fn for_stream(is_terminal: bool) -> Self {
Self {
enabled: is_terminal,
}
}
#[must_use]
pub fn paint(self, text: &str, style: Style) -> String {
if !self.enabled {
return text.to_owned();
}
format!("{style}{text}{style:#}")
}
#[must_use]
pub fn paint_padded(self, text: &str, width: usize, style: Style) -> String {
let visible = text.chars().count();
let padding = width.saturating_sub(visible);
format!("{}{}", self.paint(text, style), " ".repeat(padding))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_plain_painter_changes_nothing() {
assert_eq!(Painter::plain().paint("PROJ-1", Palette::key()), "PROJ-1");
}
#[test]
fn a_colour_painter_wraps_and_resets() {
let painted = Painter::colour().paint("PROJ-1", Palette::key());
assert!(painted.starts_with('\u{1b}'));
assert!(painted.contains("PROJ-1"));
assert!(painted.ends_with("\u{1b}[0m"));
}
#[test]
fn padding_counts_visible_characters_only() {
let plain = Painter::plain().paint_padded("PROJ-1", 12, Palette::key());
let coloured = Painter::colour().paint_padded("PROJ-1", 12, Palette::key());
assert_eq!(plain, "PROJ-1 ");
assert!(coloured.ends_with(" "));
assert_eq!(
coloured.matches(' ').count(),
plain.matches(' ').count(),
"same visible width in both modes"
);
}
#[test]
fn text_longer_than_the_column_is_not_truncated_by_padding() {
assert_eq!(
Painter::plain().paint_padded("very-long-key", 4, Palette::key()),
"very-long-key"
);
}
}