Documentation
extern crate chrono;
extern crate colored;

use chrono::prelude::*;
use colored::*;

pub struct Logger {
    pub name: String,
    pub config: LoggerConfig,
}

pub struct LoggerConfig {
    pub time_format: String,
    pub short_name: bool,
}

pub enum LoggerLevel {
    Info,
    Error,
    Debug,
    Warn,
}

impl LoggerConfig {
    pub fn new() -> LoggerConfig {
        LoggerConfig {
            time_format: "%Y-%m-%d %H:%M:%S".to_string(),
            short_name: false,
        }
    }

    pub fn time(mut self, format: &str) -> LoggerConfig {
        self.time_format = format.to_string();
        self
    }

    pub fn short_name(mut self, short: bool) -> LoggerConfig {
        self.short_name = short;
        self
    }
}

impl Logger {
    pub fn get<T>() -> Logger {
        let name = std::any::type_name::<T>();
        Logger {
            name: name.to_string(),
            config: LoggerConfig {
                time_format: "%Y-%m-%d %H:%M:%S".to_string(),
                short_name: false,
            },
        }
    }

    pub fn get_config<T>(config: LoggerConfig) -> Logger {
        let full_name = std::any::type_name::<T>();
        let name = full_name.split("::").last().unwrap();
        Logger {
            name: name.to_string(),
            config,
        }
    }

    pub fn get_name(name: &str) -> Logger {
        Logger {
            name: name.to_string(),
            config: LoggerConfig {
                time_format: "%Y-%m-%d %H:%M:%S".to_string(),
                short_name: false,
            },
        }
    }

    pub fn get_name_config(name: &str, config: LoggerConfig) -> Logger {
        Logger {
            name: name.to_string(),
            config,
        }
    }

    pub fn info(&self, m: &str) {
        self.log(LoggerLevel::Info, m)
    }

    pub fn error(&self, m: &str) {
        self.log(LoggerLevel::Error, m)
    }

    pub fn debug(&self, m: &str) {
        self.log(LoggerLevel::Debug, m)
    }

    pub fn warn(&self, m: &str) {
        self.log(LoggerLevel::Warn, m)
    }

    pub fn log(&self, level: LoggerLevel, m: &str) {
        let lvl: ColoredString;

        match level {
            LoggerLevel::Info => lvl = "info".blue(),
            LoggerLevel::Error => lvl = "err".red(),
            LoggerLevel::Debug => lvl = "debug".yellow(),
            LoggerLevel::Warn => lvl = "warn".bold().yellow(),
        }

        println!(
            "[{}] [{}] {}: {}",
            Local::now()
                .format(&self.config.time_format)
                .to_string()
                .purple(),
            self.name.bold().green(),
            lvl,
            m
        )
    }
}

#[cfg(test)]
mod tests {
    use crate::{Logger, LoggerConfig};

    #[test]
    pub fn log_test() {
        let logger = Logger::get::<Logger>();
        logger.info("hi");
        logger.error("hi");
        logger.debug("hi");
        logger.warn("hi");
    }

    #[test]
    pub fn log_config_test() {
        let logger = Logger::get_config::<Logger>(LoggerConfig::new().time("%Y").short_name(false));

        logger.info("hi");
        logger.error("hi");
        logger.debug("hi");
        logger.warn("hi");
    }
}