env_settings_utils/
lib.rs1#![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
7use std::{collections, env};
10
11pub type EnvSettingsResult<T> = Result<T, EnvSettingsError>;
13
14#[derive(Debug, thiserror::Error)]
16pub enum EnvSettingsError {
17 #[error("Unable to convert the field `{0}`: `{1}` to `{2}`")]
19 Convert(&'static str, String, &'static str),
20
21 #[error("Error occurs while reading `{0}` as environment variable file: {1}")]
23 File(String, dotenvy::Error),
24
25 #[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
45pub 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
57pub 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}