1use std::env;
2use std::error::Error;
3use std::fmt;
4
5#[derive(Debug)]
6pub enum EnvError {
7 VarError(env::VarError),
8 IoError(std::io::Error),
9 MissingSecureEnvSupport,
10 #[cfg(feature = "secure-env")]
11 DecryptionFailed(String),
12 #[cfg(feature = "secure-env")]
13 MissingKeyFile,
14 #[cfg(feature = "secure-env")]
15 KeyFileFormatError(String),
16}
17
18impl From<env::VarError> for EnvError {
19 fn from(err: env::VarError) -> Self {
20 EnvError::VarError(err)
21 }
22}
23
24impl From<std::io::Error> for EnvError {
25 fn from(err: std::io::Error) -> Self {
26 EnvError::IoError(err)
27 }
28}
29
30impl fmt::Display for EnvError {
31 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32 match self {
33 EnvError::VarError(e) => write!(f, "Environment variable error: {}", e),
34 EnvError::IoError(e) => write!(f, "IO error: {}", e),
35 EnvError::MissingSecureEnvSupport => {
36 write!(
37 f,
38 "Secure environment support is disabled (enable the 'secure-env' feature)"
39 )
40 }
41 #[cfg(feature = "secure-env")]
42 EnvError::DecryptionFailed(msg) => write!(f, "Failed to decrypt: {}", msg),
43 #[cfg(feature = "secure-env")]
44 EnvError::MissingKeyFile => write!(f, "Missing key file for decryption"),
45 #[cfg(feature = "secure-env")]
46 EnvError::KeyFileFormatError(msg) => write!(f, "Key file format error: {}", msg),
47 }
48 }
49}
50
51impl Error for EnvError {
52 fn source(&self) -> Option<&(dyn Error + 'static)> {
53 match self {
54 EnvError::VarError(e) => Some(e),
55 EnvError::IoError(e) => Some(e),
56 _ => None,
57 }
58 }
59}
60
61pub fn get_var(name: &str) -> Result<String, EnvError> {
71 let val = env::var(name)?;
72 #[cfg(not(feature = "secure-env"))]
73 if is_encrypted(&val) {
74 return Err(EnvError::MissingSecureEnvSupport);
75 }
76 Ok(val)
77}
78
79pub fn get_var_or(name: &str, default: &str) -> Result<String, EnvError> {
88 match env::var(name) {
89 Ok(val) => {
90 #[cfg(not(feature = "secure-env"))]
91 if is_encrypted(&val) {
92 return Err(EnvError::MissingSecureEnvSupport);
93 }
94 Ok(val)
95 }
96 Err(env::VarError::NotPresent) => Ok(default.to_string()),
97 Err(e) => Err(EnvError::VarError(e)),
98 }
99}
100
101pub fn is_encrypted(value: &str) -> bool {
103 value.starts_with("+encs+")
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109 use temp_env::with_var;
110
111 #[test]
112 fn test_get_env() {
113 with_var("TEST_VAR", Some("test_value"), || {
114 assert_eq!(get_var("TEST_VAR").unwrap(), "test_value");
115 assert_eq!(get_var_or("TEST_VAR", "default").unwrap(), "test_value");
116 });
117
118 with_var::<_, &str, _, _>("NON_EXISTENT_VAR", None, || {
119 assert!(get_var("NON_EXISTENT_VAR").is_err());
120 assert_eq!(
121 get_var_or("NON_EXISTENT_VAR", "default").unwrap(),
122 "default"
123 );
124 });
125 }
126
127 #[test]
128 fn test_is_encrypted() {
129 assert!(is_encrypted("+encs+1234567890ABCDEF"));
130 assert!(!is_encrypted("plain_text"));
131 assert!(!is_encrypted(""));
132 }
133}