rlg/log_level.rs
1// log_level.rs
2// Copyright © 2024-2026 RustLogs (RLG). All rights reserved.
3// SPDX-License-Identifier: Apache-2.0
4// SPDX-License-Identifier: MIT
5
6// Import necessary traits and modules.
7use serde::{Deserialize, Serialize};
8use std::{convert::TryFrom, error::Error, fmt, str::FromStr};
9
10/// Custom error type for `LogLevel` parsing with context.
11#[derive(Debug, Clone)]
12pub struct ParseLogLevelError {
13 /// The invalid log level value.
14 pub invalid_value: String,
15}
16
17impl ParseLogLevelError {
18 /// Creates a new instance of `ParseLogLevelError` with the given invalid log level value.
19 ///
20 /// # Arguments
21 ///
22 /// * `invalid_value` - A reference to a string representing the invalid log level value.
23 ///
24 /// # Returns
25 ///
26 /// A new instance of `ParseLogLevelError` containing the provided invalid log level value.
27 #[must_use]
28 pub fn new(invalid_value: &str) -> Self {
29 Self {
30 invalid_value: invalid_value.to_string(),
31 }
32 }
33}
34
35impl fmt::Display for ParseLogLevelError {
36 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37 write!(f, "Invalid log level: {0}", self.invalid_value)
38 }
39}
40
41impl Error for ParseLogLevelError {}
42
43/// An enumeration of the different levels that a log message can have, ordered by severity.
44#[derive(
45 Clone,
46 Copy,
47 Debug,
48 Default,
49 Deserialize,
50 Eq,
51 Hash,
52 Ord,
53 PartialEq,
54 PartialOrd,
55 Serialize,
56)]
57pub enum LogLevel {
58 /// `ALL`: The log level includes all levels.
59 ALL,
60 /// `NONE`: No logging.
61 NONE,
62 /// `DISABLED`: Logging is disabled.
63 DISABLED,
64 /// `TRACE`: Finer-grained informational events than `DEBUG`.
65 TRACE,
66 /// `DEBUG`: Debugging information, typically useful for developers.
67 DEBUG,
68 /// `VERBOSE`: Detailed logging, often more detailed than `INFO`.
69 VERBOSE,
70 /// `INFO`: Informational messages that highlight the progress of the application.
71 #[default]
72 INFO,
73 /// `WARN`: Potentially harmful situations.
74 WARN,
75 /// `ERROR`: Error events that might still allow the application to continue running.
76 ERROR,
77 /// `FATAL`: Very severe error events that will presumably lead the application to abort.
78 FATAL,
79 /// `CRITICAL`: Critical conditions, often requiring immediate attention.
80 CRITICAL,
81}
82
83macro_rules! define_log_levels {
84 ( $( $variant:ident, $num:expr, $upper:expr, $lower:expr );+ $(;)? ) => {
85 impl LogLevel {
86 /// Converts the log level to its corresponding numeric value, similar to syslog severity levels.
87 ///
88 /// # Examples
89 ///
90 /// ```
91 /// use rlg::log_level::LogLevel;
92 /// assert_eq!(LogLevel::ERROR.to_numeric(), 8);
93 /// assert_eq!(LogLevel::DEBUG.to_numeric(), 4);
94 /// ```
95 #[must_use]
96 pub const fn to_numeric(self) -> u8 {
97 match self { $( Self::$variant => $num, )+ }
98 }
99
100 /// Returns the uppercase string representation of the log level.
101 #[must_use]
102 pub const fn as_str(&self) -> &'static str {
103 match self { $( Self::$variant => $upper, )+ }
104 }
105
106 /// Returns the lowercase string representation of the log level.
107 #[must_use]
108 pub const fn as_str_lowercase(&self) -> &'static str {
109 match self { $( Self::$variant => $lower, )+ }
110 }
111
112 /// Creates a `LogLevel` from a numeric value, similar to syslog severity levels.
113 ///
114 /// # Arguments
115 ///
116 /// * `value` - The numeric value to convert.
117 ///
118 /// # Examples
119 ///
120 /// ```
121 /// use rlg::log_level::LogLevel;
122 /// assert_eq!(LogLevel::from_numeric(8), Some(LogLevel::ERROR));
123 /// assert_eq!(LogLevel::from_numeric(5), Some(LogLevel::VERBOSE));
124 /// ```
125 #[must_use]
126 pub const fn from_numeric(value: u8) -> Option<Self> {
127 match value {
128 $( $num => Some(Self::$variant), )+
129 _ => None,
130 }
131 }
132 }
133
134 impl FromStr for LogLevel {
135 type Err = ParseLogLevelError;
136
137 fn from_str(s: &str) -> Result<Self, Self::Err> {
138 match s.to_uppercase().as_str() {
139 $( $upper => Ok(Self::$variant), )+
140 _ => Err(ParseLogLevelError::new(s)),
141 }
142 }
143 }
144 };
145}
146
147define_log_levels! {
148 ALL, 0, "ALL", "all";
149 NONE, 1, "NONE", "none";
150 DISABLED, 2, "DISABLED", "disabled";
151 TRACE, 3, "TRACE", "trace";
152 DEBUG, 4, "DEBUG", "debug";
153 VERBOSE, 5, "VERBOSE", "verbose";
154 INFO, 6, "INFO", "info";
155 WARN, 7, "WARN", "warn";
156 ERROR, 8, "ERROR", "error";
157 FATAL, 9, "FATAL", "fatal";
158 CRITICAL, 10, "CRITICAL", "critical";
159}
160
161impl LogLevel {
162 /// Checks if the current log level includes another log level.
163 ///
164 /// # Arguments
165 ///
166 /// * `other` - The log level to compare with.
167 ///
168 /// # Examples
169 ///
170 /// ```
171 /// use rlg::log_level::LogLevel;
172 /// assert!(LogLevel::ERROR.includes(LogLevel::DEBUG)); // ERROR includes DEBUG
173 /// assert!(!LogLevel::DEBUG.includes(LogLevel::WARN)); // DEBUG does not include WARN
174 /// assert!(LogLevel::WARN.includes(LogLevel::DEBUG)); // WARN includes DEBUG
175 /// ```
176 #[must_use]
177 pub const fn includes(self, other: Self) -> bool {
178 match self {
179 Self::ALL => true, // ALL includes everything
180 Self::NONE => false, // NONE includes nothing
181 _ => self.to_numeric() >= other.to_numeric(), // Default behavior for other levels
182 }
183 }
184}
185
186impl TryFrom<String> for LogLevel {
187 type Error = ParseLogLevelError;
188
189 fn try_from(value: String) -> Result<Self, Self::Error> {
190 Self::from_str(&value)
191 }
192}
193
194impl fmt::Display for LogLevel {
195 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
196 f.write_str(self.as_str())
197 }
198}