use std::fmt;
use std::io::Write as _;
use crate::ansi;
use crate::style::Style;
#[derive(Debug, Clone, Copy)]
pub struct Styled<T> {
pub(crate) inner: T,
pub(crate) style: Style,
}
impl<T: fmt::Display> Styled<T> {
pub(crate) fn new(inner: T, style: Style) -> Self {
Self { inner, style }
}
pub fn paint(&self) {
println!("{self}");
}
pub fn paint_inline(&self) {
print!("{self}");
let _ = std::io::stdout().flush();
}
pub fn paint_err(&self) {
eprintln!("{self}");
}
}
impl<T: fmt::Display> fmt::Display for Styled<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
ansi::write_open(f, &self.style)?;
self.inner.fmt(f)?;
ansi::write_close(f, &self.style)
}
}
impl<T: fmt::Display> Styled<T> {
pub fn and(mut self, extra: Style) -> Self {
self.style = self.style.merge(extra);
self
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::detect::{set_color_level, ColorLevel};
fn with_truecolor<F: FnOnce()>(f: F) {
set_color_level(ColorLevel::TrueColor);
f();
set_color_level(ColorLevel::TrueColor);
}
#[test]
fn display_contains_reset() {
with_truecolor(|| {
let s = Style::new().red().bold().apply("hello");
let rendered = s.to_string();
assert!(rendered.contains("\x1b["), "should contain escape");
assert!(rendered.contains("\x1b[0m"), "should contain reset");
assert!(rendered.contains("hello"), "should contain text");
});
}
#[test]
fn no_color_strips_ansi() {
set_color_level(ColorLevel::None);
let s = Style::new().red().bold().apply("hello");
let rendered = s.to_string();
assert_eq!(rendered, "hello");
set_color_level(ColorLevel::TrueColor);
}
}