captains_log/
env.rs

1use log::Level;
2use std::path::{Path, PathBuf};
3use std::str::FromStr;
4
5#[macro_export(local_inner_macros)]
6macro_rules! impl_from_env {
7    ($type: tt) => {
8        impl<'a> Into<$type> for EnvVarDefault<'a, $type> {
9            #[inline]
10            fn into(self) -> $type {
11                if let Ok(v) = std::env::var(&self.name) {
12                    match $type::from_str(&v) {
13                        Ok(r) => return r,
14                        Err(_) => {
15                            std::eprintln!(
16                                "env {}={} is not valid, set to {:?}",
17                                self.name,
18                                v,
19                                self.default
20                            );
21                        }
22                    }
23                }
24                return self.default;
25            }
26        }
27    };
28}
29
30pub struct EnvVarDefault<'a, T> {
31    pub(crate) name: &'a str,
32    pub(crate) default: T,
33}
34
35/// To config some logger setting with env.
36///
37/// Read value from environment, and set with default if not exists.
38///
39/// NOTE: the arguments to load from env_or() must support owned values.
40///
41/// Example:
42///
43/// ```rust
44/// use captains_log::*;
45/// let _level: log::Level = env_or("LOG_LEVEL", Level::Info).into();
46/// let _file_path: String = env_or("LOG_FILE", "/tmp/test.log").into();
47/// let _console: ConsoleTarget = env_or("LOG_CONSOLE", ConsoleTarget::Stdout).into();
48/// ```
49pub fn env_or<'a, T>(name: &'a str, default: T) -> EnvVarDefault<'a, T> {
50    EnvVarDefault { name, default }
51}
52
53impl<'a> Into<String> for EnvVarDefault<'a, &'a str> {
54    fn into(self) -> String {
55        if let Ok(v) = std::env::var(&self.name) {
56            return v;
57        }
58        return self.default.to_string();
59    }
60}
61
62impl<'a, P: AsRef<Path>> Into<PathBuf> for EnvVarDefault<'a, P> {
63    fn into(self) -> PathBuf {
64        if let Some(v) = std::env::var_os(&self.name) {
65            if v.len() > 0 {
66                return PathBuf::from(v);
67            }
68        }
69        return self.default.as_ref().to_path_buf();
70    }
71}
72
73crate::impl_from_env!(Level);
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78    use crate::recipe;
79    use crate::*;
80
81    #[test]
82    fn test_env_config() {
83        // test log level
84        unsafe { std::env::set_var("LEVEL", "warn") };
85        let level: Level = env_or("LEVEL", Level::Debug).into();
86        assert_eq!(level, Level::Warn);
87        unsafe { std::env::set_var("LEVEL", "WARN") };
88        let level: Level = env_or("LEVEL", Level::Debug).into();
89        assert_eq!(level, Level::Warn);
90
91        assert_eq!(ConsoleTarget::from_str("Stdout").unwrap(), ConsoleTarget::Stdout);
92        assert_eq!(ConsoleTarget::from_str("StdERR").unwrap(), ConsoleTarget::Stderr);
93        assert_eq!(ConsoleTarget::from_str("1").unwrap(), ConsoleTarget::Stdout);
94        assert_eq!(ConsoleTarget::from_str("2").unwrap(), ConsoleTarget::Stderr);
95        assert_eq!(ConsoleTarget::from_str("0").unwrap_err(), ());
96
97        // test console target
98        unsafe { std::env::set_var("CONSOLE", "stderr") };
99        let target: ConsoleTarget = env_or("CONSOLE", ConsoleTarget::Stdout).into();
100        assert_eq!(target, ConsoleTarget::Stderr);
101        unsafe { std::env::set_var("CONSOLE", "") };
102        let target: ConsoleTarget = env_or("CONSOLE", ConsoleTarget::Stdout).into();
103        assert_eq!(target, ConsoleTarget::Stdout);
104
105        // test path
106        unsafe { std::env::set_var("LOG_PATH", "/tmp/test.log") };
107        let path: PathBuf = env_or("LOG_PATH", "/tmp/other.log").into();
108        assert_eq!(path, Path::new("/tmp/test.log").to_path_buf());
109
110        unsafe { std::env::set_var("LOG_PATH", "") };
111        let path: PathBuf = env_or("LOG_PATH", "/tmp/other.log").into();
112        assert_eq!(path, Path::new("/tmp/other.log").to_path_buf());
113
114        let _builder = recipe::raw_file_logger(env_or("LOG_PATH", "/tmp/other.log"), Level::Info);
115        let _builder =
116            recipe::raw_file_logger(env_or("LOG_PATH", "/tmp/other.log".to_string()), Level::Info);
117    }
118}