label_logger/
log.rs

1//! The actual implementation of the logger core
2
3use crate::util::shorten;
4use console::{pad_str, style, Alignment, StyledObject};
5use std::sync::LazyLock;
6use std::{
7	fmt::Display,
8	io::{stdout, IsTerminal},
9};
10use term_size::dimensions as terminal_dimensions;
11
12/// Checks if the output is piped and simplify the output for better debugging
13pub static PAD_OUTPUT: LazyLock<bool> = LazyLock::new(|| stdout().is_terminal());
14
15/// The maximum length of a log label
16pub const LABEL_WIDTH: usize = 12;
17
18/// The enum of possible output labels
19#[derive(Default)]
20pub enum OutputLabel<'a> {
21	/// Outputs `Error` in red
22	Error(&'a str),
23	/// Outputs `Warning` in yellow
24	Warning(&'a str),
25	/// Outputs the provided label in blue
26	Info(&'a str),
27	/// Outputs the provided label in green
28	Success(&'a str),
29	/// Outputs the provided label in the provided color
30	Custom(StyledObject<&'a str>),
31	/// Outputs a blank space with no label
32	#[default]
33	None,
34}
35
36/// Print a message with the specified label
37pub fn println_label<M: AsRef<str> + Display>(label: OutputLabel, message: M) {
38	match label {
39		OutputLabel::Error(_) => {
40			eprintln!("{}", pretty_output(label, message));
41		}
42		_ => {
43			println!("{}", pretty_output(label, message));
44		}
45	}
46}
47
48/// Pretty a message with a given label and a given message color
49///
50/// # Panics
51/// We panic if we can't determine the width of the stdout `TTY`.
52/// But it is only used in a part where we check that we are connected a real `TTY`
53pub fn pretty_output<M: AsRef<str> + Display>(out_label: OutputLabel, message: M) -> String {
54	let (label, label_is_empty) = match out_label {
55		OutputLabel::Error(error) => (style(error).bold().red(), false),
56		OutputLabel::Warning(warn) => (style(warn).bold().yellow(), false),
57		OutputLabel::Info(info) => (style(info).bold().cyan(), false),
58		OutputLabel::Success(success) => (style(success).bold().green(), false),
59		OutputLabel::Custom(custom) => (custom, false),
60		OutputLabel::None => (style(""), true),
61	};
62
63	// Pad output if the stdout is a tty
64	if *PAD_OUTPUT {
65		let (term_width, _) = terminal_dimensions().unwrap_or((80, 80));
66
67		let message = shorten(message.to_string(), term_width - LABEL_WIDTH - 1);
68
69		format!(
70			"{} {}",
71			pad_str(
72				label.to_string().as_str(),
73				LABEL_WIDTH,
74				Alignment::Right,
75				None
76			),
77			message
78		)
79	} else {
80		// Special case for piped output, none label adds a tabulation at the start
81		if label_is_empty {
82			format!("\t{message}")
83		} else {
84			format!("{label} {message}")
85		}
86	}
87}