1use crate::emoji;
4use anyhow::{bail, Error, Result};
5use console::style;
6use std::sync::atomic::{AtomicBool, AtomicU8, Ordering};
7
8#[repr(u8)]
9#[derive(Debug, Clone, Copy)]
10pub enum LogLevel {
14 Error,
16 Warn,
18 Info,
20}
21
22impl std::str::FromStr for LogLevel {
23 type Err = Error;
24 fn from_str(s: &str) -> Result<Self> {
25 match s {
26 "error" => Ok(LogLevel::Error),
27 "warn" => Ok(LogLevel::Warn),
28 "info" => Ok(LogLevel::Info),
29 _ => bail!("Unknown log-level: {}", s),
30 }
31 }
32}
33
34pub struct ProgressOutput {
36 quiet: AtomicBool,
37 log_level: AtomicU8,
38}
39
40impl ProgressOutput {
41 pub const fn new() -> Self {
43 Self {
44 quiet: AtomicBool::new(false),
45 log_level: AtomicU8::new(LogLevel::Info as u8),
46 }
47 }
48
49 fn message(&self, message: &str) {
51 eprintln!("{}", message);
52 }
53
54 pub fn quiet(&self) -> bool {
56 self.quiet.load(Ordering::SeqCst)
57 }
58
59 pub fn set_quiet(&self, quiet: bool) {
61 self.quiet.store(quiet, Ordering::SeqCst);
62 }
63
64 pub fn is_log_enabled(&self, level: LogLevel) -> bool {
66 (level as u8) <= self.log_level.load(Ordering::SeqCst)
67 }
68
69 pub fn set_log_level(&self, log_level: LogLevel) {
71 self.log_level.store(log_level as u8, Ordering::SeqCst);
72 }
73
74 pub fn info(&self, message: &str) {
76 if !self.quiet() && self.is_log_enabled(LogLevel::Info) {
77 let info = format!("{}: {}", style("[INFO]").bold().dim(), message,);
78 self.message(&info);
79 }
80 }
81
82 pub fn warn(&self, message: &str) {
84 if !self.quiet() && self.is_log_enabled(LogLevel::Warn) {
85 let warn = format!(
86 "{}: {} {}",
87 style("[WARN]").bold().dim(),
88 emoji::WARN,
89 message
90 );
91 self.message(&warn);
92 }
93 }
94
95 pub fn error(&self, message: &str) {
97 if self.is_log_enabled(LogLevel::Error) {
98 let err = format!(
99 "{}: {} {}",
100 style("[ERR]").bold().dim(),
101 emoji::ERROR,
102 message
103 );
104 self.message(&err);
105 }
106 }
107}
108
109impl Default for ProgressOutput {
110 fn default() -> Self {
111 ProgressOutput::new()
112 }
113}