@@ -4,20 +4,44 @@ use std::path::Path;
44
55use crate :: errors:: * ;
66use crate :: file;
7+ use crate :: shell:: MessageInfo ;
8+ use once_cell:: sync:: OnceCell ;
79
810type Table = toml:: value:: Table ;
911type Value = toml:: value:: Value ;
1012
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+
1129// the strategy is to merge, with arrays merging together
1230// and the deeper the config file is, the higher its priority.
1331// arrays merge, numbers/strings get replaced, objects merge in.
1432// we don't want to make any assumptions about the cargo
1533// config data, in case we need to use it later.
1634#[ derive( Debug , Clone , Default ) ]
17- pub struct CargoConfig ( Table ) ;
35+ pub struct CargoToml ( Table ) ;
1836
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 < ( ) > {
2145 // can error on mismatched-data
2246
2347 fn validate_types ( x : & Value , y : & Value ) -> Option < ( ) > {
@@ -66,6 +90,8 @@ impl CargoConfig {
6690 }
6791
6892 // 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
6995 pub fn alias < ' a > ( & ' a self ) -> Option < BTreeMap < & ' a str , Vec < & ' a str > > > {
7096 let parse_alias = |v : & ' a Value | -> Option < Vec < & ' a str > > {
7197 if let Some ( s) = v. as_str ( ) {
@@ -86,35 +112,99 @@ impl CargoConfig {
86112 }
87113}
88114
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+ }
93183}
94184
95185// finding cargo config files actually runs from the
96186// current working directory the command is invoked,
97187// not from the project root. same is true with work
98188// spaces: the project layout does not matter.
99- pub fn read_config_files ( ) -> Result < CargoConfig > {
189+ pub fn read_config_files ( ) -> Result < CargoToml > {
100190 // note: cargo supports both `config` and `config.toml`
101191 // `config` exists for compatibility reasons, but if
102192 // 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 < ( ) > {
104194 let noext = dir. join ( "config" ) ;
105195 let ext = dir. join ( "config.toml" ) ;
106196 if noext. exists ( ) {
107- let parent = parse_config_file ( & noext) ?;
197+ let parent = CargoToml :: parse ( & noext) ?;
108198 result. merge ( & parent) ?;
109199 } else if ext. exists ( ) {
110- let parent = parse_config_file ( & ext) ?;
200+ let parent = CargoToml :: parse ( & ext) ?;
111201 result. merge ( & parent) ?;
112202 }
113203
114204 Ok ( ( ) )
115205 } ;
116206
117- let mut result = CargoConfig :: default ( ) ;
207+ let mut result = CargoToml :: default ( ) ;
118208 let cwd = env:: current_dir ( ) ?;
119209 let mut dir: & Path = & cwd;
120210 loop {
@@ -131,14 +221,40 @@ pub fn read_config_files() -> Result<CargoConfig> {
131221 Ok ( result)
132222}
133223
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+
134250#[ cfg( test) ]
135251mod tests {
136252 use super :: * ;
137253
138254 #[ test]
139255 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 ) ?) ;
142258 let alias1 = config1. alias ( ) . expect ( "unable to get aliases." ) ;
143259 let alias2 = config2. alias ( ) . expect ( "unable to get aliases." ) ;
144260 assert_eq ! (
@@ -184,9 +300,9 @@ mod tests {
184300 }
185301
186302 #[ test]
187- fn test_read_config ( ) -> Result < ( ) > {
303+ fn test_read_config ( ) {
188304 // cross contains a few aliases, so test those
189- let config = read_config_files ( ) ? ;
305+ let config = get_cargo_toml ( ) ;
190306 let aliases = config. alias ( ) . expect ( "must have aliases" ) ;
191307 assert_eq ! (
192308 aliases. get( "build-docker-image" ) ,
@@ -196,8 +312,6 @@ mod tests {
196312 aliases. get( "xtask" ) ,
197313 Some ( & vec![ "run" , "-p" , "xtask" , "--" ] ) ,
198314 ) ;
199-
200- Ok ( ( ) )
201315 }
202316
203317 const CARGO_TOML1 : & str = r#"
0 commit comments