use std::io::{self, IsTerminal, Write};
pub(in crate::cli) const RESET: &[u8] = b"\x1b[0m";
#[derive(Clone, Copy)]
pub(in crate::cli) struct ColorStyles {
pub match_: &'static [u8],
pub path: &'static [u8],
pub line: &'static [u8],
}
impl Default for ColorStyles {
fn default() -> Self {
Self {
match_: b"\x1b[1;31m",
path: b"\x1b[35m",
line: b"\x1b[32m",
}
}
}
pub(in crate::cli) enum ColorWhen {
Always,
Never,
Auto,
Ansi,
}
impl ColorWhen {
pub(in crate::cli) fn parse(s: Option<&str>) -> Option<ColorWhen> {
match s {
None => None,
Some("always") => Some(ColorWhen::Always),
Some("never") => Some(ColorWhen::Never),
Some("auto") => Some(ColorWhen::Auto),
Some("ansi") => Some(ColorWhen::Ansi),
Some(_) => Some(ColorWhen::Auto),
}
}
}
pub(in crate::cli) fn resolve_color(when: Option<ColorWhen>, pretty: bool) -> bool {
match when {
Some(ColorWhen::Always) | Some(ColorWhen::Ansi) => true,
Some(ColorWhen::Never) => false,
None | Some(ColorWhen::Auto) => {
if no_color_set() {
return false;
}
pretty || io::stdout().is_terminal()
}
}
}
fn no_color_set() -> bool {
std::env::var_os("NO_COLOR").is_some_and(|v| !v.is_empty())
}
pub(in crate::cli) fn write_styled(
out: &mut dyn Write,
color: bool,
style: &[u8],
bytes: &[u8],
) -> io::Result<()> {
if color {
out.write_all(style)?;
out.write_all(bytes)?;
out.write_all(RESET)
} else {
out.write_all(bytes)
}
}
pub(in crate::cli) fn write_styled_num(
out: &mut dyn Write,
color: bool,
style: &[u8],
n: usize,
) -> io::Result<()> {
if color {
out.write_all(style)?;
write!(out, "{n}")?;
out.write_all(RESET)
} else {
write!(out, "{n}")
}
}
pub(in crate::cli) fn write_highlighted(
out: &mut dyn Write,
color: bool,
styles: ColorStyles,
content: &[u8],
spans: &[(usize, usize)],
) -> io::Result<()> {
if !color {
return out.write_all(content);
}
let mut cursor = 0usize;
for &(start, end) in spans {
if start < cursor {
continue;
}
let start = start.min(content.len());
let end = end.min(content.len());
if start >= end {
continue;
}
if start > cursor {
out.write_all(&content[cursor..start])?;
}
out.write_all(styles.match_)?;
out.write_all(&content[start..end])?;
out.write_all(RESET)?;
cursor = end;
}
if cursor < content.len() {
out.write_all(&content[cursor..])?;
}
Ok(())
}