1use std::env;
2use std::error::Error;
3use std::fmt;
4
5pub enum EnvError {
6 VarError(env::VarError),
7 IoError(std::io::Error),
8 MissingSecureEnvSupport,
9 #[cfg(feature = "secure-env")]
10 DecryptionFailed(String),
11 #[cfg(feature = "secure-env")]
12 MissingKeyFile,
13 #[cfg(feature = "secure-env")]
14 KeyFileFormatError(String),
15}
16
17impl fmt::Debug for EnvError {
18 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
19 match self {
20 EnvError::VarError(e) => f.debug_tuple("VarError").field(e).finish(),
21 EnvError::IoError(e) => f.debug_tuple("IoError").field(e).finish(),
22 EnvError::MissingSecureEnvSupport => write!(f, "MissingSecureEnvSupport"),
23 #[cfg(feature = "secure-env")]
24 EnvError::DecryptionFailed(_) => write!(f, "DecryptionFailed([REDACTED])"),
25 #[cfg(feature = "secure-env")]
26 EnvError::MissingKeyFile => write!(f, "MissingKeyFile"),
27 #[cfg(feature = "secure-env")]
28 EnvError::KeyFileFormatError(_) => write!(f, "KeyFileFormatError([REDACTED])"),
29 }
30 }
31}
32
33impl From<env::VarError> for EnvError {
34 fn from(err: env::VarError) -> Self {
35 EnvError::VarError(err)
36 }
37}
38
39impl From<std::io::Error> for EnvError {
40 fn from(err: std::io::Error) -> Self {
41 EnvError::IoError(err)
42 }
43}
44
45impl fmt::Display for EnvError {
46 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47 match self {
48 EnvError::VarError(e) => write!(f, "Environment variable error: {}", e),
49 EnvError::IoError(e) => write!(f, "IO error: {}", e),
50 EnvError::MissingSecureEnvSupport => {
51 write!(
52 f,
53 "Secure environment support is disabled (enable the 'secure-env' feature)"
54 )
55 }
56 #[cfg(feature = "secure-env")]
57 EnvError::DecryptionFailed(_) => write!(f, "decryption failed"),
58 #[cfg(feature = "secure-env")]
59 EnvError::MissingKeyFile => write!(f, "Missing key file for decryption"),
60 #[cfg(feature = "secure-env")]
61 EnvError::KeyFileFormatError(msg) => write!(f, "Key file format error: {}", msg),
62 }
63 }
64}
65
66impl Error for EnvError {
67 fn source(&self) -> Option<&(dyn Error + 'static)> {
68 match self {
69 EnvError::VarError(e) => Some(e),
70 EnvError::IoError(e) => Some(e),
71 _ => None,
72 }
73 }
74}
75
76pub fn get_var(name: &str) -> Result<String, EnvError> {
86 let val = env::var(name)?;
87 #[cfg(not(feature = "secure-env"))]
88 if is_encrypted(&val) {
89 return Err(EnvError::MissingSecureEnvSupport);
90 }
91 Ok(val)
92}
93
94pub fn get_var_or(name: &str, default: &str) -> Result<String, EnvError> {
103 match env::var(name) {
104 Ok(val) => {
105 #[cfg(not(feature = "secure-env"))]
106 if is_encrypted(&val) {
107 return Err(EnvError::MissingSecureEnvSupport);
108 }
109 Ok(val)
110 }
111 Err(env::VarError::NotPresent) => Ok(default.to_string()),
112 Err(e) => Err(EnvError::VarError(e)),
113 }
114}
115
116pub fn is_encrypted(value: &str) -> bool {
118 value.starts_with("+encs+")
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124 use temp_env::with_var;
125
126 #[test]
127 fn test_get_env() {
128 with_var("TEST_VAR", Some("test_value"), || {
129 assert_eq!(get_var("TEST_VAR").unwrap(), "test_value");
130 assert_eq!(get_var_or("TEST_VAR", "default").unwrap(), "test_value");
131 });
132
133 with_var::<_, &str, _, _>("NON_EXISTENT_VAR", None, || {
134 assert!(get_var("NON_EXISTENT_VAR").is_err());
135 assert_eq!(
136 get_var_or("NON_EXISTENT_VAR", "default").unwrap(),
137 "default"
138 );
139 });
140 }
141
142 #[test]
143 fn test_is_encrypted() {
144 assert!(is_encrypted("+encs+1234567890ABCDEF"));
145 assert!(!is_encrypted("plain_text"));
146 assert!(!is_encrypted(""));
147 }
148
149 #[test]
150 fn test_is_encrypted_edge_cases() {
151 assert!(is_encrypted("+encs+"));
153
154 assert!(!is_encrypted("+encs"));
156 assert!(!is_encrypted("+enc+"));
157 assert!(!is_encrypted("+ENCS+1234"));
158 assert!(!is_encrypted("encs+1234"));
159 assert!(!is_encrypted(" +encs+1234"));
160 }
161}