@@ -100,6 +100,9 @@ pub use path::{ConfigRelativePath, PathAndArgs};
100
100
mod target;
101
101
pub use target:: { TargetCfgConfig , TargetConfig } ;
102
102
103
+ mod environment;
104
+ use environment:: Env ;
105
+
103
106
// Helper macro for creating typed access methods.
104
107
macro_rules! get_value_typed {
105
108
( $name: ident, $ty: ty, $variant: ident, $expected: expr) => {
@@ -124,30 +127,6 @@ macro_rules! get_value_typed {
124
127
} ;
125
128
}
126
129
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
-
151
130
/// Indicates why a config value is being loaded.
152
131
#[ derive( Clone , Copy , Debug ) ]
153
132
enum WhyLoad {
@@ -223,15 +202,8 @@ pub struct Config {
223
202
creation_time : Instant ,
224
203
/// Target Directory via resolved Cli parameter
225
204
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 ,
235
207
/// Tracks which sources have been updated to avoid multiple updates.
236
208
updated_sources : LazyCell < RefCell < HashSet < SourceId > > > ,
237
209
/// Cache of credentials from configuration or credential providers.
@@ -289,11 +261,10 @@ impl Config {
289
261
}
290
262
} ) ;
291
263
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 ( ) ;
294
265
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) {
297
268
Some ( cache) => cache != "0" ,
298
269
_ => true ,
299
270
} ;
@@ -327,8 +298,6 @@ impl Config {
327
298
creation_time : Instant :: now ( ) ,
328
299
target_dir : None ,
329
300
env,
330
- case_insensitive_env,
331
- normalized_env,
332
301
updated_sources : LazyCell :: new ( ) ,
333
302
credential_cache : LazyCell :: new ( ) ,
334
303
package_cache_lock : RefCell :: new ( None ) ,
@@ -683,7 +652,7 @@ impl Config {
683
652
// Root table can't have env value.
684
653
return Ok ( cv) ;
685
654
}
686
- let env = self . get_env_str ( key. as_env_key ( ) ) ;
655
+ let env = self . env . get_str ( key. as_env_key ( ) ) ;
687
656
let env_def = Definition :: Environment ( key. as_env_key ( ) . to_string ( ) ) ;
688
657
let use_env = match ( & cv, env) {
689
658
// Lists are always merged.
@@ -754,32 +723,26 @@ impl Config {
754
723
755
724
/// Helper primarily for testing.
756
725
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) ;
762
727
}
763
728
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.
766
731
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 ( )
770
733
}
771
734
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.
773
736
fn env_keys ( & self ) -> impl Iterator < Item = & str > {
774
- self . env . iter ( ) . filter_map ( | ( k , _ ) | k . to_str ( ) )
737
+ self . env . keys_str ( )
775
738
}
776
739
777
740
fn get_config_env < T > ( & self , key : & ConfigKey ) -> Result < OptValue < T > , ConfigError >
778
741
where
779
742
T : FromStr ,
780
743
<T as FromStr >:: Err : fmt:: Display ,
781
744
{
782
- match self . get_env_str ( key. as_env_key ( ) ) {
745
+ match self . env . get_str ( key. as_env_key ( ) ) {
783
746
Some ( value) => {
784
747
let definition = Definition :: Environment ( key. as_env_key ( ) . to_string ( ) ) ;
785
748
Ok ( Some ( Value {
@@ -800,59 +763,21 @@ impl Config {
800
763
///
801
764
/// This can be used similarly to `std::env::var`.
802
765
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)
812
767
}
813
768
814
769
/// Get the value of environment variable `key` through the `Config` snapshot.
815
770
///
816
771
/// This can be used similarly to `std::env::var_os`.
817
772
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)
849
774
}
850
775
851
776
/// Check if the [`Config`] contains a given [`ConfigKey`].
852
777
///
853
778
/// See `ConfigMapAccess` for a description of `env_prefix_ok`.
854
779
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 ( ) ) {
856
781
return Ok ( true ) ;
857
782
}
858
783
if env_prefix_ok {
@@ -870,7 +795,7 @@ impl Config {
870
795
}
871
796
872
797
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 ( ) ) {
874
799
let _ = self . shell ( ) . warn ( format ! (
875
800
"Environment variables are expected to use uppercase letters and underscores, \
876
801
the variable `{}` will be ignored and have no effect",
@@ -969,7 +894,7 @@ impl Config {
969
894
key : & ConfigKey ,
970
895
output : & mut Vec < ( String , Definition ) > ,
971
896
) -> 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 ( ) ) {
973
898
Some ( v) => v,
974
899
None => {
975
900
self . check_environment_key_case_mismatch ( key) ;
0 commit comments