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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
use crate::*;
use paste::paste;
/// Trait for parsing nested environment variables.
pub trait EnvParseNested {
/// Creates a new instance by parsing environment variables.
///
/// # Errors
///
/// Returns an `EnvStructError` if parsing fails.
fn new() -> Result<Self, EnvStructError>
where
Self: Sized,
{
Self::parse_from_env_var("", None)
}
/// Creates a new instance with a specified prefix by parsing environment variables.
///
/// # Arguments
///
/// * `prefix` - A prefix for the environment variables.
///
/// # Errors
///
/// Returns an `EnvStructError` if parsing fails.
fn with_prefix(prefix: impl AsRef<str>) -> Result<Self, EnvStructError>
where
Self: Sized,
{
Self::parse_from_env_var(prefix, None)
}
/// Parses the environment variable with an optional default value.
///
/// # Arguments
///
/// * `var_name` - The name of the environment variable.
/// * `default` - An optional default value.
///
/// # Errors
///
/// Returns an `EnvStructError` if parsing fails.
fn parse_from_env_var(
var_name: impl AsRef<str>,
default: Option<&str>,
) -> Result<Self, EnvStructError>
where
Self: Sized;
/// Retrieves the environment entries with a specified prefix and optional default value.
///
/// # Arguments
///
/// * `prefix` - A prefix for the environment variables.
/// * `default` - An optional default value.
///
/// # Errors
///
/// Returns an `EnvStructError` if retrieval fails.
fn get_env_entries(
prefix: impl AsRef<str>,
default: Option<&str>,
) -> Result<Vec<EnvEntry>, EnvStructError>;
}
impl<T: EnvParseNested> EnvParseNested for Option<T> {
fn parse_from_env_var(
var_name: impl AsRef<str>,
default: Option<&str>,
) -> Result<Self, EnvStructError>
where
Self: Sized,
{
let var_name = var_name.as_ref();
// Defining any environment variable of optional type makes the field required
// otherwise it is None.
if !T::get_env_entries(var_name, default)?
.iter()
.any(|entry| std::env::var_os(&entry.name).is_some())
{
return Ok(None);
}
Ok(Some(T::parse_from_env_var(var_name, default)?))
}
fn get_env_entries(
prefix: impl AsRef<str>,
default: Option<&str>,
) -> Result<Vec<EnvEntry>, EnvStructError> {
T::get_env_entries(prefix, default)
}
}
/// Concatenates two environment variable names with an underscore.
///
/// # Arguments
///
/// * `lhs` - The left-hand side of the environment variable name.
/// * `rhs` - The right-hand side of the environment variable name.
///
/// # Returns
///
/// A concatenated string of the two environment variable names.
pub fn concat_env_name(lhs: impl AsRef<str>, rhs: impl AsRef<str>) -> String {
let (lhs, rhs) = (lhs.as_ref().to_uppercase(), rhs.as_ref().to_uppercase());
#[cfg(feature = "env_uppercase")]
let (lhs, rhs) = (lhs.to_uppercase(), rhs.to_uppercase());
match (lhs.is_empty(), rhs.is_empty()) {
(false, true) => lhs.to_string(),
(true, false) => rhs.to_string(),
_ => format!("{lhs}_{rhs}"),
}
}
macro_rules! implement_nested_t {
($x:ty) => {
paste! {
impl<T: EnvParseNested> EnvParseNested for $x::<T> {
fn parse_from_env_var(var_name: impl AsRef<str>, default: Option<&str>) -> Result<Self, EnvStructError> {
Ok(T::parse_from_env_var(var_name, default)?.into())
}
fn get_env_entries(
prefix: impl AsRef<str>,
default: Option<&str>,
) -> Result<Vec<EnvEntry>, EnvStructError> {
T::get_env_entries(prefix, default)
}
}
}
};
}
implement_nested_t!(std::cell::Cell);
implement_nested_t!(std::cell::RefCell);
implement_nested_t!(std::rc::Rc);
implement_nested_t!(std::sync::Arc);