pub mod json;
pub mod text;
use std::io::Write;
use crate::config::{ColorChoice, Config, OutputFormat};
use crate::model::Report;
pub fn render(
report: &Report,
config: &Config,
color: bool,
out: &mut impl Write,
) -> std::io::Result<()> {
match config.format {
OutputFormat::Text => text::render(report, config, color, out),
OutputFormat::Json => json::render(report, out),
}
}
#[must_use]
pub fn color_enabled(choice: ColorChoice, is_terminal: bool) -> bool {
match choice {
ColorChoice::Always => true,
ColorChoice::Never => false,
ColorChoice::Auto => is_terminal && std::env::var_os("NO_COLOR").is_none(),
}
}
pub(crate) struct Paint {
on: bool,
}
impl Paint {
pub(crate) fn new(on: bool) -> Self {
Self { on }
}
fn wrap(&self, code: &str, text: &str) -> String {
if self.on {
format!("\x1b[{code}m{text}\x1b[0m")
} else {
text.to_owned()
}
}
pub(crate) fn red(&self, text: &str) -> String {
self.wrap("31", text)
}
pub(crate) fn green(&self, text: &str) -> String {
self.wrap("32", text)
}
pub(crate) fn yellow(&self, text: &str) -> String {
self.wrap("33", text)
}
pub(crate) fn bold(&self, text: &str) -> String {
self.wrap("1", text)
}
pub(crate) fn dim(&self, text: &str) -> String {
self.wrap("2", text)
}
pub(crate) fn link(&self, text: &str) -> String {
if self.on {
format!("\x1b[4;36m{text}\x1b[0m")
} else {
text.to_owned()
}
}
pub(crate) fn green_bold(&self, prefix: &str, bold_part: &str, suffix: &str) -> String {
if self.on {
format!("\x1b[32m{prefix}\x1b[1m{bold_part}\x1b[0m\x1b[32m{suffix}\x1b[0m")
} else {
format!("{prefix}{bold_part}{suffix}")
}
}
}
pub(crate) fn plural(count: usize, singular: &str) -> String {
if count == 1 {
format!("{count} {singular}")
} else {
format!("{count} {singular}s")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn always_and_never_ignore_the_terminal() {
assert!(color_enabled(ColorChoice::Always, false));
assert!(!color_enabled(ColorChoice::Never, true));
}
#[test]
fn auto_needs_a_terminal() {
assert!(!color_enabled(ColorChoice::Auto, false));
}
#[test]
fn paint_is_a_no_op_when_off() {
let plain = Paint::new(false);
assert_eq!(plain.red("x"), "x");
assert_eq!(plain.bold("x"), "x");
}
#[test]
fn paint_wraps_when_on() {
let colored = Paint::new(true);
assert_eq!(colored.red("x"), "\x1b[31mx\x1b[0m");
}
#[test]
fn counts_are_pluralized() {
assert_eq!(plural(0, "key"), "0 keys");
assert_eq!(plural(1, "key"), "1 key");
assert_eq!(plural(2, "key"), "2 keys");
}
}