@@ -33,7 +33,6 @@ mod types;
33
33
use std:: collections:: { BTreeMap , HashMap , HashSet } ;
34
34
use std:: fmt:: Debug ;
35
35
use std:: hash:: Hash ;
36
- use std:: ops:: Deref ;
37
36
use std:: os:: unix:: ffi:: OsStrExt ;
38
37
use std:: path;
39
38
use std:: path:: Path ;
@@ -56,7 +55,7 @@ pub use types::OptionType;
56
55
// We only use this for parsing values in dicts, as in other cases we know that the type must
57
56
// be some scalar or string, or a uniform list of one type of scalar or string, so we can
58
57
// parse as such.
59
- #[ derive( Debug , PartialEq ) ]
58
+ #[ derive( Clone , Debug , PartialEq ) ]
60
59
pub enum Val {
61
60
Bool ( bool ) ,
62
61
Int ( i64 ) ,
@@ -67,26 +66,26 @@ pub enum Val {
67
66
}
68
67
69
68
#[ derive( Copy , Clone , Debug , Eq , PartialEq ) ]
70
- pub ( crate ) enum ListEditAction {
69
+ pub enum ListEditAction {
71
70
Replace ,
72
71
Add ,
73
72
Remove ,
74
73
}
75
74
76
- #[ derive( Debug , Eq , PartialEq ) ]
77
- pub ( crate ) struct ListEdit < T > {
75
+ #[ derive( Clone , Debug , Eq , PartialEq ) ]
76
+ pub struct ListEdit < T > {
78
77
pub action : ListEditAction ,
79
78
pub items : Vec < T > ,
80
79
}
81
80
82
81
#[ derive( Copy , Clone , Debug , Eq , PartialEq ) ]
83
- pub ( crate ) enum DictEditAction {
82
+ pub enum DictEditAction {
84
83
Replace ,
85
84
Add ,
86
85
}
87
86
88
- #[ derive( Debug , PartialEq ) ]
89
- pub ( crate ) struct DictEdit {
87
+ #[ derive( Clone , Debug , PartialEq ) ]
88
+ pub struct DictEdit {
90
89
pub action : DictEditAction ,
91
90
pub items : HashMap < String , Val > ,
92
91
}
@@ -200,20 +199,28 @@ pub enum Source {
200
199
201
200
#[ derive( Debug ) ]
202
201
pub struct OptionValue < T > {
202
+ pub derivation : Option < Vec < ( Source , T ) > > ,
203
+ // Scalar options are always set from a single source, so we provide that
204
+ // here, as it can be useful in user-facing messages.
203
205
pub source : Source ,
204
206
pub value : T ,
205
207
}
206
208
207
- impl < T > Deref for OptionValue < T > {
208
- type Target = T ;
209
+ #[ derive( Debug ) ]
210
+ pub struct ListOptionValue < T > {
211
+ pub derivation : Option < Vec < ( Source , Vec < ListEdit < T > > ) > > ,
212
+ pub value : Vec < T > ,
213
+ }
209
214
210
- fn deref ( & self ) -> & Self :: Target {
211
- & self . value
212
- }
215
+ #[ derive( Debug ) ]
216
+ pub struct DictOptionValue {
217
+ pub derivation : Option < Vec < ( Source , Vec < DictEdit > ) > > ,
218
+ pub value : HashMap < String , Val > ,
213
219
}
214
220
215
221
pub struct OptionParser {
216
222
sources : BTreeMap < Source , Rc < dyn OptionsSource > > ,
223
+ include_derivation : bool ,
217
224
}
218
225
219
226
impl OptionParser {
@@ -224,6 +231,7 @@ impl OptionParser {
224
231
env : Env ,
225
232
config_paths : Option < Vec < & str > > ,
226
233
allow_pantsrc : bool ,
234
+ include_derivation : bool ,
227
235
) -> Result < OptionParser , String > {
228
236
let buildroot = BuildRoot :: find ( ) ?;
229
237
let buildroot_string = String :: from_utf8 ( buildroot. as_os_str ( ) . as_bytes ( ) . to_vec ( ) )
@@ -246,6 +254,7 @@ impl OptionParser {
246
254
sources. insert ( Source :: Flag , Rc :: new ( args) ) ;
247
255
let mut parser = OptionParser {
248
256
sources : sources. clone ( ) ,
257
+ include_derivation : false ,
249
258
} ;
250
259
251
260
fn path_join ( a : & str , b : & str ) -> String {
@@ -256,10 +265,12 @@ impl OptionParser {
256
265
Some ( paths) => paths. iter ( ) . map ( |s| s. to_string ( ) ) . collect ( ) ,
257
266
None => {
258
267
let default_config_path = path_join ( & buildroot_string, "pants.toml" ) ;
259
- parser. parse_string_list (
260
- & option_id ! ( "pants" , "config" , "files" ) ,
261
- & [ & default_config_path] ,
262
- ) ?
268
+ parser
269
+ . parse_string_list (
270
+ & option_id ! ( "pants" , "config" , "files" ) ,
271
+ & [ & default_config_path] ,
272
+ ) ?
273
+ . value
263
274
}
264
275
} ;
265
276
@@ -285,17 +296,21 @@ impl OptionParser {
285
296
sources. insert ( Source :: Config , Rc :: new ( config. clone ( ) ) ) ;
286
297
parser = OptionParser {
287
298
sources : sources. clone ( ) ,
299
+ include_derivation : false ,
288
300
} ;
289
301
290
- if allow_pantsrc && * parser. parse_bool ( & option_id ! ( "pantsrc" ) , true ) ? {
291
- for rcfile in parser. parse_string_list (
292
- & option_id ! ( "pantsrc" , "files" ) ,
293
- & [
294
- "/etc/pantsrc" ,
295
- shellexpand:: tilde ( "~/.pants.rc" ) . as_ref ( ) ,
296
- ".pants.rc" ,
297
- ] ,
298
- ) ? {
302
+ if allow_pantsrc && parser. parse_bool ( & option_id ! ( "pantsrc" ) , true ) ?. value {
303
+ for rcfile in parser
304
+ . parse_string_list (
305
+ & option_id ! ( "pantsrc" , "files" ) ,
306
+ & [
307
+ "/etc/pantsrc" ,
308
+ shellexpand:: tilde ( "~/.pants.rc" ) . as_ref ( ) ,
309
+ ".pants.rc" ,
310
+ ] ,
311
+ ) ?
312
+ . value
313
+ {
299
314
let rcfile_path = Path :: new ( & rcfile) ;
300
315
if rcfile_path. exists ( ) {
301
316
let rc_config = Config :: parse ( & [ rcfile_path] , & seed_values) ?;
@@ -304,7 +319,10 @@ impl OptionParser {
304
319
}
305
320
}
306
321
sources. insert ( Source :: Config , Rc :: new ( config) ) ;
307
- Ok ( OptionParser { sources } )
322
+ Ok ( OptionParser {
323
+ sources,
324
+ include_derivation,
325
+ } )
308
326
}
309
327
310
328
#[ allow( clippy:: type_complexity) ]
@@ -314,15 +332,27 @@ impl OptionParser {
314
332
default : & T ,
315
333
getter : fn ( & Rc < dyn OptionsSource > , & OptionId ) -> Result < Option < T :: Owned > , String > ,
316
334
) -> Result < OptionValue < T :: Owned > , String > {
335
+ let mut derivation = None ;
336
+ if self . include_derivation {
337
+ let mut derivations = vec ! [ ( Source :: Default , default . to_owned( ) ) ] ;
338
+ for ( source_type, source) in self . sources . iter ( ) . rev ( ) {
339
+ if let Some ( val) = getter ( source, id) ? {
340
+ derivations. push ( ( * source_type, val) ) ;
341
+ }
342
+ }
343
+ derivation = Some ( derivations) ;
344
+ }
317
345
for ( source_type, source) in self . sources . iter ( ) {
318
346
if let Some ( value) = getter ( source, id) ? {
319
347
return Ok ( OptionValue {
348
+ derivation,
320
349
source : * source_type,
321
350
value,
322
351
} ) ;
323
352
}
324
353
}
325
354
Ok ( OptionValue {
355
+ derivation,
326
356
source : Source :: Default ,
327
357
value : default. to_owned ( ) ,
328
358
} )
@@ -349,14 +379,32 @@ impl OptionParser {
349
379
}
350
380
351
381
#[ allow( clippy:: type_complexity) ]
352
- fn parse_list < T > (
382
+ fn parse_list < T : Clone > (
353
383
& self ,
354
384
id : & OptionId ,
355
385
default : Vec < T > ,
356
386
getter : fn ( & Rc < dyn OptionsSource > , & OptionId ) -> Result < Option < Vec < ListEdit < T > > > , String > ,
357
387
remover : fn ( & mut Vec < T > , & Vec < T > ) ,
358
- ) -> Result < Vec < T > , String > {
388
+ ) -> Result < ListOptionValue < T > , String > {
359
389
let mut list = default;
390
+ let mut derivation = None ;
391
+ if self . include_derivation {
392
+ let mut derivations = vec ! [ (
393
+ Source :: Default ,
394
+ vec![ ListEdit {
395
+ action: ListEditAction :: Replace ,
396
+ items: list. clone( ) ,
397
+ } ] ,
398
+ ) ] ;
399
+ for ( source_type, source) in self . sources . iter ( ) . rev ( ) {
400
+ if let Some ( list_edits) = getter ( source, id) ? {
401
+ if !list_edits. is_empty ( ) {
402
+ derivations. push ( ( * source_type, list_edits) ) ;
403
+ }
404
+ }
405
+ }
406
+ derivation = Some ( derivations) ;
407
+ }
360
408
for ( _source_type, source) in self . sources . iter ( ) . rev ( ) {
361
409
if let Some ( list_edits) = getter ( source, id) ? {
362
410
for list_edit in list_edits {
@@ -368,7 +416,10 @@ impl OptionParser {
368
416
}
369
417
}
370
418
}
371
- Ok ( list)
419
+ Ok ( ListOptionValue {
420
+ derivation,
421
+ value : list,
422
+ } )
372
423
}
373
424
374
425
// For Eq+Hash types we can use a HashSet when computing removals, which will be avg O(N+M).
@@ -378,28 +429,40 @@ impl OptionParser {
378
429
// However this is still more than fast enough, and inoculates us against a very unlikely
379
430
// pathological case of a very large removal set.
380
431
#[ allow( clippy:: type_complexity) ]
381
- fn parse_list_hashable < T : Eq + Hash > (
432
+ fn parse_list_hashable < T : Clone + Eq + Hash > (
382
433
& self ,
383
434
id : & OptionId ,
384
435
default : Vec < T > ,
385
436
getter : fn ( & Rc < dyn OptionsSource > , & OptionId ) -> Result < Option < Vec < ListEdit < T > > > , String > ,
386
- ) -> Result < Vec < T > , String > {
437
+ ) -> Result < ListOptionValue < T > , String > {
387
438
self . parse_list ( id, default, getter, |list, remove| {
388
439
let to_remove = remove. iter ( ) . collect :: < HashSet < _ > > ( ) ;
389
440
list. retain ( |item| !to_remove. contains ( item) ) ;
390
441
} )
391
442
}
392
443
393
- pub fn parse_bool_list ( & self , id : & OptionId , default : & [ bool ] ) -> Result < Vec < bool > , String > {
444
+ pub fn parse_bool_list (
445
+ & self ,
446
+ id : & OptionId ,
447
+ default : & [ bool ] ,
448
+ ) -> Result < ListOptionValue < bool > , String > {
394
449
self . parse_list_hashable ( id, default. to_vec ( ) , |source, id| source. get_bool_list ( id) )
395
450
}
396
451
397
- pub fn parse_int_list ( & self , id : & OptionId , default : & [ i64 ] ) -> Result < Vec < i64 > , String > {
452
+ pub fn parse_int_list (
453
+ & self ,
454
+ id : & OptionId ,
455
+ default : & [ i64 ] ,
456
+ ) -> Result < ListOptionValue < i64 > , String > {
398
457
self . parse_list_hashable ( id, default. to_vec ( ) , |source, id| source. get_int_list ( id) )
399
458
}
400
459
401
460
// Floats are not Eq or Hash, so we fall back to the brute-force O(N*M) lookups.
402
- pub fn parse_float_list ( & self , id : & OptionId , default : & [ f64 ] ) -> Result < Vec < f64 > , String > {
461
+ pub fn parse_float_list (
462
+ & self ,
463
+ id : & OptionId ,
464
+ default : & [ f64 ] ,
465
+ ) -> Result < ListOptionValue < f64 > , String > {
403
466
self . parse_list (
404
467
id,
405
468
default. to_vec ( ) ,
@@ -414,7 +477,7 @@ impl OptionParser {
414
477
& self ,
415
478
id : & OptionId ,
416
479
default : & [ & str ] ,
417
- ) -> Result < Vec < String > , String > {
480
+ ) -> Result < ListOptionValue < String > , String > {
418
481
self . parse_list_hashable :: < String > (
419
482
id,
420
483
default. iter ( ) . map ( |s| s. to_string ( ) ) . collect ( ) ,
@@ -426,8 +489,24 @@ impl OptionParser {
426
489
& self ,
427
490
id : & OptionId ,
428
491
default : HashMap < String , Val > ,
429
- ) -> Result < HashMap < String , Val > , String > {
492
+ ) -> Result < DictOptionValue , String > {
430
493
let mut dict = default;
494
+ let mut derivation = None ;
495
+ if self . include_derivation {
496
+ let mut derivations = vec ! [ (
497
+ Source :: Default ,
498
+ vec![ DictEdit {
499
+ action: DictEditAction :: Replace ,
500
+ items: dict. clone( ) ,
501
+ } ] ,
502
+ ) ] ;
503
+ for ( source_type, source) in self . sources . iter ( ) . rev ( ) {
504
+ if let Some ( dict_edits) = source. get_dict ( id) ? {
505
+ derivations. push ( ( * source_type, dict_edits) ) ;
506
+ }
507
+ }
508
+ derivation = Some ( derivations) ;
509
+ }
431
510
for ( _, source) in self . sources . iter ( ) . rev ( ) {
432
511
if let Some ( dict_edits) = source. get_dict ( id) ? {
433
512
for dict_edit in dict_edits {
@@ -438,7 +517,10 @@ impl OptionParser {
438
517
}
439
518
}
440
519
}
441
- Ok ( dict)
520
+ Ok ( DictOptionValue {
521
+ derivation,
522
+ value : dict,
523
+ } )
442
524
}
443
525
}
444
526
0 commit comments