env_vars_config/lib.rs
1//! A simple lib for configuring your applications via environment variables
2
3#[doc(hidden)]
4pub extern crate log;
5
6/// Creates new mod with specified environment variables
7/// # Examples
8///
9/// ```
10/// use env_vars_config::env_vars_config;
11///
12/// env_vars_config! {
13/// SERVER_ADDRESS: String = "0.0.0.0:8080",
14/// WORKERS_COUNT: i32 = 32,
15/// }
16///
17/// assert_eq!(config::SERVER_ADDRESS.as_str(), "0.0.0.0:8080");
18/// assert_eq!(config::WORKERS_COUNT.clone(), 32);
19/// ```
20///
21/// # Panics
22///
23/// ```no_compile
24/// use env_vars_config::env_vars_config;
25///
26/// env_vars_config! {
27/// // incompatible type
28/// ENABLE_REGISTRATION: bool = "false",
29/// }
30/// ```
31#[macro_export]
32macro_rules! env_vars_config {
33 ( $( $name:ident: $type:tt = $default_value:expr ),* $(,)? ) => {
34 pub mod config {
35 use std::{
36 any::type_name,
37 env,
38 fmt::Display,
39 str::FromStr,
40 sync::LazyLock,
41 };
42
43 #[inline]
44 fn get_variable_type<T>(_: &T) -> &'static str {
45 type_name::<T>().split("::").last().unwrap()
46 }
47
48 #[inline]
49 fn missing_in_env_warn(variable_name: &str, default_value: impl Display) {
50 $crate::log::warn!(
51 "Variable `{variable_name}` is missing in the env! Using default value `{default_value}`",
52 );
53 }
54
55 #[inline]
56 fn get_variable_value<T: FromStr>(variable_name: &str, default_value: impl Into<T> + Clone + Display) -> T {
57 let (value, is_missing) = if let Ok(value) = env::var(variable_name) {
58 let value = value.parse::<T>().unwrap_or_else(|_| panic!(
59 "Invalid value type for the variable `{variable_name}`! Expected type `{}`, got `{}`.",
60 stringify!(T),
61 get_variable_type(&value)
62 ));
63 let is_missing = env::var(format!("_{variable_name}_WAS_SET"))
64 .unwrap_or("false".to_string())
65 .eq("true");
66 (value, is_missing)
67 } else {
68 (default_value.clone().into(), true)
69 };
70
71 if is_missing {
72 missing_in_env_warn(&variable_name, &default_value);
73 }
74
75 value
76 }
77
78 $(
79 /// Our environment variable. Lazy-evaluated by default
80 pub static $name: LazyLock<$type> = LazyLock::new(|| {
81 get_variable_value(stringify!($name), $default_value)
82 });
83 )*
84
85 /// Inits all environment variables
86 pub fn init() {
87 $(
88 LazyLock::force(&$name);
89 )*
90 }
91
92 /// Tries to get every variable value. Usable when you need
93 pub fn test_values() {
94 $(
95 get_variable_value::<$type>(stringify!($name), $default_value);
96 )*
97 }
98
99 /// Updates the environment with variable values
100 ///
101 /// Does `set_env_only` under the hood
102 pub fn set_env() {
103 $(
104 unsafe {
105 $crate::set_env_only!($name);
106 }
107 )*
108 }
109 }
110 };
111}
112
113/// Inits config value and sets it in the runtime environment
114/// Note: uses `std::env::set_var` under the hood.
115/// # Examples
116/// ```
117/// use env_vars_config::{env_vars_config, set_env_only};
118/// use std::env;
119///
120/// env_vars_config! {
121/// OTEL_SERVICE_NAME: String = "test-service",
122/// }
123///
124/// assert_eq!(config::OTEL_SERVICE_NAME.as_str(), "test-service");
125///
126/// unsafe {
127/// use config::OTEL_SERVICE_NAME;
128/// set_env_only!(OTEL_SERVICE_NAME);
129/// }
130///
131/// assert_eq!(env::var("OTEL_SERVICE_NAME").unwrap().as_str(), "test-service");
132/// ```
133#[macro_export]
134macro_rules! set_env_only {
135 ($name:ident) => {
136 env::set_var(stringify!($name), $name.to_string());
137 env::set_var(format!("_{}_WAS_SET", stringify!($name)), "true");
138 };
139}