1
1
mod args;
2
2
mod check;
3
3
mod files;
4
+ mod fix;
4
5
5
6
use anyhow:: Result ;
6
7
use clap:: Parser ;
@@ -13,7 +14,8 @@ use std::sync::{
13
14
} ;
14
15
15
16
use crate :: args:: Args ;
16
- use crate :: files:: { FileIssues , find_files, process_file} ;
17
+ use crate :: files:: { FileIssues , find_files, process_file, should_exclude} ;
18
+ use crate :: fix:: fix_file;
17
19
18
20
#[ derive( Debug , Serialize , Deserialize ) ]
19
21
struct LintResult {
@@ -28,8 +30,29 @@ struct Summary {
28
30
total_issues : usize ,
29
31
}
30
32
33
+ #[ derive( Debug , Serialize , Deserialize ) ]
34
+ struct FixResult {
35
+ files : HashMap < String , FixedFileInfo > ,
36
+ summary : FixSummary ,
37
+ }
38
+
39
+ #[ derive( Debug , Clone , Serialize , Deserialize ) ]
40
+ struct FixedFileInfo {
41
+ trailing_whitespace_fixed : usize ,
42
+ tabs_converted : usize ,
43
+ final_newline_added : bool ,
44
+ }
45
+
46
+ #[ derive( Debug , Serialize , Deserialize ) ]
47
+ struct FixSummary {
48
+ total_files : usize ,
49
+ files_fixed : usize ,
50
+ total_fixes : usize ,
51
+ }
52
+
31
53
fn main ( ) -> Result < ( ) > {
32
54
let args = Args :: parse ( ) ;
55
+
33
56
let mut all_files = Vec :: new ( ) ;
34
57
for path in & args. paths {
35
58
if !path. exists ( ) {
@@ -44,8 +67,72 @@ fn main() -> Result<()> {
44
67
return Ok ( ( ) ) ;
45
68
}
46
69
70
+ if args. fix {
71
+ let mut fixed_files = HashMap :: new ( ) ;
72
+ let mut error_count = 0 ;
73
+ let mut total_files_checked = 0 ;
74
+
75
+ for file in & all_files {
76
+ if should_exclude ( file) {
77
+ continue ;
78
+ }
79
+
80
+ total_files_checked += 1 ;
81
+
82
+ match fix_file ( file) {
83
+ Ok ( Some ( result) ) => {
84
+ fixed_files. insert (
85
+ file. display ( ) . to_string ( ) ,
86
+ FixedFileInfo {
87
+ trailing_whitespace_fixed : result. trailing_whitespace_fixed ,
88
+ tabs_converted : result. tabs_converted ,
89
+ final_newline_added : result. final_newline_added ,
90
+ } ,
91
+ ) ;
92
+ }
93
+ Ok ( None ) => {
94
+ // No fixes needed for this file
95
+ }
96
+ Err ( e) => {
97
+ eprintln ! ( "Error fixing {}: {}" , file. display( ) , e) ;
98
+ error_count += 1 ;
99
+ }
100
+ }
101
+ }
102
+
103
+ if error_count > 0 {
104
+ std:: process:: exit ( 1 ) ;
105
+ }
106
+
107
+ if !fixed_files. is_empty ( ) {
108
+ let files_fixed = fixed_files. len ( ) ;
109
+ let total_fixes: usize = fixed_files
110
+ . values ( )
111
+ . map ( |f| {
112
+ f. trailing_whitespace_fixed
113
+ + f. tabs_converted
114
+ + if f. final_newline_added { 1 } else { 0 }
115
+ } )
116
+ . sum ( ) ;
117
+
118
+ let result = FixResult {
119
+ files : fixed_files,
120
+ summary : FixSummary {
121
+ total_files : total_files_checked,
122
+ files_fixed,
123
+ total_fixes,
124
+ } ,
125
+ } ;
126
+
127
+ println ! ( "{}" , serde_json:: to_string_pretty( & result) ?) ;
128
+ }
129
+
130
+ return Ok ( ( ) ) ;
131
+ }
132
+
47
133
let file_issues = Mutex :: new ( HashMap :: new ( ) ) ;
48
134
let had_error = AtomicBool :: new ( false ) ;
135
+
49
136
all_files
50
137
. par_iter ( )
51
138
. for_each ( |file| match process_file ( file) {
@@ -61,8 +148,6 @@ fn main() -> Result<()> {
61
148
} ) ;
62
149
63
150
let files = file_issues. into_inner ( ) . unwrap ( ) ;
64
-
65
- // Calculate summary
66
151
let total_files = all_files. len ( ) ;
67
152
let files_with_issues = files. len ( ) ;
68
153
let total_issues: usize = files. values ( ) . map ( |f| f. total_issues ( ) ) . sum ( ) ;
@@ -76,13 +161,9 @@ fn main() -> Result<()> {
76
161
} ,
77
162
} ;
78
163
79
- // Output JSON
80
164
println ! ( "{}" , serde_json:: to_string_pretty( & result) ?) ;
81
-
82
- // Exit with non-zero status if there were issues
83
165
if files_with_issues > 0 || had_error. load ( Ordering :: Relaxed ) {
84
166
std:: process:: exit ( 1 ) ;
85
167
}
86
-
87
168
Ok ( ( ) )
88
169
}
0 commit comments