@@ -4,20 +4,44 @@ use std::path::Path;
4
4
5
5
use crate :: errors:: * ;
6
6
use crate :: file;
7
+ use crate :: shell:: MessageInfo ;
8
+ use once_cell:: sync:: OnceCell ;
7
9
8
10
type Table = toml:: value:: Table ;
9
11
type Value = toml:: value:: Value ;
10
12
13
+ pub const CARGO_NO_PREFIX_ENVVARS : & [ & str ] = & [
14
+ "http_proxy" ,
15
+ "TERM" ,
16
+ "RUSTDOCFLAGS" ,
17
+ "RUSTFLAGS" ,
18
+ "BROWSER" ,
19
+ "HTTPS_PROXY" ,
20
+ "HTTP_TIMEOUT" ,
21
+ "https_proxy" ,
22
+ ] ;
23
+
24
+ // TODO(ahuszagh) This should really be cargo_toml and cargo_env.
25
+ // Should have it as a once_cell?
26
+
27
+ // CARGO_ALIAS_
28
+
11
29
// the strategy is to merge, with arrays merging together
12
30
// and the deeper the config file is, the higher its priority.
13
31
// arrays merge, numbers/strings get replaced, objects merge in.
14
32
// we don't want to make any assumptions about the cargo
15
33
// config data, in case we need to use it later.
16
34
#[ derive( Debug , Clone , Default ) ]
17
- pub struct CargoConfig ( Table ) ;
35
+ pub struct CargoToml ( Table ) ;
18
36
19
- impl CargoConfig {
20
- fn merge ( & mut self , parent : & CargoConfig ) -> Result < ( ) > {
37
+ impl CargoToml {
38
+ fn parse ( path : & Path ) -> Result < CargoToml > {
39
+ let contents = file:: read ( & path)
40
+ . wrap_err_with ( || format ! ( "could not read cargo config file at `{path:?}`" ) ) ?;
41
+ Ok ( CargoToml ( toml:: from_str ( & contents) ?) )
42
+ }
43
+
44
+ fn merge ( & mut self , parent : & CargoToml ) -> Result < ( ) > {
21
45
// can error on mismatched-data
22
46
23
47
fn validate_types ( x : & Value , y : & Value ) -> Option < ( ) > {
@@ -66,6 +90,8 @@ impl CargoConfig {
66
90
}
67
91
68
92
// get all the aliases from the map
93
+ // TODO(ahuszagh) This needs to be an result of an option.
94
+ // If we don't have as_str, needs to fail as a result
69
95
pub fn alias < ' a > ( & ' a self ) -> Option < BTreeMap < & ' a str , Vec < & ' a str > > > {
70
96
let parse_alias = |v : & ' a Value | -> Option < Vec < & ' a str > > {
71
97
if let Some ( s) = v. as_str ( ) {
@@ -86,35 +112,99 @@ impl CargoConfig {
86
112
}
87
113
}
88
114
89
- fn parse_config_file ( path : & Path ) -> Result < CargoConfig > {
90
- let contents = file:: read ( & path)
91
- . wrap_err_with ( || format ! ( "could not read cargo config file `{path:?}`" ) ) ?;
92
- Ok ( CargoConfig ( toml:: from_str ( & contents) ?) )
115
+ // wrapper for all the cargo environment variables.
116
+ #[ derive( Debug , Clone , Default ) ]
117
+ pub struct CargoEnv ( BTreeMap < String , String > ) ;
118
+
119
+ impl CargoEnv {
120
+ fn create ( ) -> CargoEnv {
121
+ let mut result = BTreeMap :: new ( ) ;
122
+ for ( key, value) in env:: vars ( ) {
123
+ if key. starts_with ( "CARGO_" ) || CARGO_NO_PREFIX_ENVVARS . contains ( & key. as_str ( ) ) {
124
+ result. insert ( key, value) ;
125
+ }
126
+ }
127
+
128
+ CargoEnv ( result)
129
+ }
130
+
131
+ // get all the aliases from the map
132
+ pub fn alias < ' a > ( & ' a self ) -> Result < BTreeMap < String , Vec < & ' a str > > > {
133
+ let mut result = BTreeMap :: new ( ) ;
134
+ for ( key, value) in & self . 0 {
135
+ if let Some ( alias) = key. strip_prefix ( "CARGO_ALIAS_" ) {
136
+ // cargo skips empty aliases
137
+ match alias. chars ( ) . all ( char:: is_uppercase) {
138
+ true if !alias. is_empty ( ) => {
139
+ let value = value. split_whitespace ( ) . collect ( ) ;
140
+ result. insert ( key. to_lowercase ( ) , value) ;
141
+ }
142
+ false => {
143
+ MessageInfo :: default ( ) . warn ( format_args ! (
144
+ "Environment variables are expected to use uppercase letters and underscores, the variable `{key}` will be ignored and have no effect"
145
+ ) ) ?;
146
+ }
147
+ // `CARGO_ALIAS_` is ignored
148
+ _ => ( ) ,
149
+ }
150
+ }
151
+ }
152
+
153
+ Ok ( result)
154
+ }
155
+ }
156
+
157
+ // merged result of cargo TOML config and environment variables
158
+ // we use a toml setting here, since it abstracts the types
159
+ #[ derive( Debug , Clone , Default ) ]
160
+ pub struct CargoConfig ( BTreeMap < String , Value > ) ;
161
+
162
+ impl CargoConfig {
163
+ fn create ( config_toml : & CargoToml , config_env : & CargoEnv ) -> Result < CargoConfig > {
164
+ let mut result = BTreeMap :: new ( ) ;
165
+ let toml_aliases = config_toml. alias ( ) ; // TODO(ahuszagh) Must be used
166
+ let env_aliases = config_env. alias ( ) ?;
167
+ // TODO(ahuszagh) Here...
168
+ // We only care about values we actually know.
169
+
170
+
171
+ // for (key, value) in env::vars() {
172
+ // if key.starts_with("CARGO_") || CARGO_NO_PREFIX_ENVVARS.contains(&key.as_str()) {
173
+ // result.insert(key, value);
174
+ // }
175
+ // }
176
+
177
+ Ok ( CargoConfig ( result) )
178
+ }
179
+
180
+ fn alias ( & self ) -> BTreeMap < String , Vec < String > > {
181
+ todo ! ( ) ;
182
+ }
93
183
}
94
184
95
185
// finding cargo config files actually runs from the
96
186
// current working directory the command is invoked,
97
187
// not from the project root. same is true with work
98
188
// spaces: the project layout does not matter.
99
- pub fn read_config_files ( ) -> Result < CargoConfig > {
189
+ pub fn read_config_files ( ) -> Result < CargoToml > {
100
190
// note: cargo supports both `config` and `config.toml`
101
191
// `config` exists for compatibility reasons, but if
102
192
// present, only it will be read.
103
- let read_and_merge = |result : & mut CargoConfig , dir : & Path | -> Result < ( ) > {
193
+ let read_and_merge = |result : & mut CargoToml , dir : & Path | -> Result < ( ) > {
104
194
let noext = dir. join ( "config" ) ;
105
195
let ext = dir. join ( "config.toml" ) ;
106
196
if noext. exists ( ) {
107
- let parent = parse_config_file ( & noext) ?;
197
+ let parent = CargoToml :: parse ( & noext) ?;
108
198
result. merge ( & parent) ?;
109
199
} else if ext. exists ( ) {
110
- let parent = parse_config_file ( & ext) ?;
200
+ let parent = CargoToml :: parse ( & ext) ?;
111
201
result. merge ( & parent) ?;
112
202
}
113
203
114
204
Ok ( ( ) )
115
205
} ;
116
206
117
- let mut result = CargoConfig :: default ( ) ;
207
+ let mut result = CargoToml :: default ( ) ;
118
208
let cwd = env:: current_dir ( ) ?;
119
209
let mut dir: & Path = & cwd;
120
210
loop {
@@ -131,14 +221,40 @@ pub fn read_config_files() -> Result<CargoConfig> {
131
221
Ok ( result)
132
222
}
133
223
224
+ static CARGO_TOML : OnceCell < CargoToml > = OnceCell :: new ( ) ;
225
+
226
+ pub fn get_cargo_toml ( ) -> & ' static CargoToml {
227
+ CARGO_TOML
228
+ . get_or_try_init :: < _ , eyre:: Report > ( || {
229
+ read_config_files ( )
230
+ } )
231
+ . unwrap ( )
232
+ }
233
+
234
+ static CARGO_ENV : OnceCell < CargoEnv > = OnceCell :: new ( ) ;
235
+
236
+ pub fn get_cargo_env ( ) -> & ' static CargoEnv {
237
+ CARGO_ENV . get_or_init ( || CargoEnv :: create ( ) )
238
+ }
239
+
240
+ static CARGO_CONFIG : OnceCell < CargoConfig > = OnceCell :: new ( ) ;
241
+
242
+ pub fn get_cargo_config ( ) -> & ' static CargoConfig {
243
+ CARGO_CONFIG
244
+ . get_or_try_init :: < _ , eyre:: Report > ( || {
245
+ CargoConfig :: create ( get_cargo_toml ( ) , get_cargo_env ( ) )
246
+ } )
247
+ . unwrap ( )
248
+ }
249
+
134
250
#[ cfg( test) ]
135
251
mod tests {
136
252
use super :: * ;
137
253
138
254
#[ test]
139
255
fn test_parse ( ) -> Result < ( ) > {
140
- let config1 = CargoConfig ( toml:: from_str ( CARGO_TOML1 ) ?) ;
141
- let config2 = CargoConfig ( toml:: from_str ( CARGO_TOML2 ) ?) ;
256
+ let config1 = CargoToml ( toml:: from_str ( CARGO_TOML1 ) ?) ;
257
+ let config2 = CargoToml ( toml:: from_str ( CARGO_TOML2 ) ?) ;
142
258
let alias1 = config1. alias ( ) . expect ( "unable to get aliases." ) ;
143
259
let alias2 = config2. alias ( ) . expect ( "unable to get aliases." ) ;
144
260
assert_eq ! (
@@ -184,9 +300,9 @@ mod tests {
184
300
}
185
301
186
302
#[ test]
187
- fn test_read_config ( ) -> Result < ( ) > {
303
+ fn test_read_config ( ) {
188
304
// cross contains a few aliases, so test those
189
- let config = read_config_files ( ) ? ;
305
+ let config = get_cargo_toml ( ) ;
190
306
let aliases = config. alias ( ) . expect ( "must have aliases" ) ;
191
307
assert_eq ! (
192
308
aliases. get( "build-docker-image" ) ,
@@ -196,8 +312,6 @@ mod tests {
196
312
aliases. get( "xtask" ) ,
197
313
Some ( & vec![ "run" , "-p" , "xtask" , "--" ] ) ,
198
314
) ;
199
-
200
- Ok ( ( ) )
201
315
}
202
316
203
317
const CARGO_TOML1 : & str = r#"
0 commit comments