summaryrefslogtreecommitdiff
path: root/internal/engine/match.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/engine/match.go')
-rw-r--r--internal/engine/match.go49
1 files changed, 43 insertions, 6 deletions
diff --git a/internal/engine/match.go b/internal/engine/match.go
index ffee7f0..8daea9d 100644
--- a/internal/engine/match.go
+++ b/internal/engine/match.go
@@ -34,6 +34,10 @@ type FileMatch struct {
Rules []RuleMatch // matching rules in order, ending at the first with (stop)
Warnings []string // "<rule>: <warning>", e.g. "acme: content unreadable: needs pdftotext, not installed"
+ // Excluded is the (exclude ...) form that set this file aside before any
+ // rule ran, as written; "" when none did. An excluded file has no Rules.
+ Excluded string
+
// NoDelete is non-empty when no delete step may run for this file (spec
// ยง5.5 rule 2), and says why: the file is a duplicate under a scope its
// directory's rules use, or that check failed. Only set for a file some
@@ -92,7 +96,7 @@ func (e *Engine) Match(ctx context.Context, d *Dir) (*Result, error) {
var matched, unmatched []FileMatch
for _, fm := range fileMatches {
- if len(fm.Rules) > 0 {
+ if len(fm.Rules) > 0 || fm.Excluded != "" {
matched = append(matched, fm)
} else {
unmatched = append(unmatched, fm)
@@ -119,6 +123,16 @@ func (e *Engine) Match(ctx context.Context, d *Dir) (*Result, error) {
func evalFile(run *matchRun, file scan.File) FileMatch {
f := newFacts(run, file)
fm := FileMatch{File: file}
+ for _, x := range run.d.Excludes {
+ res := x.Cond.Eval(f)
+ for _, w := range res.Warnings {
+ fm.Warnings = append(fm.Warnings, "exclude: "+w)
+ }
+ if res.Match {
+ fm.Excluded = x.Text
+ return fm
+ }
+ }
for _, r := range run.d.Rules {
res := r.Cond.Eval(f)
for _, w := range res.Warnings {
@@ -180,12 +194,21 @@ type RuleTrace struct {
Stopped string // "stopped by rule acme" when an earlier (stop) ended the search
}
+// ExcludeTrace is one (exclude ...) form's outcome in an Explain call.
+type ExcludeTrace struct {
+ Text string
+ Match bool
+ Trace *cond.Trace
+}
+
// Explanation is why (or why not) krino would act on one file.
type Explanation struct {
- Dir *Dir
- File scan.File
- Skip string // why krino would not look at this file at all; "" when it would
- Rules []RuleTrace
+ Dir *Dir
+ File scan.File
+ Skip string // why krino would not look at this file at all; "" when it would
+ Excludes []ExcludeTrace
+ Excluded string // the first exclude that matches, which sets the file aside; "" when none does
+ Rules []RuleTrace
}
// Explain reports, for one file, whether krino's ordinary scan would ever
@@ -235,6 +258,16 @@ func (e *Engine) Explain(ctx context.Context, path string) (*Explanation, error)
run := newMatchRun(e, d, ctx, now, e.filesForExplain(d, sf, excl, now))
f := newFacts(run, sf)
+ var excludes []ExcludeTrace
+ excluded := ""
+ for _, x := range d.Excludes {
+ trace := x.Cond.Explain(f)
+ excludes = append(excludes, ExcludeTrace{Text: x.Text, Match: trace.Value, Trace: trace})
+ if trace.Value && excluded == "" {
+ excluded = x.Text
+ }
+ }
+
var rules []RuleTrace
stoppedBy := ""
for _, r := range d.Rules {
@@ -253,7 +286,7 @@ func (e *Engine) Explain(ctx context.Context, path string) (*Explanation, error)
}
}
- return &Explanation{Dir: d, File: sf, Skip: skip, Rules: rules}, nil
+ return &Explanation{Dir: d, File: sf, Skip: skip, Excludes: excludes, Excluded: excluded, Rules: rules}, nil
}
// filesForExplain returns the file set Explain's duplicate checks run
@@ -285,6 +318,7 @@ func walkOptions(d *Dir, excl []string, now time.Time) scan.Options {
Exclude: excl,
Busy: d.Settings.Busy,
MinAge: d.Settings.MinAge,
+ MaxSize: d.Settings.MaxSize,
Now: now,
}
}
@@ -328,6 +362,9 @@ func explainSkip(d *Dir, sf scan.File, excl []string, now time.Time) string {
if now.Sub(sf.ModTime) < d.Settings.MinAge {
return "too new"
}
+ if d.Settings.MaxSize > 0 && sf.Size > d.Settings.MaxSize {
+ return "too big"
+ }
return ""
}