diff --git a/src/cargo/util/context/de.rs b/src/cargo/util/context/de.rs index e84322422cb..cbb5cf0424f 100644 --- a/src/cargo/util/context/de.rs +++ b/src/cargo/util/context/de.rs @@ -265,7 +265,15 @@ impl<'gctx> ConfigMapAccess<'gctx> { let mut field_key = de.key.clone(); field_key.push(field); for env_key in de.gctx.env_keys() { - if env_key.starts_with(field_key.as_env_key()) { + let Some(nested_field) = env_key.strip_prefix(field_key.as_env_key()) else { + continue; + }; + // This distinguishes fields that share the same prefix. + // For example, when env_key is UNSTABLE_GITOXIDE_FETCH + // and field_key is UNSTABLE_GIT, the field shouldn't be + // added because `unstable.gitoxide.fetch` doesn't + // belong to `unstable.git` struct. + if nested_field.is_empty() || nested_field.starts_with('_') { fields.insert(KeyKind::Normal(field.to_string())); } } diff --git a/tests/testsuite/config.rs b/tests/testsuite/config.rs index 6f0b262dbc5..e962a42efc6 100644 --- a/tests/testsuite/config.rs +++ b/tests/testsuite/config.rs @@ -1454,7 +1454,6 @@ fn struct_with_overlapping_inner_struct_and_defaults() { // If, in the future, we can handle this more correctly, feel free to delete // this case. #[derive(Deserialize, Default)] - #[serde(default)] struct PrefixContainer { inn: bool, inner: Inner, @@ -1466,7 +1465,7 @@ fn struct_with_overlapping_inner_struct_and_defaults() { .get::("prefixcontainer") .err() .unwrap(); - assert!(format!("{}", err).contains("missing config key `prefixcontainer.inn`")); + assert!(format!("{}", err).contains("missing field `inn`")); let gctx = GlobalContextBuilder::new() .env("CARGO_PREFIXCONTAINER_INNER_VALUE", "12") .env("CARGO_PREFIXCONTAINER_INN", "true") @@ -1475,6 +1474,22 @@ fn struct_with_overlapping_inner_struct_and_defaults() { assert_eq!(f.inner.value, 12); assert_eq!(f.inn, true); + // Use default attribute of serde, then we can skip setting the inn field + #[derive(Deserialize, Default)] + #[serde(default)] + struct PrefixContainerFieldDefault { + inn: bool, + inner: Inner, + } + let gctx = GlobalContextBuilder::new() + .env("CARGO_PREFIXCONTAINER_INNER_VALUE", "12") + .build(); + let f = gctx + .get::("prefixcontainer") + .unwrap(); + assert_eq!(f.inner.value, 12); + assert_eq!(f.inn, false); + // Containing struct where the inner value's field is a prefix of another // // This is a limitation of mapping environment variables on to a hierarchy.