@@ -14,7 +14,6 @@ import (
14
14
15
15
"git.numtide.com/numtide/treefmt/format"
16
16
"git.numtide.com/numtide/treefmt/stats"
17
- "github.com/gobwas/glob"
18
17
19
18
"git.numtide.com/numtide/treefmt/cache"
20
19
"git.numtide.com/numtide/treefmt/config"
@@ -28,23 +27,15 @@ const (
28
27
BatchSize = 1024
29
28
)
30
29
31
- var (
32
- excludes []glob.Glob
33
- formatters map [string ]* format.Formatter
34
-
35
- filesCh chan * walk.File
36
- processedCh chan * walk.File
37
-
38
- ErrFailOnChange = errors .New ("unexpected changes detected, --fail-on-change is enabled" )
39
- )
30
+ var ErrFailOnChange = errors .New ("unexpected changes detected, --fail-on-change is enabled" )
40
31
41
32
func (f * Format ) Run () (err error ) {
42
33
// set log level and other options
43
- configureLogging ()
34
+ f . configureLogging ()
44
35
45
36
// cpu profiling
46
- if Cli .CpuProfile != "" {
47
- cpuProfile , err := os .Create (Cli .CpuProfile )
37
+ if f .CpuProfile != "" {
38
+ cpuProfile , err := os .Create (f .CpuProfile )
48
39
if err != nil {
49
40
return fmt .Errorf ("failed to open file for writing cpu profile: %w" , err )
50
41
} else if err = pprof .StartCPUProfile (cpuProfile ); err != nil {
@@ -69,66 +60,66 @@ func (f *Format) Run() (err error) {
69
60
}()
70
61
71
62
// find the config file unless specified
72
- if Cli .ConfigFile == "" {
63
+ if f .ConfigFile == "" {
73
64
pwd , err := os .Getwd ()
74
65
if err != nil {
75
66
return err
76
67
}
77
- Cli .ConfigFile , _ , err = findUp (pwd , "treefmt.toml" )
68
+ f .ConfigFile , _ , err = findUp (pwd , "treefmt.toml" )
78
69
if err != nil {
79
70
return err
80
71
}
81
72
}
82
73
83
74
// default the tree root to the directory containing the config file
84
- if Cli .TreeRoot == "" {
85
- Cli .TreeRoot = filepath .Dir (Cli .ConfigFile )
75
+ if f .TreeRoot == "" {
76
+ f .TreeRoot = filepath .Dir (f .ConfigFile )
86
77
}
87
78
88
79
// search the tree root using the --tree-root-file if specified
89
- if Cli .TreeRootFile != "" {
80
+ if f .TreeRootFile != "" {
90
81
pwd , err := os .Getwd ()
91
82
if err != nil {
92
83
return err
93
84
}
94
- _ , Cli .TreeRoot , err = findUp (pwd , Cli .TreeRootFile )
85
+ _ , f .TreeRoot , err = findUp (pwd , f .TreeRootFile )
95
86
if err != nil {
96
87
return err
97
88
}
98
89
}
99
90
100
- log .Debugf ("config-file=%s tree-root=%s" , Cli .ConfigFile , Cli .TreeRoot )
91
+ log .Debugf ("config-file=%s tree-root=%s" , f .ConfigFile , f .TreeRoot )
101
92
102
93
// read config
103
- cfg , err := config .ReadFile (Cli .ConfigFile , Cli .Formatters )
94
+ cfg , err := config .ReadFile (f .ConfigFile , f .Formatters )
104
95
if err != nil {
105
- return fmt .Errorf ("failed to read config file %v: %w" , Cli .ConfigFile , err )
96
+ return fmt .Errorf ("failed to read config file %v: %w" , f .ConfigFile , err )
106
97
}
107
98
108
99
// compile global exclude globs
109
- if excludes , err = format .CompileGlobs (cfg .Global .Excludes ); err != nil {
100
+ if f . excludes , err = format .CompileGlobs (cfg .Global .Excludes ); err != nil {
110
101
return fmt .Errorf ("failed to compile global excludes: %w" , err )
111
102
}
112
103
113
104
// initialise formatters
114
- formatters = make (map [string ]* format.Formatter )
105
+ f . formatters = make (map [string ]* format.Formatter )
115
106
116
107
for name , formatterCfg := range cfg .Formatters {
117
- formatter , err := format .NewFormatter (name , Cli .TreeRoot , formatterCfg , excludes )
108
+ formatter , err := format .NewFormatter (name , f .TreeRoot , formatterCfg , f . excludes )
118
109
119
- if errors .Is (err , format .ErrCommandNotFound ) && Cli .AllowMissingFormatter {
110
+ if errors .Is (err , format .ErrCommandNotFound ) && f .AllowMissingFormatter {
120
111
log .Debugf ("formatter command not found: %v" , name )
121
112
continue
122
113
} else if err != nil {
123
114
return fmt .Errorf ("%w: failed to initialise formatter: %v" , err , name )
124
115
}
125
116
126
117
// store formatter by name
127
- formatters [name ] = formatter
118
+ f . formatters [name ] = formatter
128
119
}
129
120
130
121
// open the cache
131
- if err = cache .Open (Cli .TreeRoot , Cli .ClearCache , formatters ); err != nil {
122
+ if err = cache .Open (f .TreeRoot , f .ClearCache , f . formatters ); err != nil {
132
123
return err
133
124
}
134
125
@@ -151,28 +142,28 @@ func (f *Format) Run() (err error) {
151
142
152
143
// create a channel for files needing to be processed
153
144
// we use a multiple of batch size here as a rudimentary concurrency optimization based on the host machine
154
- filesCh = make (chan * walk.File , BatchSize * runtime .NumCPU ())
145
+ f . filesCh = make (chan * walk.File , BatchSize * runtime .NumCPU ())
155
146
156
147
// create a channel for files that have been processed
157
- processedCh = make (chan * walk.File , cap (filesCh ))
148
+ f . processedCh = make (chan * walk.File , cap (f . filesCh ))
158
149
159
150
// start concurrent processing tasks in reverse order
160
- eg .Go (updateCache (ctx ))
161
- eg .Go (applyFormatters (ctx ))
162
- eg .Go (walkFilesystem (ctx ))
151
+ eg .Go (f . updateCache (ctx ))
152
+ eg .Go (f . applyFormatters (ctx ))
153
+ eg .Go (f . walkFilesystem (ctx ))
163
154
164
155
// wait for everything to complete
165
156
return eg .Wait ()
166
157
}
167
158
168
- func updateCache (ctx context.Context ) func () error {
159
+ func ( f * Format ) updateCache (ctx context.Context ) func () error {
169
160
return func () error {
170
161
// used to batch updates for more efficient txs
171
162
batch := make ([]* walk.File , 0 , BatchSize )
172
163
173
164
// apply a batch
174
165
processBatch := func () error {
175
- if Cli .Stdin {
166
+ if f .Stdin {
176
167
// do nothing
177
168
return nil
178
169
}
@@ -190,13 +181,13 @@ func updateCache(ctx context.Context) func() error {
190
181
case <- ctx .Done ():
191
182
return ctx .Err ()
192
183
// respond to processed files
193
- case file , ok := <- processedCh :
184
+ case file , ok := <- f . processedCh :
194
185
if ! ok {
195
186
// channel has been closed, no further files to process
196
187
break LOOP
197
188
}
198
189
199
- if Cli .Stdin {
190
+ if f .Stdin {
200
191
// dump file into stdout
201
192
f , err := os .Open (file .Path )
202
193
if err != nil {
@@ -229,38 +220,38 @@ func updateCache(ctx context.Context) func() error {
229
220
}
230
221
231
222
// if fail on change has been enabled, check that no files were actually formatted, throwing an error if so
232
- if Cli .FailOnChange && stats .Value (stats .Formatted ) != 0 {
223
+ if f .FailOnChange && stats .Value (stats .Formatted ) != 0 {
233
224
return ErrFailOnChange
234
225
}
235
226
236
227
// print stats to stdout unless we are processing stdin and printing the results to stdout
237
- if ! Cli .Stdin {
228
+ if ! f .Stdin {
238
229
stats .Print ()
239
230
}
240
231
241
232
return nil
242
233
}
243
234
}
244
235
245
- func walkFilesystem (ctx context.Context ) func () error {
236
+ func ( f * Format ) walkFilesystem (ctx context.Context ) func () error {
246
237
return func () error {
247
238
eg , ctx := errgroup .WithContext (ctx )
248
239
pathsCh := make (chan string , BatchSize )
249
240
250
241
// By default, we use the cli arg, but if the stdin flag has been set we force a filesystem walk
251
242
// since we will only be processing one file from a temp directory
252
- walkerType := Cli .Walk
243
+ walkerType := f .Walk
253
244
254
- if Cli .Stdin {
245
+ if f .Stdin {
255
246
walkerType = walk .Filesystem
256
247
257
248
// check we have only received one path arg which we use for the file extension / matching to formatters
258
- if len (Cli .Paths ) != 1 {
249
+ if len (f .Paths ) != 1 {
259
250
return fmt .Errorf ("only one path should be specified when using the --stdin flag" )
260
251
}
261
252
262
253
// read stdin into a temporary file with the same file extension
263
- pattern := fmt .Sprintf ("*%s" , filepath .Ext (Cli .Paths [0 ]))
254
+ pattern := fmt .Sprintf ("*%s" , filepath .Ext (f .Paths [0 ]))
264
255
file , err := os .CreateTemp ("" , pattern )
265
256
if err != nil {
266
257
return fmt .Errorf ("failed to create a temporary file for processing stdin: %w" , err )
@@ -270,45 +261,45 @@ func walkFilesystem(ctx context.Context) func() error {
270
261
return fmt .Errorf ("failed to copy stdin into a temporary file" )
271
262
}
272
263
273
- Cli .Paths [0 ] = file .Name ()
264
+ f .Paths [0 ] = file .Name ()
274
265
}
275
266
276
267
walkPaths := func () error {
277
268
defer close (pathsCh )
278
269
279
270
var idx int
280
- for idx < len (Cli .Paths ) {
271
+ for idx < len (f .Paths ) {
281
272
select {
282
273
case <- ctx .Done ():
283
274
return ctx .Err ()
284
275
default :
285
- pathsCh <- Cli .Paths [idx ]
276
+ pathsCh <- f .Paths [idx ]
286
277
idx += 1
287
278
}
288
279
}
289
280
290
281
return nil
291
282
}
292
283
293
- if len (Cli .Paths ) > 0 {
284
+ if len (f .Paths ) > 0 {
294
285
eg .Go (walkPaths )
295
286
} else {
296
287
// no explicit paths to process, so we only need to process root
297
- pathsCh <- Cli .TreeRoot
288
+ pathsCh <- f .TreeRoot
298
289
close (pathsCh )
299
290
}
300
291
301
292
// create a filesystem walker
302
- walker , err := walk .New (walkerType , Cli .TreeRoot , pathsCh )
293
+ walker , err := walk .New (walkerType , f .TreeRoot , pathsCh )
303
294
if err != nil {
304
295
return fmt .Errorf ("failed to create walker: %w" , err )
305
296
}
306
297
307
298
// close the files channel when we're done walking the file system
308
- defer close (filesCh )
299
+ defer close (f . filesCh )
309
300
310
301
// if no cache has been configured, or we are processing from stdin, we invoke the walker directly
311
- if Cli .NoCache || Cli .Stdin {
302
+ if f .NoCache || f .Stdin {
312
303
return walker .Walk (ctx , func (file * walk.File , err error ) error {
313
304
select {
314
305
case <- ctx .Done ():
@@ -318,7 +309,7 @@ func walkFilesystem(ctx context.Context) func() error {
318
309
if ! (file .Info .IsDir () || file .Info .Mode ()& os .ModeSymlink == os .ModeSymlink ) {
319
310
stats .Add (stats .Traversed , 1 )
320
311
stats .Add (stats .Emitted , 1 )
321
- filesCh <- file
312
+ f . filesCh <- file
322
313
}
323
314
return nil
324
315
}
@@ -327,14 +318,14 @@ func walkFilesystem(ctx context.Context) func() error {
327
318
328
319
// otherwise we pass the walker to the cache and have it generate files for processing based on whether or not
329
320
// they have been added/changed since the last invocation
330
- if err = cache .ChangeSet (ctx , walker , filesCh ); err != nil {
321
+ if err = cache .ChangeSet (ctx , walker , f . filesCh ); err != nil {
331
322
return fmt .Errorf ("failed to generate change set: %w" , err )
332
323
}
333
324
return nil
334
325
}
335
326
}
336
327
337
- func applyFormatters (ctx context.Context ) func () error {
328
+ func ( f * Format ) applyFormatters (ctx context.Context ) func () error {
338
329
// create our own errgroup for concurrent formatting tasks
339
330
fg , ctx := errgroup .WithContext (ctx )
340
331
// simple optimization to avoid too many concurrent formatting tasks
@@ -371,7 +362,7 @@ func applyFormatters(ctx context.Context) func() error {
371
362
372
363
// pass each file to the processed channel
373
364
for _ , task := range tasks {
374
- processedCh <- task .File
365
+ f . processedCh <- task .File
375
366
}
376
367
377
368
return nil
@@ -393,26 +384,26 @@ func applyFormatters(ctx context.Context) func() error {
393
384
return func () error {
394
385
defer func () {
395
386
// close processed channel
396
- close (processedCh )
387
+ close (f . processedCh )
397
388
}()
398
389
399
390
// iterate the files channel
400
- for file := range filesCh {
391
+ for file := range f . filesCh {
401
392
402
393
// determine a list of formatters that are interested in file
403
394
var matches []* format.Formatter
404
- for _ , formatter := range formatters {
395
+ for _ , formatter := range f . formatters {
405
396
if formatter .Wants (file ) {
406
397
matches = append (matches , formatter )
407
398
}
408
399
}
409
400
410
401
if len (matches ) == 0 {
411
- if Cli .OnUnmatched == log .FatalLevel {
402
+ if f .OnUnmatched == log .FatalLevel {
412
403
return fmt .Errorf ("no formatter for path: %s" , file .Path )
413
404
}
414
- log .Logf (Cli .OnUnmatched , "no formatter for path: %s" , file .Path )
415
- processedCh <- file
405
+ log .Logf (f .OnUnmatched , "no formatter for path: %s" , file .Path )
406
+ f . processedCh <- file
416
407
} else {
417
408
// record the match
418
409
stats .Add (stats .Matched , 1 )
0 commit comments