1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
use std::{
    panic::{RefUnwindSafe, UnwindSafe},
    sync::Arc,
};

use serde::Serialize;
use strum_macros::{Display, EnumIter};

use crate::enums::{unit_enum_deserialize, unit_enum_from_str};

/// Log levels that can be used with a [`Logger`].
#[derive(
    Default, EnumIter, Display, Eq, PartialEq, PartialOrd, Ord, Clone, Copy, Debug, Serialize,
)]
pub enum LogLevel {
    Debug,
    #[default]
    Warn,
    Info,
    Error,
}

unit_enum_from_str!(LogLevel);
unit_enum_deserialize!(LogLevel);

/// Implementation of how a logger logs messages. See [`Logger`] struct.
pub trait LoggingStrategy: Send {
    fn log(&self, message: &str, level: LogLevel);
}

/// A logger object that delegates to a [`LoggingStrategy`]. Handles boxing and wrapping in [`Arc`].
#[derive(Clone)]
pub struct Logger(
    Arc<Box<dyn LoggingStrategy + Send + Sync + UnwindSafe + RefUnwindSafe + 'static>>,
);

impl Logger {
    /// Create a new logger object from the provided [`LoggingStrategy`] implementation.
    pub fn new<T: LoggingStrategy + Send + Sync + UnwindSafe + RefUnwindSafe + 'static>(
        strategy: T,
    ) -> Self {
        Self(Arc::new(Box::new(strategy)))
    }

    /// Log a message with a specific log level.
    pub fn log<M: AsRef<str>>(&self, message: M, level: LogLevel) {
        self.0.log(message.as_ref(), level)
    }

    /// Log an information message.
    pub fn info<M: AsRef<str>>(&self, message: M) {
        self.0.log(message.as_ref(), LogLevel::Info)
    }

    /// Log a warning message.
    pub fn warn<M: AsRef<str>>(&self, message: M) {
        self.0.log(message.as_ref(), LogLevel::Warn)
    }

    /// Log an error message.
    pub fn error<M: AsRef<str>>(&self, message: M) {
        self.0.log(message.as_ref(), LogLevel::Error)
    }

    /// Log a debugging message.
    pub fn debug<M: AsRef<str>>(&self, message: M) {
        self.0.log(message.as_ref(), LogLevel::Debug)
    }
}