captains_log/
env.rs

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