11//! Cargo-like environment variables injection.
22use base_db:: Env ;
33use paths:: Utf8Path ;
4+ use rustc_hash:: FxHashMap ;
45use toolchain:: Tool ;
56
67use crate :: { ManifestPath , PackageData , TargetKind , cargo_config_file:: CargoConfigFile } ;
@@ -60,8 +61,14 @@ pub(crate) fn inject_rustc_tool_env(env: &mut Env, cargo_name: &str, kind: Targe
6061 env. set ( "CARGO_CRATE_NAME" , cargo_name. replace ( '-' , "_" ) ) ;
6162}
6263
63- pub ( crate ) fn cargo_config_env ( manifest : & ManifestPath , config : & Option < CargoConfigFile > ) -> Env {
64+ pub ( crate ) fn cargo_config_env (
65+ manifest : & ManifestPath ,
66+ config : & Option < CargoConfigFile > ,
67+ extra_env : & FxHashMap < String , Option < String > > ,
68+ ) -> Env {
6469 let mut env = Env :: default ( ) ;
70+ env. extend ( extra_env. iter ( ) . filter_map ( |( k, v) | v. as_ref ( ) . map ( |v| ( k. clone ( ) , v. clone ( ) ) ) ) ) ;
71+
6572 let Some ( serde_json:: Value :: Object ( env_json) ) = config. as_ref ( ) . and_then ( |c| c. get ( "env" ) )
6673 else {
6774 return env;
@@ -72,22 +79,34 @@ pub(crate) fn cargo_config_env(manifest: &ManifestPath, config: &Option<CargoCon
7279 let base = <_ as AsRef < Utf8Path > >:: as_ref ( manifest. parent ( ) ) ;
7380
7481 for ( key, entry) in env_json {
75- let serde_json:: Value :: Object ( entry) = entry else {
76- continue ;
77- } ;
78- let Some ( value) = entry. get ( "value" ) . and_then ( |v| v. as_str ( ) ) else {
79- continue ;
80- } ;
82+ let value = match entry {
83+ serde_json:: Value :: String ( s) => s. clone ( ) ,
84+ serde_json:: Value :: Object ( entry) => {
85+ // Each entry MUST have a `value` key.
86+ let Some ( value) = entry. get ( "value" ) . and_then ( |v| v. as_str ( ) ) else {
87+ continue ;
88+ } ;
89+ // If the entry already exists in the environment AND the `force` key is not set to
90+ // true, then don't overwrite the value.
91+ if extra_env. get ( key) . is_some_and ( Option :: is_some)
92+ && !entry. get ( "force" ) . and_then ( |v| v. as_bool ( ) ) . unwrap_or ( false )
93+ {
94+ continue ;
95+ }
8196
82- let value = if entry
83- . get ( "relative" )
84- . and_then ( |v| v. as_bool ( ) )
85- . is_some_and ( std:: convert:: identity)
86- {
87- base. join ( value) . to_string ( )
88- } else {
89- value. to_owned ( )
97+ if entry
98+ . get ( "relative" )
99+ . and_then ( |v| v. as_bool ( ) )
100+ . is_some_and ( std:: convert:: identity)
101+ {
102+ base. join ( value) . to_string ( )
103+ } else {
104+ value. to_owned ( )
105+ }
106+ }
107+ _ => continue ,
90108 } ;
109+
91110 env. insert ( key, value) ;
92111 }
93112
@@ -113,17 +132,42 @@ fn parse_output_cargo_config_env_works() {
113132 },
114133 "TEST": {
115134 "value": "test"
116- }
135+ },
136+ "FORCED": {
137+ "value": "test",
138+ "force": true
139+ },
140+ "UNFORCED": {
141+ "value": "test",
142+ "force": false
143+ },
144+ "OVERWRITTEN": {
145+ "value": "test"
146+ },
147+ "NOT_AN_OBJECT": "value"
117148 }
118149}
119150"# ;
120151 let config: CargoConfigFile = serde_json:: from_str ( raw) . unwrap ( ) ;
121152 let cwd = paths:: Utf8PathBuf :: try_from ( std:: env:: current_dir ( ) . unwrap ( ) ) . unwrap ( ) ;
122153 let manifest = paths:: AbsPathBuf :: assert ( cwd. join ( "Cargo.toml" ) ) ;
123154 let manifest = ManifestPath :: try_from ( manifest) . unwrap ( ) ;
124- let env = cargo_config_env ( & manifest, & Some ( config) ) ;
155+ let extra_env = [
156+ ( "FORCED" , Some ( "ignored" ) ) ,
157+ ( "UNFORCED" , Some ( "newvalue" ) ) ,
158+ ( "OVERWRITTEN" , Some ( "newvalue" ) ) ,
159+ ( "TEST" , None ) ,
160+ ]
161+ . iter ( )
162+ . map ( |( k, v) | ( k. to_string ( ) , v. map ( ToString :: to_string) ) )
163+ . collect ( ) ;
164+ let env = cargo_config_env ( & manifest, & Some ( config) , & extra_env) ;
125165 assert_eq ! ( env. get( "CARGO_WORKSPACE_DIR" ) . as_deref( ) , Some ( cwd. join( "" ) . as_str( ) ) ) ;
126166 assert_eq ! ( env. get( "RELATIVE" ) . as_deref( ) , Some ( cwd. join( "../relative" ) . as_str( ) ) ) ;
127167 assert_eq ! ( env. get( "INVALID" ) . as_deref( ) , Some ( "../relative" ) ) ;
128168 assert_eq ! ( env. get( "TEST" ) . as_deref( ) , Some ( "test" ) ) ;
169+ assert_eq ! ( env. get( "FORCED" ) . as_deref( ) , Some ( "test" ) ) ;
170+ assert_eq ! ( env. get( "UNFORCED" ) . as_deref( ) , Some ( "newvalue" ) ) ;
171+ assert_eq ! ( env. get( "OVERWRITTEN" ) . as_deref( ) , Some ( "newvalue" ) ) ;
172+ assert_eq ! ( env. get( "NOT_AN_OBJECT" ) . as_deref( ) , Some ( "value" ) ) ;
129173}
0 commit comments