diff --git a/src/cargo/util/auth/mod.rs b/src/cargo/util/auth/mod.rs index 1e221a2c7b7..8a85fceb3ab 100644 --- a/src/cargo/util/auth/mod.rs +++ b/src/cargo/util/auth/mod.rs @@ -12,7 +12,6 @@ use cargo_credential::{ use core::fmt; use serde::Deserialize; -use std::collections::HashMap; use std::error::Error; use time::{Duration, OffsetDateTime}; use url::Url; @@ -31,7 +30,7 @@ use super::{ /// `[registries.NAME]` tables. /// /// The values here should be kept in sync with `RegistryConfigExtended` -#[derive(Deserialize)] +#[derive(Deserialize, Clone, Debug)] #[serde(rename_all = "kebab-case")] pub struct RegistryConfig { pub index: Option, @@ -207,6 +206,19 @@ fn credential_provider(config: &Config, sid: &SourceId) -> CargoResult CargoResult> { + let mut cache = config.registry_config(); + if let Some(cfg) = cache.get(&sid) { + return Ok(cfg.clone()); + } + let cfg = registry_credential_config_raw_uncached(config, sid)?; + cache.insert(*sid, cfg.clone()); + return Ok(cfg); +} + +fn registry_credential_config_raw_uncached( + config: &Config, + sid: &SourceId, ) -> CargoResult> { log::trace!("loading credential config for {}", sid); config.load_credentials()?; @@ -237,6 +249,7 @@ pub fn registry_credential_config_raw( // This also allows the authorization token for a registry to be set // without knowing the registry name by using the _INDEX and _TOKEN // environment variables. + let name = { // Discover names from environment variables. let index = sid.canonical_url(); @@ -256,14 +269,17 @@ pub fn registry_credential_config_raw( // Discover names from the configuration only if none were found in the environment. if names.len() == 0 { - names = config - .get::>("registries")? - .iter() - .filter_map(|(k, v)| Some((k, v.index.as_deref()?))) - .filter_map(|(k, v)| Some((k, CanonicalUrl::new(&v.into_url().ok()?).ok()?))) - .filter(|(_, v)| v == index) - .map(|(k, _)| k.to_string()) - .collect(); + if let Some(registries) = config.values()?.get("registries") { + let (registries, _) = registries.table("registries")?; + for (name, value) in registries { + if let Some(v) = value.table(&format!("registries.{name}"))?.0.get("index") { + let (v, _) = v.string(&format!("registries.{name}.index"))?; + if index == &CanonicalUrl::new(&v.into_url()?)? { + names.push(name.clone()); + } + } + } + } } names.sort(); match names.len() { diff --git a/src/cargo/util/config/mod.rs b/src/cargo/util/config/mod.rs index 8f1cfe073f5..024137dd79a 100644 --- a/src/cargo/util/config/mod.rs +++ b/src/cargo/util/config/mod.rs @@ -106,6 +106,8 @@ pub use target::{TargetCfgConfig, TargetConfig}; mod environment; use environment::Env; +use super::auth::RegistryConfig; + // Helper macro for creating typed access methods. macro_rules! get_value_typed { ($name:ident, $ty:ty, $variant:ident, $expected:expr) => { @@ -208,6 +210,8 @@ pub struct Config { /// Cache of credentials from configuration or credential providers. /// Maps from url to credential value. credential_cache: LazyCell>>, + /// Cache of registry config from from the `[registries]` table. + registry_config: LazyCell>>>, /// Lock, if held, of the global package cache along with the number of /// acquisitions so far. package_cache_lock: RefCell, usize)>>, @@ -299,6 +303,7 @@ impl Config { env, updated_sources: LazyCell::new(), credential_cache: LazyCell::new(), + registry_config: LazyCell::new(), package_cache_lock: RefCell::new(None), http_config: LazyCell::new(), future_incompat_config: LazyCell::new(), @@ -488,6 +493,13 @@ impl Config { .borrow_mut() } + /// Cache of already parsed registries from the `[registries]` table. + pub(crate) fn registry_config(&self) -> RefMut<'_, HashMap>> { + self.registry_config + .borrow_with(|| RefCell::new(HashMap::new())) + .borrow_mut() + } + /// Gets all config values from disk. /// /// This will lazy-load the values as necessary. Callers are responsible diff --git a/tests/testsuite/alt_registry.rs b/tests/testsuite/alt_registry.rs index ac60ca92fa8..91157cd5327 100644 --- a/tests/testsuite/alt_registry.rs +++ b/tests/testsuite/alt_registry.rs @@ -1506,3 +1506,44 @@ fn publish_with_transitive_dep() { .build(); p2.cargo("publish").run(); } + +#[cargo_test] +fn warn_for_unused_fields() { + let _ = RegistryBuilder::new() + .no_configure_token() + .alternative() + .build(); + let p = project() + .file("src/lib.rs", "") + .file( + ".cargo/config.toml", + "[registry] + unexpected-field = 'foo' + [registries.alternative] + unexpected-field = 'foo' + ", + ) + .build(); + + p.cargo("publish --registry alternative") + .with_status(101) + .with_stderr( + "\ +[UPDATING] `alternative` index +[WARNING] unused config key `registries.alternative.unexpected-field` in `[..]config.toml` +[ERROR] no token found for `alternative`, please run `cargo login --registry alternative` +or use environment variable CARGO_REGISTRIES_ALTERNATIVE_TOKEN", + ) + .run(); + + p.cargo("publish --registry crates-io") + .with_status(101) + .with_stderr( + "\ +[UPDATING] crates.io index +[WARNING] unused config key `registry.unexpected-field` in `[..]config.toml` +[ERROR] no token found, please run `cargo login` +or use environment variable CARGO_REGISTRY_TOKEN", + ) + .run(); +}