use console::{style, StyledObject};
pub fn success(msg: impl std::fmt::Display) -> StyledObject<String> {
style(msg.to_string()).green()
}
pub fn error(msg: impl std::fmt::Display) -> StyledObject<String> {
style(msg.to_string()).red().bold()
}
pub fn warn(msg: impl std::fmt::Display) -> StyledObject<String> {
style(msg.to_string()).yellow()
}
pub fn heading(msg: impl std::fmt::Display) -> StyledObject<String> {
style(msg.to_string()).cyan().bold()
}
pub fn muted(msg: impl std::fmt::Display) -> StyledObject<String> {
style(msg.to_string()).dim()
}
pub fn separator(width: usize) -> StyledObject<String> {
muted("━".repeat(width))
}
pub fn step(msg: impl std::fmt::Display) {
tracing::debug!("{msg}");
}
const PALETTE: [Palette; 5] = [
Palette::Cyan,
Palette::Magenta,
Palette::Green,
Palette::Yellow,
Palette::Blue,
];
#[derive(Clone, Copy)]
enum Palette {
Cyan,
Magenta,
Green,
Yellow,
Blue,
}
pub fn palette(index: usize, msg: impl std::fmt::Display) -> StyledObject<String> {
let styled = style(msg.to_string());
match PALETTE[index % PALETTE.len()] {
Palette::Cyan => styled.cyan(),
Palette::Magenta => styled.magenta(),
Palette::Green => styled.green(),
Palette::Yellow => styled.yellow(),
Palette::Blue => styled.blue(),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn palette_wraps_around() {
assert_eq!(format!("{}", palette(0, "x")), format!("{}", palette(5, "x")));
}
#[test]
fn step_is_gated_on_log_level() {
use std::io;
use std::sync::{Arc, Mutex};
use tracing_subscriber::prelude::*;
#[derive(Clone)]
struct SharedBuf(Arc<Mutex<Vec<u8>>>);
impl io::Write for SharedBuf {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.0.lock().unwrap().extend_from_slice(buf);
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
let buf = Arc::new(Mutex::new(Vec::new()));
let make_writer = {
let buf = buf.clone();
move || SharedBuf(buf.clone())
};
let info_subscriber = tracing_subscriber::registry().with(
tracing_subscriber::fmt::layer()
.with_writer(make_writer.clone())
.with_filter(tracing_subscriber::filter::LevelFilter::INFO),
);
tracing::subscriber::with_default(info_subscriber, || {
step("hidden at the default info level");
});
assert!(
buf.lock().unwrap().is_empty(),
"step() must stay silent under the default --log-level info filter"
);
let debug_subscriber = tracing_subscriber::registry().with(
tracing_subscriber::fmt::layer()
.with_writer(make_writer)
.with_filter(tracing_subscriber::filter::LevelFilter::DEBUG),
);
tracing::subscriber::with_default(debug_subscriber, || {
step("visible under --log-level debug");
});
assert!(
!buf.lock().unwrap().is_empty(),
"step() must print once --log-level is debug or below"
);
}
}