use anstyle::Style;
use ctl_text::Line;
use crate::color::ColorDepth;
pub const RESET: &str = "\u{1b}[0m";
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Encoder {
depth: Option<ColorDepth>,
}
impl Encoder {
#[must_use]
pub const fn new(depth: Option<ColorDepth>) -> Self {
Self { depth }
}
#[must_use]
pub fn encode(self, lines: &[Line]) -> String {
let mut out = String::new();
for line in lines {
self.encode_line(line, &mut out);
out.push('\n');
}
out
}
fn encode_line(self, line: &Line, out: &mut String) {
let Some(depth) = self.depth else {
for span in line.spans() {
out.push_str(span.text());
}
return;
};
let mut current = Style::new();
for span in line.spans().iter().filter(|span| !span.text().is_empty()) {
let style = fit(depth, span.style());
if style != current {
if !current.is_plain() {
out.push_str(RESET);
}
if !style.is_plain() {
out.push_str(&style.render().to_string());
}
current = style;
}
out.push_str(span.text());
}
if !current.is_plain() {
out.push_str(RESET);
}
}
}
fn fit(depth: ColorDepth, style: Style) -> Style {
let underline = match depth {
ColorDepth::Ansi16 => None,
ColorDepth::Ansi256 | ColorDepth::TrueColor => style
.get_underline_color()
.map(|color| depth.downgrade(color)),
};
Style::new()
.fg_color(style.get_fg_color().map(|color| depth.downgrade(color)))
.bg_color(style.get_bg_color().map(|color| depth.downgrade(color)))
.underline_color(underline)
.effects(style.get_effects())
}