@@ -100,6 +100,9 @@ pub use path::{ConfigRelativePath, PathAndArgs};
100100mod target;
101101pub use target:: { TargetCfgConfig , TargetConfig } ;
102102
103+ mod environment;
104+ use environment:: Env ;
105+
103106// Helper macro for creating typed access methods.
104107macro_rules! get_value_typed {
105108 ( $name: ident, $ty: ty, $variant: ident, $expected: expr) => {
@@ -124,30 +127,6 @@ macro_rules! get_value_typed {
124127 } ;
125128}
126129
127- /// Generate `case_insensitive_env` and `normalized_env` from the `env`.
128- fn make_case_insensitive_and_normalized_env (
129- env : & HashMap < OsString , OsString > ,
130- ) -> ( HashMap < String , String > , HashMap < String , String > ) {
131- // See `Config.case_insensitive_env`.
132- // Maps from uppercased key to actual environment key.
133- // For example, `"PATH" => "Path"`.
134- let case_insensitive_env: HashMap < _ , _ > = env
135- . keys ( )
136- . filter_map ( |k| k. to_str ( ) )
137- . map ( |k| ( k. to_uppercase ( ) , k. to_owned ( ) ) )
138- . collect ( ) ;
139- // See `Config.normalized_env`.
140- // Maps from normalized (uppercased with "-" replaced by "_") key
141- // to actual environment key. For example, `"MY_KEY" => "my-key"`.
142- let normalized_env = env
143- . iter ( )
144- // Only keep entries where both the key and value are valid UTF-8
145- . filter_map ( |( k, v) | Some ( ( k. to_str ( ) ?, v. to_str ( ) ?) ) )
146- . map ( |( k, _) | ( k. to_uppercase ( ) . replace ( "-" , "_" ) , k. to_owned ( ) ) )
147- . collect ( ) ;
148- ( case_insensitive_env, normalized_env)
149- }
150-
151130/// Indicates why a config value is being loaded.
152131#[ derive( Clone , Copy , Debug ) ]
153132enum WhyLoad {
@@ -223,15 +202,8 @@ pub struct Config {
223202 creation_time : Instant ,
224203 /// Target Directory via resolved Cli parameter
225204 target_dir : Option < Filesystem > ,
226- /// Environment variables, separated to assist testing.
227- env : HashMap < OsString , OsString > ,
228- /// Environment variables converted to uppercase to check for case mismatch
229- /// (relevant on Windows, where environment variables are case-insensitive).
230- case_insensitive_env : HashMap < String , String > ,
231- /// Environment variables converted to uppercase and with "-" replaced by "_"
232- /// (the format expected by Cargo). This only contains entries where the key and variable are
233- /// both valid UTF-8.
234- normalized_env : HashMap < String , String > ,
205+ /// Environment variable snapshot.
206+ env : Env ,
235207 /// Tracks which sources have been updated to avoid multiple updates.
236208 updated_sources : LazyCell < RefCell < HashSet < SourceId > > > ,
237209 /// Cache of credentials from configuration or credential providers.
@@ -289,11 +261,10 @@ impl Config {
289261 }
290262 } ) ;
291263
292- let env: HashMap < _ , _ > = env:: vars_os ( ) . collect ( ) ;
293- let ( case_insensitive_env, normalized_env) = make_case_insensitive_and_normalized_env ( & env) ;
264+ let env = Env :: new ( ) ;
294265
295- let cache_key: & OsStr = "CARGO_CACHE_RUSTC_INFO" . as_ref ( ) ;
296- let cache_rustc_info = match env. get ( cache_key) {
266+ let cache_key = "CARGO_CACHE_RUSTC_INFO" ;
267+ let cache_rustc_info = match env. get_env_os ( cache_key) {
297268 Some ( cache) => cache != "0" ,
298269 _ => true ,
299270 } ;
@@ -327,8 +298,6 @@ impl Config {
327298 creation_time : Instant :: now ( ) ,
328299 target_dir : None ,
329300 env,
330- case_insensitive_env,
331- normalized_env,
332301 updated_sources : LazyCell :: new ( ) ,
333302 credential_cache : LazyCell :: new ( ) ,
334303 package_cache_lock : RefCell :: new ( None ) ,
@@ -683,7 +652,7 @@ impl Config {
683652 // Root table can't have env value.
684653 return Ok ( cv) ;
685654 }
686- let env = self . get_env_str ( key. as_env_key ( ) ) ;
655+ let env = self . env . get_str ( key. as_env_key ( ) ) ;
687656 let env_def = Definition :: Environment ( key. as_env_key ( ) . to_string ( ) ) ;
688657 let use_env = match ( & cv, env) {
689658 // Lists are always merged.
@@ -754,32 +723,26 @@ impl Config {
754723
755724 /// Helper primarily for testing.
756725 pub fn set_env ( & mut self , env : HashMap < String , String > ) {
757- self . env = env. into_iter ( ) . map ( |( k, v) | ( k. into ( ) , v. into ( ) ) ) . collect ( ) ;
758- let ( case_insensitive_env, normalized_env) =
759- make_case_insensitive_and_normalized_env ( & self . env ) ;
760- self . case_insensitive_env = case_insensitive_env;
761- self . normalized_env = normalized_env;
726+ self . env = Env :: from_map ( env) ;
762727 }
763728
764- /// Returns all environment variables as an iterator, filtering out entries
765- /// that are not valid UTF-8.
729+ /// Returns all environment variables as an iterator,
730+ /// keeping only entries where both the key and value are valid UTF-8.
766731 pub ( crate ) fn env ( & self ) -> impl Iterator < Item = ( & str , & str ) > {
767- self . env
768- . iter ( )
769- . filter_map ( |( k, v) | Some ( ( k. to_str ( ) ?, v. to_str ( ) ?) ) )
732+ self . env . iter_str ( )
770733 }
771734
772- /// Returns all environment variable keys, filtering out entries that are not valid UTF-8.
735+ /// Returns all environment variable keys, filtering out keys that are not valid UTF-8.
773736 fn env_keys ( & self ) -> impl Iterator < Item = & str > {
774- self . env . iter ( ) . filter_map ( | ( k , _ ) | k . to_str ( ) )
737+ self . env . keys_str ( )
775738 }
776739
777740 fn get_config_env < T > ( & self , key : & ConfigKey ) -> Result < OptValue < T > , ConfigError >
778741 where
779742 T : FromStr ,
780743 <T as FromStr >:: Err : fmt:: Display ,
781744 {
782- match self . get_env_str ( key. as_env_key ( ) ) {
745+ match self . env . get_str ( key. as_env_key ( ) ) {
783746 Some ( value) => {
784747 let definition = Definition :: Environment ( key. as_env_key ( ) . to_string ( ) ) ;
785748 Ok ( Some ( Value {
@@ -800,59 +763,21 @@ impl Config {
800763 ///
801764 /// This can be used similarly to `std::env::var`.
802765 pub fn get_env ( & self , key : impl AsRef < OsStr > ) -> CargoResult < String > {
803- let key = key. as_ref ( ) ;
804- let s = self
805- . get_env_os ( key)
806- . ok_or_else ( || anyhow ! ( "{key:?} could not be found in the environment snapshot" ) ) ?;
807-
808- match s. to_str ( ) {
809- Some ( s) => Ok ( s. to_owned ( ) ) ,
810- None => bail ! ( "environment variable value is not valid unicode: {s:?}" ) ,
811- }
766+ self . env . get_env ( key)
812767 }
813768
814769 /// Get the value of environment variable `key` through the `Config` snapshot.
815770 ///
816771 /// This can be used similarly to `std::env::var_os`.
817772 pub fn get_env_os ( & self , key : impl AsRef < OsStr > ) -> Option < OsString > {
818- match self . env . get ( key. as_ref ( ) ) {
819- Some ( s) => Some ( s. clone ( ) ) ,
820- None => {
821- if cfg ! ( windows) {
822- self . get_env_case_insensitive ( key) . cloned ( )
823- } else {
824- None
825- }
826- }
827- }
828- }
829-
830- /// Wrapper for `self.env.get` when `key` should be case-insensitive.
831- /// This is relevant on Windows, where environment variables are case-insensitive.
832- /// Note that this only works on keys that are valid UTF-8.
833- fn get_env_case_insensitive ( & self , key : impl AsRef < OsStr > ) -> Option < & OsString > {
834- let upper_case_key = key. as_ref ( ) . to_str ( ) ?. to_uppercase ( ) ;
835- // `self.case_insensitive_env` holds pairs like `("PATH", "Path")`
836- // or `("MY-VAR", "my-var")`.
837- let env_key: & OsStr = self . case_insensitive_env . get ( & upper_case_key) ?. as_ref ( ) ;
838- self . env . get ( env_key)
839- }
840-
841- /// Get the value of environment variable `key`.
842- /// Returns `None` if `key` is not in `self.env` or if the value is not valid UTF-8.
843- fn get_env_str ( & self , key : impl AsRef < OsStr > ) -> Option < & str > {
844- self . env . get ( key. as_ref ( ) ) . and_then ( |s| s. to_str ( ) )
845- }
846-
847- fn env_has_key ( & self , key : impl AsRef < OsStr > ) -> bool {
848- self . env . contains_key ( key. as_ref ( ) )
773+ self . env . get_env_os ( key)
849774 }
850775
851776 /// Check if the [`Config`] contains a given [`ConfigKey`].
852777 ///
853778 /// See `ConfigMapAccess` for a description of `env_prefix_ok`.
854779 fn has_key ( & self , key : & ConfigKey , env_prefix_ok : bool ) -> CargoResult < bool > {
855- if self . env_has_key ( key. as_env_key ( ) ) {
780+ if self . env . contains_key ( key. as_env_key ( ) ) {
856781 return Ok ( true ) ;
857782 }
858783 if env_prefix_ok {
@@ -870,7 +795,7 @@ impl Config {
870795 }
871796
872797 fn check_environment_key_case_mismatch ( & self , key : & ConfigKey ) {
873- if let Some ( env_key) = self . normalized_env . get ( key. as_env_key ( ) ) {
798+ if let Some ( env_key) = self . env . get_normalized ( key. as_env_key ( ) ) {
874799 let _ = self . shell ( ) . warn ( format ! (
875800 "Environment variables are expected to use uppercase letters and underscores, \
876801 the variable `{}` will be ignored and have no effect",
@@ -969,7 +894,7 @@ impl Config {
969894 key : & ConfigKey ,
970895 output : & mut Vec < ( String , Definition ) > ,
971896 ) -> CargoResult < ( ) > {
972- let env_val = match self . get_env_str ( key. as_env_key ( ) ) {
897+ let env_val = match self . env . get_str ( key. as_env_key ( ) ) {
973898 Some ( v) => v,
974899 None => {
975900 self . check_environment_key_case_mismatch ( key) ;
0 commit comments