From 06d760e33d1f13d7cdf6cb656a78139d80249d94 Mon Sep 17 00:00:00 2001 From: Sai Asish Y Date: Wed, 13 May 2026 13:46:54 -0700 Subject: [PATCH 1/2] test: capture missing nested-segment case conversion in Environment --- tests/testsuite/env.rs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/testsuite/env.rs b/tests/testsuite/env.rs index 58d3d575..cb0b605e 100644 --- a/tests/testsuite/env.rs +++ b/tests/testsuite/env.rs @@ -574,6 +574,39 @@ fn test_parse_nested_kebab() { ); } +#[test] +#[cfg(feature = "convert-case")] +#[should_panic = "missing configuration field"] +fn test_parse_nested_upper_camel() { + use config::Case; + + #[derive(Deserialize, Debug)] + struct TestConfig { + #[serde(rename = "Otel")] + otel: Inner, + } + + #[derive(Deserialize, Debug)] + struct Inner { + #[serde(rename = "Endpoint")] + endpoint: String, + } + + temp_env::with_var("CONFIG_OTEL__ENDPOINT", Some("from env"), || { + let environment = Environment::default() + .prefix("CONFIG") + .prefix_separator("_") + .separator("__") + .convert_case(Case::UpperCamel); + + let config = Config::builder().add_source(environment).build().unwrap(); + + let config: TestConfig = config.try_deserialize().unwrap(); + + assert_eq!(config.otel.endpoint, "from env"); + }); +} + #[test] fn test_parse_string() { // using a struct in an enum here to make serde use `deserialize_any` From c2920ac686a408af8e03462c845263feb18ab09f Mon Sep 17 00:00:00 2001 From: Sai Asish Y Date: Wed, 13 May 2026 13:48:08 -0700 Subject: [PATCH 2/2] fix(env): apply convert_case to each nested key segment --- CHANGELOG.md | 4 ++++ src/env.rs | 6 +++++- tests/testsuite/env.rs | 1 - 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a8899f6..e0c8d2bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/). ## [Unreleased] - ReleaseDate +### Fixes + +- `Environment::convert_case` now applies the case conversion to each nested key segment + ## [0.15.22] - 2026-03-17 ### Documentation diff --git a/src/env.rs b/src/env.rs index c647da30..b78c7e53 100644 --- a/src/env.rs +++ b/src/env.rs @@ -296,7 +296,11 @@ impl Source for Environment { #[cfg(feature = "convert-case")] if let Some(convert_case) = convert_case { - key = key.to_case(*convert_case); + key = key + .split('.') + .map(|segment| segment.to_case(*convert_case)) + .collect::>() + .join("."); } let value = if self.try_parsing { diff --git a/tests/testsuite/env.rs b/tests/testsuite/env.rs index cb0b605e..e83a0947 100644 --- a/tests/testsuite/env.rs +++ b/tests/testsuite/env.rs @@ -576,7 +576,6 @@ fn test_parse_nested_kebab() { #[test] #[cfg(feature = "convert-case")] -#[should_panic = "missing configuration field"] fn test_parse_nested_upper_camel() { use config::Case;