1
1
use std:: collections:: HashSet ;
2
- use std:: fmt:: Display ;
2
+ use std:: fmt:: { Display , Formatter } ;
3
3
use std:: path:: { Path , PathBuf } ;
4
4
use std:: sync:: { Arc , Mutex } ;
5
5
6
- use termcolor:: WriteColor ;
6
+ use termcolor:: { Color , WriteColor } ;
7
7
8
8
/// Collects diagnostics from all tidy steps, and contains shared information
9
9
/// that determines how should message and logs be presented.
@@ -14,40 +14,56 @@ use termcolor::WriteColor;
14
14
pub struct DiagCtx ( Arc < Mutex < DiagCtxInner > > ) ;
15
15
16
16
impl DiagCtx {
17
- pub fn new ( verbose : bool ) -> Self {
17
+ pub fn new ( root_path : & Path , verbose : bool ) -> Self {
18
18
Self ( Arc :: new ( Mutex :: new ( DiagCtxInner {
19
19
running_checks : Default :: default ( ) ,
20
20
finished_checks : Default :: default ( ) ,
21
+ root_path : root_path. to_path_buf ( ) ,
21
22
verbose,
22
23
} ) ) )
23
24
}
24
25
25
26
pub fn start_check < Id : Into < CheckId > > ( & self , id : Id ) -> RunningCheck {
26
- let id = id. into ( ) ;
27
+ let mut id = id. into ( ) ;
27
28
28
29
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
+
29
37
ctx. start_check ( id. clone ( ) ) ;
30
- RunningCheck { id, bad : false , ctx : self . 0 . clone ( ) }
38
+ RunningCheck {
39
+ id,
40
+ bad : false ,
41
+ ctx : self . 0 . clone ( ) ,
42
+ #[ cfg( test) ]
43
+ errors : vec ! [ ] ,
44
+ }
31
45
}
32
46
33
- pub fn into_conclusion ( self ) -> bool {
34
- let ctx = self . 0 . lock ( ) . unwrap ( ) ;
47
+ pub fn into_failed_checks ( self ) -> Vec < FinishedCheck > {
48
+ let ctx = Arc :: into_inner ( self . 0 ) . unwrap ( ) . into_inner ( ) . unwrap ( ) ;
35
49
assert ! ( ctx. running_checks. is_empty( ) , "Some checks are still running" ) ;
36
- ctx. finished_checks . iter ( ) . any ( |c| c. bad )
50
+ ctx. finished_checks . into_iter ( ) . filter ( |c| c. bad ) . collect ( )
37
51
}
38
52
}
39
53
40
54
struct DiagCtxInner {
41
55
running_checks : HashSet < CheckId > ,
42
56
finished_checks : HashSet < FinishedCheck > ,
43
57
verbose : bool ,
58
+ root_path : PathBuf ,
44
59
}
45
60
46
61
impl DiagCtxInner {
47
62
fn start_check ( & mut self , id : CheckId ) {
48
63
if self . has_check_id ( & id) {
49
64
panic ! ( "Starting a check named `{id:?}` for the second time" ) ;
50
65
}
66
+
51
67
self . running_checks . insert ( id) ;
52
68
}
53
69
@@ -57,6 +73,13 @@ impl DiagCtxInner {
57
73
"Finishing check `{:?}` that was not started" ,
58
74
check. id
59
75
) ;
76
+
77
+ if check. bad {
78
+ output_message ( "FAIL" , Some ( & check. id ) , Some ( COLOR_ERROR ) ) ;
79
+ } else if self . verbose {
80
+ output_message ( "OK" , Some ( & check. id ) , Some ( COLOR_SUCCESS ) ) ;
81
+ }
82
+
60
83
self . finished_checks . insert ( check) ;
61
84
}
62
85
@@ -71,8 +94,8 @@ impl DiagCtxInner {
71
94
/// Identifies a single step
72
95
#[ derive( PartialEq , Eq , Hash , Clone , Debug ) ]
73
96
pub struct CheckId {
74
- name : String ,
75
- path : Option < PathBuf > ,
97
+ pub name : String ,
98
+ pub path : Option < PathBuf > ,
76
99
}
77
100
78
101
impl CheckId {
@@ -91,40 +114,70 @@ impl From<&'static str> for CheckId {
91
114
}
92
115
}
93
116
117
+ impl Display for CheckId {
118
+ fn fmt ( & self , f : & mut Formatter < ' _ > ) -> std:: fmt:: Result {
119
+ write ! ( f, "{}" , self . name) ?;
120
+ if let Some ( path) = & self . path {
121
+ write ! ( f, " ({})" , path. display( ) ) ?;
122
+ }
123
+ Ok ( ( ) )
124
+ }
125
+ }
126
+
94
127
#[ derive( PartialEq , Eq , Hash , Debug ) ]
95
- struct FinishedCheck {
128
+ pub struct FinishedCheck {
96
129
id : CheckId ,
97
130
bad : bool ,
98
131
}
99
132
133
+ impl FinishedCheck {
134
+ pub fn id ( & self ) -> & CheckId {
135
+ & self . id
136
+ }
137
+ }
138
+
100
139
/// Represents a single tidy check, identified by its `name`, running.
101
140
pub struct RunningCheck {
102
141
id : CheckId ,
103
142
bad : bool ,
104
143
ctx : Arc < Mutex < DiagCtxInner > > ,
144
+ #[ cfg( test) ]
145
+ errors : Vec < String > ,
105
146
}
106
147
107
148
impl RunningCheck {
149
+ /// Creates a new instance of a running check without going through the diag
150
+ /// context.
151
+ /// Useful if you want to run some functions from tidy without configuring
152
+ /// diagnostics.
153
+ pub fn new_noop ( ) -> Self {
154
+ let ctx = DiagCtx :: new ( Path :: new ( "" ) , false ) ;
155
+ ctx. start_check ( "noop" )
156
+ }
157
+
108
158
/// Immediately output an error and mark the check as failed.
109
- pub fn error < T : Display > ( & mut self , t : T ) {
159
+ pub fn error < T : Display > ( & mut self , msg : T ) {
110
160
self . mark_as_bad ( ) ;
111
- tidy_error ( & t. to_string ( ) ) . expect ( "failed to output error" ) ;
161
+ let msg = msg. to_string ( ) ;
162
+ output_message ( & msg, Some ( & self . id ) , Some ( COLOR_ERROR ) ) ;
163
+ #[ cfg( test) ]
164
+ self . errors . push ( msg) ;
112
165
}
113
166
114
167
/// Immediately output a warning.
115
- pub fn warning < T : Display > ( & mut self , t : T ) {
116
- eprintln ! ( "WARNING: {t}" ) ;
168
+ pub fn warning < T : Display > ( & mut self , msg : T ) {
169
+ output_message ( & msg . to_string ( ) , Some ( & self . id ) , Some ( COLOR_WARNING ) ) ;
117
170
}
118
171
119
172
/// Output an informational message
120
- pub fn message < T : Display > ( & mut self , t : T ) {
121
- eprintln ! ( "{t}" ) ;
173
+ pub fn message < T : Display > ( & mut self , msg : T ) {
174
+ output_message ( & msg . to_string ( ) , Some ( & self . id ) , None ) ;
122
175
}
123
176
124
177
/// Output a message only if verbose output is enabled.
125
- pub fn verbose_msg < T : Display > ( & mut self , t : T ) {
178
+ pub fn verbose_msg < T : Display > ( & mut self , msg : T ) {
126
179
if self . is_verbose_enabled ( ) {
127
- self . message ( t ) ;
180
+ self . message ( msg ) ;
128
181
}
129
182
}
130
183
@@ -138,6 +191,11 @@ impl RunningCheck {
138
191
self . ctx . lock ( ) . unwrap ( ) . verbose
139
192
}
140
193
194
+ #[ cfg( test) ]
195
+ pub fn get_errors ( & self ) -> Vec < String > {
196
+ self . errors . clone ( )
197
+ }
198
+
141
199
fn mark_as_bad ( & mut self ) {
142
200
self . bad = true ;
143
201
}
@@ -149,17 +207,37 @@ impl Drop for RunningCheck {
149
207
}
150
208
}
151
209
152
- fn tidy_error ( args : & str ) -> std:: io:: Result < ( ) > {
210
+ pub const COLOR_SUCCESS : Color = Color :: Green ;
211
+ pub const COLOR_ERROR : Color = Color :: Red ;
212
+ pub const COLOR_WARNING : Color = Color :: Yellow ;
213
+
214
+ /// Output a message to stderr.
215
+ /// The message can be optionally scoped to a certain check, and it can also have a certain color.
216
+ pub fn output_message ( msg : & str , id : Option < & CheckId > , color : Option < Color > ) {
153
217
use std:: io:: Write ;
154
218
155
- use termcolor:: { Color , ColorChoice , ColorSpec , StandardStream } ;
219
+ use termcolor:: { ColorChoice , ColorSpec , StandardStream } ;
156
220
157
- let mut stderr = StandardStream :: stdout ( ColorChoice :: Auto ) ;
158
- stderr. set_color ( ColorSpec :: new ( ) . set_fg ( Some ( Color :: Red ) ) ) ?;
221
+ let mut stderr = StandardStream :: stderr ( ColorChoice :: Auto ) ;
222
+ if let Some ( color) = & color {
223
+ stderr. set_color ( ColorSpec :: new ( ) . set_fg ( Some ( * color) ) ) . unwrap ( ) ;
224
+ }
159
225
160
- write ! ( & mut stderr, "tidy error" ) ?;
161
- stderr. set_color ( & ColorSpec :: new ( ) ) ?;
226
+ match id {
227
+ Some ( id) => {
228
+ write ! ( & mut stderr, "tidy [{}" , id. name) . unwrap ( ) ;
229
+ if let Some ( path) = & id. path {
230
+ write ! ( & mut stderr, " ({})" , path. display( ) ) . unwrap ( ) ;
231
+ }
232
+ write ! ( & mut stderr, "]" ) . unwrap ( ) ;
233
+ }
234
+ None => {
235
+ write ! ( & mut stderr, "tidy" ) . unwrap ( ) ;
236
+ }
237
+ }
238
+ if color. is_some ( ) {
239
+ stderr. set_color ( & ColorSpec :: new ( ) ) . unwrap ( ) ;
240
+ }
162
241
163
- writeln ! ( & mut stderr, ": {args}" ) ?;
164
- Ok ( ( ) )
242
+ writeln ! ( & mut stderr, ": {msg}" ) . unwrap ( ) ;
165
243
}
0 commit comments