@@ -54,7 +54,7 @@ use std::cell::{RefCell, RefMut};
54
54
use std:: collections:: hash_map:: Entry :: { Occupied , Vacant } ;
55
55
use std:: collections:: { HashMap , HashSet } ;
56
56
use std:: env;
57
- use std:: ffi:: OsStr ;
57
+ use std:: ffi:: { OsStr , OsString } ;
58
58
use std:: fmt;
59
59
use std:: fs:: { self , File } ;
60
60
use std:: io:: prelude:: * ;
@@ -200,7 +200,7 @@ pub struct Config {
200
200
/// Target Directory via resolved Cli parameter
201
201
target_dir : Option < Filesystem > ,
202
202
/// Environment variables, separated to assist testing.
203
- env : HashMap < String , String > ,
203
+ env : HashMap < OsString , OsString > ,
204
204
/// Environment variables, converted to uppercase to check for case mismatch
205
205
upper_case_env : HashMap < String , String > ,
206
206
/// Tracks which sources have been updated to avoid multiple updates.
@@ -260,23 +260,16 @@ impl Config {
260
260
}
261
261
} ) ;
262
262
263
- let env: HashMap < _ , _ > = env:: vars_os ( )
264
- . filter_map ( |( k, v) | {
265
- // Ignore any key/values that are not valid Unicode.
266
- match ( k. into_string ( ) , v. into_string ( ) ) {
267
- ( Ok ( k) , Ok ( v) ) => Some ( ( k, v) ) ,
268
- _ => None ,
269
- }
270
- } )
271
- . collect ( ) ;
263
+ let env: HashMap < _ , _ > = env:: vars_os ( ) . collect ( ) ;
272
264
273
265
let upper_case_env = env
274
- . clone ( )
275
- . into_iter ( )
276
- . map ( |( k , _ ) | ( k. to_uppercase ( ) . replace ( "-" , "_" ) , k) )
266
+ . iter ( )
267
+ . filter_map ( | ( k , _ ) | k . to_str ( ) ) // Only keep valid UTF-8
268
+ . map ( |k | ( k. to_uppercase ( ) . replace ( "-" , "_" ) , k. to_owned ( ) ) )
277
269
. collect ( ) ;
278
270
279
- let cache_rustc_info = match env. get ( "CARGO_CACHE_RUSTC_INFO" ) {
271
+ let cache_key: & OsStr = "CARGO_CACHE_RUSTC_INFO" . as_ref ( ) ;
272
+ let cache_rustc_info = match env. get ( cache_key) {
280
273
Some ( cache) => cache != "0" ,
281
274
_ => true ,
282
275
} ;
@@ -566,7 +559,7 @@ impl Config {
566
559
pub fn target_dir ( & self ) -> CargoResult < Option < Filesystem > > {
567
560
if let Some ( dir) = & self . target_dir {
568
561
Ok ( Some ( dir. clone ( ) ) )
569
- } else if let Some ( dir) = self . env . get ( "CARGO_TARGET_DIR" ) {
562
+ } else if let Some ( dir) = self . get_env_os ( "CARGO_TARGET_DIR" ) {
570
563
// Check if the CARGO_TARGET_DIR environment variable is set to an empty string.
571
564
if dir. is_empty ( ) {
572
565
bail ! (
@@ -664,7 +657,7 @@ impl Config {
664
657
// Root table can't have env value.
665
658
return Ok ( cv) ;
666
659
}
667
- let env = self . env . get ( key. as_env_key ( ) ) ;
660
+ let env = self . get_env_str ( key. as_env_key ( ) ) ;
668
661
let env_def = Definition :: Environment ( key. as_env_key ( ) . to_string ( ) ) ;
669
662
let use_env = match ( & cv, env) {
670
663
// Lists are always merged.
@@ -735,20 +728,28 @@ impl Config {
735
728
736
729
/// Helper primarily for testing.
737
730
pub fn set_env ( & mut self , env : HashMap < String , String > ) {
738
- self . env = env;
731
+ self . env = env. into_iter ( ) . map ( | ( k , v ) | ( k . into ( ) , v . into ( ) ) ) . collect ( ) ;
739
732
}
740
733
741
- /// Returns all environment variables.
742
- pub ( crate ) fn env ( & self ) -> & HashMap < String , String > {
743
- & self . env
734
+ /// Returns all environment variables as an iterator, filtering out entries
735
+ /// that are not valid UTF-8.
736
+ pub ( crate ) fn env ( & self ) -> impl Iterator < Item = ( & str , & str ) > {
737
+ self . env
738
+ . iter ( )
739
+ . filter_map ( |( k, v) | Some ( ( k. to_str ( ) ?, v. to_str ( ) ?) ) )
740
+ }
741
+
742
+ /// Returns all environment variable keys, filtering out entries that are not valid UTF-8.
743
+ fn env_keys ( & self ) -> impl Iterator < Item = & str > {
744
+ self . env . iter ( ) . filter_map ( |( k, _) | k. to_str ( ) )
744
745
}
745
746
746
747
fn get_config_env < T > ( & self , key : & ConfigKey ) -> Result < OptValue < T > , ConfigError >
747
748
where
748
749
T : FromStr ,
749
750
<T as FromStr >:: Err : fmt:: Display ,
750
751
{
751
- match self . env . get ( key. as_env_key ( ) ) {
752
+ match self . get_env_str ( key. as_env_key ( ) ) {
752
753
Some ( value) => {
753
754
let definition = Definition :: Environment ( key. as_env_key ( ) . to_string ( ) ) ;
754
755
Ok ( Some ( Value {
@@ -768,33 +769,45 @@ impl Config {
768
769
/// Get the value of environment variable `key` through the `Config` snapshot.
769
770
///
770
771
/// This can be used similarly to `std::env::var`.
771
- pub fn get_env ( & self , key : impl AsRef < str > ) -> CargoResult < String > {
772
- match self . env . get ( key. as_ref ( ) ) {
773
- Some ( s) => Ok ( s. clone ( ) ) ,
774
- None => bail ! (
775
- "{} could not be found in the environment snapshot" ,
776
- key. as_ref( )
777
- ) ,
772
+ pub fn get_env ( & self , key : impl AsRef < OsStr > ) -> CargoResult < String > {
773
+ let key = key. as_ref ( ) ;
774
+ let s = match self . env . get ( key) {
775
+ Some ( s) => s,
776
+ None => bail ! ( "{key:?} could not be found in the environment snapshot" , ) ,
777
+ } ;
778
+ match s. to_str ( ) {
779
+ Some ( s) => Ok ( s. to_owned ( ) ) ,
780
+ None => bail ! ( "environment variable value is not valid unicode: {s:?}" ) ,
778
781
}
779
782
}
780
783
781
784
/// Get the value of environment variable `key` through the `Config` snapshot.
782
785
///
783
786
/// This can be used similarly to `std::env::var_os`.
784
- pub fn get_env_os ( & self , key : impl AsRef < str > ) -> Option < String > {
787
+ pub fn get_env_os ( & self , key : impl AsRef < OsStr > ) -> Option < OsString > {
785
788
self . env . get ( key. as_ref ( ) ) . cloned ( )
786
789
}
787
790
791
+ /// Get the value of environment variable `key`.
792
+ /// Returns `None` if `key` is not in `self.env` or if the value is not valid UTF-8.
793
+ fn get_env_str ( & self , key : impl AsRef < OsStr > ) -> Option < & str > {
794
+ self . env . get ( key. as_ref ( ) ) . and_then ( |s| s. to_str ( ) )
795
+ }
796
+
797
+ fn env_has_key ( & self , key : impl AsRef < OsStr > ) -> bool {
798
+ self . env . contains_key ( key. as_ref ( ) )
799
+ }
800
+
788
801
/// Check if the [`Config`] contains a given [`ConfigKey`].
789
802
///
790
803
/// See `ConfigMapAccess` for a description of `env_prefix_ok`.
791
804
fn has_key ( & self , key : & ConfigKey , env_prefix_ok : bool ) -> CargoResult < bool > {
792
- if self . env . contains_key ( key. as_env_key ( ) ) {
805
+ if self . env_has_key ( key. as_env_key ( ) ) {
793
806
return Ok ( true ) ;
794
807
}
795
808
if env_prefix_ok {
796
809
let env_prefix = format ! ( "{}_" , key. as_env_key( ) ) ;
797
- if self . env . keys ( ) . any ( |k| k. starts_with ( & env_prefix) ) {
810
+ if self . env_keys ( ) . any ( |k| k. starts_with ( & env_prefix) ) {
798
811
return Ok ( true ) ;
799
812
}
800
813
}
@@ -906,7 +919,7 @@ impl Config {
906
919
key : & ConfigKey ,
907
920
output : & mut Vec < ( String , Definition ) > ,
908
921
) -> CargoResult < ( ) > {
909
- let env_val = match self . env . get ( key. as_env_key ( ) ) {
922
+ let env_val = match self . get_env_str ( key. as_env_key ( ) ) {
910
923
Some ( v) => v,
911
924
None => {
912
925
self . check_environment_key_case_mismatch ( key) ;
@@ -1637,7 +1650,7 @@ impl Config {
1637
1650
) -> Option < PathBuf > {
1638
1651
let var = tool. to_uppercase ( ) ;
1639
1652
1640
- match self . get_env_os ( & var) {
1653
+ match self . get_env_os ( & var) . as_ref ( ) . and_then ( |s| s . to_str ( ) ) {
1641
1654
Some ( tool_path) => {
1642
1655
let maybe_relative = tool_path. contains ( '/' ) || tool_path. contains ( '\\' ) ;
1643
1656
let path = if maybe_relative {
0 commit comments