anvil_db 0.2.2

an embedded key-value store
Documentation
use std::fmt::{Display, Formatter};
use std::sync::{Arc, RwLock};
use std::time::{SystemTime, UNIX_EPOCH};

pub(crate) enum LogLevel {
    Info,
    Error,
    #[allow(dead_code)]
    Debug,
}

impl Display for LogLevel {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            LogLevel::Info => write!(f, "INFO"),
            LogLevel::Error => write!(f, "ERROR"),
            LogLevel::Debug => write!(f, "DEBUG"),
        }
    }
}

pub(crate) trait Logger: Clone + Send + Sync + 'static {
    fn log(&self, level: LogLevel, file_name: &str, line_no: u32, column_no: u32, msg: &str);
    fn info(&self, file_name: &str, line_no: u32, column_no: u32, msg: &str) {
        self.log(LogLevel::Info, file_name, line_no, column_no, msg)
    }
    fn error(&self, file_name: &str, line_no: u32, column_no: u32, msg: &str) {
        self.log(LogLevel::Error, file_name, line_no, column_no, msg)
    }
    #[allow(dead_code)]
    fn debug(&self, file_name: &str, line_no: u32, column_no: u32, msg: &str) {
        self.log(LogLevel::Debug, file_name, line_no, column_no, msg)
    }
}

fn default_prefix(level: LogLevel, file_name: &str, line_no: u32, column_no: u32) -> String {
    let level_str = level.to_string();
    let now = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .unwrap()
        .as_millis();
    format!("[{level_str}]\t{now} {file_name}:{line_no}:{column_no}")
}

fn prefix_lines(prefix: &str, msg: &str) -> String {
    let spaces = prefix
        .chars()
        .map(|c| if c == '\t' { c } else { ' ' })
        .collect::<String>();
    let formatted_lines = msg.lines().enumerate().map(|(i, line)| {
        if i == 0 {
            format!("{prefix} {line}",)
        } else {
            format!("{spaces} {line}",)
        }
    });
    formatted_lines.collect::<Vec<_>>().join("\n")
}

#[cfg(test)]
impl Logger for () {
    fn log(&self, level: LogLevel, file_name: &str, line_no: u32, column_no: u32, msg: &str) {
        let prefix_1 = default_prefix(level, file_name, line_no, column_no);
        let prefix = format!("{prefix_1} (null-logger)");
        let formatted_message = prefix_lines(&prefix, msg);
        println!("{formatted_message}",);
    }
}

#[derive(Clone, Debug, Default)]
pub(crate) struct DefaultLogger {}

impl Logger for DefaultLogger {
    fn log(&self, level: LogLevel, file_name: &str, line_no: u32, column_no: u32, msg: &str) {
        let prefix = default_prefix(level, file_name, line_no, column_no);
        let formatted_message = prefix_lines(&prefix, msg);
        println!("{formatted_message}",);
    }
}

#[allow(dead_code)]
#[derive(Debug)]
pub(crate) struct ArchiveLogger {
    name: String,
    clone_id: usize,
    history: Arc<RwLock<Vec<String>>>,
}

impl ArchiveLogger {
    #[allow(dead_code)]
    pub(crate) fn new(name: &str) -> Self {
        ArchiveLogger {
            name: name.to_string(),
            clone_id: 0,
            history: Arc::new(RwLock::new(Vec::new())),
        }
    }

    #[allow(dead_code)]
    pub(crate) fn history_ref(&self) -> Vec<String> {
        self.history.read().unwrap().clone()
    }
}

unsafe impl Send for ArchiveLogger {}
unsafe impl Sync for ArchiveLogger {}

impl Clone for ArchiveLogger {
    fn clone(&self) -> Self {
        ArchiveLogger {
            name: self.name.clone(),
            clone_id: self.clone_id + 1,
            history: self.history.clone(),
        }
    }
}

impl Logger for ArchiveLogger {
    fn log(&self, level: LogLevel, file_name: &str, line_no: u32, column_no: u32, msg: &str) {
        let prefix_1 = default_prefix(level, file_name, line_no, column_no);
        let prefix = format!(
            "{prefix_1} {name}-{clone_id} ",
            name = self.name,
            clone_id = self.clone_id
        );
        let formatted_message = prefix_lines(&prefix, msg);
        self.history
            .write()
            .unwrap()
            .push(formatted_message.clone());
    }
}

#[allow(dead_code)]
#[derive(Debug, Clone)]
pub(crate) struct FixPrefixLogger {
    prefix: String,
}

unsafe impl Send for FixPrefixLogger {}
unsafe impl Sync for FixPrefixLogger {}

impl FixPrefixLogger {
    #[allow(dead_code)]
    pub(crate) fn new(prefix: &str) -> Self {
        FixPrefixLogger {
            prefix: prefix.to_string(),
        }
    }
}

impl Logger for FixPrefixLogger {
    fn log(&self, _level: LogLevel, file_name: &str, line_no: u32, column_no: u32, msg: &str) {
        let prefix_1 = format!("{file_name}:{line_no}:{column_no}");
        let prefix = if self.prefix.is_empty() {
            format!("{prefix_1}:")
        } else {
            format!("{} {}:", prefix_1, self.prefix)
        };
        let formatted_message = prefix_lines(&prefix, msg);
        println!("{formatted_message}",);
    }
}

macro_rules! info {
    ($logger:expr, $($arg:tt)*) => {
        crate::logging::Logger::info($logger, file!(), line!(), column!(), &format!($($arg)*));
    };
}
pub(crate) use info;

macro_rules! error {
    ($logger:expr, $($arg:tt)*) => {
        crate::logging::Logger::error($logger, file!(), line!(), column!(), &format!($($arg)*));
    };
}
pub(crate) use error;

#[allow(unused_macros)]
macro_rules! debug {
    ($logger:expr, $($arg:tt)*) => {
        #[cfg(test)]
        crate::logging::Logger::debug($logger, file!(), line!(), column!(), &format!($($arg)*));
    };
}
#[cfg(test)]
pub(crate) use debug;