kellnr_settings/
log.rs

1use std::fmt::Display;
2
3use serde::{Deserialize, Deserializer, Serialize};
4
5use crate::deserialize_with::DeserializeWith;
6
7#[derive(Debug, Deserialize, Serialize, Eq, PartialEq, Clone)]
8#[serde(default)]
9pub struct Log {
10    #[serde(deserialize_with = "LogFormat::deserialize_with")]
11    pub format: LogFormat,
12    #[serde(deserialize_with = "LogLevel::deserialize_with")]
13    pub level: LogLevel,
14    #[serde(deserialize_with = "LogLevel::deserialize_with")]
15    pub level_web_server: LogLevel,
16}
17
18impl Default for Log {
19    fn default() -> Self {
20        Self {
21            format: LogFormat::Compact,
22            level: LogLevel::Info,
23            level_web_server: LogLevel::Warn,
24        }
25    }
26}
27
28#[derive(Debug, PartialEq, Eq, Clone, Copy)]
29pub enum LogFormat {
30    Compact,
31    Pretty,
32    Json,
33}
34
35impl DeserializeWith for LogFormat {
36    fn deserialize_with<'de, D>(de: D) -> Result<Self, D::Error>
37    where
38        D: Deserializer<'de>,
39    {
40        let s = String::deserialize(de)?.to_lowercase();
41
42        match s.as_ref() {
43            "compact" => Ok(LogFormat::Compact),
44            "pretty" => Ok(LogFormat::Pretty),
45            "json" => Ok(LogFormat::Json),
46            _ => Err(serde::de::Error::custom(
47                "error trying to deserialize log format: {s}",
48            )),
49        }
50    }
51}
52
53impl Serialize for LogFormat {
54    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
55    where
56        S: serde::Serializer,
57    {
58        serializer.serialize_str(&self.to_string())
59    }
60}
61
62impl Display for LogFormat {
63    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64        match self {
65            LogFormat::Compact => write!(f, "compact"),
66            LogFormat::Pretty => write!(f, "pretty"),
67            LogFormat::Json => write!(f, "json"),
68        }
69    }
70}
71
72#[derive(Debug, PartialEq, Eq, Clone, Copy)]
73pub enum LogLevel {
74    Trace,
75    Debug,
76    Info,
77    Warn,
78    Error,
79}
80
81impl Display for LogLevel {
82    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83        match self {
84            LogLevel::Trace => write!(f, "trace"),
85            LogLevel::Debug => write!(f, "debug"),
86            LogLevel::Info => write!(f, "info"),
87            LogLevel::Warn => write!(f, "warn"),
88            LogLevel::Error => write!(f, "error"),
89        }
90    }
91}
92
93impl Serialize for LogLevel {
94    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
95    where
96        S: serde::Serializer,
97    {
98        serializer.serialize_str(&self.to_string())
99    }
100}
101
102impl DeserializeWith for LogLevel {
103    fn deserialize_with<'de, D>(de: D) -> Result<Self, D::Error>
104    where
105        D: Deserializer<'de>,
106    {
107        let s = String::deserialize(de)?.to_lowercase();
108
109        match s.as_ref() {
110            "trace" => Ok(LogLevel::Trace),
111            "debug" => Ok(LogLevel::Debug),
112            "info" => Ok(LogLevel::Info),
113            "warn" => Ok(LogLevel::Warn),
114            "error" => Ok(LogLevel::Error),
115            _ => Err(serde::de::Error::custom(
116                "error trying to deserialize log level: {s}",
117            )),
118        }
119    }
120}
121
122impl From<LogLevel> for tracing::Level {
123    fn from(value: LogLevel) -> Self {
124        match value {
125            LogLevel::Trace => tracing::Level::TRACE,
126            LogLevel::Debug => tracing::Level::DEBUG,
127            LogLevel::Info => tracing::Level::INFO,
128            LogLevel::Warn => tracing::Level::WARN,
129            LogLevel::Error => tracing::Level::ERROR,
130        }
131    }
132}
133
134impl From<LogLevel> for tracing::level_filters::LevelFilter {
135    fn from(value: LogLevel) -> Self {
136        Self::from_level(value.into())
137    }
138}
139
140#[cfg(test)]
141mod log_format_tests {
142    use serde::Deserialize;
143
144    use super::*;
145
146    #[derive(Debug, Deserialize)]
147    struct Settings {
148        #[serde(deserialize_with = "LogFormat::deserialize_with")]
149        log_format: LogFormat,
150    }
151
152    #[test]
153    fn test_deserialize_log_format_compact() {
154        let toml = r#"
155            log_format = "compact"
156        "#;
157
158        let settings: Settings = toml::from_str(toml).unwrap();
159        assert_eq!(settings.log_format, LogFormat::Compact);
160    }
161
162    #[test]
163    fn test_deserialize_log_format_pretty() {
164        let toml = r#"
165            log_format = "pretty"
166        "#;
167
168        let settings: Settings = toml::from_str(toml).unwrap();
169        assert_eq!(settings.log_format, LogFormat::Pretty);
170    }
171
172    #[test]
173    fn test_deserialize_log_format_json() {
174        let toml = r#"
175            log_format = "json"
176        "#;
177
178        let settings: Settings = toml::from_str(toml).unwrap();
179        assert_eq!(settings.log_format, LogFormat::Json);
180    }
181
182    #[test]
183    fn test_deserialize_log_format_invalid() {
184        let toml = r#"
185        log_level = "no_log_format"
186        "#;
187
188        let settings: Result<Settings, toml::de::Error> = toml::from_str(toml);
189        assert!(settings.is_err());
190    }
191}
192
193#[cfg(test)]
194mod log_level_tests {
195    use serde::Deserialize;
196
197    use super::*;
198
199    #[derive(Debug, Deserialize)]
200    struct Settings {
201        #[serde(deserialize_with = "LogLevel::deserialize_with")]
202        log_level: LogLevel,
203    }
204
205    #[test]
206    fn test_deserialize_log_level_trace() {
207        let toml = r#"
208            log_level = "trace"
209        "#;
210
211        let settings: Settings = toml::from_str(toml).unwrap();
212        assert_eq!(settings.log_level, LogLevel::Trace);
213    }
214
215    #[test]
216    fn test_deserialize_log_level_debug() {
217        let toml = r#"
218            log_level = "debug"
219        "#;
220
221        let settings: Settings = toml::from_str(toml).unwrap();
222        assert_eq!(settings.log_level, LogLevel::Debug);
223    }
224
225    #[test]
226    fn test_deserialize_log_level_info() {
227        let toml = r#"
228            log_level = "info"
229        "#;
230
231        let settings: Settings = toml::from_str(toml).unwrap();
232        assert_eq!(settings.log_level, LogLevel::Info);
233    }
234
235    #[test]
236    fn test_deserialize_log_level_warn() {
237        let toml = r#"
238            log_level = "warn"
239        "#;
240
241        let settings: Settings = toml::from_str(toml).unwrap();
242        assert_eq!(settings.log_level, LogLevel::Warn);
243    }
244
245    #[test]
246    fn test_deserialize_log_level_error() {
247        let toml = r#"
248            log_level = "error"
249        "#;
250
251        let settings: Settings = toml::from_str(toml).unwrap();
252        assert_eq!(settings.log_level, LogLevel::Error);
253    }
254
255    #[test]
256    fn test_deserialize_log_level_uppercase() {
257        let toml = r#"
258        log_level = "DEBUG"
259        "#;
260
261        let settings: Settings = toml::from_str(toml).unwrap();
262        assert_eq!(settings.log_level, LogLevel::Debug);
263    }
264
265    #[test]
266    fn test_deserialize_log_level_invalid() {
267        let toml = r#"
268        log_level = "no_log_level"
269        "#;
270
271        let settings: Result<Settings, toml::de::Error> = toml::from_str(toml);
272        assert!(settings.is_err());
273    }
274}
275
276#[cfg(test)]
277mod log_tests {
278    use super::*;
279
280    #[derive(Debug, Deserialize)]
281    struct Settings {
282        pub log: Log,
283    }
284
285    #[test]
286    fn test_deserialize_whole_log() {
287        let toml = r#"
288        [no_log]
289        foo = "bar"
290
291        [log]
292        level = "trace"
293        level_web_server = "debug"
294        format = "compact"
295        "#;
296
297        let settings: Settings = toml::from_str(toml).unwrap();
298        assert_eq!(settings.log.level, LogLevel::Trace);
299        assert_eq!(settings.log.level_web_server, LogLevel::Debug);
300        assert_eq!(settings.log.format, LogFormat::Compact);
301    }
302}