aboutsummaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-09-14 22:23:42 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-09-14 22:23:42 +0200
commit394d117b90ffb64c87adfc8c2436b57007840c2b (patch)
tree400c7cff286c6d10e0ec62dfe28c6954faf740d0 /internal
parent79c746ceac46188deababcf5453c7ca37e173a6a (diff)
downloadkrino-394d117b90ffb64c87adfc8c2436b57007840c2b.tar.gz
krino-394d117b90ffb64c87adfc8c2436b57007840c2b.zip
plan 10: a format with no text is no match, not unreadable
Diffstat (limited to 'internal')
-rw-r--r--internal/engine/exclude_test.go42
-rw-r--r--internal/engine/facts.go9
2 files changed, 50 insertions, 1 deletions
diff --git a/internal/engine/exclude_test.go b/internal/engine/exclude_test.go
index 87fa16c..4bb1512 100644
--- a/internal/engine/exclude_test.go
+++ b/internal/engine/exclude_test.go
@@ -260,3 +260,45 @@ func TestLoadChecksMainExcludesWithoutDirectories(t *testing.T) {
t.Error("a broken krino.conf exclude was not reported")
}
}
+
+// TestNoTextFormatIsNoMatch: a file whose format has no text cannot contain
+// a keyword, so a content exclude without a type does not set it aside, and
+// no "content unreadable" warning is raised - while a real read failure (over
+// max-read) still fails closed (re-review N2, Ɓukasz's decision).
+func TestNoTextFormatIsNoMatch(t *testing.T) {
+ big := "confidential " + strings.Repeat("x", 2048)
+ h, dl := excludeTree(t, map[string]string{"photo.jpg": "\xff\xd8\xff\x00\x01binary", "big.txt": big})
+ main := writeConfig(t, h, `(include "dl")`, map[string]string{"dl": `
+(path "~/dl")
+(max-read 1K)
+(exclude (content "confidential"))
+(rule "pics" (when (type jpg)) (move "Pictures"))
+`})
+ e, errs := Load(main)
+ if len(errs) > 0 {
+ t.Fatal(errs)
+ }
+ r, err := e.Match(context.Background(), e.Dirs[0])
+ if err != nil {
+ t.Fatal(err)
+ }
+ for _, fm := range r.Matched {
+ switch fm.File.Rel {
+ case "photo.jpg":
+ if fm.Excluded != "" || len(fm.Rules) != 1 || len(fm.Warnings) != 0 {
+ t.Errorf("photo.jpg: Excluded %q, rules %d, warnings %v; want the pics rule and no warning", fm.Excluded, len(fm.Rules), fm.Warnings)
+ }
+ case "big.txt":
+ if !strings.HasSuffix(fm.Excluded, "(content unreadable)") {
+ t.Errorf("big.txt: Excluded %q; a real read failure must still fail closed", fm.Excluded)
+ }
+ }
+ }
+ x, err := e.Explain(context.Background(), filepath.Join(dl, "photo.jpg"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if x.Excluded != "" {
+ t.Errorf("explain: Excluded %q, want none", x.Excluded)
+ }
+}
diff --git a/internal/engine/facts.go b/internal/engine/facts.go
index 487604b..ccdf18d 100644
--- a/internal/engine/facts.go
+++ b/internal/engine/facts.go
@@ -4,6 +4,7 @@ package engine
import (
"context"
+ "errors"
"path/filepath"
"sort"
"strings"
@@ -178,7 +179,13 @@ func (f *facts) ContentContains(opt cond.Options, keywords []string) (int, error
func (f *facts) extract(opt cond.Options, keywords []string) {
f.contentDone = true
text, err := f.run.e.Extract.Text(f.run.ctx, f.file.Path, f.file.Size, f.run.d.Settings.MaxRead)
- if err != nil {
+ switch {
+ case errors.Is(err, extract.ErrUnsupported):
+ // A format with no text cannot contain a keyword: every answer is
+ // no, with no warning, and the answers are cached like any other
+ // (re-review N2). Only a real read failure is "unreadable".
+ text = ""
+ case err != nil:
f.contentErr = err
return
}