1use chrono::Local;
2use once_cell::sync::OnceCell;
3use serde::{Deserialize, Serialize};
4use std::error::Error;
5use std::fmt::Display;
6use std::fs::File;
7use std::fs::OpenOptions;
8use std::io::Write;
9use std::sync::Mutex;
10
11pub static LOGGER: OnceCell<Logger> = OnceCell::new();
13
14#[derive(Debug)]
16pub struct Logger {
17 file_path: Mutex<String>,
18 level: LogLevel,
19}
20
21impl Logger {
22 pub fn new<S: Into<String>>(log_file_path: S, log_level: LogLevel) -> Self {
24 Self {
25 file_path: Mutex::new(log_file_path.into()),
26 level: log_level,
27 }
28 }
29
30 pub fn init<S: Into<String>>(file_path: S, log_level: LogLevel) -> Result<(), LoggerError> {
36 let logger = Logger::new(file_path.into(), log_level);
37 LOGGER
38 .set(logger)
39 .map_err(|_| LoggerError::new("Failed to initialize logger"))
40 }
41
42 pub fn set_file_path(&self, file_path: String) {
44 if let Ok(mut path) = self.file_path.lock() {
45 *path = file_path;
46 }
47 }
48
49 pub fn debug<S: AsRef<str>>(message: S) {
51 if let Some(logger) = LOGGER.get() {
52 let _ = logger.log(LogLevel::Debug, message);
53 }
54 }
55
56 pub fn critical<S: AsRef<str>>(message: S) {
58 if let Some(logger) = LOGGER.get() {
59 let _ = logger.log(LogLevel::Critical, message);
60 }
61 }
62
63 fn open_log_file(&self) -> Result<File, LoggerError> {
65 let path = self
66 .file_path
67 .lock()
68 .map_err(|e| LoggerError::new(format!("Could not obtain lock: {e}")))?;
69 OpenOptions::new()
70 .create(true)
71 .append(true)
72 .open(&*path)
73 .map_err(|e| LoggerError::new(format!("Failed to open or create log file: {e}")))
74 }
75
76 fn log<S: AsRef<str>>(&self, level: LogLevel, message: S) -> Result<(), LoggerError> {
78 if level >= self.level {
79 let mut file = self.open_log_file()?;
80 let now = Local::now().format("%Y-%m-%d %H:%M:%S").to_string();
81 writeln!(file, "[{level}] {} [{now}]", message.as_ref())
82 .map_err(|e| LoggerError::new(format!("Failed to write to log file: {e}")))?;
83 }
84 Ok(())
85 }
86}
87
88#[derive(Debug, Copy, Clone, Serialize, Deserialize, Default, PartialEq, Eq, PartialOrd)]
90pub enum LogLevel {
91 #[default]
92 Debug,
93 Critical,
94 None,
95}
96
97impl Display for LogLevel {
98 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
99 let name = match self {
100 Self::None => "None",
101 Self::Debug => "Debug",
102 Self::Critical => "Critical",
103 };
104 write!(f, "{name}")
105 }
106}
107
108#[derive(Debug)]
110pub struct LoggerError {
111 pub error_msg: String,
113}
114
115impl LoggerError {
116 pub fn new<S: Into<String>>(error_msg: S) -> Self {
118 Self {
119 error_msg: error_msg.into(),
120 }
121 }
122}
123
124impl Display for LoggerError {
125 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
126 write!(f, "{}", self.error_msg)
127 }
128}
129
130impl Error for LoggerError {
131 fn description(&self) -> &str {
132 &self.error_msg
133 }
134}