1use anyhow::{bail, Error, Result};
4use console::style;
5use std::sync::atomic::{AtomicBool, AtomicU8, Ordering};
6
7#[repr(u8)]
8#[derive(Debug, Clone, Copy)]
9pub enum LogLevel {
13 Error,
15 Warn,
17 Info,
19}
20
21impl std::str::FromStr for LogLevel {
22 type Err = Error;
23 fn from_str(s: &str) -> Result<Self> {
24 match s {
25 "error" => Ok(LogLevel::Error),
26 "warn" => Ok(LogLevel::Warn),
27 "info" => Ok(LogLevel::Info),
28 _ => bail!("Unknown log-level: {}", s),
29 }
30 }
31}
32
33pub struct ProgressOutput {
35 quiet: AtomicBool,
36 log_level: AtomicU8,
37}
38
39impl ProgressOutput {
40 pub const fn new() -> Self {
42 Self {
43 quiet: AtomicBool::new(false),
44 log_level: AtomicU8::new(LogLevel::Info as u8),
45 }
46 }
47
48 fn message(&self, message: &str) {
50 eprintln!("{}", message);
51 }
52
53 pub fn quiet(&self) -> bool {
55 self.quiet.load(Ordering::SeqCst)
56 }
57
58 pub fn set_quiet(&self, quiet: bool) {
60 self.quiet.store(quiet, Ordering::SeqCst);
61 }
62
63 pub fn is_log_enabled(&self, level: LogLevel) -> bool {
65 (level as u8) <= self.log_level.load(Ordering::SeqCst)
66 }
67
68 pub fn set_log_level(&self, log_level: LogLevel) {
70 self.log_level.store(log_level as u8, Ordering::SeqCst);
71 }
72
73 pub fn info(&self, message: &str) {
75 if !self.quiet() && self.is_log_enabled(LogLevel::Info) {
76 let info = format!("{}: {}", style("[INFO]").bold().dim(), message,);
77 self.message(&info);
78 }
79 }
80
81 pub fn warn(&self, message: &str) {
83 if !self.quiet() && self.is_log_enabled(LogLevel::Warn) {
84 let warn = format!("{}: {}", style("[WARN]").bold().dim(), message);
85 self.message(&warn);
86 }
87 }
88
89 pub fn error(&self, message: &str) {
91 if self.is_log_enabled(LogLevel::Error) {
92 let err = format!("{}: {}", style("[ERR]").bold().dim(), message);
93 self.message(&err);
94 }
95 }
96}
97
98impl Default for ProgressOutput {
99 fn default() -> Self {
100 ProgressOutput::new()
101 }
102}