env_type/types.rs
1use std::str::FromStr;
2use thiserror::Error;
3
4/// EnvType is an enum that represents the environment type.
5/// EnvType is derived from the strum crate, which provides the ability to convert the string to the enum.
6///
7/// # Example
8///
9/// ```
10/// use env_type::types::EnvType;
11/// use std::str::FromStr;
12///
13/// let env = EnvType::from_str("d").unwrap();
14/// assert_eq!(EnvType::Dev, env);
15///
16/// let custom_env = EnvType::Custom("Custom");
17/// assert_eq!(EnvType::Custom("Custom"), custom_env);
18/// ```
19#[derive(Debug, Clone, Copy, PartialEq, Eq, strum::EnumString, strum::EnumIs, Default, Hash)]
20#[strum(serialize_all = "PascalCase")]
21pub enum EnvType {
22 #[default]
23 #[strum(
24 serialize = "develop",
25 serialize = "Develop",
26 serialize = "dev",
27 serialize = "Dev",
28 serialize = "DEV",
29 serialize = "d",
30 serialize = "D"
31 )]
32 Dev,
33 #[strum(
34 serialize = "test",
35 serialize = "Test",
36 serialize = "TEST",
37 serialize = "t",
38 serialize = "T"
39 )]
40 Test,
41 #[strum(
42 serialize = "staging",
43 serialize = "Staging",
44 serialize = "stg",
45 serialize = "Stg",
46 serialize = "STG",
47 serialize = "s",
48 serialize = "S"
49 )]
50 Stg,
51 #[strum(
52 serialize = "production",
53 serialize = "Production",
54 serialize = "prod",
55 serialize = "Prod",
56 serialize = "PROD",
57 serialize = "p",
58 serialize = "P"
59 )]
60 Prod,
61 Custom(&'static str),
62}
63
64/// EnvError is an enum that represents the environment error type.
65#[derive(Debug, Error)]
66pub enum EnvError {
67 #[error("No current environment specified")]
68 NoCurrentEnv,
69 #[error("Context not found for type")]
70 ContextNotFound,
71 #[error("Context value not found for env")]
72 ContextValueNotFound,
73 #[error("Invalid configuration: {0}")]
74 InvalidConfig(String),
75 #[error("Provider error: {0}")]
76 ProviderError(String),
77}
78
79/// EnvKey is a trait that represents the environment key.
80///
81/// # Example
82///
83/// ```
84/// use env_type::types::{EnvType, EnvKey};
85/// use std::str::FromStr;
86///
87/// struct NewEnvKey;
88///
89/// impl EnvKey for NewEnvKey {
90/// fn key() -> &'static str {
91/// "NEW_ENV"
92/// }
93/// }
94///
95/// std::env::set_var("NEW_ENV", "Production");
96/// let env = EnvType::from_env_key::<NewEnvKey>();
97/// assert_eq!(EnvType::Prod, env);
98/// ```
99pub trait EnvKey {
100 fn key() -> &'static str;
101}
102
103/// EnvType is an implementation of the EnvKey trait.
104/// The default environment key is "ENV".
105impl EnvKey for EnvType {
106 fn key() -> &'static str {
107 "ENV"
108 }
109}
110
111/// AsEnvTypeStr is a trait that covert some type to a string with Key Type, which is the environment type.
112/// This trait can extend the existing configuration struct to get the environment type.
113///
114/// # Example
115///
116/// ```
117/// use env_type::types::{EnvType, EnvKey, AsEnvStr};
118/// use std::collections::HashMap;
119///
120/// struct Config {
121/// map: HashMap<&'static str, String>,
122/// }
123///
124/// impl AsEnvStr for Config {
125/// fn as_env_str<T: EnvKey>(&self) -> String {
126/// self.map.get(T::key()).map(|v|v.to_string()).unwrap_or_default()
127/// }
128/// }
129/// let mut map = HashMap::new();
130/// map.insert("ENV", "test".to_string());
131/// let config = Config {
132/// map,
133/// };
134/// assert_eq!("test", config.as_env_str::<EnvType>());
135///
136/// ```
137pub trait AsEnvStr {
138 fn as_env_str<T: EnvKey>(&self) -> String;
139}
140
141/// EnvType is an implementation of the AsEnvStr trait.
142/// EnvType is based on env var.
143impl AsEnvStr for EnvType {
144 fn as_env_str<T: EnvKey>(&self) -> String {
145 std::env::var(T::key()).unwrap_or_default()
146 }
147}
148
149/// FromKey<V, S> is a trait like From<T> with a key.
150pub trait FromKey<V, S> {
151 fn from_key<K: EnvKey>(value: V) -> S;
152}
153
154/// AsEnvTypeStr is a trait that covert some type to a string, which is the environment type.
155/// This trait can extend the existing configuration struct to get the environment type.
156///
157/// # Example
158///
159/// ```
160/// use env_type::types::{EnvType, AsEnvTypeStr};
161///
162/// struct Config {
163/// env_str: String,
164/// }
165///
166/// impl AsEnvTypeStr for Config {
167/// fn as_env_type_str(&self) -> Option<String> {
168/// Some(self.env_str.clone())
169/// }
170/// }
171/// let config = Config {
172/// env_str: "dev".to_string(),
173/// };
174/// assert_eq!("dev", config.as_env_type_str().unwrap());
175///
176/// ```
177pub trait AsEnvTypeStr {
178 fn as_env_type_str(&self) -> Option<String>;
179}
180
181impl EnvType {
182 /// EnvType::from_env is a function that returns the environment type from the environment variable.
183 /// This is deligated to from_env_key with EnvType as default from env key.
184 /// The default environment type is Dev.
185 ///
186 /// # Example
187 ///
188 /// ```
189 /// use env_type::types::EnvType;
190 /// use std::str::FromStr;
191 ///
192 /// std::env::set_var("ENV", "Production");
193 /// let env = EnvType::from_env();
194 /// assert_eq!(EnvType::Prod, env);
195 /// ```
196 pub fn from_env() -> Self {
197 Self::from_env_types::<Self, Self>(Self::default())
198 }
199
200 /// EnvType::from_env_key is a function that returns the environment type from the environment variable.
201 /// The default environment type is Dev.
202 ///
203 /// # Example
204 ///
205 /// ```
206 /// use env_type::types::EnvType;
207 /// use std::str::FromStr;
208 ///
209 /// std::env::set_var("ENV", "Test");
210 /// let env = EnvType::from_env_key::<EnvType>();
211 /// assert_eq!(EnvType::Test, env);
212 /// ```
213 pub fn from_env_key<K: EnvKey>() -> Self {
214 Self::from_env_types::<Self, K>(Self::default())
215 }
216
217 /// EnvType::from_env_types is a function that returns the EnvType from AsEnvStr and EnvKey.
218 pub fn from_env_types<S: AsEnvStr, K: EnvKey>(s: S) -> Self {
219 Self::from_str(&s.as_env_str::<K>()).unwrap_or_default()
220 }
221
222 /// EnvType::from_env_str is a function that returns the environment type from the string.
223 /// The default environment type is Dev.
224 ///
225 /// # Example
226 ///
227 /// ```
228 /// use env_type::types::{EnvType, AsEnvTypeStr};
229 /// use std::str::FromStr;
230 ///
231 /// struct Config {
232 /// env_str: String,
233 /// }
234 /// let config = Config {
235 /// env_str: "Production".to_string(),
236 /// };
237 ///
238 /// impl AsEnvTypeStr for Config {
239 /// fn as_env_type_str(&self) -> Option<String> {
240 /// Some(self.env_str.clone())
241 /// }
242 /// }
243 /// let env = EnvType::from_env_str(config);
244 /// assert_eq!(EnvType::Prod, env);
245 pub fn from_env_str<T: AsEnvTypeStr>(t: T) -> Self {
246 Self::from_str(t.as_env_type_str().unwrap_or_default().as_str()).unwrap_or_default()
247 }
248}
249
250#[cfg(test)]
251mod tests {
252 use super::*;
253 use std::str::FromStr;
254
255 #[test]
256 fn test_env_type() {
257 assert_eq!(EnvType::from_str("Develop").unwrap(), EnvType::Dev);
258 assert_eq!(EnvType::from_str("develop").unwrap(), EnvType::Dev);
259 assert_eq!(EnvType::from_str("DEV").unwrap(), EnvType::Dev);
260 assert_eq!(EnvType::from_str("Dev").unwrap(), EnvType::Dev);
261 assert_eq!(EnvType::from_str("dev").unwrap(), EnvType::Dev);
262 assert_eq!(EnvType::from_str("D").unwrap(), EnvType::Dev);
263 assert_eq!(EnvType::from_str("d").unwrap(), EnvType::Dev);
264 assert_eq!(EnvType::from_str("TEST").unwrap(), EnvType::Test);
265 assert_eq!(EnvType::from_str("Test").unwrap(), EnvType::Test);
266 assert_eq!(EnvType::from_str("test").unwrap(), EnvType::Test);
267 assert_eq!(EnvType::from_str("T").unwrap(), EnvType::Test);
268 assert_eq!(EnvType::from_str("t").unwrap(), EnvType::Test);
269 assert_eq!(EnvType::from_str("Staging").unwrap(), EnvType::Stg);
270 assert_eq!(EnvType::from_str("staging").unwrap(), EnvType::Stg);
271 assert_eq!(EnvType::from_str("STG").unwrap(), EnvType::Stg);
272 assert_eq!(EnvType::from_str("Stg").unwrap(), EnvType::Stg);
273 assert_eq!(EnvType::from_str("stg").unwrap(), EnvType::Stg);
274 assert_eq!(EnvType::from_str("S").unwrap(), EnvType::Stg);
275 assert_eq!(EnvType::from_str("s").unwrap(), EnvType::Stg);
276 assert_eq!(EnvType::from_str("Production").unwrap(), EnvType::Prod);
277 assert_eq!(EnvType::from_str("production").unwrap(), EnvType::Prod);
278 assert_eq!(EnvType::from_str("PROD").unwrap(), EnvType::Prod);
279 assert_eq!(EnvType::from_str("Prod").unwrap(), EnvType::Prod);
280 assert_eq!(EnvType::from_str("prod").unwrap(), EnvType::Prod);
281 assert_eq!(EnvType::from_str("P").unwrap(), EnvType::Prod);
282 assert_eq!(EnvType::from_str("p").unwrap(), EnvType::Prod);
283 }
284
285 #[test]
286 fn test_is_debug() {
287 assert!(EnvType::Dev.is_dev());
288 assert!(!EnvType::Test.is_dev());
289 assert!(!EnvType::Stg.is_dev());
290 assert!(!EnvType::Prod.is_dev());
291
292 assert!(EnvType::Test.is_test());
293 assert!(EnvType::Stg.is_stg());
294 assert!(EnvType::Prod.is_prod());
295 }
296
297 #[test]
298 fn test_from_env() {
299 std::env::set_var("ENV", "d");
300 assert_eq!(EnvType::from_env(), EnvType::Dev);
301
302 struct TestEnv;
303 impl EnvKey for TestEnv {
304 fn key() -> &'static str {
305 "TEST_ENV"
306 }
307 }
308 std::env::set_var("TEST_ENV", "t");
309 assert_eq!(EnvType::from_env_key::<TestEnv>(), EnvType::Test);
310 // fallback to default
311 assert_eq!(EnvType::from_env_key::<EnvType>(), EnvType::Dev);
312 }
313
314 #[test]
315 fn test_from_env_str() {
316 struct TestEnv(&'static str);
317
318 impl AsEnvTypeStr for TestEnv {
319 fn as_env_type_str(&self) -> Option<String> {
320 Some(self.0.to_string())
321 }
322 }
323
324 assert_eq!(EnvType::from_env_str(TestEnv("d")), EnvType::Dev);
325 assert_eq!(EnvType::from_env_str(TestEnv("t")), EnvType::Test);
326 assert_eq!(EnvType::from_env_str(TestEnv("s")), EnvType::Stg);
327 assert_eq!(EnvType::from_env_str(TestEnv("p")), EnvType::Prod);
328 }
329}