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}