mod enums;
mod write_buffer;
pub use enums::{Level, OutputKind};
pub use write_buffer::WriteBuffer;
use crate::timestamp::get_timestamp;
use std::collections::HashMap;
use std::fs::OpenOptions;
use std::io;
pub struct Logger<'o> {
file_options: OpenOptions,
level_outputs: HashMap<Level, OutputKind<'o>>,
write_buffers: HashMap<OutputKind<'o>, WriteBuffer>,
format: String,
}
impl<'o> Logger<'o> {
pub fn new() -> Option<Self> {
let mut options = OpenOptions::new();
options.write(true).create(true).truncate(true);
let mut map_level = HashMap::new();
map_level.insert(Level::INFO, OutputKind::STDOUT);
map_level.insert(Level::DEBUG, OutputKind::STDOUT);
map_level.insert(Level::WARNING, OutputKind::STDERR);
map_level.insert(Level::ERROR, OutputKind::STDERR);
let mut stdout = if let Ok(buff) = WriteBuffer::new(&OutputKind::STDOUT, &options) {
buff
} else {
return None;
};
let mut stderr = if let Ok(buff) = WriteBuffer::new(&OutputKind::STDERR, &options) {
buff
} else {
return None;
};
stdout.increase_count();
stderr.increase_count();
let mut map_buffer = HashMap::new();
map_buffer.insert(OutputKind::STDOUT, stdout);
map_buffer.insert(OutputKind::STDERR, stderr);
let log = Logger {
file_options: options,
level_outputs: map_level,
write_buffers: map_buffer,
format: String::from("[%l](%t) - %m"),
};
return Some(log);
}
pub fn config_format<S: ToString + ?Sized>(&mut self, new_format: &S) {
self.format = new_format.to_string();
}
fn config_output(&mut self, kind: OutputKind<'o>, level: Level) -> io::Result<()> {
match self.write_buffers.get_mut(&kind) {
None => {
let buffer = WriteBuffer::new(&kind, &self.file_options)?;
self.write_buffers.insert(kind.clone(), buffer);
}
Some(buffer) => buffer.increase_count(),
};
let output = self.level_outputs.get_mut(&level).unwrap();
if match self.write_buffers.get_mut(output) {
None => false,
Some(buffer) => buffer.decrease_count(),
} {
self.write_buffers.remove(output);
}
*output = kind;
Ok(())
}
pub fn config_info(&mut self, kind: OutputKind<'o>) {
if let Err(err) = self.config_output(kind, Level::INFO) {
self.error(&format!("Failed to configure INFO: {}", err));
}
}
pub fn config_debug(&mut self, kind: OutputKind<'o>) {
if let Err(err) = self.config_output(kind, Level::DEBUG) {
self.error(&format!("Failed to configure DEBUG: {}", err));
}
}
pub fn config_warning(&mut self, kind: OutputKind<'o>) {
if let Err(err) = self.config_output(kind, Level::WARNING) {
self.error(&format!("Failed to configure WARNING: {}", err));
}
}
pub fn config_error(&mut self, kind: OutputKind<'o>) {
if let Err(err) = self.config_output(kind, Level::ERROR) {
self.error(&format!("Failed to configure ERROR: {}", err));
}
}
fn write_log(&mut self, msg: &str, level: Level) {
if let Some(buffer) = self
.write_buffers
.get_mut(self.level_outputs.get(&level).unwrap())
{
let mut log_msg = self
.format
.replace("%l", &level.to_string())
.replace("%t", &get_timestamp().to_string())
.replace("%m", msg);
log_msg.push('\n');
if let Err(err) = buffer.log(log_msg) {
match level {
Level::ERROR => {}
_ => self.error(&format!("Failed to log to `{}`: {}", level, err)),
}
};
};
}
pub fn info(&mut self, msg: &str) {
self.write_log(msg, Level::INFO);
}
pub fn debug(&mut self, msg: &str) {
self.write_log(msg, Level::DEBUG);
}
pub fn warning(&mut self, msg: &str) {
self.write_log(msg, Level::WARNING);
}
pub fn error(&mut self, msg: &str) {
self.write_log(msg, Level::ERROR);
}
}