Repository: mehcode/config-rs (rust)

Issue:
Environment variable names are converted to lower case
Environment variable names are converted to lower case internally. This is surprising undocumented behavior. We want to be able to override values from a configuration file with environment variables. It would be natural to use the same spelling (not counting the prefix), but this doesn't work if the configuration file keys are upper case. To make it work, the configuration file keys must be lower case.

config.toml
```bash
FOO="You can't override me!"
bar="You can override me just fine."
```
In the shell
```bash
APP_FOO="Why are you ignoring me?" APP_BAR="Yay! New bar." ./target/debug/app
```
I was using TOML for the config file and bash for the shell, if it matters.


Pull request description:
Fix uppercase lowercase isses
fix #340


Patch:
diff --git a/README.md b/README.md
index 627cc87..b68813e 100644
--- a/README.md
+++ b/README.md
@@ -23,8 +23,10 @@
 [RON]: https://github.com/ron-rs/ron
 [JSON5]: https://github.com/callum-oakley/json5-rs
 
-Please note that this library can not be used to write changed configuration
-values back to the configuration file(s)!
+Please note this library
+
+ - can not be used to write changed configuration values back to the configuration file(s)!
+ - Is case insensitive and all the keys are converted to lowercase internally
 
 ## Usage
 
@@ -63,4 +65,4 @@ We currently support Rust 1.56.1 and newer.
 
 config-rs is primarily distributed under the terms of both the MIT license and the Apache License (Version 2.0).
 
-See LICENSE-APACHE and LICENSE-MIT for details.
+See LICENSE-APACHE and LICENSE-MIT for details.
\ No newline at end of file
diff --git a/src/config.rs b/src/config.rs
index 27b318d..320b72e 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -110,7 +110,8 @@ impl Config {
     where
         T: Into<Value>,
     {
-        self.defaults.insert(key.parse()?, value.into());
+        self.defaults
+            .insert(key.to_lowercase().as_str().parse()?, value.into());
 
         #[allow(deprecated)]
         self.refresh()
@@ -129,7 +130,8 @@ impl Config {
     where
         T: Into<Value>,
     {
-        self.overrides.insert(key.parse()?, value.into());
+        self.overrides
+            .insert(key.to_lowercase().as_str().parse()?, value.into());
 
         #[allow(deprecated)]
         self.refresh()
@@ -137,7 +139,7 @@ impl Config {
 
     #[deprecated(since = "0.12.0", note = "please use 'ConfigBuilder' instead")]
     pub fn set_once(&mut self, key: &str, value: Value) -> Result<()> {
-        let expr: path::Expression = key.parse()?;
+        let expr: path::Expression = key.to_lowercase().as_str().parse()?;
 
         // Traverse the cache using the path to (possibly) retrieve a value
         if let Some(ref mut val) = expr.get_mut(&mut self.cache) {
@@ -149,6 +151,8 @@ impl Config {
     }
 
     pub fn get<'de, T: Deserialize<'de>>(&self, key: &str) -> Result<T> {
+        let k = key.to_lowercase();
+        let key = k.as_str();
         // Parse the key into a path expression
         let expr: path::Expression = key.parse()?;
 
diff --git a/src/de.rs b/src/de.rs
index d1271b2..96c5b5e 100644
--- a/src/de.rs
+++ b/src/de.rs
@@ -246,7 +246,7 @@ impl EnumAccess {
     fn variant_deserializer(&self, name: &str) -> Result<StrDeserializer> {
         self.variants
             .iter()
-            .find(|&&s| s == name)
+            .find(|&&s| s.to_lowercase() == name.to_lowercase()) // changing to lowercase will enable deserialization of lowercase values to enums
             .map(|&s| StrDeserializer(s))
             .ok_or_else(|| self.no_constructor_error(name))
     }
diff --git a/src/path/mod.rs b/src/path/mod.rs
index 7456aa3..b3773d1 100644
--- a/src/path/mod.rs
+++ b/src/path/mod.rs
@@ -120,7 +120,7 @@ impl Expression {
         match *self {
             Self::Identifier(ref id) => match root.kind {
                 ValueKind::Table(ref mut map) => Some(
-                    map.entry(id.clone())
+                    map.entry(id.to_lowercase())
                         .or_insert_with(|| Value::new(None, ValueKind::Nil)),
                 ),
 
@@ -131,7 +131,7 @@ impl Expression {
                 Some(value) => {
                     if let ValueKind::Table(ref mut map) = value.kind {
                         Some(
-                            map.entry(key.clone())
+                            map.entry(key.to_lowercase())
                                 .or_insert_with(|| Value::new(None, ValueKind::Nil)),
                         )
                     } else {
@@ -139,7 +139,7 @@ impl Expression {
 
                         if let ValueKind::Table(ref mut map) = value.kind {
                             Some(
-                                map.entry(key.clone())
+                                map.entry(key.to_lowercase())
                                     .or_insert_with(|| Value::new(None, ValueKind::Nil)),
                             )
                         } else {
@@ -194,7 +194,7 @@ impl Expression {
                     ValueKind::Table(ref incoming_map) => {
                         // Pull out another table
                         let target = if let ValueKind::Table(ref mut map) = root.kind {
-                            map.entry(id.clone())
+                            map.entry(id.to_lowercase())
                                 .or_insert_with(|| Map::<String, Value>::new().into())
                         } else {
                             unreachable!();
@@ -202,17 +202,17 @@ impl Expression {
 
                         // Continue the deep merge
                         for (key, val) in incoming_map {
-                            Self::Identifier(key.clone()).set(target, val.clone());
+                            Self::Identifier(key.to_lowercase()).set(target, val.clone());
                         }
                     }
 
                     _ => {
                         if let ValueKind::Table(ref mut map) = root.kind {
                             // Just do a simple set
-                            if let Some(existing) = map.get_mut(id) {
+                            if let Some(existing) = map.get_mut(&id.to_lowercase()) {
                                 *existing = value;
                             } else {
-                                map.insert(id.clone(), value);
+                                map.insert(id.to_lowercase(), value);
                             }
                         }
                     }
@@ -225,7 +225,7 @@ impl Expression {
                         // Didn't find a table. Oh well. Make a table and do this anyway
                         *parent = Map::<String, Value>::new().into();
                     }
-                    Self::Identifier(key.clone()).set(parent, value);
+                    Self::Identifier(key.to_lowercase()).set(parent, value);
                 }
             }
 


Test patch:
diff --git a/tests/Settings-enum-test.ini b/tests/Settings-enum-test.ini
new file mode 100644
index 0000000..778e772
--- /dev/null
+++ b/tests/Settings-enum-test.ini
@@ -0,0 +1,2 @@
+# Test for Enum deserialization. See file_tomls.rs override tests 
+bar = "bar is a lowercase param"
\ No newline at end of file
diff --git a/tests/Settings-enum-test.json b/tests/Settings-enum-test.json
new file mode 100644
index 0000000..5616949
--- /dev/null
+++ b/tests/Settings-enum-test.json
@@ -0,0 +1,3 @@
+{
+    "bar": "bar is a lowercase param"
+}
diff --git a/tests/Settings-enum-test.json5 b/tests/Settings-enum-test.json5
new file mode 100644
index 0000000..098be56
--- /dev/null
+++ b/tests/Settings-enum-test.json5
@@ -0,0 +1,3 @@
+{
+    bar: "bar is a lowercase param",
+}
diff --git a/tests/Settings-enum-test.ron b/tests/Settings-enum-test.ron
new file mode 100644
index 0000000..845149d
--- /dev/null
+++ b/tests/Settings-enum-test.ron
@@ -0,0 +1,3 @@
+(
+    bar: "bar is a lowercase param"
+)
\ No newline at end of file
diff --git a/tests/Settings-enum-test.toml b/tests/Settings-enum-test.toml
new file mode 100644
index 0000000..778e772
--- /dev/null
+++ b/tests/Settings-enum-test.toml
@@ -0,0 +1,2 @@
+# Test for Enum deserialization. See file_tomls.rs override tests 
+bar = "bar is a lowercase param"
\ No newline at end of file
diff --git a/tests/Settings-enum-test.yaml b/tests/Settings-enum-test.yaml
new file mode 100644
index 0000000..7126b98
--- /dev/null
+++ b/tests/Settings-enum-test.yaml
@@ -0,0 +1,2 @@
+# Test for Enum deserialization. See file_yaml.rs override tests
+bar: bar is a lowercase param
diff --git a/tests/Settings-upper-struct.toml b/tests/Settings-upper-struct.toml
new file mode 100644
index 0000000..341a5e0
--- /dev/null
+++ b/tests/Settings-upper-struct.toml
@@ -0,0 +1,3 @@
+# For override tests 
+FOO = "FOO should be overridden"
+bar = "I am bar"
\ No newline at end of file
diff --git a/tests/Settings.ini b/tests/Settings.ini
index 16badd4..d707568 100644
--- a/tests/Settings.ini
+++ b/tests/Settings.ini
@@ -1,5 +1,7 @@
 debug = true
 production = false
+FOO = FOO should be overridden
+bar = I am bar
 [place]
 name = Torre di Pisa
 longitude = 43.7224985
diff --git a/tests/Settings.json b/tests/Settings.json
index babb0ba..8f2f4e5 100644
--- a/tests/Settings.json
+++ b/tests/Settings.json
@@ -15,5 +15,7 @@
       "username": "jsmith",
       "email": "jsmith@localhost"
     }
-  }
+  },
+  "FOO": "FOO should be overridden",
+  "bar": "I am bar"
 }
diff --git a/tests/Settings.json5 b/tests/Settings.json5
index 4c93e42..cc2dab8 100644
--- a/tests/Settings.json5
+++ b/tests/Settings.json5
@@ -16,5 +16,7 @@
       "username": "jsmith",
       "email": "jsmith@localhost",
     }
-  }
+  },
+  FOO: "FOO should be overridden",
+  bar: "I am bar",
 }
diff --git a/tests/Settings.ron b/tests/Settings.ron
index 7882840..ed3009e 100644
--- a/tests/Settings.ron
+++ b/tests/Settings.ron
@@ -16,5 +16,7 @@
       "username": "jsmith",
       "email": "jsmith@localhost"
     }
-  )
+  ),
+  FOO: "FOO should be overridden",
+  bar: "I am bar"
 )
diff --git a/tests/Settings.toml b/tests/Settings.toml
index bb53808..65b3c4e 100644
--- a/tests/Settings.toml
+++ b/tests/Settings.toml
@@ -8,6 +8,10 @@ code = 53
 # errors
 boolean_s_parse = "fals"
 
+# For override tests 
+FOO="FOO should be overridden"
+bar="I am bar"
+
 arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
 quarks = ["up", "down", "strange", "charm", "bottom", "top"]
 
diff --git a/tests/Settings.yaml b/tests/Settings.yaml
index 8e79c29..c7b1549 100644
--- a/tests/Settings.yaml
+++ b/tests/Settings.yaml
@@ -12,3 +12,6 @@ place:
     name: John Smith
     username: jsmith
     email: jsmith@localhost
+# For override tests
+FOO: FOO should be overridden
+bar: I am bar
diff --git a/tests/file_ini.rs b/tests/file_ini.rs
index 4ebf6e3..e7f369a 100644
--- a/tests/file_ini.rs
+++ b/tests/file_ini.rs
@@ -66,3 +66,106 @@ fn test_error_parse() {
         )
     );
 }
+
+#[derive(Debug, Deserialize, PartialEq)]
+enum EnumSettings {
+    Bar(String),
+}
+
+#[derive(Debug, Deserialize, PartialEq)]
+struct StructSettings {
+    foo: String,
+    bar: String,
+}
+#[derive(Debug, Deserialize, PartialEq)]
+#[allow(non_snake_case)]
+struct CapSettings {
+    FOO: String,
+}
+
+#[test]
+fn test_override_uppercase_value_for_struct() {
+    std::env::set_var("APP_FOO", "I HAVE BEEN OVERRIDDEN_WITH_UPPER_CASE");
+
+    let cfg = Config::builder()
+        .add_source(File::new("tests/Settings", FileFormat::Ini))
+        .add_source(config::Environment::with_prefix("APP").separator("_"))
+        .build()
+        .unwrap();
+    let cap_settings = cfg.clone().try_deserialize::<CapSettings>();
+    let lower_settings = cfg.try_deserialize::<StructSettings>().unwrap();
+
+    match cap_settings {
+        Ok(v) => {
+            // this assertion will ensure that the map has only lowercase keys
+            assert_ne!(v.FOO, "FOO should be overridden");
+            assert_eq!(
+                lower_settings.foo,
+                "I HAVE BEEN OVERRIDDEN_WITH_UPPER_CASE".to_string()
+            );
+        }
+        Err(e) => {
+            if e.to_string().contains("missing field `FOO`") {
+                assert_eq!(
+                    lower_settings.foo,
+                    "I HAVE BEEN OVERRIDDEN_WITH_UPPER_CASE".to_string()
+                );
+            } else {
+                panic!("{}", e);
+            }
+        }
+    }
+}
+
+#[test]
+fn test_override_lowercase_value_for_struct() {
+    std::env::set_var("config_foo", "I have been overridden_with_lower_case");
+
+    let cfg = Config::builder()
+        .add_source(File::new("tests/Settings", FileFormat::Ini))
+        .add_source(config::Environment::with_prefix("config").separator("_"))
+        .build()
+        .unwrap();
+
+    let values: StructSettings = cfg.try_deserialize().unwrap();
+    assert_eq!(
+        values.foo,
+        "I have been overridden_with_lower_case".to_string()
+    );
+    assert_ne!(values.foo, "I am bar".to_string());
+}
+
+#[test]
+fn test_override_uppercase_value_for_enums() {
+    std::env::set_var("APPS_BAR", "I HAVE BEEN OVERRIDDEN_WITH_UPPER_CASE");
+
+    let cfg = Config::builder()
+        .add_source(File::new("tests/Settings-enum-test", FileFormat::Ini))
+        .add_source(config::Environment::with_prefix("APPS").separator("_"))
+        .build()
+        .unwrap();
+    let val: EnumSettings = cfg.try_deserialize().unwrap();
+
+    assert_eq!(
+        val,
+        EnumSettings::Bar("I HAVE BEEN OVERRIDDEN_WITH_UPPER_CASE".to_string())
+    );
+}
+
+#[test]
+fn test_override_lowercase_value_for_enums() {
+    std::env::set_var("test_bar", "I have been overridden_with_lower_case");
+
+    let cfg = Config::builder()
+        .add_source(File::new("tests/Settings-enum-test", FileFormat::Ini))
+        .add_source(config::Environment::with_prefix("test").separator("_"))
+        .build()
+        .unwrap();
+
+    let param: EnumSettings = cfg.try_deserialize().unwrap();
+
+    assert_eq!(
+        param,
+        EnumSettings::Bar("I have been overridden_with_lower_case".to_string())
+    );
+}
diff --git a/tests/file_json.rs b/tests/file_json.rs
index e660997..91cbc9b 100644
--- a/tests/file_json.rs
+++ b/tests/file_json.rs
@@ -111,3 +111,108 @@ fn test_json_vec() {
     assert_eq!(vi.next().unwrap().into_string().unwrap(), "example_dir2");
     assert!(vi.next().is_none());
 }
+
+#[derive(Debug, Deserialize, PartialEq)]
+enum EnumSettings {
+    Bar(String),
+}
+
+#[derive(Debug, Deserialize, PartialEq)]
+struct StructSettings {
+    foo: String,
+    bar: String,
+}
+#[derive(Debug, Deserialize, PartialEq)]
+#[allow(non_snake_case)]
+struct CapSettings {
+    FOO: String,
+}
+
+#[test]
+fn test_override_uppercase_value_for_struct() {
+    std::env::set_var("APP_FOO", "I HAVE BEEN OVERRIDDEN_WITH_UPPER_CASE");
+
+    let cfg = Config::builder()
+        .add_source(File::new("tests/Settings", FileFormat::Json))
+        .add_source(config::Environment::with_prefix("APP").separator("_"))
+        .build()
+        .unwrap();
+
+    let cap_settings = cfg.clone().try_deserialize::<CapSettings>();
+    let lower_settings = cfg.try_deserialize::<StructSettings>().unwrap();
+
+    match cap_settings {
+        Ok(v) => {
+            // this assertion will ensure that the map has only lowercase keys
+            assert_ne!(v.FOO, "FOO should be overridden");
+            assert_eq!(
+                lower_settings.foo,
+                "I HAVE BEEN OVERRIDDEN_WITH_UPPER_CASE".to_string()
+            );
+        }
+        Err(e) => {
+            if e.to_string().contains("missing field `FOO`") {
+                println!("triggered error {:?}", e);
+                assert_eq!(
+                    lower_settings.foo,
+                    "I HAVE BEEN OVERRIDDEN_WITH_UPPER_CASE".to_string()
+                );
+            } else {
+                panic!("{}", e);
+            }
+        }
+    }
+}
+
+#[test]
+fn test_override_lowercase_value_for_struct() {
+    std::env::set_var("config_foo", "I have been overridden_with_lower_case");
+
+    let cfg = Config::builder()
+        .add_source(File::new("tests/Settings", FileFormat::Json))
+        .add_source(config::Environment::with_prefix("config").separator("_"))
+        .build()
+        .unwrap();
+
+    let values: StructSettings = cfg.try_deserialize().unwrap();
+    assert_eq!(
+        values.foo,
+        "I have been overridden_with_lower_case".to_string()
+    );
+    assert_ne!(values.foo, "I am bar".to_string());
+}
+
+#[test]
+fn test_override_uppercase_value_for_enums() {
+    std::env::set_var("APPS_BAR", "I HAVE BEEN OVERRIDDEN_WITH_UPPER_CASE");
+
+    let cfg = Config::builder()
+        .add_source(File::new("tests/Settings-enum-test", FileFormat::Json))
+        .add_source(config::Environment::with_prefix("APPS").separator("_"))
+        .build()
+        .unwrap();
+    let val: EnumSettings = cfg.try_deserialize().unwrap();
+
+    assert_eq!(
+        val,
+        EnumSettings::Bar("I HAVE BEEN OVERRIDDEN_WITH_UPPER_CASE".to_string())
+    );
+}
+
+#[test]
+fn test_override_lowercase_value_for_enums() {
+    std::env::set_var("test_bar", "I have been overridden_with_lower_case");
+
+    let cfg = Config::builder()
+        .add_source(File::new("tests/Settings-enum-test", FileFormat::Json))
+        .add_source(config::Environment::with_prefix("test").separator("_"))
+        .build()
+        .unwrap();
+
+    let param: EnumSettings = cfg.try_deserialize().unwrap();
+
+    assert_eq!(
+        param,
+        EnumSettings::Bar("I have been overridden_with_lower_case".to_string())
+    );
+}
diff --git a/tests/file_json5.rs b/tests/file_json5.rs
index 4bc17d7..74d06c2 100644
--- a/tests/file_json5.rs
+++ b/tests/file_json5.rs
@@ -89,3 +89,107 @@ fn test_error_parse() {
         )
     );
 }
+
+#[derive(Debug, Deserialize, PartialEq)]
+enum EnumSettings {
+    Bar(String),
+}
+
+#[derive(Debug, Deserialize, PartialEq)]
+struct StructSettings {
+    foo: String,
+    bar: String,
+}
+#[derive(Debug, Deserialize, PartialEq)]
+#[allow(non_snake_case)]
+struct CapSettings {
+    FOO: String,
+}
+
+#[test]
+fn test_override_uppercase_value_for_struct() {
+    std::env::set_var("APP_FOO", "I HAVE BEEN OVERRIDDEN_WITH_UPPER_CASE");
+
+    let cfg = Config::builder()
+        .add_source(File::new("tests/Settings", FileFormat::Json5))
+        .add_source(config::Environment::with_prefix("APP").separator("_"))
+        .build()
+        .unwrap();
+
+    let cap_settings = cfg.clone().try_deserialize::<CapSettings>();
+    let lower_settings = cfg.try_deserialize::<StructSettings>().unwrap();
+
+    match cap_settings {
+        Ok(v) => {
+            // this assertion will ensure that the map has only lowercase keys
+            assert_ne!(v.FOO, "FOO should be overridden");
+            assert_eq!(
+                lower_settings.foo,
+                "I HAVE BEEN OVERRIDDEN_WITH_UPPER_CASE".to_string()
+            );
+        }
+        Err(e) => {
+            if e.to_string().contains("missing field `FOO`") {
+                assert_eq!(
+                    lower_settings.foo,
+                    "I HAVE BEEN OVERRIDDEN_WITH_UPPER_CASE".to_string()
+                );
+            } else {
+                panic!("{}", e);
+            }
+        }
+    }
+}
+
+#[test]
+fn test_override_lowercase_value_for_struct() {
+    std::env::set_var("config_foo", "I have been overridden_with_lower_case");
+
+    let cfg = Config::builder()
+        .add_source(File::new("tests/Settings", FileFormat::Json5))
+        .add_source(config::Environment::with_prefix("config").separator("_"))
+        .build()
+        .unwrap();
+
+    let values: StructSettings = cfg.try_deserialize().unwrap();
+    assert_eq!(
+        values.foo,
+        "I have been overridden_with_lower_case".to_string()
+    );
+    assert_ne!(values.foo, "I am bar".to_string());
+}
+
+#[test]
+fn test_override_uppercase_value_for_enums() {
+    std::env::set_var("APPS_BAR", "I HAVE BEEN OVERRIDDEN_WITH_UPPER_CASE");
+
+    let cfg = Config::builder()
+        .add_source(File::new("tests/Settings-enum-test", FileFormat::Json5))
+        .add_source(config::Environment::with_prefix("APPS").separator("_"))
+        .build()
+        .unwrap();
+    let val: EnumSettings = cfg.try_deserialize().unwrap();
+
+    assert_eq!(
+        val,
+        EnumSettings::Bar("I HAVE BEEN OVERRIDDEN_WITH_UPPER_CASE".to_string())
+    );
+}
+
+#[test]
+fn test_override_lowercase_value_for_enums() {
+    std::env::set_var("test_bar", "I have been overridden_with_lower_case");
+
+    let cfg = Config::builder()
+        .add_source(File::new("tests/Settings-enum-test", FileFormat::Json5))
+        .add_source(config::Environment::with_prefix("test").separator("_"))
+        .build()
+        .unwrap();
+
+    let param: EnumSettings = cfg.try_deserialize().unwrap();
+
+    assert_eq!(
+        param,
+        EnumSettings::Bar("I have been overridden_with_lower_case".to_string())
+    );
+}
diff --git a/tests/file_ron.rs b/tests/file_ron.rs
index 64e2cae..9f1ea35 100644
--- a/tests/file_ron.rs
+++ b/tests/file_ron.rs
@@ -89,3 +89,107 @@ fn test_error_parse() {
         format!("4:1: Expected colon in {}", path_with_extension.display())
     );
 }
+
+#[derive(Debug, Deserialize, PartialEq)]
+enum EnumSettings {
+    Bar(String),
+}
+
+#[derive(Debug, Deserialize, PartialEq)]
+struct StructSettings {
+    foo: String,
+    bar: String,
+}
+#[derive(Debug, Deserialize, PartialEq)]
+#[allow(non_snake_case)]
+struct CapSettings {
+    FOO: String,
+}
+
+#[test]
+fn test_override_uppercase_value_for_struct() {
+    std::env::set_var("APP_FOO", "I HAVE BEEN OVERRIDDEN_WITH_UPPER_CASE");
+
+    let cfg = Config::builder()
+        .add_source(File::new("tests/Settings", FileFormat::Ron))
+        .add_source(config::Environment::with_prefix("APP").separator("_"))
+        .build()
+        .unwrap();
+
+    let cap_settings = cfg.clone().try_deserialize::<CapSettings>();
+    let lower_settings = cfg.try_deserialize::<StructSettings>().unwrap();
+
+    match cap_settings {
+        Ok(v) => {
+            // this assertion will ensure that the map has only lowercase keys
+            assert_ne!(v.FOO, "FOO should be overridden");
+            assert_eq!(
+                lower_settings.foo,
+                "I HAVE BEEN OVERRIDDEN_WITH_UPPER_CASE".to_string()
+            );
+        }
+        Err(e) => {
+            if e.to_string().contains("missing field `FOO`") {
+                assert_eq!(
+                    lower_settings.foo,
+                    "I HAVE BEEN OVERRIDDEN_WITH_UPPER_CASE".to_string()
+                );
+            } else {
+                panic!("{}", e);
+            }
+        }
+    }
+}
+
+#[test]
+fn test_override_lowercase_value_for_struct() {
+    std::env::set_var("config_foo", "I have been overridden_with_lower_case");
+
+    let cfg = Config::builder()
+        .add_source(File::new("tests/Settings", FileFormat::Ron))
+        .add_source(config::Environment::with_prefix("config").separator("_"))
+        .build()
+        .unwrap();
+
+    let values: StructSettings = cfg.try_deserialize().unwrap();
+    assert_eq!(
+        values.foo,
+        "I have been overridden_with_lower_case".to_string()
+    );
+    assert_ne!(values.foo, "I am bar".to_string());
+}
+
+#[test]
+fn test_override_uppercase_value_for_enums() {
+    std::env::set_var("APPS_BAR", "I HAVE BEEN OVERRIDDEN_WITH_UPPER_CASE");
+
+    let cfg = Config::builder()
+        .add_source(File::new("tests/Settings-enum-test", FileFormat::Ron))
+        .add_source(config::Environment::with_prefix("APPS").separator("_"))
+        .build()
+        .unwrap();
+    let val: EnumSettings = cfg.try_deserialize().unwrap();
+
+    assert_eq!(
+        val,
+        EnumSettings::Bar("I HAVE BEEN OVERRIDDEN_WITH_UPPER_CASE".to_string())
+    );
+}
+
+#[test]
+fn test_override_lowercase_value_for_enums() {
+    std::env::set_var("test_bar", "I have been overridden_with_lower_case");
+
+    let cfg = Config::builder()
+        .add_source(File::new("tests/Settings-enum-test", FileFormat::Ron))
+        .add_source(config::Environment::with_prefix("test").separator("_"))
+        .build()
+        .unwrap();
+
+    let param: EnumSettings = cfg.try_deserialize().unwrap();
+
+    assert_eq!(
+        param,
+        EnumSettings::Bar("I have been overridden_with_lower_case".to_string())
+    );
+}
diff --git a/tests/file_toml.rs b/tests/file_toml.rs
index 9c5ee57..6ab1a2c 100644
--- a/tests/file_toml.rs
+++ b/tests/file_toml.rs
@@ -101,3 +101,107 @@ fn test_error_parse() {
         )
     );
 }
+
+#[derive(Debug, Deserialize, PartialEq)]
+enum EnumSettings {
+    Bar(String),
+}
+#[derive(Debug, Deserialize, PartialEq)]
+struct StructSettings {
+    foo: String,
+    bar: String,
+}
+#[derive(Debug, Deserialize, PartialEq)]
+#[allow(non_snake_case)]
+struct CapSettings {
+    FOO: String,
+}
+
+#[test]
+fn test_override_uppercase_value_for_struct() {
+    std::env::set_var("APP_FOO", "I HAVE BEEN OVERRIDDEN_WITH_UPPER_CASE");
+
+    let cfg = Config::builder()
+        .add_source(File::new("tests/Settings.toml", FileFormat::Toml))
+        .add_source(config::Environment::with_prefix("APP").separator("_"))
+        .build()
+        .unwrap();
+
+    let cap_settings = cfg.clone().try_deserialize::<CapSettings>();
+    let lower_settings = cfg.try_deserialize::<StructSettings>().unwrap();
+
+    match cap_settings {
+        Ok(v) => {
+            // this assertion will ensure that the map has only lowercase keys
+            assert_ne!(v.FOO, "FOO should be overridden");
+            assert_eq!(
+                lower_settings.foo,
+                "I HAVE BEEN OVERRIDDEN_WITH_UPPER_CASE".to_string()
+            );
+        }
+        Err(e) => {
+            if e.to_string().contains("missing field `FOO`") {
+                assert_eq!(
+                    lower_settings.foo,
+                    "I HAVE BEEN OVERRIDDEN_WITH_UPPER_CASE".to_string()
+                );
+            } else {
+                panic!("{}", e);
+            }
+        }
+    }
+}
+
+#[test]
+fn test_override_lowercase_value_for_struct() {
+    std::env::set_var("config_bar", "I have been overridden_with_lower_case");
+
+    let cfg = Config::builder()
+        .add_source(File::new("tests/Settings.toml", FileFormat::Toml))
+        .add_source(config::Environment::with_prefix("config").separator("_"))
+        .build()
+        .unwrap();
+
+    let values: StructSettings = cfg.try_deserialize().unwrap();
+    assert_eq!(
+        values.bar,
+        "I have been overridden_with_lower_case".to_string()
+    );
+    assert_ne!(values.bar, "I am bar".to_string());
+}
+
+#[test]
+fn test_override_uppercase_value_for_enums() {
+    std::env::set_var("APPS_BAR", "I HAVE BEEN OVERRIDDEN_WITH_UPPER_CASE");
+
+    let cfg = Config::builder()
+        .add_source(File::new("tests/Settings-enum-test.toml", FileFormat::Toml))
+        .add_source(config::Environment::with_prefix("APPS").separator("_"))
+        .build()
+        .unwrap();
+
+    let values: EnumSettings = cfg.try_deserialize().unwrap();
+
+    assert_eq!(
+        values,
+        EnumSettings::Bar("I HAVE BEEN OVERRIDDEN_WITH_UPPER_CASE".to_string())
+    );
+}
+
+#[test]
+fn test_override_lowercase_value_for_enums() {
+    std::env::set_var("test_bar", "I have been overridden_with_lower_case");
+
+    let cfg = Config::builder()
+        .add_source(File::new("tests/Settings-enum-test.toml", FileFormat::Toml))
+        .add_source(config::Environment::with_prefix("test").separator("_"))
+        .build()
+        .unwrap();
+
+    let values: EnumSettings = cfg.try_deserialize().unwrap();
+
+    assert_eq!(
+        values,
+        EnumSettings::Bar("I have been overridden_with_lower_case".to_string())
+    );
+}
diff --git a/tests/file_yaml.rs b/tests/file_yaml.rs
index 0ece482..b961c2a 100644
--- a/tests/file_yaml.rs
+++ b/tests/file_yaml.rs
@@ -120,3 +120,108 @@ fn test_yaml_parsing_key() {
         "Test String"
     );
 }
+
+#[derive(Debug, Deserialize, PartialEq)]
+enum EnumSettings {
+    Bar(String),
+}
+
+#[derive(Debug, Deserialize, PartialEq)]
+struct StructSettings {
+    foo: String,
+    bar: String,
+}
+#[derive(Debug, Deserialize, PartialEq)]
+#[allow(non_snake_case)]
+struct CapSettings {
+    FOO: String,
+}
+
+#[test]
+fn test_override_uppercase_value_for_struct() {
+    std::env::set_var("APP_FOO", "I HAVE BEEN OVERRIDDEN_WITH_UPPER_CASE");
+
+    let cfg = Config::builder()
+        .add_source(File::new("tests/Settings.yaml", FileFormat::Yaml))
+        .add_source(config::Environment::with_prefix("APP").separator("_"))
+        .build()
+        .unwrap();
+
+    let cap_settings = cfg.clone().try_deserialize::<CapSettings>();
+    let lower_settings = cfg.try_deserialize::<StructSettings>().unwrap();
+
+    match cap_settings {
+        Ok(v) => {
+            // this assertion will ensure that the map has only lowercase keys
+            assert_ne!(v.FOO, "FOO should be overridden");
+            assert_eq!(
+                lower_settings.foo,
+                "I HAVE BEEN OVERRIDDEN_WITH_UPPER_CASE".to_string()
+            );
+        }
+        Err(e) => {
+            if e.to_string().contains("missing field `FOO`") {
+                println!("triggered error {:?}", e);
+                assert_eq!(
+                    lower_settings.foo,
+                    "I HAVE BEEN OVERRIDDEN_WITH_UPPER_CASE".to_string()
+                );
+            } else {
+                panic!("{}", e);
+            }
+        }
+    }
+}
+
+#[test]
+fn test_override_lowercase_value_for_struct() {
+    std::env::set_var("config_bar", "I have been overridden_with_lower_case");
+
+    let cfg = Config::builder()
+        .add_source(File::new("tests/Settings.yaml", FileFormat::Yaml))
+        .add_source(config::Environment::with_prefix("config").separator("_"))
+        .build()
+        .unwrap();
+
+    let values: StructSettings = cfg.try_deserialize().unwrap();
+    assert_eq!(
+        values.bar,
+        "I have been overridden_with_lower_case".to_string()
+    );
+    assert_ne!(values.bar, "I am bar".to_string());
+}
+
+#[test]
+fn test_override_uppercase_value_for_enums() {
+    std::env::set_var("APPS_BAR", "I HAVE BEEN OVERRIDDEN_WITH_UPPER_CASE");
+
+    let cfg = Config::builder()
+        .add_source(File::new("tests/Settings-enum-test.yaml", FileFormat::Yaml))
+        .add_source(config::Environment::with_prefix("APPS").separator("_"))
+        .build()
+        .unwrap();
+    let values: EnumSettings = cfg.try_deserialize().unwrap();
+
+    assert_eq!(
+        values,
+        EnumSettings::Bar("I HAVE BEEN OVERRIDDEN_WITH_UPPER_CASE".to_string())
+    );
+}
+
+#[test]
+fn test_override_lowercase_value_for_enums() {
+    std::env::set_var("test_bar", "I have been overridden_with_lower_case");
+
+    let cfg = Config::builder()
+        .add_source(File::new("tests/Settings-enum-test.yaml", FileFormat::Yaml))
+        .add_source(config::Environment::with_prefix("test").separator("_"))
+        .build()
+        .unwrap();
+
+    let values: EnumSettings = cfg.try_deserialize().unwrap();
+
+    assert_eq!(
+        values,
+        EnumSettings::Bar("I have been overridden_with_lower_case".to_string())
+    );
+}