use std::io::{self, Write};
use super::{
fragments::{Fragment, FragmentText},
style::Style,
};
pub enum Command<'a> {
NewLine,
Space(u16),
SetStyle(Style),
ResetStyle(Style),
Text(FragmentText<'a>),
}
impl<'a> Command<'a> {
fn is_set_style(&self) -> bool {
matches!(self, Command::SetStyle(_))
}
}
const SPACES_LEN: usize = 64;
static SPACES: [u8; SPACES_LEN] = [b' '; SPACES_LEN];
pub fn print<'a, W, I>(
writer: &mut W,
input: I,
use_color: bool,
) -> io::Result<()>
where
W: Write,
I: Iterator<Item = &'a Command<'a>>,
{
use Command as C;
let mut current_color = None;
let mut it = input.peekable();
while let Some(command) = it.next() {
match command {
C::NewLine => writeln!(writer)?,
C::Space(amount) => {
let mut remaining = *amount as usize;
while remaining > SPACES_LEN {
writer.write_all(&SPACES)?;
remaining -= SPACES_LEN;
}
if remaining > 0 {
writer.write_all(&SPACES[0..remaining])?;
}
}
C::SetStyle(style) => {
if use_color {
let new_color = style.to_ansi_color();
match current_color {
Some(old_color) if old_color == new_color => (),
_ => {
write!(writer, "{}", new_color.render_fg())?;
current_color = Some(new_color);
}
}
}
}
C::ResetStyle(_style) => {
if use_color
&& it.peek().map(|next| !next.is_set_style()).unwrap_or(true)
{
write!(writer, "{}", anstyle::Reset.render())?;
current_color = None;
}
}
C::Text(text) => writer.write_all(text.as_bytes())?,
}
}
Ok(())
}
pub trait BuildInput<'a> {
fn add_new_line(&mut self);
fn add_spaces(&mut self, amount: u16);
fn add_fragment(&mut self, fragment: &Fragment<'a>);
fn add_styled<T: Into<FragmentText<'a>>>(&mut self, style: Style, text: T);
fn add_plain<T: Into<FragmentText<'a>>>(&mut self, text: T);
}
impl<'a> BuildInput<'a> for Vec<Command<'a>> {
fn add_new_line(&mut self) {
self.push(Command::NewLine);
}
fn add_spaces(&mut self, amount: u16) {
self.push(Command::Space(amount))
}
fn add_fragment(&mut self, fragment: &Fragment<'a>) {
self.push(Command::SetStyle(fragment.style));
self.push(Command::Text(fragment.text.clone()));
self.push(Command::ResetStyle(fragment.style));
}
fn add_styled<T>(&mut self, style: Style, text: T)
where
T: Into<FragmentText<'a>>,
{
self.push(Command::SetStyle(style));
self.add_plain(text);
self.push(Command::ResetStyle(style));
}
fn add_plain<T>(&mut self, text: T)
where
T: Into<FragmentText<'a>>,
{
self.push(Command::Text(text.into()));
}
}