use chrono::Utc;
use std::{
fs,
io::Write,
sync::mpsc::{channel, Receiver, Sender},
thread,
};
use crate::{LogLevel, Loggable, LogLocation};
#[derive(Clone)]
pub struct Logger<T: LogLevel> {
sender: Sender<LogTransmit<T>>,
}
impl<T: LogLevel> Logger<T> {
pub fn new(output: LogLocation) -> Logger<T> {
let (sender, receiver) = channel();
let output_path = output.resolve();
LoggerTool {
receiver,
output_path,
}
.start();
Logger::<T> { sender }
}
pub fn log(&self, level: T, message: &dyn Loggable) {
self.sender
.send(LogTransmit::Log(LogInstance {
level,
message: message.as_log(),
}))
.unwrap();
}
}
struct LoggerTool<T: LogLevel> {
receiver: Receiver<LogTransmit<T>>,
output_path: String,
}
impl<T: LogLevel> LoggerTool<T> {
fn start(self) {
thread::spawn(move || {
match fs::OpenOptions::new()
.append(true)
.create(true)
.open(&self.output_path)
{
Ok(mut file) => {
while let Ok(message) = self.receiver.recv() {
match message {
LogTransmit::Close => {
self.write_msg(&mut file, "SYSTEM", "Abort message recieved".to_string());
break;
},
LogTransmit::Log(log) => {
self.write_msg(&mut file, log.level.level_string(), log.message);
}
};
}
},
Err(e) => {
println!("[CRITICAL] Failed to open log file: {}", e);
}
}
});
}
fn write_msg(&self, file: &mut fs::File, level: &'static str, message: String) {
let msg = format!(
"{} [{}]: {}\n",
self.current_ts(),
level,
message
);
if let Err(e) = file.write(msg.as_bytes()) {
println!("[CRITICAL] Failed to write to log file: {}", e)
}
}
fn current_ts(&self) -> String {
Utc::now().format("%Y-%m-%d %H:%M:%S").to_string()
}
}
pub enum LogTransmit<T: LogLevel> {
Log(LogInstance<T>),
Close,
}
pub struct LogInstance<T: LogLevel> {
pub level: T,
pub message: String,
}