meowg 0.0.0

Trivial terminal loggers with Catppuccin colour palette
Documentation
//! Trivial terminal loggers with [Catppuccin](https://catppuccin.com/) colour
//! palette.
//!
//!
//! # Usage
//!
//! Set up your preferred [Catppuccin flavour](https://catppuccin.com/palette/)
//! [`Latte`], [`Frappe`], [`Macchiato`] or [`Mocha`] through its associated
//! `install` function or manually via [`log::set_logger`].
//!
//! The `install` function sets the maximum log level to
//! [`log::LevelFilter::Trace`] for debug builds or [`log::LevelFilter::Info`]
//! for release builds.
//!
//! The logger may then be used through the standard [`log`] facilities.
//!
//! These loggers write to standard error, are not configurable and do not log
//! anything beyond the message itself. They will however adapt to a terminal’s
//! capabilities thanks to [`anstream`].
//!
//! They are intended to be used for development alongside more featureful
//! loggers like
//! [`systemd-journal-logger`](https://docs.rs/systemd-journal-logger).
//!
//!
//! ## Example
//!
//! ```
//! use log::warn;
//!
//! meowg::Mocha::install().unwrap();
//! warn!("This is your final warning");
//! ```

use anstyle::Style;
use paste::paste;

macro_rules! colour {
	($flavour:ident, $colour:ident) => {{
		let catppuccin::Rgb { r, g, b } = catppuccin::PALETTE.$flavour.colors.$colour.rgb;
		anstyle::Color::Rgb(anstyle::RgbColor(r, g, b))
	}};
}

macro_rules! meowg {
	($($flavour:ident)+) => {
		paste! {
			$(
				#[doc = "Logger based on the Catppuccin " $flavour:camel " flavour."]
				pub struct [<$flavour:camel>];

				impl [<$flavour:camel>] {
					const ERROR: Style = Style::new().fg_color(Some(colour!($flavour, red))).bold();
					const WARN: Style = Style::new().fg_color(Some(colour!($flavour, yellow))).italic();
					const INFO: Style = Style::new().fg_color(Some(colour!($flavour, text)));
					const DEBUG: Style = Style::new().fg_color(Some(colour!($flavour, subtext0)));
					const TRACE: Style = Style::new().fg_color(Some(colour!($flavour, overlay1)));

					/// Install logger.
					///
					/// Installs the logger through [`log::set_logger`] and sets
					/// the maximum log level to [`log::Level::Trace`] for debug
					/// builds or [`log::Level::Info`] for release builds.
					pub fn install() -> Result<(), log::SetLoggerError> {
						use log::LevelFilter;

						log::set_max_level(if cfg!(debug_assertions) { LevelFilter::Trace } else { LevelFilter::Info });
						log::set_logger(&Self)
					}
				}

				impl log::Log for [<$flavour:camel>] {
					/// Whether this logger is enabled.
					///
					/// Always returns `true`.
					fn enabled(&self, _metadata: &log::Metadata<'_>) -> bool {
						true
					}

					#[doc = "Log a " $flavour:camel "‐coloured message to standard error."]
					fn log(&self, record: &log::Record<'_>) {
						use log::Level;

						let style = match record.level() {
							Level::Error => Self::ERROR,
							Level::Warn => Self::WARN,
							Level::Info => Self::INFO,
							Level::Debug => Self::DEBUG,
							Level::Trace => Self::TRACE,
						};

						anstream::eprintln!("{style}{}{style:#}", record.args());
					}

					/// Flush standard error.
					fn flush(&self) {
						use std::io::Write;
						let _ = anstream::stderr().flush();
					}
				}
			)+
		}
	};
}

meowg!(latte frappe macchiato mocha);

#[cfg(test)]
mod tests {
	use super::*;
	use log::{debug, error, info, trace, warn};

	#[test]
	fn log() {
		Mocha::install().unwrap();

		error!("error");
		warn!("warn");
		info!("info");
		debug!("debug");
		trace!("trace");
	}
}