2024-04-17
Recently I wanted to use std::env::var to read environment variables from my application. I was suprised to see that when the function returns a VarError::NotPresent error, the name of the missing environment variable is not included in the error message. I wanted to display a much more user friendly error message, so I looked at the documentation:
use std::env;
let key = "HOME";
match env::var(key) {
Ok(val) => println!("{key}: {val:?}"),
Err(e) => println!("couldn't interpret {key}: {e}"),
}
Easy enough, but there is one problem though. What if I want to return a custom error, and later in the chain propagate it upwards with the ? operator? How to pass the key and preserve it while converting the error to something else? It took me a bit of digging to come up with a solution I am satisfied with:
1 use std::env;
2 use std::env::VarError;
3
4 #[derive(Debug, Eq, Hash, PartialEq)]
5 enum EnvVar {
6 AwsAccessKeyId,
7 }
8
9 // Converting EnvVar enum keys to &str type (line #36)
10 impl From for &str {
11 fn from(key: EnvVar) -> Self {
12 match key {
13 EnvVar::AwsAccessKeyId => "AWS_ACCESS_KEY_ID",
14 }
15 }
16 }
17
18 #[derive(Debug)]
19 pub enum ClientError<'a> {
20 CredentialNotSet(&'a str, VarError)
21 }
22
23 // Human readable display of ClientError.
24 // CredentialNotSet receives both the key, both the original env::VarError.
25 impl fmt::Display for ClientError<'_> {
26 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
27 match self {
28 ClientError::CredentialNotSet(key, err) => {
29 write!(f, "{} {}", key, err)
30 }
31 }
32 }
33 }
34
35 fn env_var(key: EnvVar) -> Result> {
36 let key_str: &str = key.into();
37
38 // Mapping env::VarError to our own ClientError::CredentialNotSet
39 // by passing the missing env var key and the original error.
40 env::var(key_str).map_err(|e| ClientError::CredentialNotSet(key_str, e))
41 }
42
43 fn get_aws_credentials(config: &Configuration) -> Result> {
44 ...
45
46 Ok(CredentialsStore(
47 ...
48 // ClientError can be easily propagated with the ? operator
49 env_var(EnvVar::AwsAccessKeyId)?,
50 ))
51 }
Finally, if the expected environment variables are unset, the error message includes the missing variable name: AWS_ACCESS_KEY_ID environment variable not found.