@@ -3,6 +3,7 @@ package difflint
3
3
import (
4
4
"fmt"
5
5
"io"
6
+ "os"
6
7
"path/filepath"
7
8
"strings"
8
9
@@ -49,10 +50,6 @@ type LintOptions struct {
49
50
// TemplatesFromFile returns the directive templates for the given file type.
50
51
func (o * LintOptions ) TemplatesFromFile (file string ) ([]string , error ) {
51
52
fileType := strings .TrimPrefix (filepath .Ext (file ), "." )
52
- if fileType == "" {
53
- return nil , errors .Errorf ("file %q has no extension" , file )
54
- }
55
-
56
53
templateIndices , ok := o .FileExtMap [fileType ]
57
54
if ! ok {
58
55
templateIndices = []int {o .DefaultTemplate }
@@ -87,6 +84,7 @@ type UnsatisfiedRule struct {
87
84
UnsatisfiedTargets map [int ]struct {}
88
85
}
89
86
87
+ // UnsatisfiedRules is a list of unsatisfied rules.
90
88
type UnsatisfiedRules []UnsatisfiedRule
91
89
92
90
// String returns a string representation of the unsatisfied rules.
@@ -122,6 +120,45 @@ type LintResult struct {
122
120
UnsatisfiedRules UnsatisfiedRules
123
121
}
124
122
123
+ // Walk walks the file tree rooted at root, calling callback for each file or
124
+ // directory in the tree, including root.
125
+ func Walk (root string , include []string , exclude []string , callback filepath.WalkFunc ) error {
126
+ isHidden := func (path string ) bool {
127
+ components := strings .Split (path , string (os .PathSeparator ))
128
+ for _ , component := range components {
129
+ if strings .HasPrefix (component , "." ) && component != "." && component != ".." {
130
+ return true
131
+ }
132
+ }
133
+ return false
134
+ }
135
+
136
+ return filepath .Walk (root , func (path string , info os.FileInfo , err error ) error {
137
+ if err != nil {
138
+ return err
139
+ }
140
+
141
+ if info .IsDir () {
142
+ return nil
143
+ }
144
+
145
+ if isHidden (path ) {
146
+ return nil
147
+ }
148
+
149
+ included , err := Include (path , include , exclude )
150
+ if err != nil {
151
+ return err
152
+ }
153
+
154
+ if ! included {
155
+ return nil
156
+ }
157
+
158
+ return callback (path , info , nil )
159
+ })
160
+ }
161
+
125
162
// Lint lints the given hunks against the given rules and returns the result.
126
163
func Lint (o LintOptions ) (* LintResult , error ) {
127
164
// Parse the diff hunks.
@@ -131,18 +168,33 @@ func Lint(o LintOptions) (*LintResult, error) {
131
168
}
132
169
133
170
// Parse rules from hunks.
134
- rulesMap , _ , err := RulesMapFromHunks (hunks , o )
171
+ rulesMap , presentTargetsMap , err := RulesMapFromHunks (hunks , o )
135
172
if err != nil {
136
173
return nil , errors .Wrap (err , "failed to parse rules from hunks" )
137
174
}
138
175
139
176
// Collect the rules that are not satisfied.
140
- unsatisfiedRules , err := Check (rulesMap )
177
+ unsatisfiedRules , err := Check (rulesMap , presentTargetsMap )
141
178
if err != nil {
142
179
return nil , errors .Wrap (err , "failed to check rules" )
143
180
}
144
181
145
- return & LintResult {UnsatisfiedRules : unsatisfiedRules }, nil
182
+ // Filter out rules that are not intended to be included in the output.
183
+ var filteredUnsatisfiedRules UnsatisfiedRules
184
+ for _ , rule := range unsatisfiedRules {
185
+ included , err := Include (rule .Rule .Hunk .File , o .Include , o .Exclude )
186
+ if err != nil {
187
+ return nil , errors .Wrap (err , "failed to check if file is included" )
188
+ }
189
+
190
+ if ! included {
191
+ continue
192
+ }
193
+
194
+ filteredUnsatisfiedRules = append (filteredUnsatisfiedRules , rule )
195
+ }
196
+
197
+ return & LintResult {UnsatisfiedRules : filteredUnsatisfiedRules }, nil
146
198
}
147
199
148
200
// TargetKey returns the key for the given target.
@@ -176,52 +228,35 @@ func isRelativeToCurrentDirectory(path string) bool {
176
228
}
177
229
178
230
// Check returns the list of unsatisfied rules for the given map of rules.
179
- func Check (rulesMap map [string ][]Rule ) (UnsatisfiedRules , error ) {
231
+ func Check (rulesMap map [string ][]Rule , targetsMap map [ string ] struct {} ) (UnsatisfiedRules , error ) {
180
232
var unsatisfiedRules UnsatisfiedRules
181
- for pathnameA , rulesA := range rulesMap {
182
- outer:
183
- for i , ruleA := range rulesA {
184
- // Skip if ruleA is not present or if it has no targets.
185
- if len (ruleA .Targets ) == 0 || ! ruleA .Present {
186
- continue
187
- }
188
233
189
- for pathnameB , rulesB := range rulesMap {
190
- inner:
191
- for j , ruleB := range rulesB {
192
- // Skip if both rules are present or if ruleA is the same as ruleB.
193
- if ruleB .Present || (pathnameA == pathnameB && i == j ) {
194
- continue
195
- }
196
-
197
- // Given that ruleA is present and ruleB is not present, check if ruleA
198
- // is satisfied by ruleB.
199
- unsatisfiedTargetIndices := make (map [int ]struct {})
200
- for k , target := range ruleA .Targets {
201
- // ruleA is satisfied by ruleB if ruleB matches a target of ruleA.
202
- satisfied := target .ID == ruleB .ID && ((target .File == nil && pathnameA == pathnameB ) || (* target .File == pathnameB ))
203
- if satisfied {
204
- continue inner
205
- }
206
-
207
- // Otherwise, add the target index to the list of unsatisfied targets.
208
- unsatisfiedTargetIndices [k ] = struct {}{}
209
- }
210
-
211
- unsatisfiedRules = append (unsatisfiedRules , UnsatisfiedRule {
212
- Rule : ruleA ,
213
- UnsatisfiedTargets : unsatisfiedTargetIndices ,
214
- })
215
- continue outer
234
+ // Check each rule.
235
+ for _ , rules := range rulesMap {
236
+ for _ , rule := range rules {
237
+ unsatisfiedTargets := make (map [int ]struct {}, len (rule .Targets ))
238
+ for i , target := range rule .Targets {
239
+ key := TargetKey (rule .Hunk .File , target )
240
+ if _ , ok := targetsMap [key ]; rule .Present != ok {
241
+ unsatisfiedTargets [i ] = struct {}{}
216
242
}
217
243
}
244
+
245
+ // If there are unsatisfied targets, then the rule is unsatisfied.
246
+ if len (unsatisfiedTargets ) > 0 {
247
+ unsatisfiedRules = append (unsatisfiedRules , UnsatisfiedRule {
248
+ Rule : rule ,
249
+ UnsatisfiedTargets : unsatisfiedTargets ,
250
+ })
251
+ }
218
252
}
219
253
}
220
254
255
+ // Return the unordered list of unsatisfied rules.
221
256
return unsatisfiedRules , nil
222
257
}
223
258
224
- // Entrypoint for the difflint command.
259
+ // Do is the difflint command's entrypoint .
225
260
func Do (r io.Reader , include , exclude []string , extMapPath string ) (UnsatisfiedRules , error ) {
226
261
// Parse options.
227
262
extMap := NewExtMap (extMapPath )
@@ -239,25 +274,6 @@ func Do(r io.Reader, include, exclude []string, extMapPath string) (UnsatisfiedR
239
274
return nil , errors .Wrap (err , "failed to lint hunks" )
240
275
}
241
276
242
- // If there are no unsatisfied rules, return nil.
243
- if len (result .UnsatisfiedRules ) == 0 {
244
- return nil , nil
245
- }
246
-
247
- // Print the unsatisfied rules.
248
- var included bool
249
- for _ , rule := range result .UnsatisfiedRules {
250
- // Skip if the rule is not intended to be included in the output.
251
- included , err = Include (rule .Hunk .File , include , exclude )
252
- if err != nil {
253
- return nil , errors .Wrap (err , "failed to check if file is included" )
254
- }
255
-
256
- if ! included {
257
- continue
258
- }
259
- }
260
-
261
277
return result .UnsatisfiedRules , nil
262
278
}
263
279
0 commit comments