summaryrefslogtreecommitdiff
path: root/internal/extract
diff options
context:
space:
mode:
Diffstat (limited to 'internal/extract')
-rw-r--r--internal/extract/extract.go26
-rw-r--r--internal/extract/tools_test.go24
2 files changed, 50 insertions, 0 deletions
diff --git a/internal/extract/extract.go b/internal/extract/extract.go
index aa3ae93..9768db1 100644
--- a/internal/extract/extract.go
+++ b/internal/extract/extract.go
@@ -7,12 +7,18 @@ package extract
import (
"context"
"errors"
+ "fmt"
"os"
"path/filepath"
"strings"
"time"
)
+// Version is the version of the text this package extracts. Bump it
+// whenever a change could make any format's text differ, so every keyword
+// cache built from the old text is discarded (Fingerprint).
+const Version = 1
+
var (
// ErrUnsupported is returned when the format carries no text krino
// knows how to extract.
@@ -111,6 +117,26 @@ func newWithPath(path string) *Extractor {
return &Extractor{tools: tools, Timeout: 30 * time.Second}
}
+// Fingerprint identifies what text this Extractor would produce: Version,
+// and each external tool found with its path, size and modification time.
+// A keyword cache built under another fingerprint is discarded, so
+// installing, removing or upgrading a tool invalidates it.
+func (e *Extractor) Fingerprint() string {
+ var b strings.Builder
+ fmt.Fprintf(&b, "v%d", Version)
+ for _, name := range toolNames {
+ p := e.tools[name]
+ if p == "" {
+ continue
+ }
+ fmt.Fprintf(&b, " %s=%s", name, p)
+ if fi, err := os.Stat(p); err == nil {
+ fmt.Fprintf(&b, ":%d:%d", fi.Size(), fi.ModTime().UnixNano())
+ }
+ }
+ return b.String()
+}
+
// Tools reports every external tool Extractor knows about, in a fixed
// order, with the path it was found at or "" if it was not found.
func (e *Extractor) Tools() []Tool {
diff --git a/internal/extract/tools_test.go b/internal/extract/tools_test.go
index 4a03f1b..b481e8a 100644
--- a/internal/extract/tools_test.go
+++ b/internal/extract/tools_test.go
@@ -237,3 +237,27 @@ func TestMaxReadCapsToolOutput(t *testing.T) {
t.Fatalf("got %v, want ErrTooLarge (max-read 1024 should have capped a 200000-byte tool output)", err)
}
}
+
+// TestFingerprintFollowsTools: the fingerprint changes when a tool is
+// installed or replaced, and not otherwise.
+func TestFingerprintFollowsTools(t *testing.T) {
+ bin := t.TempDir()
+ none := newWithPath(bin).Fingerprint()
+ if none != newWithPath(bin).Fingerprint() {
+ t.Fatal("fingerprint not stable")
+ }
+ tool := filepath.Join(bin, "pdftotext")
+ if err := os.WriteFile(tool, []byte("#!/bin/sh\n"), 0o755); err != nil {
+ t.Fatal(err)
+ }
+ installed := newWithPath(bin).Fingerprint()
+ if installed == none {
+ t.Error("installing pdftotext did not change the fingerprint")
+ }
+ if err := os.WriteFile(tool, []byte("#!/bin/sh\nexit 0\n"), 0o755); err != nil {
+ t.Fatal(err)
+ }
+ if newWithPath(bin).Fingerprint() == installed {
+ t.Error("replacing pdftotext did not change the fingerprint")
+ }
+}