Skip to content

Commit d347704

Browse files
committed
Fix: Skip deserialization of unrelated fields with overlapping name
1 parent 9ba3894 commit d347704

File tree

2 files changed

+26
-3
lines changed

2 files changed

+26
-3
lines changed

src/cargo/util/context/de.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,15 @@ impl<'gctx> ConfigMapAccess<'gctx> {
265265
let mut field_key = de.key.clone();
266266
field_key.push(field);
267267
for env_key in de.gctx.env_keys() {
268-
if env_key.starts_with(field_key.as_env_key()) {
268+
let Some(nested_field) = env_key.strip_prefix(field_key.as_env_key()) else {
269+
continue;
270+
};
271+
// This distinguishes fields that share the same prefix.
272+
// For example, when env_key is UNSTABLE_GITOXIDE_FETCH
273+
// and field_key is UNSTABLE_GIT, the field shouldn't be
274+
// added because `unstable.gitoxide.fetch` doesn't
275+
// belong to `unstable.git` struct.
276+
if nested_field.is_empty() || nested_field.starts_with('_') {
269277
fields.insert(KeyKind::Normal(field.to_string()));
270278
}
271279
}

tests/testsuite/config.rs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1454,7 +1454,6 @@ fn struct_with_overlapping_inner_struct_and_defaults() {
14541454
// If, in the future, we can handle this more correctly, feel free to delete
14551455
// this case.
14561456
#[derive(Deserialize, Default)]
1457-
#[serde(default)]
14581457
struct PrefixContainer {
14591458
inn: bool,
14601459
inner: Inner,
@@ -1466,7 +1465,7 @@ fn struct_with_overlapping_inner_struct_and_defaults() {
14661465
.get::<PrefixContainer>("prefixcontainer")
14671466
.err()
14681467
.unwrap();
1469-
assert!(format!("{}", err).contains("missing config key `prefixcontainer.inn`"));
1468+
assert!(format!("{}", err).contains("missing field `inn`"));
14701469
let gctx = GlobalContextBuilder::new()
14711470
.env("CARGO_PREFIXCONTAINER_INNER_VALUE", "12")
14721471
.env("CARGO_PREFIXCONTAINER_INN", "true")
@@ -1475,6 +1474,22 @@ fn struct_with_overlapping_inner_struct_and_defaults() {
14751474
assert_eq!(f.inner.value, 12);
14761475
assert_eq!(f.inn, true);
14771476

1477+
// Use default attribute of serde, then we can skip setting the inn field
1478+
#[derive(Deserialize, Default)]
1479+
#[serde(default)]
1480+
struct PrefixContainerFieldDefault {
1481+
inn: bool,
1482+
inner: Inner,
1483+
}
1484+
let gctx = GlobalContextBuilder::new()
1485+
.env("CARGO_PREFIXCONTAINER_INNER_VALUE", "12")
1486+
.build();
1487+
let f = gctx
1488+
.get::<PrefixContainerFieldDefault>("prefixcontainer")
1489+
.unwrap();
1490+
assert_eq!(f.inner.value, 12);
1491+
assert_eq!(f.inn, false);
1492+
14781493
// Containing struct where the inner value's field is a prefix of another
14791494
//
14801495
// This is a limitation of mapping environment variables on to a hierarchy.

0 commit comments

Comments
 (0)