@@ -2,6 +2,7 @@ use std::collections::BTreeMap;
2
2
use std:: env;
3
3
use std:: fmt;
4
4
use std:: fs;
5
+ use std:: io:: { BufReader , BufRead , ErrorKind } ;
5
6
use std:: path:: { Path , PathBuf } ;
6
7
7
8
use git2:: Config as GitConfig ;
@@ -409,69 +410,117 @@ pub fn init(opts: &NewOptions, config: &Config) -> CargoResult<()> {
409
410
Ok ( ( ) )
410
411
}
411
412
412
- fn mk ( config : & Config , opts : & MkOptions < ' _ > ) -> CargoResult < ( ) > {
413
- let path = opts. path ;
414
- let name = opts. name ;
415
- let cfg = global_config ( config) ?;
416
- // Please ensure that ignore and hgignore are in sync.
417
- let ignore = [
418
- "/target\n " ,
419
- "**/*.rs.bk\n " ,
420
- if !opts. bin { "Cargo.lock\n " } else { "" } ,
421
- ]
422
- . concat ( ) ;
423
- // Mercurial glob ignores can't be rooted, so just sticking a 'syntax: glob' at the top of the
424
- // file will exclude too much. Instead, use regexp-based ignores. See 'hg help ignore' for
425
- // more.
426
- let hgignore = [
427
- "^target/\n " ,
428
- "glob:*.rs.bk\n " ,
429
- if !opts. bin { "glob:Cargo.lock\n " } else { "" } ,
430
- ]
431
- . concat ( ) ;
432
413
433
- let vcs = opts. version_control . unwrap_or_else ( || {
434
- let in_existing_vcs = existing_vcs_repo ( path. parent ( ) . unwrap_or ( path) , config. cwd ( ) ) ;
435
- match ( cfg. version_control , in_existing_vcs) {
436
- ( None , false ) => VersionControl :: Git ,
437
- ( Some ( opt) , false ) => opt,
438
- ( _, true ) => VersionControl :: NoVcs ,
414
+ /// IgnoreList
415
+ struct IgnoreList {
416
+ /// git like formatted entries
417
+ ignore : Vec < String > ,
418
+ /// mercurial formatted entries
419
+ hg_ignore : Vec < String > ,
420
+ }
421
+
422
+ impl IgnoreList {
423
+ /// constructor to build a new ignore file
424
+ fn new ( ) -> IgnoreList {
425
+ return IgnoreList {
426
+ ignore : Vec :: new ( ) ,
427
+ hg_ignore : Vec :: new ( ) ,
439
428
}
440
- } ) ;
429
+ }
430
+
431
+ /// add a new entry to the ignore list. Requires two arguments with the
432
+ /// entry in two different formats. One for "git style" entries and one for
433
+ /// "mercurial like" entries.
434
+ fn push ( & mut self , ignore : & str , hg_ignore : & str ) {
435
+ self . ignore . push ( ignore. to_string ( ) ) ;
436
+ self . hg_ignore . push ( hg_ignore. to_string ( ) ) ;
437
+ }
438
+
439
+ /// Return the correctly formatted content of the ignore file for the given
440
+ /// version control system as `String`.
441
+ fn format_new ( & self , vcs : VersionControl ) -> String {
442
+ match vcs {
443
+ VersionControl :: Hg => return self . hg_ignore . join ( "\n " ) ,
444
+ _ => return self . ignore . join ( "\n " ) ,
445
+ } ;
446
+ }
447
+
448
+ /// format_existing is used to format the IgnoreList when the ignore file
449
+ /// already exists. It reads the contents of the given `BufRead` and
450
+ /// checks if the contents of the ignore list are already existing in the
451
+ /// file.
452
+ fn format_existing < T : BufRead > ( & self , existing : T , vcs : VersionControl ) -> String {
453
+ // TODO: is unwrap safe?
454
+ let existing_items = existing. lines ( ) . collect :: < Result < Vec < _ > , _ > > ( ) . unwrap ( ) ;
455
+
456
+ let ignore_items = match vcs {
457
+ VersionControl :: Hg => & self . hg_ignore ,
458
+ _ => & self . ignore ,
459
+ } ;
460
+
461
+ let mut out = "\n \n #Added by cargo\n \
462
+ #\n \
463
+ #already existing elements are commented out\n ".
464
+ to_string ( ) ;
465
+
466
+ for item in ignore_items {
467
+ out. push ( '\n' ) ;
468
+ if existing_items. contains ( item) {
469
+ out. push ( '#' ) ;
470
+ }
471
+ out. push_str ( item)
472
+ }
473
+
474
+ out
475
+ }
476
+ }
477
+
478
+ /// write the ignore file to the given directory. If the ignore file for the
479
+ /// given vcs system already exists, its content is read and duplicate ignore
480
+ /// file entries are filtered out.
481
+ fn write_ignore_file ( base_path : & Path , list : & IgnoreList , vcs : VersionControl ) -> CargoResult < String > {
482
+ let fp_ignore = match vcs {
483
+ VersionControl :: Git => base_path. join ( ".gitignore" ) ,
484
+ VersionControl :: Hg => base_path. join ( ".hgignore" ) ,
485
+ VersionControl :: Pijul => base_path. join ( ".ignore" ) ,
486
+ VersionControl :: Fossil => return Ok ( "" . to_string ( ) ) ,
487
+ VersionControl :: NoVcs => return Ok ( "" . to_string ( ) ) ,
488
+ } ;
489
+
490
+ let ignore: String = match fs:: File :: open ( & fp_ignore) {
491
+ Err ( why) => {
492
+ match why. kind ( ) {
493
+ ErrorKind :: NotFound => list. format_new ( vcs) ,
494
+ _ => return Err ( failure:: format_err!( "{}" , why) ) ,
495
+ }
496
+ } ,
497
+ Ok ( file) => {
498
+ list. format_existing ( BufReader :: new ( file) , vcs)
499
+ } ,
500
+ } ;
441
501
502
+ paths:: append ( & fp_ignore, ignore. as_bytes ( ) ) ?;
503
+
504
+ return Ok ( ignore)
505
+ }
506
+
507
+ /// initialize the correct vcs system based on the provided config
508
+ fn init_vcs ( path : & Path , vcs : VersionControl , config : & Config ) -> CargoResult < ( ) > {
442
509
match vcs {
443
510
VersionControl :: Git => {
444
511
if !path. join ( ".git" ) . exists ( ) {
445
512
GitRepo :: init ( path, config. cwd ( ) ) ?;
446
513
}
447
- let ignore = if path. join ( ".gitignore" ) . exists ( ) {
448
- format ! ( "\n {}" , ignore)
449
- } else {
450
- ignore
451
- } ;
452
- paths:: append ( & path. join ( ".gitignore" ) , ignore. as_bytes ( ) ) ?;
453
514
}
454
515
VersionControl :: Hg => {
455
516
if !path. join ( ".hg" ) . exists ( ) {
456
517
HgRepo :: init ( path, config. cwd ( ) ) ?;
457
518
}
458
- let hgignore = if path. join ( ".hgignore" ) . exists ( ) {
459
- format ! ( "\n {}" , hgignore)
460
- } else {
461
- hgignore
462
- } ;
463
- paths:: append ( & path. join ( ".hgignore" ) , hgignore. as_bytes ( ) ) ?;
464
519
}
465
520
VersionControl :: Pijul => {
466
521
if !path. join ( ".pijul" ) . exists ( ) {
467
522
PijulRepo :: init ( path, config. cwd ( ) ) ?;
468
523
}
469
- let ignore = if path. join ( ".ignore" ) . exists ( ) {
470
- format ! ( "\n {}" , ignore)
471
- } else {
472
- ignore
473
- } ;
474
- paths:: append ( & path. join ( ".ignore" ) , ignore. as_bytes ( ) ) ?;
475
524
}
476
525
VersionControl :: Fossil => {
477
526
if path. join ( ".fossil" ) . exists ( ) {
@@ -483,6 +532,37 @@ fn mk(config: &Config, opts: &MkOptions<'_>) -> CargoResult<()> {
483
532
}
484
533
} ;
485
534
535
+ Ok ( ( ) )
536
+ }
537
+
538
+ fn mk ( config : & Config , opts : & MkOptions < ' _ > ) -> CargoResult < ( ) > {
539
+ let path = opts. path ;
540
+ let name = opts. name ;
541
+ let cfg = global_config ( config) ?;
542
+
543
+
544
+ // using the push method with two arguments ensures that the entries for
545
+ // both ignore and hgignore are in sync.
546
+ let mut ignore = IgnoreList :: new ( ) ;
547
+ ignore. push ( "/target" , "^target/" ) ;
548
+ ignore. push ( "**/*.rs.bk" , "glob:*.rs.bk\n " ) ;
549
+ if !opts. bin {
550
+ ignore. push ( "Cargo.lock" , "glob:Cargo.lock" ) ;
551
+ }
552
+
553
+ let vcs = opts. version_control . unwrap_or_else ( || {
554
+ let in_existing_vcs = existing_vcs_repo ( path. parent ( ) . unwrap_or ( path) , config. cwd ( ) ) ;
555
+ match ( cfg. version_control , in_existing_vcs) {
556
+ ( None , false ) => VersionControl :: Git ,
557
+ ( Some ( opt) , false ) => opt,
558
+ ( _, true ) => VersionControl :: NoVcs ,
559
+ }
560
+ } ) ;
561
+
562
+ init_vcs ( path, vcs, config) ?;
563
+ write_ignore_file ( path, & ignore, vcs) ?;
564
+
565
+
486
566
let ( author_name, email) = discover_author ( ) ?;
487
567
// Hoo boy, sure glad we've got exhaustiveness checking behind us.
488
568
let author = match ( cfg. name , cfg. email , author_name, email) {
0 commit comments