use std::time::SystemTime;
use chrono::{ DateTime, Utc };
use super::node_tree_base::NodeIdentity;
use crate::prelude::{ RID, NodeTreeBase };
use crate::utils::functions::draw_tree;
#[derive(Debug, Clone)]
pub enum LoggerVerbosity {
All,
NoDebug,
OnlyIssues,
OnlyPanics
}
#[derive(Debug, Clone)]
pub enum SystemCall {
Named(String),
NodePath(String)
}
impl SystemCall {
pub fn format(&self) -> String {
match self {
Self::Named(str) => str.to_string(),
Self::NodePath(str) => format!("[{}]", str)
}
}
pub fn to_str(&self) -> &str {
match self {
Self::Named(str) => str,
Self::NodePath(str) => str
}
}
}
#[derive(Debug, Clone)]
pub enum Log<'a> {
Debug(&'a str),
Info(&'a str),
Warn(&'a str),
Panic(&'a str)
}
impl <'a >Log<'a> {
pub fn get_lv(&self) -> String {
match self {
Log::Debug(_) => "DEBUG".to_string(),
Log::Info(_) => "INFO".to_string(),
Log::Warn(_) => "WARN".to_string(),
Log::Panic(_) => "PANIC!".to_string()
}
}
pub fn get_msg(&self) -> &'a str {
match self {
Log::Debug(str) => str,
Log::Info(str) => str,
Log::Warn(str) => str,
Log::Panic(str) => str
}
}
pub fn get_colour(&self) -> String {
match self {
Log::Debug(_) => "\u{001b}[30m".to_string(), Log::Info(_) => "\u{001b}[37m".to_string(), Log::Warn(_) => "\u{001b}[33m".to_string(), Log::Panic(_) => "\u{001b}[31m".to_string() }
}
pub fn is_debug(&self) -> bool {
match self {
Log::Debug(_) => true,
_ => false
}
}
pub fn is_problematic(&self) -> bool {
match self {
Log::Warn(_) | Log::Panic(_) => true,
_ => false
}
}
pub fn is_panic(&self) -> bool {
match self {
Log::Panic(_) => true,
_ => false
}
}
}
#[derive(Debug, Clone)]
pub struct Logger {
log: String,
verbosity_lv: LoggerVerbosity,
crash_header: String,
crash_footer: String
}
impl Logger {
pub fn new(verbosity_lv: LoggerVerbosity) -> Self {
let mut logger: Logger = Logger {
log: String::new(),
verbosity_lv,
crash_header: "Unfortunately the program has crashed. Please contact the development team with the following crash report as well as the attachment of the log posted during the time of the crash.".to_string(),
crash_footer: "Goodbye World! (Program Exited)".to_string()
};
logger.post_manual(SystemCall::Named("SysLogger".to_string()), Log::Debug("System logger has initialized. Hello World!"));
logger
}
pub fn set_default_header_on_panic(&mut self, msg: &str) {
self.crash_header = msg.to_string();
}
pub fn set_default_footer_on_panic(&mut self, msg: &str) {
self.crash_footer = msg.to_string();
}
pub unsafe fn post(&mut self, calling: RID, log: Log, node_tree: *mut NodeTreeBase) -> bool {
match &self.verbosity_lv {
LoggerVerbosity::All => {},
LoggerVerbosity::NoDebug => if log.is_debug() { return false; },
LoggerVerbosity::OnlyIssues => if !log.is_problematic() { return false; },
LoggerVerbosity::OnlyPanics => if !log.is_panic() { return false; }
}
let node_tree: &NodeTreeBase = unsafe { &*node_tree };
let system: SystemCall = {
match node_tree.get_node_identity(calling) {
Some(NodeIdentity::NodePath) => SystemCall::NodePath(unsafe { node_tree.get_node(calling).unwrap_unchecked() }.get_absolute_path().to_string()),
Some(NodeIdentity::UniqueName(name)) => SystemCall::Named(name),
None => unimplemented!()
}
};
let colour: String = log.get_colour();
let panic: bool = log.is_panic();
let time: String = self.post_manual(system, log);
if panic {
let node_tree_visual: String = draw_tree(node_tree, calling, 6, 6);
println!("
{}{}
\u{001b}[0m{}{}
Time of Crash: {}
Exit Code: {}
{}\u{001b}[0m", colour, self.crash_header, node_tree_visual, colour, time, 1, self.crash_footer);
self.log += &format!("
{}
{}
Time of Crash: {}
Exit Code: {}
{}", self.crash_header, node_tree_visual, time, 1, self.crash_footer);
}
panic
}
pub fn post_manual(&mut self, system: SystemCall, log: Log) -> String {
let time: String = DateTime::<Utc>::from(SystemTime::now()).format("%d/%m/%Y %T").to_string();
match &self.verbosity_lv {
LoggerVerbosity::All => {},
LoggerVerbosity::NoDebug => if log.is_debug() { return time; },
LoggerVerbosity::OnlyIssues => if !log.is_problematic() { return time; },
LoggerVerbosity::OnlyPanics => if !log.is_panic() { return time; }
}
println!(
"{}<{} UTC> | {} | {} | {}\u{001b}[0m",
log.get_colour(),
time,
system.format(),
log.get_lv(),
log.get_msg()
);
self.log += &format!(
"<{} UTC> | {} | {} | {}\n",
time,
system.format(),
log.get_lv(),
log.get_msg()
);
time
}
pub fn to_str(&self) -> &str {
&self.log
}
}
#[macro_export]
macro_rules! debug {
($self:ident, $fmt_str:literal) => {{
$self.post(Log::Debug(&format!($fmt_str)))
}};
($self:ident, $fmt_str:literal, $($args:expr),*) => {{
$self.post(Log::Debug(&format!($fmt_str, $($args),*)))
}};
}
#[macro_export]
macro_rules! info {
($self:ident, $fmt_str:literal) => {{
$self.post(Log::Info(&format!($fmt_str)))
}};
($self:ident, $fmt_str:literal, $($args:expr),*) => {{
$self.post(Log::Info(&format!($fmt_str, $($args),*)))
}};
}
#[macro_export]
macro_rules! warn {
($self:ident, $fmt_str:literal) => {{
$self.post(Log::Warn(&format!($fmt_str)))
}};
($self:ident, $fmt_str:literal, $($args:expr),*) => {{
$self.post(Log::Warn(&format!($fmt_str, $($args),*)))
}};
}
#[macro_export]
macro_rules! error {
($self:ident, $fmt_str:literal) => {{
$self.post(Log::Panic(&format!($fmt_str)))
}};
($self:ident, $fmt_str:literal, $($args:expr),*) => {{
$self.post(Log::Panic(&format!($fmt_str, $($args),*)))
}};
}