11use std:: collections:: HashSet ;
2- use std:: fmt:: Display ;
2+ use std:: fmt:: { Display , Formatter } ;
33use std:: path:: { Path , PathBuf } ;
44use std:: sync:: { Arc , Mutex } ;
55
6- use termcolor:: WriteColor ;
6+ use termcolor:: { Color , WriteColor } ;
77
88/// Collects diagnostics from all tidy steps, and contains shared information
99/// that determines how should message and logs be presented.
@@ -14,40 +14,50 @@ use termcolor::WriteColor;
1414pub struct DiagCtx ( Arc < Mutex < DiagCtxInner > > ) ;
1515
1616impl DiagCtx {
17- pub fn new ( verbose : bool ) -> Self {
17+ pub fn new ( root_path : & Path , verbose : bool ) -> Self {
1818 Self ( Arc :: new ( Mutex :: new ( DiagCtxInner {
1919 running_checks : Default :: default ( ) ,
2020 finished_checks : Default :: default ( ) ,
21+ root_path : root_path. to_path_buf ( ) ,
2122 verbose,
2223 } ) ) )
2324 }
2425
2526 pub fn start_check < Id : Into < CheckId > > ( & self , id : Id ) -> RunningCheck {
26- let id = id. into ( ) ;
27+ let mut id = id. into ( ) ;
2728
2829 let mut ctx = self . 0 . lock ( ) . unwrap ( ) ;
30+
31+ // Shorten path for shorter diagnostics
32+ id. path = match id. path {
33+ Some ( path) => Some ( path. strip_prefix ( & ctx. root_path ) . unwrap_or ( & path) . to_path_buf ( ) ) ,
34+ None => None ,
35+ } ;
36+
2937 ctx. start_check ( id. clone ( ) ) ;
3038 RunningCheck { id, bad : false , ctx : self . 0 . clone ( ) }
3139 }
3240
33- pub fn into_conclusion ( self ) -> bool {
34- let ctx = self . 0 . lock ( ) . unwrap ( ) ;
41+ pub fn into_failed_checks ( self ) -> Vec < FinishedCheck > {
42+ let ctx = Arc :: into_inner ( self . 0 ) . unwrap ( ) . into_inner ( ) . unwrap ( ) ;
3543 assert ! ( ctx. running_checks. is_empty( ) , "Some checks are still running" ) ;
36- ctx. finished_checks . iter ( ) . any ( |c| c. bad )
44+ ctx. finished_checks . into_iter ( ) . filter ( |c| c. bad ) . collect ( )
3745 }
3846}
3947
4048struct DiagCtxInner {
4149 running_checks : HashSet < CheckId > ,
4250 finished_checks : HashSet < FinishedCheck > ,
4351 verbose : bool ,
52+ root_path : PathBuf ,
4453}
4554
4655impl DiagCtxInner {
4756 fn start_check ( & mut self , id : CheckId ) {
4857 if self . has_check_id ( & id) {
4958 panic ! ( "Starting a check named `{id:?}` for the second time" ) ;
5059 }
60+
5161 self . running_checks . insert ( id) ;
5262 }
5363
@@ -57,6 +67,13 @@ impl DiagCtxInner {
5767 "Finishing check `{:?}` that was not started" ,
5868 check. id
5969 ) ;
70+
71+ if check. bad {
72+ output_message ( "FAIL" , Some ( & check. id ) , Some ( COLOR_ERROR ) ) ;
73+ } else if self . verbose {
74+ output_message ( "OK" , Some ( & check. id ) , Some ( COLOR_SUCCESS ) ) ;
75+ }
76+
6077 self . finished_checks . insert ( check) ;
6178 }
6279
@@ -71,8 +88,8 @@ impl DiagCtxInner {
7188/// Identifies a single step
7289#[ derive( PartialEq , Eq , Hash , Clone , Debug ) ]
7390pub struct CheckId {
74- name : String ,
75- path : Option < PathBuf > ,
91+ pub name : String ,
92+ pub path : Option < PathBuf > ,
7693}
7794
7895impl CheckId {
@@ -91,12 +108,28 @@ impl From<&'static str> for CheckId {
91108 }
92109}
93110
111+ impl Display for CheckId {
112+ fn fmt ( & self , f : & mut Formatter < ' _ > ) -> std:: fmt:: Result {
113+ write ! ( f, "{}" , self . name) ?;
114+ if let Some ( path) = & self . path {
115+ write ! ( f, " ({})" , path. display( ) ) ?;
116+ }
117+ Ok ( ( ) )
118+ }
119+ }
120+
94121#[ derive( PartialEq , Eq , Hash , Debug ) ]
95- struct FinishedCheck {
122+ pub struct FinishedCheck {
96123 id : CheckId ,
97124 bad : bool ,
98125}
99126
127+ impl FinishedCheck {
128+ pub fn id ( & self ) -> & CheckId {
129+ & self . id
130+ }
131+ }
132+
100133/// Represents a single tidy check, identified by its `name`, running.
101134pub struct RunningCheck {
102135 id : CheckId ,
@@ -106,25 +139,25 @@ pub struct RunningCheck {
106139
107140impl RunningCheck {
108141 /// Immediately output an error and mark the check as failed.
109- pub fn error < T : Display > ( & mut self , t : T ) {
142+ pub fn error < T : Display > ( & mut self , msg : T ) {
110143 self . mark_as_bad ( ) ;
111- tidy_error ( & t . to_string ( ) ) . expect ( "failed to output error" ) ;
144+ output_message ( & msg . to_string ( ) , Some ( & self . id ) , Some ( COLOR_ERROR ) ) ;
112145 }
113146
114147 /// Immediately output a warning.
115- pub fn warning < T : Display > ( & mut self , t : T ) {
116- eprintln ! ( "WARNING: {t}" ) ;
148+ pub fn warning < T : Display > ( & mut self , msg : T ) {
149+ output_message ( & msg . to_string ( ) , Some ( & self . id ) , Some ( COLOR_WARNING ) ) ;
117150 }
118151
119152 /// Output an informational message
120- pub fn message < T : Display > ( & mut self , t : T ) {
121- eprintln ! ( "{t}" ) ;
153+ pub fn message < T : Display > ( & mut self , msg : T ) {
154+ output_message ( & msg . to_string ( ) , Some ( & self . id ) , None ) ;
122155 }
123156
124157 /// Output a message only if verbose output is enabled.
125- pub fn verbose_msg < T : Display > ( & mut self , t : T ) {
158+ pub fn verbose_msg < T : Display > ( & mut self , msg : T ) {
126159 if self . is_verbose_enabled ( ) {
127- self . message ( t ) ;
160+ self . message ( msg ) ;
128161 }
129162 }
130163
@@ -149,17 +182,37 @@ impl Drop for RunningCheck {
149182 }
150183}
151184
152- fn tidy_error ( args : & str ) -> std:: io:: Result < ( ) > {
185+ pub const COLOR_SUCCESS : Color = Color :: Green ;
186+ pub const COLOR_ERROR : Color = Color :: Red ;
187+ pub const COLOR_WARNING : Color = Color :: Yellow ;
188+
189+ /// Output a message to stderr.
190+ /// The message can be optionally scoped to a certain check, and it can also have a certain color.
191+ pub fn output_message ( msg : & str , id : Option < & CheckId > , color : Option < Color > ) {
153192 use std:: io:: Write ;
154193
155- use termcolor:: { Color , ColorChoice , ColorSpec , StandardStream } ;
194+ use termcolor:: { ColorChoice , ColorSpec , StandardStream } ;
156195
157- let mut stderr = StandardStream :: stdout ( ColorChoice :: Auto ) ;
158- stderr. set_color ( ColorSpec :: new ( ) . set_fg ( Some ( Color :: Red ) ) ) ?;
196+ let mut stderr = StandardStream :: stderr ( ColorChoice :: Auto ) ;
197+ if let Some ( color) = & color {
198+ stderr. set_color ( ColorSpec :: new ( ) . set_fg ( Some ( * color) ) ) . unwrap ( ) ;
199+ }
159200
160- write ! ( & mut stderr, "tidy error" ) ?;
161- stderr. set_color ( & ColorSpec :: new ( ) ) ?;
201+ match id {
202+ Some ( id) => {
203+ write ! ( & mut stderr, "tidy [{}" , id. name) . unwrap ( ) ;
204+ if let Some ( path) = & id. path {
205+ write ! ( & mut stderr, " ({})" , path. display( ) ) . unwrap ( ) ;
206+ }
207+ write ! ( & mut stderr, "]" ) . unwrap ( ) ;
208+ }
209+ None => {
210+ write ! ( & mut stderr, "tidy" ) . unwrap ( ) ;
211+ }
212+ }
213+ if color. is_some ( ) {
214+ stderr. set_color ( & ColorSpec :: new ( ) ) . unwrap ( ) ;
215+ }
162216
163- writeln ! ( & mut stderr, ": {args}" ) ?;
164- Ok ( ( ) )
217+ writeln ! ( & mut stderr, ": {msg}" ) . unwrap ( ) ;
165218}
0 commit comments