1#![allow(non_camel_case_types)]
58use sys_time::DateTime;
59use std::fmt;
60use std::fs::OpenOptions;
61use std::io::Write;
62use std::path::{Path, PathBuf};
63
64pub mod prelude {
66 pub use crate::{debug, error, info, success, warn, Logger};
67}
68
69const INFO_COLOR: &str = "\x1b[0;34m";
70const SUCCESS_COLOR: &str = "\x1b[0;32m";
71const WARNING_COLOR: &str = "\x1b[0;33m";
72const ERROR_COLOR: &str = "\x1b[0;31m";
73const DEBUG_COLOR: &str = "\x1b[0;35m";
74const ESCAPE: &str = "\x1b[0m";
75const DELIMITER: &str = "\x1b[1;1m";
76const INFO_TEXT: &str = "INFO";
77const SUCCESS_TEXT: &str = "SUCCESS";
78const WARNING_TEXT: &str = "WARNING";
79const ERROR_TEXT: &str = "ERROR";
80const DEBUG_TEXT: &str = "DEBUG";
81
82enum LogfileType {
83 Nofile,
84 FileHandler(PathBuf),
85}
86
87impl LogfileType {
88 fn get_path(&self) -> Option<PathBuf> {
89 match self {
90 LogfileType::FileHandler(p) => Some(p.to_path_buf()),
91 _ => None,
92 }
93 }
94}
95
96pub struct Logger {
97 debug: bool,
98 file: LogfileType,
99 multi: bool,
100}
101
102impl Logger {
103 fn _timestamp(&self) -> String {
104 let now = DateTime::now_utc();
108 format!(
109 "{}-{}-{} {}:{}:{}",
110 now.year(),
111 now.month(),
112 now.day(),
113 now.hour(),
114 now.minute(),
115 now.second()
116 )
117 }
118
119 fn _is_logfile(&self) -> bool {
120 match &self.file {
121 LogfileType::FileHandler(_) => true,
122 _ => false,
123 }
124 }
125
126 fn _is_stdout(&self) -> bool {
127 match &self.file {
128 LogfileType::FileHandler(_) => {
129 if self.multi {
130 true
131 } else {
132 false
133 }
134 }
135 LogfileType::Nofile => true,
136 }
137 }
138
139 #[must_use = "You must call init() afterwards to begin logging"]
140 pub fn new() -> Logger {
141 Logger {
142 debug: false,
143 file: LogfileType::Nofile,
144 multi: false,
145 }
146 }
147
148 pub fn init(self) -> Result<(), LoggerInitError> {
149 set_logger(Box::new(self))?;
150 Ok(())
151 }
152
153 pub fn set_debug(&mut self) {
154 self.debug = true;
155 }
156
157 pub fn set_multi(&mut self) {
158 self.multi = true;
159 }
160
161 pub fn set_logfile(&mut self, path_str: &str) {
162 let path = Path::new(&path_str).to_path_buf();
163 self.file = LogfileType::FileHandler(path);
164 }
165
166 pub fn get_path(self) -> Option<PathBuf> {
167 self.file.get_path()
168 }
169
170 fn print(&self, title: &str, color: &str, msg: String) {
171 if self._is_logfile() {
172 let path = &self.file.get_path().unwrap();
173 let mut logfile = OpenOptions::new()
174 .write(true)
175 .create_new(true)
176 .open(&path)
177 .unwrap();
178 let log_message = format!("[{}] {} {}\n", self._timestamp(), title, msg);
179 write!(&mut logfile, "{}", log_message).unwrap();
180 }
181
182 if self._is_stdout() {
183 println!(
184 "{} {}{}{} {}",
185 self._timestamp(),
186 DELIMITER,
187 format!("{}{}{}{}{}", ESCAPE, color, title, ESCAPE, DELIMITER),
188 ESCAPE,
189 msg
190 );
191 }
192 }
193}
194
195pub enum LogLevel {
196 Info,
197 Success,
198 Warn,
199 Debug,
200 Error,
201}
202
203impl Log for Logger {
204 fn log(&self, level: LogLevel, msg: String){
205 match level {
206 LogLevel::Info => self.print(INFO_TEXT, INFO_COLOR, msg),
207 LogLevel::Success => self.print(SUCCESS_TEXT, SUCCESS_COLOR, msg),
208 LogLevel::Warn => self.print(WARNING_TEXT, WARNING_COLOR, msg),
209 LogLevel::Debug => self.print(DEBUG_TEXT, DEBUG_COLOR, msg),
210 LogLevel::Error => self.print(ERROR_TEXT, ERROR_COLOR, msg)
211
212 }
213 }
214}
215
216pub trait Log {
217 fn log(&self, level: LogLevel, msg: String);
218}
219
220#[derive(Debug)]
221pub struct LoggerInitError;
222
223struct NoLogger;
225
226impl Log for NoLogger {
227 fn log(&self, _level: LogLevel, _msg: String) {}
228}
229
230impl fmt::Display for LoggerInitError {
231 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
232 write!(f, "Logger was initialized more than once or couldn't initialize.")
233 }
234}
235
236static mut LOGGER: &dyn Log = &NoLogger;
237
238pub fn set_logger(logger: Box<dyn Log>) -> Result<(), LoggerInitError> {
241 _set_logger(|| Box::leak(logger))
242}
243
244fn _set_logger<F>(make_logger: F) -> Result<(), LoggerInitError>
245where
246 F: FnOnce() -> &'static dyn Log,
247{
248 unsafe {
249 LOGGER = make_logger();
250 }
251 Ok(())
252}
253
254pub fn logger() -> &'static dyn Log {
255 unsafe { LOGGER }
256}
257
258#[macro_export]
259macro_rules! info {
260 ($($arg:tt)+) => ($crate::logger().log($crate::LogLevel::Info, format!($($arg)+)))
261}
262
263#[macro_export]
264macro_rules! warn {
265 ($($arg:tt)+) => ($crate::logger().log($crate::LogLevel::Warn, format!($($arg)+)))
266}
267
268#[macro_export]
269macro_rules! debug {
270 ($($arg:tt)+) => ($crate::logger().log($crate::LogLevel::Debug, format!($($arg)+)))
271}
272
273#[macro_export]
274macro_rules! success {
275 ($($arg:tt)+) => ($crate::logger().log($crate::LogLevel::Success, format!($($arg)+)))
276}
277
278#[macro_export]
279macro_rules! error {
280 ($($arg:tt)+) => ($crate::logger().log($crate::LogLevel::Error, format!($($arg)+)))
281}