1use std::str::FromStr;
2
3use anyhow::anyhow;
4
5#[derive(Debug, Clone)]
6pub struct LogLevel<'a>(&'a str);
7
8impl<'a> FromStr for LogLevel<'a> {
9 type Err = anyhow::Error;
10
11 fn from_str(s: &str) -> Result<Self, Self::Err> {
12 let level = s.to_ascii_lowercase();
13
14 match level.as_str() {
15 "error" | "warn" | "info" | "debug" | "trace" => {
16 Ok(LogLevel(Box::leak(level.into_boxed_str())))
17 }
18 _ => Err(anyhow!(
19 "Invalid log level: {} (expected one of error, warn, info, debug, trace)",
20 s
21 )),
22 }
23 }
24}
25
26impl<'a> From<&'a str> for LogLevel<'a> {
27 fn from(s: &'a str) -> Self {
28 LogLevel::from_str(s).unwrap_or_else(|_| LogLevel("info"))
29 }
30}
31
32impl<'a> LogLevel<'a> {
33 fn as_str(&self) -> &str {
34 self.0
35 }
36}
37
38impl<'a> Default for LogLevel<'a> {
39 fn default() -> Self {
40 LogLevel("info")
41 }
42}
43
44#[cfg(feature = "logging")]
45pub fn init_logging<L>(level: L)
46where
47 L: Into<LogLevel<'static>>,
48{
49 use std::sync::Once;
50 static INIT: Once = Once::new();
51 let level = level.into();
52
53 INIT.call_once(|| {
54 let level_str = level.as_str();
55 unsafe { std::env::set_var("RUST_LOG", format!("tydle={}", level_str)) };
56
57 env_logger::init();
58 #[cfg(feature = "logging")]
59 log::info!("Logging initialized at level: {}", level_str);
60 });
61}
62
63#[cfg(not(feature = "logging"))]
64pub fn init_logging(_: Option<&str>) {}