1pub fn get_env_var(key: &str) -> anyhow::Result<String> {
27 match std::env::var(key) {
28 Ok(var) => Ok(var),
29 Err(_) => anyhow::bail!("environment variable '{key}' must be set"),
30 }
31}
32
33pub fn get_or_env_var(value: Option<String>, key: &str) -> anyhow::Result<String> {
43 match value {
44 Some(v) => Ok(v),
45 None => get_env_var(key),
46 }
47}
48
49#[must_use]
56pub fn get_or_env_var_opt(value: Option<String>, key: &str) -> Option<String> {
57 value.or_else(|| std::env::var(key).ok())
58}
59
60#[must_use]
65pub fn resolve_env_var_pair(
66 key: Option<String>,
67 secret: Option<String>,
68 key_var: &str,
69 secret_var: &str,
70) -> Option<(String, String)> {
71 let key = get_or_env_var_opt(key, key_var)?;
72 let secret = get_or_env_var_opt(secret, secret_var)?;
73 Some((key, secret))
74}
75
76#[cfg(test)]
77mod tests {
78 use rstest::*;
79
80 use super::*;
81
82 #[rstest]
83 fn test_get_env_var_success() {
84 if let Ok(path) = std::env::var("PATH") {
86 let result = get_env_var("PATH");
87 assert!(result.is_ok());
88 assert_eq!(result.unwrap(), path);
89 }
90 }
91
92 #[rstest]
93 fn test_get_env_var_not_set() {
94 let result = get_env_var("NONEXISTENT_ENV_VAR_THAT_SHOULD_NOT_EXIST_12345");
96 assert!(result.is_err());
97 assert!(result.unwrap_err().to_string().contains(
98 "environment variable 'NONEXISTENT_ENV_VAR_THAT_SHOULD_NOT_EXIST_12345' must be set"
99 ));
100 }
101
102 #[rstest]
103 fn test_get_env_var_error_message_format() {
104 let var_name = "DEFINITELY_NONEXISTENT_VAR_123456789";
105 let result = get_env_var(var_name);
106 assert!(result.is_err());
107 let error_msg = result.unwrap_err().to_string();
108 assert!(error_msg.contains(var_name));
109 assert!(error_msg.contains("must be set"));
110 }
111
112 #[rstest]
113 fn test_get_or_env_var_with_some_value() {
114 let provided_value = Some("provided_value".to_string());
115 let result = get_or_env_var(provided_value, "PATH");
116 assert!(result.is_ok());
117 assert_eq!(result.unwrap(), "provided_value");
118 }
119
120 #[rstest]
121 fn test_get_or_env_var_with_none_and_env_var_set() {
122 if let Ok(path) = std::env::var("PATH") {
124 let result = get_or_env_var(None, "PATH");
125 assert!(result.is_ok());
126 assert_eq!(result.unwrap(), path);
127 }
128 }
129
130 #[rstest]
131 fn test_get_or_env_var_with_none_and_env_var_not_set() {
132 let result = get_or_env_var(None, "NONEXISTENT_ENV_VAR_THAT_SHOULD_NOT_EXIST_67890");
133 assert!(result.is_err());
134 assert!(result.unwrap_err().to_string().contains(
135 "environment variable 'NONEXISTENT_ENV_VAR_THAT_SHOULD_NOT_EXIST_67890' must be set"
136 ));
137 }
138
139 #[rstest]
140 fn test_get_or_env_var_empty_string_value() {
141 let provided_value = Some(String::new());
143 let result = get_or_env_var(provided_value, "PATH");
144 assert!(result.is_ok());
145 assert_eq!(result.unwrap(), "");
146 }
147
148 #[rstest]
149 fn test_get_or_env_var_priority() {
150 if std::env::var("PATH").is_ok() {
153 let provided = Some("custom_value_takes_priority".to_string());
154 let result = get_or_env_var(provided, "PATH");
155 assert!(result.is_ok());
156 assert_eq!(result.unwrap(), "custom_value_takes_priority");
157 }
158 }
159
160 #[rstest]
161 fn test_get_or_env_var_opt_with_some_value() {
162 let provided_value = Some("provided_value".to_string());
163 let result = get_or_env_var_opt(provided_value, "PATH");
164 assert_eq!(result, Some("provided_value".to_string()));
165 }
166
167 #[rstest]
168 fn test_get_or_env_var_opt_with_none_and_env_var_set() {
169 if let Ok(path) = std::env::var("PATH") {
170 let result = get_or_env_var_opt(None, "PATH");
171 assert_eq!(result, Some(path));
172 }
173 }
174
175 #[rstest]
176 fn test_get_or_env_var_opt_with_none_and_env_var_not_set() {
177 let result = get_or_env_var_opt(None, "NONEXISTENT_ENV_VAR_OPT_12345");
178 assert_eq!(result, None);
179 }
180
181 #[rstest]
182 fn test_get_or_env_var_opt_priority() {
183 if std::env::var("PATH").is_ok() {
185 let provided = Some("custom_value".to_string());
186 let result = get_or_env_var_opt(provided, "PATH");
187 assert_eq!(result, Some("custom_value".to_string()));
188 }
189 }
190
191 #[rstest]
192 fn test_resolve_env_var_pair_both_provided() {
193 let result = resolve_env_var_pair(
194 Some("my_key".to_string()),
195 Some("my_secret".to_string()),
196 "NONEXISTENT_KEY_VAR",
197 "NONEXISTENT_SECRET_VAR",
198 );
199 assert_eq!(
200 result,
201 Some(("my_key".to_string(), "my_secret".to_string()))
202 );
203 }
204
205 #[rstest]
206 fn test_resolve_env_var_pair_key_missing_returns_none() {
207 let result = resolve_env_var_pair(
208 None,
209 Some("my_secret".to_string()),
210 "NONEXISTENT_PAIR_KEY_12345",
211 "NONEXISTENT_PAIR_SECRET_12345",
212 );
213 assert_eq!(result, None);
214 }
215
216 #[rstest]
217 fn test_resolve_env_var_pair_secret_missing_returns_none() {
218 let result = resolve_env_var_pair(
219 Some("my_key".to_string()),
220 None,
221 "NONEXISTENT_PAIR_KEY_12345",
222 "NONEXISTENT_PAIR_SECRET_12345",
223 );
224 assert_eq!(result, None);
225 }
226
227 #[rstest]
228 fn test_resolve_env_var_pair_both_missing_returns_none() {
229 let result = resolve_env_var_pair(
230 None,
231 None,
232 "NONEXISTENT_PAIR_KEY_12345",
233 "NONEXISTENT_PAIR_SECRET_12345",
234 );
235 assert_eq!(result, None);
236 }
237}