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
14pub 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 pub fn info<S: AsRef<str>>(&self, msg: S) {
80 self.emit("[🦑::INFO]".blue().bold(), msg);
81 }
82
83 pub fn warning<S: AsRef<str>>(&self, msg: S) {
85 self.emit("[🦑::WARN]".yellow().bold(), msg);
86 }
87
88 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 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}