10
10
11
11
#![ feature( rustc_private) ]
12
12
13
+ #[ macro_use]
14
+ extern crate lazy_static;
13
15
#[ macro_use]
14
16
extern crate log;
15
17
extern crate regex;
@@ -19,7 +21,7 @@ extern crate term;
19
21
use std:: collections:: HashMap ;
20
22
use std:: fs;
21
23
use std:: io:: { self , BufRead , BufReader , Read } ;
22
- use std:: iter:: Peekable ;
24
+ use std:: iter:: { Enumerate , Peekable } ;
23
25
use std:: path:: { Path , PathBuf } ;
24
26
use std:: str:: Chars ;
25
27
@@ -504,18 +506,191 @@ fn string_eq_ignore_newline_repr_test() {
504
506
assert ! ( !string_eq_ignore_newline_repr( "a\r \n bcd" , "a\n bcdefghijk" ) ) ;
505
507
}
506
508
507
- struct ConfigPair {
508
- name : String ,
509
- value : String ,
509
+ enum ConfigurationSection {
510
+ CodeBlock ( ( String , u32 ) ) ,
511
+ ConfigName ( String ) ,
512
+ ConfigValue ( String ) ,
510
513
}
511
514
512
- struct CodeBlock {
513
- config : ConfigPair ,
514
- code_block : String ,
515
- code_block_start_line : u32 ,
515
+ impl ConfigurationSection {
516
+ fn get_section < I : Iterator < Item = String > > (
517
+ file : & mut Enumerate < I > ,
518
+ ) -> Option < ConfigurationSection > {
519
+ lazy_static ! {
520
+ static ref CONFIG_NAME_REGEX : regex:: Regex = regex:: Regex :: new( r"^## `([^`]+)`" ) . expect( "Failed creating configuration pattern" ) ;
521
+ static ref CONFIG_VALUE_REGEX : regex:: Regex = regex:: Regex :: new( r#"^#### `"?([^`"]+)"?`"# ) . expect( "Failed creating configuration value pattern" ) ;
522
+ }
523
+
524
+ loop {
525
+ match file. next ( ) {
526
+ Some ( ( i, line) ) => {
527
+ if line. starts_with ( "```rust" ) {
528
+ // Get the lines of the code block.
529
+ let lines: Vec < String > = file. map ( |( _i, l) | l)
530
+ . take_while ( |l| !l. starts_with ( "```" ) )
531
+ . collect ( ) ;
532
+ let block = format ! ( "{}\n " , lines. join( "\n " ) ) ;
533
+
534
+ // +1 to translate to one-based indexing
535
+ // +1 to get to first line of code (line after "```")
536
+ let start_line = ( i + 2 ) as u32 ;
537
+
538
+ return Some ( ConfigurationSection :: CodeBlock ( ( block, start_line) ) ) ;
539
+ } else if let Some ( c) = CONFIG_NAME_REGEX . captures ( & line) {
540
+ return Some ( ConfigurationSection :: ConfigName ( String :: from ( & c[ 1 ] ) ) ) ;
541
+ } else if let Some ( c) = CONFIG_VALUE_REGEX . captures ( & line) {
542
+ return Some ( ConfigurationSection :: ConfigValue ( String :: from ( & c[ 1 ] ) ) ) ;
543
+ }
544
+ }
545
+ None => return None , // reached the end of the file
546
+ }
547
+ }
548
+ }
549
+ }
550
+
551
+ // This struct stores the information about code blocks in the configurations
552
+ // file, formats the code blocks, and prints formatting errors.
553
+ struct ConfigCodeBlock {
554
+ config_name : Option < String > ,
555
+ config_value : Option < String > ,
556
+ code_block : Option < String > ,
557
+ code_block_start : Option < u32 > ,
516
558
}
517
559
518
- // Read Configurations.md and build a `Vec` of `CodeBlock` structs with one
560
+ impl ConfigCodeBlock {
561
+ fn new ( ) -> ConfigCodeBlock {
562
+ ConfigCodeBlock {
563
+ config_name : None ,
564
+ config_value : None ,
565
+ code_block : None ,
566
+ code_block_start : None ,
567
+ }
568
+ }
569
+
570
+ fn set_config_name ( & mut self , name : Option < String > ) {
571
+ self . config_name = name;
572
+ self . config_value = None ;
573
+ }
574
+
575
+ fn set_config_value ( & mut self , value : Option < String > ) {
576
+ self . config_value = value;
577
+ }
578
+
579
+ fn set_code_block ( & mut self , code_block : String , code_block_start : u32 ) {
580
+ self . code_block = Some ( code_block) ;
581
+ self . code_block_start = Some ( code_block_start) ;
582
+ }
583
+
584
+ fn get_block_config ( & self ) -> Config {
585
+ let mut config = Config :: default ( ) ;
586
+ config. override_value (
587
+ self . config_name . as_ref ( ) . unwrap ( ) ,
588
+ self . config_value . as_ref ( ) . unwrap ( ) ,
589
+ ) ;
590
+ config
591
+ }
592
+
593
+ fn code_block_valid ( & self ) -> bool {
594
+ // We never expect to not have a code block.
595
+ assert ! ( self . code_block. is_some( ) && self . code_block_start. is_some( ) ) ;
596
+
597
+ if self . config_name . is_none ( ) {
598
+ write_message ( format ! (
599
+ "configuration name not found for block beginning at line {}" ,
600
+ self . code_block_start. unwrap( )
601
+ ) ) ;
602
+ return false ;
603
+ }
604
+ if self . config_value . is_none ( ) {
605
+ write_message ( format ! (
606
+ "configuration value not found for block beginning at line {}" ,
607
+ self . code_block_start. unwrap( )
608
+ ) ) ;
609
+ return false ;
610
+ }
611
+ true
612
+ }
613
+
614
+ fn has_parsing_errors ( & self , error_summary : Summary ) -> bool {
615
+ if error_summary. has_parsing_errors ( ) {
616
+ write_message ( format ! (
617
+ "\u{261d} \u{1f3fd} Failed to format block starting at Line {} in Configurations.md" ,
618
+ self . code_block_start. unwrap( )
619
+ ) ) ;
620
+ return true ;
621
+ }
622
+
623
+ false
624
+ }
625
+
626
+ fn print_diff ( & self , compare : Vec < Mismatch > ) {
627
+ let mut mismatches = HashMap :: new ( ) ;
628
+ mismatches. insert ( PathBuf :: from ( "Configurations.md" ) , compare) ;
629
+ print_mismatches ( mismatches, |line_num| {
630
+ format ! (
631
+ "\n Mismatch at Configurations.md:{}:" ,
632
+ line_num + self . code_block_start. unwrap( ) - 1
633
+ )
634
+ } ) ;
635
+ }
636
+
637
+ fn formatted_has_diff ( & self , file_map : FileMap ) -> bool {
638
+ let & ( ref _file_name, ref text) = file_map. first ( ) . unwrap ( ) ;
639
+ let compare = make_diff ( self . code_block . as_ref ( ) . unwrap ( ) , text, DIFF_CONTEXT_SIZE ) ;
640
+ if !compare. is_empty ( ) {
641
+ self . print_diff ( compare) ;
642
+ return true ;
643
+ }
644
+
645
+ false
646
+ }
647
+
648
+ // Return a bool indicating if formatting this code block is an idempotent
649
+ // operation. This function also triggers printing any formatting failure
650
+ // messages.
651
+ fn formatted_is_idempotent ( & self ) -> bool {
652
+ // Verify that we have all of the expected information.
653
+ if !self . code_block_valid ( ) {
654
+ return false ;
655
+ }
656
+
657
+ let input = Input :: Text ( self . code_block . as_ref ( ) . unwrap ( ) . to_owned ( ) ) ;
658
+ let config = self . get_block_config ( ) ;
659
+
660
+ let ( error_summary, file_map, _report) =
661
+ format_input :: < io:: Stdout > ( input, & config, None ) . unwrap ( ) ;
662
+
663
+ !self . has_parsing_errors ( error_summary) && !self . formatted_has_diff ( file_map)
664
+ }
665
+
666
+ fn extract < I : Iterator < Item = String > > (
667
+ file : & mut Enumerate < I > ,
668
+ prev : Option < & ConfigCodeBlock > ,
669
+ ) -> Option < ConfigCodeBlock > {
670
+ let mut code_block = ConfigCodeBlock :: new ( ) ;
671
+ code_block. config_name = prev. map_or ( None , |cb| cb. config_name . clone ( ) ) ;
672
+
673
+ loop {
674
+ match ConfigurationSection :: get_section ( file) {
675
+ Some ( ConfigurationSection :: CodeBlock ( ( block, start_line) ) ) => {
676
+ code_block. set_code_block ( block, start_line) ;
677
+ break ;
678
+ }
679
+ Some ( ConfigurationSection :: ConfigName ( name) ) => {
680
+ code_block. set_config_name ( Some ( name) ) ;
681
+ }
682
+ Some ( ConfigurationSection :: ConfigValue ( value) ) => {
683
+ code_block. set_config_value ( Some ( value) ) ;
684
+ }
685
+ None => return None , // end of file was reached
686
+ }
687
+ }
688
+
689
+ Some ( code_block)
690
+ }
691
+ }
692
+
693
+ // Read Configurations.md and build a `Vec` of `ConfigCodeBlock` structs with one
519
694
// entry for each Rust code block found. Behavior:
520
695
// - Rust code blocks are identifed by lines beginning with "```rust".
521
696
// - One explicit configuration setting is supported per code block.
@@ -525,120 +700,35 @@ struct CodeBlock {
525
700
// "## `NAME`".
526
701
// - Configuration values in Configurations.md must be in the form of
527
702
// "#### `VALUE`".
528
- fn get_code_blocks ( ) -> Vec < CodeBlock > {
703
+ fn get_code_blocks ( ) -> Vec < ConfigCodeBlock > {
529
704
let filename = "Configurations.md" ;
530
705
let filebuf = BufReader :: new (
531
706
fs:: File :: open ( filename) . expect ( & format ! ( "Couldn't read file {}" , filename) ) ,
532
707
) ;
533
708
534
- let config_name_regex =
535
- regex:: Regex :: new ( r"^## `([^`]+)`" ) . expect ( "Failed creating configuration pattern" ) ;
536
- let config_value_regex = regex:: Regex :: new ( r#"^#### `"?([^`"]+)"?`"# )
537
- . expect ( "Failed creating configuration value pattern" ) ;
538
-
539
- let mut code_blocks = Vec :: new ( ) ;
540
- let mut extracting_block = false ;
541
- let mut code_block = Vec :: new ( ) ;
542
- let mut code_block_start_line = 0 ;
543
- let mut config_name: Option < String > = None ;
544
- let mut config_value: Option < String > = None ;
545
-
546
- for ( i, line) in filebuf. lines ( ) . map ( |l| l. unwrap ( ) ) . enumerate ( ) {
547
- if line. starts_with ( "```" ) && extracting_block {
548
- // We've reached the end of the code block.
549
- extracting_block = false ;
550
-
551
- // Get a trailing newline in the code block.
552
- code_block. push ( String :: new ( ) ) ;
553
-
554
- // Store the code block information.
555
- code_blocks. push ( CodeBlock {
556
- config : ConfigPair {
557
- name : config_name. as_ref ( ) . unwrap ( ) . clone ( ) ,
558
- value : config_value. unwrap ( ) ,
559
- } ,
560
- code_block : code_block. join ( "\n " ) ,
561
- code_block_start_line,
562
- } ) ;
563
-
564
- config_value = None ;
565
- code_block. clear ( ) ;
566
- } else if line. starts_with ( "```rust" ) {
567
- // We've reached the beginning of a Rust code block.
568
- extracting_block = true ;
569
-
570
- // +1 to translate to one-based indexing
571
- // +1 to get to first line of code (line after "```")
572
- code_block_start_line = ( i + 2 ) as u32 ;
573
-
574
- // Assert that we have a configuration name and value.
575
- assert ! (
576
- config_name. is_some( ) ,
577
- "configuration name not found for block beginning at line {}" ,
578
- code_block_start_line
579
- ) ;
580
- assert ! (
581
- config_value. is_some( ) ,
582
- "configuration value not found for block beginning at line {}" ,
583
- code_block_start_line
584
- ) ;
585
- } else if extracting_block {
586
- code_block. push ( line) ;
587
- } else if let Some ( c) = config_name_regex. captures ( & line) {
588
- config_name = Some ( String :: from ( & c[ 1 ] ) ) ;
589
- } else if let Some ( c) = config_value_regex. captures ( & line) {
590
- config_value = Some ( String :: from ( & c[ 1 ] ) ) ;
591
- }
592
- }
709
+ let mut code_blocks: Vec < ConfigCodeBlock > = Vec :: new ( ) ;
593
710
594
- code_blocks
595
- }
596
-
597
- // Format the code blocks extracted in `get_code_blocks` and check for
598
- // failures.
599
- fn check_blocks_idempotency ( blocks : & Vec < CodeBlock > ) {
600
- let mut failures = 0 ;
601
-
602
- for block in blocks {
603
- let input = Input :: Text ( block. code_block . clone ( ) ) ;
604
- let mut config = Config :: default ( ) ;
605
- config. override_value ( & block. config . name , & block. config . value ) ;
606
-
607
- let ( error_summary, file_map, _report) =
608
- format_input :: < io:: Stdout > ( input, & config, None ) . unwrap ( ) ;
609
-
610
- if error_summary. has_parsing_errors ( ) {
611
- write_message ( String :: from ( format ! (
612
- "\u{261d} \u{1f3fd} Failed to format block starting at Line {} in Configurations.md" ,
613
- block. code_block_start_line
614
- ) ) ) ;
615
- failures += 1 ;
616
- } else {
617
- for & ( ref _file_name, ref text) in & file_map {
618
- let compare = make_diff ( & block. code_block , text, DIFF_CONTEXT_SIZE ) ;
619
- if !compare. is_empty ( ) {
620
- let mut mismatches = HashMap :: new ( ) ;
621
- mismatches. insert ( PathBuf :: from ( "Configurations.md" ) , compare) ;
622
- print_mismatches ( mismatches, |line_num| {
623
- format ! (
624
- "\n Mismatch at Configurations.md:{}:" ,
625
- line_num + block. code_block_start_line - 1
626
- )
627
- } ) ;
628
- failures += 1 ;
629
- }
630
- }
711
+ let mut file_iter = filebuf. lines ( ) . map ( |l| l. unwrap ( ) ) . enumerate ( ) ;
712
+ loop {
713
+ match ConfigCodeBlock :: extract ( & mut file_iter, code_blocks. last ( ) ) {
714
+ Some ( cb) => code_blocks. push ( cb) ,
715
+ None => break , // end of file was reached
631
716
}
632
717
}
633
718
634
- // Display results.
635
- println ! ( "Ran {} configurations tests." , blocks. len( ) ) ;
636
- assert_eq ! ( failures, 0 , "{} configurations tests failed" , failures) ;
719
+ code_blocks
637
720
}
638
721
639
722
#[ test]
640
723
#[ ignore]
641
724
fn configuration_snippet_tests ( ) {
642
725
let blocks = get_code_blocks ( ) ;
643
- check_blocks_idempotency ( & blocks) ;
726
+ let failures = blocks
727
+ . iter ( )
728
+ . map ( |b| b. formatted_is_idempotent ( ) )
729
+ . fold ( 0 , |acc, r| acc + ( !r as u32 ) ) ;
730
+
731
+ // Display results.
732
+ println ! ( "Ran {} configurations tests." , blocks. len( ) ) ;
733
+ assert_eq ! ( failures, 0 , "{} configurations tests failed" , failures) ;
644
734
}
0 commit comments