1212// See the License for the specific language governing permissions and
1313// limitations under the License.
1414
15- use std:: io:: stdout;
16- use std:: io:: IsTerminal ;
17- use std:: process:: ExitCode ;
18-
19- use crate :: rules:: api:: Rule ;
20- use clap:: crate_description;
21- use clap:: Parser as CliArgParser ;
22- use clap:: ValueEnum ;
15+ use std:: {
16+ io:: { stdout, IsTerminal } ,
17+ process:: ExitCode ,
18+ } ;
19+
20+ use clap:: { crate_description, Parser as CliArgParser , ValueEnum } ;
2321use clap_stdin:: FileOrStdin ;
24- use codespan_reporting:: diagnostic:: Diagnostic ;
25- use codespan_reporting:: diagnostic:: Label ;
26- use codespan_reporting:: diagnostic:: LabelStyle ;
27- use codespan_reporting:: diagnostic:: Severity ;
28- use codespan_reporting:: files:: Files ;
22+ use codespan_reporting:: diagnostic:: { Diagnostic , Label , LabelStyle , Severity } ;
2923use codespan_reporting:: {
30- files:: SimpleFile ,
24+ files:: { Files , SimpleFile } ,
3125 term:: {
3226 self ,
3327 termcolor:: { ColorChoice , StandardStream } ,
@@ -46,14 +40,21 @@ const LONG_ABOUT: &str = concat!("Westwood: ", crate_description!());
4640#[ derive( CliArgParser , Debug ) ]
4741#[ command( version, about = None , long_about = LONG_ABOUT ) ]
4842struct CliOptions {
49- #[ arg( help = "File to lint, or `-' for standard input" ) ]
43+ /// File to lint, or `-` for standard input
44+ #[ arg( ) ]
5045 file : FileOrStdin ,
5146
47+ /// Format in which to print diagnostics
5248 #[ arg( value_enum, short, long, default_value_t = OutputFormat :: Pretty ) ]
5349 format : OutputFormat ,
5450
51+ /// Whether to print colored output
5552 #[ arg( value_enum, long, default_value_t = ColorMode :: Auto ) ]
5653 color : ColorMode ,
54+
55+ /// How to sort diagnostics before printing
56+ #[ arg( value_enum, long, default_value_t = OutputSort :: Line ) ]
57+ sort : OutputSort ,
5758}
5859
5960/// Format in which to print diagnostics
@@ -66,6 +67,16 @@ enum OutputFormat {
6667 Machine ,
6768}
6869
70+ /// How to sort diagnostics
71+ #[ derive( Copy , Clone , Debug , ValueEnum ) ]
72+ enum OutputSort {
73+ /// Sort diagnostics by source line number
74+ Line ,
75+
76+ /// Sort diagnostics by code standard rule
77+ Rule ,
78+ }
79+
6980/// When to print colored output
7081#[ derive( Copy , Clone , Debug , ValueEnum ) ]
7182enum ColorMode {
@@ -145,18 +156,33 @@ fn main() -> ExitCode {
145156 let files = SimpleFile :: new ( filename, & code) ;
146157
147158 // Do checks
148- let rules: Vec < Box < dyn Rule > > = crate :: rules:: get_rules ( ) ;
149159 let source = SourceInfo :: new ( & code) ;
150- for rule in rules {
151- let diagnostics = rule. check ( & source) ;
152- for diagnostic in diagnostics {
153- match cli. format {
154- OutputFormat :: Pretty => {
155- term:: emit ( & mut writer. lock ( ) , & config, & files, & diagnostic)
156- . expect ( "Failed to write diagnostic" ) ;
157- }
158- OutputFormat :: Machine => print_machine_parseable ( & files, & diagnostic) ,
160+ let mut diagnostics: Vec < _ > = crate :: rules:: get_rules ( )
161+ . into_iter ( )
162+ . flat_map ( |rule| rule. check ( & source) )
163+ . collect ( ) ;
164+
165+ // Sort diagnostics
166+ match cli. sort {
167+ OutputSort :: Line => diagnostics. sort_by_key ( |d| {
168+ d. labels
169+ . iter ( )
170+ . find ( |label| label. style == LabelStyle :: Primary )
171+ . map_or ( 0 , |label| label. range . start )
172+ } ) ,
173+
174+ // Don't need to do anything because the diagnostics are already sorted by rule
175+ OutputSort :: Rule => ( ) ,
176+ }
177+
178+ // Print diagnostics
179+ for diagnostic in diagnostics {
180+ match cli. format {
181+ OutputFormat :: Pretty => {
182+ term:: emit ( & mut writer. lock ( ) , & config, & files, & diagnostic)
183+ . expect ( "Failed to write diagnostic" ) ;
159184 }
185+ OutputFormat :: Machine => print_machine_parseable ( & files, & diagnostic) ,
160186 }
161187 }
162188
0 commit comments