lunar-lib 0.10.0

Common utilities for lunar applications
Documentation
use std::{io, path::Path, sync::OnceLock};

use crossterm::{
    execute,
    style::{Color, Print, ResetColor, SetForegroundColor},
};

/// A basic default implementation of a [`Log`] for CLI applications
#[derive(Debug)]
pub struct CliLogger {
    localize_to: Option<&'static str>,
}

impl CliLogger {
    pub fn init(max_level: log::LevelFilter, localize_to: Option<&'static str>) {
        static LOGGER: OnceLock<CliLogger> = OnceLock::new();
        let _ = log::set_logger(LOGGER.get_or_init(|| CliLogger { localize_to }));
        log::set_max_level(max_level);
    }
}

impl log::Log for CliLogger {
    fn enabled(&self, metadata: &log::Metadata) -> bool {
        metadata.level().to_level_filter() <= log::max_level()
            && (self
                .localize_to
                .is_none_or(|target| metadata.target().starts_with(target)))
    }

    fn log(&self, record: &log::Record) {
        if self.enabled(record.metadata()) {
            let message = match record.level() {
                log::Level::Info => format!("[INFO]: {}\n", record.args()),
                level => {
                    if let Some((file, line)) = record
                        .file()
                        .and_then(|s| Path::new(s).file_name())
                        .and_then(|s| s.to_str())
                        .zip(record.line())
                    {
                        format!(
                            "[{level}][in {file} at line {line}]: {msg}\n",
                            msg = record.args()
                        )
                    } else {
                        format!("[{level}]: {msg}\n", msg = record.args())
                    }
                }
            };

            let color = match record.level() {
                log::Level::Warn => Some(Color::Yellow),
                log::Level::Debug => Some(Color::Blue),
                log::Level::Trace => Some(Color::Green),
                _ => None,
            };

            match record.level() {
                log::Level::Error => {
                    let _ = crossterm::execute!(
                        io::stderr(),
                        SetForegroundColor(Color::Red),
                        Print(message),
                        ResetColor
                    );
                }
                _ => {
                    if let Some(color) = color {
                        let _ = crossterm::execute!(
                            io::stdout(),
                            SetForegroundColor(color),
                            Print(message),
                            ResetColor
                        );
                    } else {
                        let _ = execute!(io::stderr(), Print(message));
                    }
                }
            }
        }
    }

    fn flush(&self) {}
}