squid/
logger.rs

1#[cfg(feature = "tui")]
2use core::time::Duration;
3use std::{
4    borrow::Cow,
5    fmt::Display,
6};
7
8use colored::Colorize;
9use indicatif::{
10    ProgressBar,
11    ProgressStyle,
12};
13
14/// The Logger is a helper struct that displays log messages to the terminal.
15///
16/// If the feature `tui` is activated, it also displays some nice TUI animations.
17/// Use the functions [`Logger::info`], [`Logger::warning`], [`Logger::error`] to emit log messages
18/// at the corresponding log levels.
19pub struct Logger {
20    bar: ProgressBar,
21    running: bool,
22    prefix: Option<String>,
23}
24
25#[cfg(feature = "tui")]
26const ANIMATION: &[&str; 9] = &[".  ", ".. ", "...", " ..", "  .", " ..", "...", "..", ""];
27
28#[cfg(not(feature = "tui"))]
29const ANIMATION: &[&str; 2] = &["...", ""];
30
31impl Logger {
32    pub(crate) fn spinner() -> Self {
33        let bar = ProgressBar::new_spinner();
34        bar.set_style(
35            ProgressStyle::with_template("{prefix:.magenta/red} {msg} {spinner}").unwrap().tick_strings(ANIMATION),
36        );
37        bar.set_prefix("[🦑]");
38
39        Self {
40            bar,
41            running: false,
42            prefix: None,
43        }
44    }
45
46    pub(crate) fn set_prefix<S: Into<String>>(&mut self, prefix: S) {
47        self.prefix = Some(prefix.into());
48    }
49
50    pub(crate) fn clear_prefix(&mut self) {
51        self.prefix = None;
52    }
53
54    pub(crate) fn set_title(&mut self, title: impl Into<Cow<'static, str>>) {
55        #[cfg(feature = "tui")]
56        if !self.running {
57            self.bar.enable_steady_tick(Duration::from_millis(100));
58            self.running = true;
59        }
60        self.bar.set_message(title.into());
61    }
62
63    fn stop(&mut self) {
64        if self.running {
65            self.running = false;
66            self.bar.finish_and_clear();
67        }
68    }
69
70    fn emit<L: Display, S: AsRef<str>>(&self, level: L, msg: S) {
71        if let Some(prefix) = &self.prefix {
72            self.bar.println(format!("{} {}{}{} {}", level, "(".bold(), prefix.bold(), ")".bold(), msg.as_ref()))
73        } else {
74            self.bar.println(format!("{} {}", level, msg.as_ref()));
75        }
76    }
77
78    /// Emit a log message with log level "INFO"
79    pub fn info<S: AsRef<str>>(&self, msg: S) {
80        self.emit("[🦑::INFO]".blue().bold(), msg);
81    }
82
83    /// Emit a log message with log level "WARN"
84    pub fn warning<S: AsRef<str>>(&self, msg: S) {
85        self.emit("[🦑::WARN]".yellow().bold(), msg);
86    }
87
88    /// Emit a log message with log level "DEBUG"
89    pub fn debug<S: AsRef<str>>(&self, _msg: S) {
90        #[cfg(debug_assertions)]
91        {
92            self.emit("[🦑::DEBUG]".black().on_white(), _msg);
93        }
94    }
95
96    /// Emit a log message with log level "ERROR"
97    pub fn error<S: AsRef<str>>(&self, msg: S) {
98        self.emit("[🦑::ERROR]".red().bold(), msg);
99    }
100}
101
102impl Drop for Logger {
103    fn drop(&mut self) {
104        self.stop();
105    }
106}
107
108#[cfg(test)]
109mod tests {
110    use super::*;
111
112    #[test]
113    #[ignore]
114    fn test_style() {
115        let mut logger = Logger::spinner();
116        logger.set_title("TITLE HERE");
117        logger.info("info");
118        logger.warning("warning");
119        logger.debug("debug");
120        logger.error("error");
121
122        std::thread::sleep(std::time::Duration::from_secs(5));
123    }
124}