@@ -4,15 +4,22 @@ use crate::util::config::value;
4
4
use crate :: util:: config:: { Config , ConfigError , ConfigKey } ;
5
5
use crate :: util:: config:: { ConfigValue as CV , Definition , Value } ;
6
6
use serde:: { de, de:: IntoDeserializer } ;
7
- use std:: collections:: HashSet ;
8
7
use std:: vec;
9
8
10
9
/// Serde deserializer used to convert config values to a target type using
11
10
/// `Config::get`.
12
11
#[ derive( Clone ) ]
13
- pub ( crate ) struct Deserializer < ' config > {
14
- pub ( crate ) config : & ' config Config ,
15
- pub ( crate ) key : ConfigKey ,
12
+ pub ( super ) struct Deserializer < ' config > {
13
+ pub ( super ) config : & ' config Config ,
14
+ /// The current key being deserialized.
15
+ pub ( super ) key : ConfigKey ,
16
+ /// Whether or not this key part is allowed to be an inner table. For
17
+ /// example, `profile.dev.build-override` needs to check if
18
+ /// CARGO_PROFILE_DEV_BUILD_OVERRIDE_ prefixes exist. But
19
+ /// CARGO_BUILD_TARGET should not check for prefixes because it would
20
+ /// collide with CARGO_BUILD_TARGET_DIR. See `ConfigMapAccess` for
21
+ /// details.
22
+ pub ( super ) env_prefix_ok : bool ,
16
23
}
17
24
18
25
macro_rules! deserialize_method {
@@ -109,7 +116,7 @@ impl<'de, 'config> de::Deserializer<'de> for Deserializer<'config> {
109
116
where
110
117
V : de:: Visitor < ' de > ,
111
118
{
112
- if self . config . has_key ( & self . key ) {
119
+ if self . config . has_key ( & self . key , self . env_prefix_ok ) {
113
120
visitor. visit_some ( self )
114
121
} else {
115
122
// Treat missing values as `None`.
@@ -179,8 +186,10 @@ impl<'de, 'config> de::Deserializer<'de> for Deserializer<'config> {
179
186
180
187
struct ConfigMapAccess < ' config > {
181
188
de : Deserializer < ' config > ,
182
- set_iter : <HashSet < KeyKind > as IntoIterator >:: IntoIter ,
183
- next : Option < KeyKind > ,
189
+ /// The fields that this map should deserialize.
190
+ fields : Vec < KeyKind > ,
191
+ /// Current field being deserialized.
192
+ field_index : usize ,
184
193
}
185
194
186
195
#[ derive( Debug , PartialEq , Eq , Hash ) ]
@@ -191,50 +200,53 @@ enum KeyKind {
191
200
192
201
impl < ' config > ConfigMapAccess < ' config > {
193
202
fn new_map ( de : Deserializer < ' config > ) -> Result < ConfigMapAccess < ' config > , ConfigError > {
194
- let mut set = HashSet :: new ( ) ;
203
+ let mut fields = Vec :: new ( ) ;
195
204
if let Some ( mut v) = de. config . get_table ( & de. key ) ? {
196
205
// `v: Value<HashMap<String, CV>>`
197
206
for ( key, _value) in v. val . drain ( ) {
198
- set . insert ( KeyKind :: CaseSensitive ( key) ) ;
207
+ fields . push ( KeyKind :: CaseSensitive ( key) ) ;
199
208
}
200
209
}
201
210
if de. config . cli_unstable ( ) . advanced_env {
202
211
// `CARGO_PROFILE_DEV_PACKAGE_`
203
- let env_pattern = format ! ( "{}_" , de. key. as_env_key( ) ) ;
212
+ let env_prefix = format ! ( "{}_" , de. key. as_env_key( ) ) ;
204
213
for env_key in de. config . env . keys ( ) {
205
- if env_key. starts_with ( & env_pattern ) {
214
+ if env_key. starts_with ( & env_prefix ) {
206
215
// `CARGO_PROFILE_DEV_PACKAGE_bar_OPT_LEVEL = 3`
207
- let rest = & env_key[ env_pattern . len ( ) ..] ;
216
+ let rest = & env_key[ env_prefix . len ( ) ..] ;
208
217
// `rest = bar_OPT_LEVEL`
209
218
let part = rest. splitn ( 2 , '_' ) . next ( ) . unwrap ( ) ;
210
219
// `part = "bar"`
211
- set . insert ( KeyKind :: CaseSensitive ( part. to_string ( ) ) ) ;
220
+ fields . push ( KeyKind :: CaseSensitive ( part. to_string ( ) ) ) ;
212
221
}
213
222
}
214
223
}
215
224
Ok ( ConfigMapAccess {
216
225
de,
217
- set_iter : set . into_iter ( ) ,
218
- next : None ,
226
+ fields ,
227
+ field_index : 0 ,
219
228
} )
220
229
}
221
230
222
231
fn new_struct (
223
232
de : Deserializer < ' config > ,
224
233
fields : & ' static [ & ' static str ] ,
225
234
) -> Result < ConfigMapAccess < ' config > , ConfigError > {
226
- let mut set = HashSet :: new ( ) ;
227
- for field in fields {
228
- set . insert ( KeyKind :: Normal ( field. to_string ( ) ) ) ;
229
- }
235
+ let fields : Vec < KeyKind > = fields
236
+ . iter ( )
237
+ . map ( |field| KeyKind :: Normal ( field. to_string ( ) ) )
238
+ . collect ( ) ;
230
239
231
240
// Assume that if we're deserializing a struct it exhaustively lists all
232
241
// possible fields on this key that we're *supposed* to use, so take
233
242
// this opportunity to warn about any keys that aren't recognized as
234
243
// fields and warn about them.
235
244
if let Some ( mut v) = de. config . get_table ( & de. key ) ? {
236
245
for ( t_key, value) in v. val . drain ( ) {
237
- if set. contains ( & KeyKind :: Normal ( t_key. to_string ( ) ) ) {
246
+ if fields. iter ( ) . any ( |k| match k {
247
+ KeyKind :: Normal ( s) => s == & t_key,
248
+ KeyKind :: CaseSensitive ( s) => s == & t_key,
249
+ } ) {
238
250
continue ;
239
251
}
240
252
de. config . shell ( ) . warn ( format ! (
@@ -248,8 +260,8 @@ impl<'config> ConfigMapAccess<'config> {
248
260
249
261
Ok ( ConfigMapAccess {
250
262
de,
251
- set_iter : set . into_iter ( ) ,
252
- next : None ,
263
+ fields ,
264
+ field_index : 0 ,
253
265
} )
254
266
}
255
267
}
@@ -261,30 +273,61 @@ impl<'de, 'config> de::MapAccess<'de> for ConfigMapAccess<'config> {
261
273
where
262
274
K : de:: DeserializeSeed < ' de > ,
263
275
{
264
- match self . set_iter . next ( ) {
265
- Some ( key) => {
266
- let name = match & key {
267
- KeyKind :: Normal ( s) | KeyKind :: CaseSensitive ( s) => s. as_str ( ) ,
268
- } ;
269
- let result = seed. deserialize ( name. into_deserializer ( ) ) . map ( Some ) ;
270
- self . next = Some ( key) ;
271
- result
272
- }
273
- None => Ok ( None ) ,
276
+ if self . field_index >= self . fields . len ( ) {
277
+ return Ok ( None ) ;
274
278
}
279
+ let field = match & self . fields [ self . field_index ] {
280
+ KeyKind :: Normal ( s) | KeyKind :: CaseSensitive ( s) => s. as_str ( ) ,
281
+ } ;
282
+ seed. deserialize ( field. into_deserializer ( ) ) . map ( Some )
275
283
}
276
284
277
285
fn next_value_seed < V > ( & mut self , seed : V ) -> Result < V :: Value , Self :: Error >
278
286
where
279
287
V : de:: DeserializeSeed < ' de > ,
280
288
{
281
- match self . next . take ( ) . expect ( "next field missing" ) {
282
- KeyKind :: Normal ( key) => self . de . key . push ( & key) ,
283
- KeyKind :: CaseSensitive ( key) => self . de . key . push_sensitive ( & key) ,
284
- }
289
+ let field = & self . fields [ self . field_index ] ;
290
+ self . field_index += 1 ;
291
+ // Set this as the current key in the deserializer.
292
+ let field = match field {
293
+ KeyKind :: Normal ( field) => {
294
+ self . de . key . push ( & field) ;
295
+ field
296
+ }
297
+ KeyKind :: CaseSensitive ( field) => {
298
+ self . de . key . push_sensitive ( & field) ;
299
+ field
300
+ }
301
+ } ;
302
+ // Env vars that are a prefix of another with a dash/underscore cannot
303
+ // be supported by our serde implementation, so check for them here.
304
+ // Example:
305
+ // CARGO_BUILD_TARGET
306
+ // CARGO_BUILD_TARGET_DIR
307
+ // or
308
+ // CARGO_PROFILE_DEV_DEBUG
309
+ // CARGO_PROFILE_DEV_DEBUG_ASSERTIONS
310
+ // The `deserialize_option` method does not know the type of the field.
311
+ // If the type is an Option<struct> (like
312
+ // `profile.dev.build-override`), then it needs to check for env vars
313
+ // starting with CARGO_FOO_BAR_. This is a problem for keys like
314
+ // CARGO_BUILD_TARGET because checking for a prefix would incorrectly
315
+ // match CARGO_BUILD_TARGET_DIR. `deserialize_option` would have no
316
+ // choice but to call `visit_some()` which would then fail if
317
+ // CARGO_BUILD_TARGET isn't set. So we check for these prefixes and
318
+ // disallow them here.
319
+ let env_prefix = format ! ( "{}_" , field) . replace ( '-' , "_" ) ;
320
+ let env_prefix_ok = !self . fields . iter ( ) . any ( |field| {
321
+ let field = match field {
322
+ KeyKind :: Normal ( s) | KeyKind :: CaseSensitive ( s) => s. as_str ( ) ,
323
+ } ;
324
+ field. replace ( '-' , "_" ) . starts_with ( & env_prefix)
325
+ } ) ;
326
+
285
327
let result = seed. deserialize ( Deserializer {
286
328
config : self . de . config ,
287
329
key : self . de . key . clone ( ) ,
330
+ env_prefix_ok,
288
331
} ) ;
289
332
self . de . key . pop ( ) ;
290
333
result
0 commit comments