]> git.proxmox.com Git - cargo.git/commitdiff
refactor(cli): Lazily do first-pass config loading
authorEd Page <eopage@gmail.com>
Mon, 29 Aug 2022 17:06:19 +0000 (12:06 -0500)
committerEd Page <eopage@gmail.com>
Thu, 1 Sep 2022 00:23:35 +0000 (19:23 -0500)
This will be a help for cases like #10952 which I would expect would
assert that the config is not loaded before changing the current_dir.

src/bin/cargo/cli.rs
src/bin/cargo/main.rs

index d5a3a72e065fef26e8b04c8acc0caefd74ab83c4..5d7fc66d37fc4c6a3b046130cf6e1127395ec5de 100644 (file)
@@ -1,4 +1,5 @@
 use anyhow::anyhow;
+use cargo::core::shell::Shell;
 use cargo::core::{features, CliUnstable};
 use cargo::{self, drop_print, drop_println, CliResult, Config};
 use clap::{AppSettings, Arg, ArgMatches};
@@ -21,12 +22,13 @@ lazy_static::lazy_static! {
     ]);
 }
 
-pub fn main(config: &mut Config) -> CliResult {
+pub fn main(config: &mut LazyConfig) -> CliResult {
+    let args = cli().try_get_matches()?;
+
     // CAUTION: Be careful with using `config` until it is configured below.
     // In general, try to avoid loading config values unless necessary (like
     // the [alias] table).
-
-    let args = cli().try_get_matches()?;
+    let config = config.get_mut();
 
     // Global args need to be extracted before expanding aliases because the
     // clap code for extracting a subcommand discards global options
@@ -463,6 +465,49 @@ See 'cargo help <command>' for more information on a specific command.\n",
         .subcommands(commands::builtin())
 }
 
+/// Delay loading [`Config`] until access.
+///
+/// In the common path, the [`Config`] is dependent on CLI parsing and shouldn't be loaded until
+/// after that is done but some other paths (like fix or earlier errors) might need access to it,
+/// so this provides a way to share the instance and the implementation across these different
+/// accesses.
+pub struct LazyConfig {
+    config: Option<Config>,
+}
+
+impl LazyConfig {
+    pub fn new() -> Self {
+        Self { config: None }
+    }
+
+    /// Check whether the config is loaded
+    ///
+    /// This is useful for asserts in case the environment needs to be setup before loading
+    pub fn is_init(&self) -> bool {
+        self.config.is_some()
+    }
+
+    /// Get the config, loading it if needed
+    ///
+    /// On error, the process is terminated
+    pub fn get(&mut self) -> &Config {
+        self.get_mut()
+    }
+
+    /// Get the config, loading it if needed
+    ///
+    /// On error, the process is terminated
+    pub fn get_mut(&mut self) -> &mut Config {
+        self.config.get_or_insert_with(|| match Config::default() {
+            Ok(cfg) => cfg,
+            Err(e) => {
+                let mut shell = Shell::new();
+                cargo::exit_with_error(e.into(), &mut shell)
+            }
+        })
+    }
+}
+
 #[test]
 fn verify_cli() {
     cli().debug_assert();
index bd439f8f193d2f97713ca758dd57d67d0e032996..e4f224031cdfa5699aa04170060feab1e5f7e4e5 100644 (file)
@@ -1,7 +1,6 @@
 #![warn(rust_2018_idioms)] // while we're getting used to 2018
 #![allow(clippy::all)]
 
-use cargo::core::shell::Shell;
 use cargo::util::toml::StringOrVec;
 use cargo::util::CliError;
 use cargo::util::{self, closest_msg, command_prelude, CargoResult, CliResult, Config};
@@ -22,23 +21,17 @@ fn main() {
     #[cfg(not(feature = "pretty-env-logger"))]
     env_logger::init_from_env("CARGO_LOG");
 
-    let mut config = match Config::default() {
-        Ok(cfg) => cfg,
-        Err(e) => {
-            let mut shell = Shell::new();
-            cargo::exit_with_error(e.into(), &mut shell)
-        }
-    };
+    let mut config = cli::LazyConfig::new();
 
     let result = if let Some(lock_addr) = cargo::ops::fix_get_proxy_lock_addr() {
-        cargo::ops::fix_exec_rustc(&config, &lock_addr).map_err(|e| CliError::from(e))
+        cargo::ops::fix_exec_rustc(config.get(), &lock_addr).map_err(|e| CliError::from(e))
     } else {
         let _token = cargo::util::job::setup();
         cli::main(&mut config)
     };
 
     match result {
-        Err(e) => cargo::exit_with_error(e, &mut *config.shell()),
+        Err(e) => cargo::exit_with_error(e, &mut config.get_mut().shell()),
         Ok(()) => {}
     }
 }