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
31pub struct EnvVarDefault<'a, T> {
33 pub(crate) name: &'a str,
34 pub(crate) default: T,
35}
36
37pub 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 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 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 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}