1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
use std::fmt::{Display, Debug};

#[doc(hidden)]
pub use paste::paste;
#[doc(hidden)]
pub use lazy_static::lazy_static;

#[macro_export]
macro_rules! env_var_opt {
    ($env_name:ident  $(-> $short_name:ident)?) => {
        env_var_opt!(impl $env_name, $($short_name)? $env_name);
    };

    (impl $env_name:ident, $var_name:ident $($extra:ident)?) => {
        $crate::paste! {
            $crate::lazy_static! {
                pub static ref $var_name: Option<String> = std::env::var(stringify!($env_name)).ok();
            }
            pub fn [<$var_name:lower>]() -> Option<&'static str> {
                $var_name.as_ref()
            }
        }
    }
}

#[macro_export]
macro_rules! env_var_opt_default {
    ($env_name:ident (default $default_env_name:ident) $(-> $short_name:ident)?) => {
        env_var_opt_default!(impl $env_name, $default_env_name, $($short_name)? $env_name);
    };

    (impl $env_name:ident, $default_env_name:ident, $var_name:ident $($extra:ident)?) => {
        $crate::paste! {
            $crate::lazy_static! {
                pub static ref $var_name: Result<String, &'static str> = std::env::var(stringify!($env_name))
                    .or_else(|_| std::env::var(stringify!($default_env_name)).map_err(|_| stringify!($env_name)));
            }
            pub fn [<$var_name:lower>]() -> &'static str {
                $var_name.as_ref().unwrap()
            }
        }
    }
}


#[macro_export]
macro_rules! env_var_req {
    ($env_name:ident $(-> $short_name:ident)?) => {
        env_var_req!(impl $env_name, $($short_name)? $env_name);
    };
    (impl $env_name:ident, $var_name:ident $($extra:ident)?) => {
        $crate::paste! {
            $crate::lazy_static! {
                pub static ref $var_name: Result<String, &'static str> = std::env::var(stringify!($env_name)).map_err(|_| stringify!($env_name));
            }
            pub fn [<$var_name:lower>]() -> &'static str {
                $var_name.as_ref().unwrap()
            }
        }
    }
}

#[macro_export]
macro_rules! assert_req_env {
    ($check_name_fn:ident: $($names:ident),+) => {
        const NAMES_LEN: usize = [$(stringify!($names)),+].len();
        pub fn $check_name_fn() -> Result<(), EnvVarErr<NAMES_LEN>> {
            let errs = [$($names.as_ref().err().map(|e| *e)),+];
            if errs.iter().any(Option::is_some) {
                Err(EnvVarErr::new(errs))
            } else {
                Ok(())
            }
        }
    };
}

pub struct EnvVarErr<const T: usize>([Option<&'static str>; T]);

impl<const T: usize> EnvVarErr<T> {
    pub fn new(inner: [Option<&'static str>; T]) -> Self {
        Self(inner)
    }
}
impl<const T: usize> Display for EnvVarErr<T> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.0
            .into_iter()
            .flatten()
            .enumerate()
            .try_for_each(
                |(idx, var_name)| if idx == 0 {
                    write!(f, "{var_name}")
                } else {
                    write!(f, ", {var_name}")
                }
            )
    }
}
impl<const T: usize> Debug for EnvVarErr<T> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{self}")
    }
}