#![doc = include_str!("../.github/README.md")]
pub mod errors;
use std;
use std::io::Write;
use chrono;
#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum Level {
#[default]
DEBUG,
INFO,
WARN,
ERROR,
FATAL,
MESSAGE
}
#[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum Output {
#[default]
STDOUT,
STDERR,
FILE {
path: String
}
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Formatter {
pub color_format_string: String,
pub format_string: String,
pub timestamp_format: String,
}
impl Default for Formatter {
fn default() -> Formatter {
return Formatter::new("[{{color.bright_blue}}{{timestamp}}{{end}}] [{{level}}] {{path}}: {{message}}", "[{{timestamp}}] [{{level}}] {{path}}: {{message}}", "%Y-%m-%d %H:%M:%S");
}
}
impl Formatter {
pub fn new(color_format_string: &str, format_string: &str, timestamp_format: &str) -> Formatter {
Formatter {
color_format_string: color_format_string.to_owned(),
format_string: format_string.to_owned(),
timestamp_format: timestamp_format.to_owned()
}
}
#[doc = include_str!("../.github/formatting_codes.md")]
pub fn format<'a>(&self, output: Output, level: Level, message: &'a str, mut extra_arguments: Vec<(&str, String)>) -> String {
let mut arguments: Vec<(&str, String)> = vec![];
let mut colors: Vec<(&str, String)> = vec![
("end", "\x1b[0m".to_string()),
("bold", "\x1b[1m".to_string()),
("italic", "\x1b[3m".to_string()),
("underline", "\x1b[4m".to_string()),
("overline", "\x1b[53m".to_string()),
("color.black", "\x1b[30m".to_string()),
("color.red", "\x1b[31m".to_string()),
("color.green", "\x1b[32m".to_string()),
("color.yellow", "\x1b[33m".to_string()),
("color.blue", "\x1b[34m".to_string()),
("color.magenta", "\x1b[35m".to_string()),
("color.cyan", "\x1b[36m".to_string()),
("color.white", "\x1b[37m".to_string()),
("color.bright_black", "\x1b[90m".to_string()),
("color.bright_red", "\x1b[91m".to_string()),
("color.bright_green", "\x1b[92m".to_string()),
("color.bright_yellow", "\x1b[93m".to_string()),
("color.bright_blue", "\x1b[94m".to_string()),
("color.bright_magenta", "\x1b[95m".to_string()),
("color.bright_cyan", "\x1b[96m".to_string()),
("color.bright_white", "\x1b[97m".to_string()),
("back.black", "\x1b[40m".to_string()),
("back.red", "\x1b[41m".to_string()),
("back.green", "\x1b[42m".to_string()),
("back.yellow", "\x1b[43m".to_string()),
("back.blue", "\x1b[44m".to_string()),
("back.magenta", "\x1b[45m".to_string()),
("back.cyan", "\x1b[46m".to_string()),
("back.white", "\x1b[47m".to_string()),
("back.bright_black", "\x1b[100m".to_string()),
("back.bright_red", "\x1b[101m".to_string()),
("back.bright_green", "\x1b[102m".to_string()),
("back.bright_yellow", "\x1b[103m".to_string()),
("back.bright_blue", "\x1b[104m".to_string()),
("back.bright_magenta", "\x1b[105m".to_string()),
("back.bright_cyan", "\x1b[106m".to_string()),
("back.bright_white", "\x1b[107m".to_string()),
];
let level_string: (&str, String) = ("level", match level {
Level::DEBUG => "DEBUG",
Level::INFO => "INFO",
Level::WARN => "WARNING",
Level::ERROR => "ERROR",
Level::FATAL => "FATAL",
Level::MESSAGE => "MESSAGE"
}.to_string());
let colored_level_string: (&str, String) = ("level", match level {
Level::DEBUG => "DEBUG",
Level::INFO => "{{color.blue}}INFO{{end}}",
Level::WARN => "{{color.yellow}}WARNING{{end}}",
Level::ERROR => "{{color.red}}ERROR{{end}}",
Level::FATAL => "{{color.red}}FATAL{{end}}",
Level::MESSAGE => "{{color.blue}}MESSAGE{{end}}"
}.to_string());
arguments.push(("message", message.to_string()));
arguments.push(("timestamp", chrono::Utc::now().format(&self.timestamp_format).to_string()));
arguments.append(&mut extra_arguments);
let mut result: String = match output {
Output::STDOUT | Output::STDERR => {
arguments.push(colored_level_string);
self.color_format_string.to_owned()
},
_ => {
arguments.push(level_string);
self.format_string.to_owned()
}
};
arguments.append(&mut colors);
for (key, value) in arguments {
result = result.replace(("{{".to_owned() + key + "}}").as_str(), &value);
}
return result.clone();
}
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Logger {
pub formatter: Formatter,
pub writable_list: Vec<Output>
}
impl Default for Logger {
fn default() -> Logger {
return Logger::new(Formatter::default(), vec![Output::STDOUT]);
}
}
impl Logger {
pub fn new(formatter: Formatter, writable_list: Vec<Output>) -> Logger {
Logger {
formatter: formatter,
writable_list: writable_list
}
}
pub fn log(&self, message: &str, level: Level, path: &str, mut arguments: Vec<(&str, String)>) {
arguments.push(("path", path.to_string()));
for writable in self.writable_list.clone() {
let formatted: String = self.formatter.format(writable.clone(), level, message, arguments.clone());
match writable {
Output::STDOUT => println!("{}", formatted),
Output::STDERR => eprintln!("{}", formatted),
Output::FILE { ref path } => {
let file: Result<std::fs::File, std::io::Error> = std::fs::OpenOptions::new().create(true).append(true).write(true).open(path);
let write: Result<_, std::io::Error> = write!(file.as_ref().unwrap(), "{}", formatted);
if let Err(error) = file {
errors::Error::new("File error", "The file could not be opened", 1).raise(format!("Path: {}\nError: {}", path, error).as_str());
}
if let Err(error) = write {
errors::Error::new("Writing error", "The file could not be edited", 2).raise(format!("File: {}\nText: {}\nError: {}", path, formatted, error).as_str());
}
}
}
}
}
}
#[macro_export]
macro_rules! debug {
($logger:expr, $message:expr) => {
{
$logger.log($message, $crate::Level::DEBUG, std::panic::Location::caller().file(), vec![]);
}
};
($logger:expr, $message:expr, $($argument_name:literal = $argument_value:literal),* $(,)?) => {
{
let mut arguments: Vec<(&str, String)> = vec![];
$(
arguments.push(($argument_name, $argument_value.to_string()));
)*
$logger.log($message, $crate::Level::DEBUG, std::panic::Location::caller().file(), arguments);
}
};
}
#[macro_export]
macro_rules! info {
($logger:expr, $message:expr) => {
{
$logger.log($message, $crate::Level::INFO, std::panic::Location::caller().file(), vec![]);
}
};
($logger:expr, $message:expr, $($argument_name:literal = $argument_value:literal),* $(,)?) => {
{
let mut arguments: Vec<(&str, String)> = vec![];
$(
arguments.push(($argument_name, $argument_value.to_string()));
)*
$logger.log($message, $crate::Level::INFO, std::panic::Location::caller().file(), arguments);
}
}
}
#[macro_export]
macro_rules! warn {
($logger:expr, $message:expr) => {
{
$logger.log($message, $crate::Level::WARN, std::panic::Location::caller().file(), vec![]);
}
};
($logger:expr, $message:expr, $($argument_name:literal = $argument_value:literal),* $(,)?) => {
{
let mut arguments: Vec<(&str, String)> = vec![];
$(
arguments.push(($argument_name, $argument_value.to_string()));
)*
$logger.log($message, $crate::Level::WARN, std::panic::Location::caller().file(), arguments);
}
}
}
#[macro_export]
macro_rules! error {
($logger:expr, $message:expr) => {
{
$logger.log($message, $crate::Level::ERROR, std::panic::Location::caller().file(), vec![]);
}
};
($logger:expr, $message:expr, $($argument_name:literal = $argument_value:literal),* $(,)?) => {
{
let mut arguments: Vec<(&str, String)> = vec![];
$(
arguments.push(($argument_name, $argument_value.to_string()));
)*
$logger.log($message, $crate::Level::ERROR, std::panic::Location::caller().file(), arguments);
}
}
}
#[macro_export]
macro_rules! fatal {
($logger:expr, $message:expr) => {
{
$logger.log($message, $crate::Level::FATAL, std::panic::Location::caller().file(), vec![]);
}
};
($logger:expr, $message:expr, $($argument_name:literal = $argument_value:literal),* $(,)?) => {
{
let mut arguments: Vec<(&str, String)> = vec![];
$(
arguments.push(($argument_name, $argument_value.to_string()));
)*
$logger.log($message, $crate::Level::FATAL, std::panic::Location::caller().file(), arguments);
}
}
}
#[macro_export]
macro_rules! log {
($logger:expr, $message:expr) => {
{
$logger.log($message, $crate::Level::MESSAGE, std::panic::Location::caller().file(), vec![]);
}
};
($logger:expr, $message:expr, $($argument_name:literal = $argument_value:literal),* $(,)?) => {
{
let mut arguments: Vec<(&str, String)> = vec![];
$(
arguments.push(($argument_name, $argument_value.to_string()));
)*
$logger.log($message, $crate::Level::MESSAGE, std::panic::Location::caller().file(), arguments);
}
}
}