use std::borrow;
use std::fmt;
use std::io;
use termion::{color, style};
use crate::{AnsiColor, AnsiMode, Color, Effect, Style, StyledStr, StyledString};
pub struct TermionStr<'a> {
s: &'a str,
style: Option<Style>,
}
impl<'a> fmt::Display for TermionStr<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(style) = &self.style {
if let Some(fg) = style.fg {
f.write_str(get_fg(fg).as_ref())?;
}
if let Some(bg) = style.bg {
f.write_str(get_bg(bg).as_ref())?;
}
for effect in style.effects {
f.write_str(get_effect(effect))?;
}
}
f.write_str(self.s)?;
if let Some(style) = &self.style {
if style.fg.is_some() || style.bg.is_some() || !style.effects.is_empty() {
f.write_str(style::Reset.as_ref())?;
}
}
Ok(())
}
}
pub trait Termion {
fn termion(&self) -> TermionStr<'_>;
}
impl<'a> Termion for StyledStr<'a> {
fn termion(&self) -> TermionStr<'_> {
TermionStr {
s: self.s,
style: self.style,
}
}
}
impl Termion for StyledString {
fn termion(&self) -> TermionStr<'_> {
TermionStr {
s: &self.s,
style: self.style,
}
}
}
fn get_bg(color: Color) -> borrow::Cow<'static, str> {
match color {
Color::Ansi { color, mode } => get_ansi_bg(color, mode).into(),
Color::Rgb { r, g, b } => color::Rgb(r, g, b).bg_string().into(),
}
}
fn get_ansi_bg(color: AnsiColor, mode: AnsiMode) -> &'static str {
use AnsiColor::*;
use AnsiMode::*;
match (mode, color) {
(Dark, Black) => color::Black.bg_str(),
(Dark, Red) => color::Red.bg_str(),
(Dark, Green) => color::Green.bg_str(),
(Dark, Yellow) => color::Yellow.bg_str(),
(Dark, Blue) => color::Blue.bg_str(),
(Dark, Magenta) => color::Magenta.bg_str(),
(Dark, Cyan) => color::Cyan.bg_str(),
(Dark, White) => color::White.bg_str(),
(Light, Black) => color::LightBlack.bg_str(),
(Light, Red) => color::LightRed.bg_str(),
(Light, Green) => color::LightGreen.bg_str(),
(Light, Yellow) => color::LightYellow.bg_str(),
(Light, Blue) => color::LightBlue.bg_str(),
(Light, Magenta) => color::LightMagenta.bg_str(),
(Light, Cyan) => color::LightCyan.bg_str(),
(Light, White) => color::LightWhite.bg_str(),
}
}
fn get_fg(color: Color) -> borrow::Cow<'static, str> {
match color {
Color::Ansi { color, mode } => get_ansi_fg(color, mode).into(),
Color::Rgb { r, g, b } => color::Rgb(r, g, b).fg_string().into(),
}
}
fn get_ansi_fg(color: AnsiColor, mode: AnsiMode) -> &'static str {
use AnsiColor::*;
use AnsiMode::*;
match (mode, color) {
(Dark, Black) => color::Black.fg_str(),
(Dark, Red) => color::Red.fg_str(),
(Dark, Green) => color::Green.fg_str(),
(Dark, Yellow) => color::Yellow.fg_str(),
(Dark, Blue) => color::Blue.fg_str(),
(Dark, Magenta) => color::Magenta.fg_str(),
(Dark, Cyan) => color::Cyan.fg_str(),
(Dark, White) => color::White.fg_str(),
(Light, Black) => color::LightBlack.fg_str(),
(Light, Red) => color::LightRed.fg_str(),
(Light, Green) => color::LightGreen.fg_str(),
(Light, Yellow) => color::LightYellow.fg_str(),
(Light, Blue) => color::LightBlue.fg_str(),
(Light, Magenta) => color::LightMagenta.fg_str(),
(Light, Cyan) => color::LightCyan.fg_str(),
(Light, White) => color::LightWhite.fg_str(),
}
}
fn get_effect(effect: Effect) -> &'static str {
match effect {
Effect::Bold => style::Bold.as_ref(),
Effect::Italic => style::Italic.as_ref(),
Effect::Underline => style::Underline.as_ref(),
Effect::Strikethrough => style::CrossedOut.as_ref(),
}
}
pub fn render<'a>(mut w: impl io::Write, s: impl Into<StyledStr<'a>>) -> io::Result<()> {
write!(w, "{}", s.into().termion())
}
pub fn render_iter<'a, I, Iter, S, W>(mut w: W, iter: I) -> io::Result<()>
where
I: IntoIterator<Item = S, IntoIter = Iter>,
Iter: Iterator<Item = S>,
S: Into<StyledStr<'a>>,
W: io::Write,
{
for s in iter {
write!(w, "{}", s.into().termion())?;
}
Ok(())
}