blob: b51360ce63583c21c64e859ba7e9f4222553587f [file] [log] [blame]
use std::io;
use std::io::prelude::*;
use termcolor::{ColorSpec, WriteColor};
// Color tester from:
// https://github.com/wycats/language-reporting/blob/b021c87e0d4916b5f32756151bf215c220eee52d/crates/render-tree/src/stylesheet/accumulator.rs
/// A facility for creating visually inspectable representations of colored output
/// so they can be easily tested.
///
/// A new color is represented as `{style}` and a reset is represented by `{/}`.
///
/// Attributes are printed in this order:
///
/// - Foreground color as `fg:Color`
/// - Background color as `bg:Color`
/// - Bold as `bold`
/// - Underline as `underline`
/// - Intense as `bright`
///
/// For example, the style "intense, bold red foreground" would be printed as:
///
/// ```text
/// {fg:Red bold intense}
/// ```
///
/// Since this implementation attempts to make it possible to faithfully
/// understand what real WriteColor implementations would do, it tries
/// to approximate the contract in the WriteColor trait: "Subsequent
/// writes to this write will use these settings until either reset is
/// called or new color settings are set.")
///
/// - If set_color is called with a style, `{...}` is emitted containing the
/// color attributes.
/// - If set_color is called with no style, `{/}` is emitted
/// - If reset is called, `{/}` is emitted.
pub struct ColorBuffer {
buf: Vec<u8>,
color: ColorSpec,
}
impl ColorBuffer {
pub fn new() -> ColorBuffer {
ColorBuffer {
buf: Vec::new(),
color: ColorSpec::new(),
}
}
pub fn into_string(self) -> String {
String::from_utf8(self.buf).unwrap()
}
}
impl io::Write for ColorBuffer {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.buf.extend(buf);
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl WriteColor for ColorBuffer {
fn supports_color(&self) -> bool {
true
}
fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
#![allow(unused_assignments)]
if self.color == *spec {
return Ok(());
} else {
self.color = spec.clone();
}
if spec.is_none() {
write!(self, "{{/}}")?;
return Ok(());
} else {
write!(self, "{{")?;
}
let mut first = true;
fn write_first(first: bool, write: &mut ColorBuffer) -> io::Result<bool> {
if !first {
write!(write, " ")?;
}
Ok(false)
};
if let Some(fg) = spec.fg() {
first = write_first(first, self)?;
write!(self, "fg:{:?}", fg)?;
}
if let Some(bg) = spec.bg() {
first = write_first(first, self)?;
write!(self, "bg:{:?}", bg)?;
}
if spec.bold() {
first = write_first(first, self)?;
write!(self, "bold")?;
}
if spec.underline() {
first = write_first(first, self)?;
write!(self, "underline")?;
}
if spec.intense() {
first = write_first(first, self)?;
write!(self, "bright")?;
}
write!(self, "}}")?;
Ok(())
}
fn reset(&mut self) -> io::Result<()> {
let color = self.color.clone();
if color != ColorSpec::new() {
write!(self, "{{/}}")?;
self.color = ColorSpec::new();
}
Ok(())
}
}