diff --git a/src/cargo/util/context/mod.rs b/src/cargo/util/context/mod.rs index 6d7e93b9a59..f2329b38cd4 100644 --- a/src/cargo/util/context/mod.rs +++ b/src/cargo/util/context/mod.rs @@ -1286,9 +1286,9 @@ impl GlobalContext { /// Start a config file discovery from a path and merges all config values found. fn load_values_from(&self, path: &Path) -> CargoResult> { - // This definition path is ignored, this is just a temporary container - // representing the entire file. - let mut cfg = CV::Table(HashMap::new(), Definition::Path(PathBuf::from("."))); + // The root config value container isn't from any external source, + // so its definition should be built-in. + let mut cfg = CV::Table(HashMap::new(), Definition::BuiltIn); let home = self.home_path.clone().into_path_unlocked(); self.walk_tree(path, &home, |path| { @@ -1546,24 +1546,18 @@ impl GlobalContext { /// Add config arguments passed on the command line. fn merge_cli_args(&mut self) -> CargoResult<()> { - let CV::Table(loaded_map, _def) = self.cli_args_as_table()? else { - unreachable!() - }; - let values = self.values_mut()?; - for (key, value) in loaded_map.into_iter() { - match values.entry(key) { - Vacant(entry) => { - entry.insert(value); - } - Occupied(mut entry) => entry.get_mut().merge(value, true).with_context(|| { - format!( - "failed to merge --config key `{}` into `{}`", - entry.key(), - entry.get().definition(), - ) - })?, - }; - } + let cv_from_cli = self.cli_args_as_table()?; + assert!(cv_from_cli.is_table(), "cv from CLI must be a table"); + + let root_cv = mem::take(self.values_mut()?); + // The root config value container isn't from any external source, + // so its definition should be built-in. + let mut root_cv = CV::Table(root_cv, Definition::BuiltIn); + root_cv.merge(cv_from_cli, true)?; + + // Put it back to gctx + mem::swap(self.values_mut()?, root_cv.table_mut("")?.0); + Ok(()) } @@ -1710,9 +1704,7 @@ impl GlobalContext { let mut value = self.load_file(&credentials)?; // Backwards compatibility for old `.cargo/credentials` layout. { - let CV::Table(ref mut value_map, ref def) = value else { - unreachable!(); - }; + let (value_map, def) = value.table_mut("")?; if let Some(token) = value_map.remove("token") { if let Vacant(entry) = value_map.entry("registry".into()) { @@ -2299,6 +2291,20 @@ impl ConfigValue { } } + pub fn table_mut( + &mut self, + key: &str, + ) -> CargoResult<(&mut HashMap, &mut Definition)> { + match self { + CV::Table(table, def) => Ok((table, def)), + _ => self.expected("table", key), + } + } + + pub fn is_table(&self) -> bool { + matches!(self, CV::Table(_table, _def)) + } + pub fn string_list(&self, key: &str) -> CargoResult> { match self { CV::List(list, _) => list diff --git a/tests/testsuite/config.rs b/tests/testsuite/config.rs index eb0b50a3cb0..fa3f1293383 100644 --- a/tests/testsuite/config.rs +++ b/tests/testsuite/config.rs @@ -652,7 +652,7 @@ Caused by: assert_error( gctx.unwrap_err(), str![[r#" -failed to merge --config key `a` into `[ROOT]/.cargo/config.toml` +failed to merge key `a` between [ROOT]/.cargo/config.toml and --config cli option Caused by: failed to merge config value from `--config cli option` into `[ROOT]/.cargo/config.toml`: expected boolean, but found array @@ -2209,6 +2209,21 @@ credential-provider = ['c', 'd'] .unwrap(); assert_eq!(provider.path.raw_value(), "c"); assert_eq!(provider.args, ["d"]); + + let cli_arg = "registries.example.credential-provider=['cli', 'cli-arg']"; + let gctx = GlobalContextBuilder::new() + .config_arg(cli_arg) + .cwd("foo") + .build(); + let provider = gctx + .get::>(&format!("registries.example")) + .unwrap() + .unwrap() + .credential_provider + .unwrap(); + // expect: no merge happens; config CLI takes precedence + assert_eq!(provider.path.raw_value(), "cli"); + assert_eq!(provider.args, ["cli-arg"]); } #[cargo_test] diff --git a/tests/testsuite/config_include.rs b/tests/testsuite/config_include.rs index 444f7e86ba4..06e71f2901d 100644 --- a/tests/testsuite/config_include.rs +++ b/tests/testsuite/config_include.rs @@ -459,7 +459,7 @@ fn cli_merge_failed() { assert_error( gctx.unwrap_err(), str![[r#" -failed to merge --config key `foo` into `[ROOT]/.cargo/config.toml` +failed to merge key `foo` between [ROOT]/.cargo/config.toml and [ROOT]/.cargo/other.toml Caused by: failed to merge config value from `[ROOT]/.cargo/other.toml` into `[ROOT]/.cargo/config.toml`: expected array, but found string