env_settings_utils/
lib.rs

1#![deny(missing_docs)]
2#![doc(
3    html_logo_url = "https://raw.githubusercontent.com/dariocurr/env-settings/main/docs/logo.svg",
4    html_favicon_url = "https://raw.githubusercontent.com/dariocurr/env-settings/main/docs/logo.ico"
5)]
6
7//! # **Env Settinsg Utils**
8
9use std::{collections, env};
10
11/// The result type provided by `EnvSettings`
12pub type EnvSettingsResult<T> = Result<T, EnvSettingsError>;
13
14/// The error that may occurs during `EnvSettings` resolution
15#[derive(Debug, thiserror::Error)]
16pub enum EnvSettingsError {
17    /// Error raised when a convertion fails
18    #[error("Unable to convert the field `{0}`: `{1}` to `{2}`")]
19    Convert(&'static str, String, &'static str),
20
21    /// Error raised when environment variables resolution from a file fails
22    #[error("Error occurs while reading `{0}` as environment variable file: {1}")]
23    File(String, dotenvy::Error),
24
25    /// Error raised when an environment variable not exists
26    #[error("Environment variable named `{0}` not found")]
27    NotExists(&'static str),
28}
29
30impl PartialEq for EnvSettingsError {
31    fn eq(&self, other: &Self) -> bool {
32        match (self, other) {
33            (Self::Convert(l0, l1, l2), Self::Convert(r0, r1, r2)) => {
34                l0 == r0 && l1 == r1 && l2 == r2
35            }
36            (Self::File(l0, l1), Self::File(r0, r1)) => {
37                l0 == r0 && l1.to_string() == r1.to_string()
38            }
39            (Self::NotExists(l0), Self::NotExists(r0)) => l0 == r0,
40            _ => false,
41        }
42    }
43}
44
45/// Get the environment variables
46pub fn get_env_variables(case_insensitive: bool) -> collections::HashMap<String, String> {
47    let env_variables = env::vars();
48    if case_insensitive {
49        env_variables
50            .map(|(key, value)| (key.to_lowercase(), value))
51            .collect()
52    } else {
53        env_variables.collect()
54    }
55}
56
57/// Load the environment variables file path
58pub fn load_env_file_path(file_path: &str) -> EnvSettingsResult<()> {
59    if let Err(err) = dotenvy::from_path(file_path) {
60        Err(EnvSettingsError::File(file_path.to_string(), err))
61    } else {
62        Ok(())
63    }
64}
65
66#[cfg(test)]
67mod tests {
68
69    use super::*;
70
71    use rstest::rstest;
72    use std::io::prelude::Write;
73    use std::{fs, path};
74
75    #[rstest]
76    #[case("KEY", "value", true, "key", Some("value"))]
77    #[case("KEY", "value", true, "KEY", None)]
78    #[case("KEY", "value", false, "key", None)]
79    #[case("KEY", "value", false, "KEY", Some("value"))]
80    fn test_get_env_variables(
81        #[case] key: &str,
82        #[case] value: &str,
83        #[case] case_insensitive: bool,
84        #[case] recover_key: &str,
85        #[case] expected_result: Option<&str>,
86    ) {
87        unsafe {
88            env::set_var(key, value);
89        }
90        let env_variables = get_env_variables(case_insensitive);
91        let actual_result = env_variables.get(recover_key).map(|value| value.as_str());
92        assert_eq!(actual_result, expected_result);
93    }
94
95    #[rstest]
96    #[case("KEY", "value", Some("file_path"))]
97    #[case("KEY", "value", None)]
98    fn test_load_env_file_path(
99        #[case] key: &str,
100        #[case] value: &str,
101        #[case] file_path: Option<&str>,
102    ) {
103        let (file_path, is_successful) = if let Some(file_path) = file_path {
104            (file_path.to_string(), false)
105        } else {
106            let temp_dir: path::PathBuf = assert_fs::TempDir::new()
107                .expect("Error occurs while creating the test temp directory!")
108                .to_path_buf();
109            fs::create_dir_all(&temp_dir)
110                .expect("Error occurs while creating the test temp directory!");
111            let temp_file_path = temp_dir.join("test_file");
112            let pair = format!("{key}={value}\n");
113            let mut temp_file = fs::File::create(&temp_file_path)
114                .expect("Error occurs while creating the test temp file!");
115            temp_file
116                .write_all(pair.as_bytes())
117                .expect("Error occurs while writing the test temp file!");
118            (temp_file_path.to_string_lossy().to_string(), true)
119        };
120        let actual_result = load_env_file_path(&file_path);
121        if is_successful {
122            let actual_value = env::var(key).expect("Test environment variable not set!");
123            assert_eq!(actual_value, value);
124        } else {
125            assert!(actual_result.is_err())
126        }
127    }
128}