summaryrefslogtreecommitdiff
path: root/internal/engine/engine.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/engine/engine.go')
-rw-r--r--internal/engine/engine.go32
1 files changed, 23 insertions, 9 deletions
diff --git a/internal/engine/engine.go b/internal/engine/engine.go
index 0c3186d..61c310e 100644
--- a/internal/engine/engine.go
+++ b/internal/engine/engine.go
@@ -159,7 +159,7 @@ func LoadWith(mainFile string, overrides map[string][]byte, names ...string) (*E
}
}
dir.ContentKeywords = contentKeywords(dir.Rules, dir.Excludes)
- dir.DupScopes = dupScopes(dir.Rules)
+ dir.DupScopes = dupScopes(dir.Rules, dir.Excludes)
dirs = append(dirs, dir)
}
@@ -267,16 +267,24 @@ func contentKeywords(rules []*Rule, excludes []*Exclude) []cond.Keyword {
return out
}
-// dupScopes returns the distinct Cond.DupDirs lists of rules, in first-seen
-// order. Lists are compared as written, with their length in the key so
-// (duplicate) and (duplicate "") stay apart; two spellings of one directory
-// stay two entries, which costs a second lookup but never a wrong answer,
-// since facts.Duplicate resolves and shares the index itself.
-func dupScopes(rules []*Rule) [][]string {
+// dupScopes returns the distinct Cond.DupDirs lists a directory uses, in
+// first-seen order, from its rules and its excludes alike. Lists are
+// compared as written, with their length in the key so (duplicate) and
+// (duplicate "") stay apart; two spellings of one directory stay two
+// entries, which costs a second lookup but never a wrong answer, since
+// facts.Duplicate resolves and shares the index itself.
+//
+// The excludes count because the protection these scopes drive - no rule
+// deletes a file krino has found to be a duplicate - is about what krino
+// knows, not about which form taught it. A directory whose only duplicate
+// test sat in an (exclude ...) had no scopes at all, so a later rule could
+// permanently delete every copy of a file krino had just called a
+// duplicate.
+func dupScopes(rules []*Rule, excludes []*Exclude) [][]string {
var out [][]string
seen := map[string]bool{}
- for _, r := range rules {
- for _, dirs := range r.Cond.DupDirs {
+ add := func(lists [][]string) {
+ for _, dirs := range lists {
key := fmt.Sprintf("%d\x00%s", len(dirs), strings.Join(dirs, "\x00"))
if seen[key] {
continue
@@ -285,6 +293,12 @@ func dupScopes(rules []*Rule) [][]string {
out = append(out, dirs)
}
}
+ for _, r := range rules {
+ add(r.Cond.DupDirs)
+ }
+ for _, x := range excludes {
+ add(x.Cond.DupDirs)
+ }
return out
}