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
35pub 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 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 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 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}