summaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-03-02 10:58:44 +0200
committerPaul Buetow <paul@buetow.org>2026-03-02 10:58:44 +0200
commit99bb2e39263dd477c8e438ff38ac1c158e4097a9 (patch)
tree34ef8d85af64112df6eba4efe0f69ebe112e7466 /internal
parentb636b12566ea6c2862bf3adc6686c57e0d785ca3 (diff)
store/cli: move search output to CLI layer (task 400)
Diffstat (limited to 'internal')
-rw-r--r--internal/cli/cli.go12
-rw-r--r--internal/store/store.go14
-rw-r--r--internal/store/store_test.go32
3 files changed, 44 insertions, 14 deletions
diff --git a/internal/cli/cli.go b/internal/cli/cli.go
index 7b18c34..11f1f7e 100644
--- a/internal/cli/cli.go
+++ b/internal/cli/cli.go
@@ -267,7 +267,7 @@ func (c *CLI) dispatch(ctx context.Context, argv []string) int {
func (c *CLI) dispatchSimple(ctx context.Context, argv []string, cmd string) (int, string, bool) {
switch cmd {
case "ls":
- indexes, err := c.st.Search(ctx, ".", store.ActionNone, nil)
+ indexes, err := c.st.Search(ctx, ".", store.ActionNone, nil, printIndex)
if err != nil {
warn(err.Error())
return 1, "", true
@@ -393,7 +393,7 @@ func (c *CLI) dispatchSearch(ctx context.Context, argv []string, cmd string) (in
default:
// Unknown command: treat as a search term, mirroring Ruby's else branch.
// This allows bare search terms to be typed without prefixing "search".
- indexes, err := c.st.Search(ctx, cmd, store.ActionNone, nil)
+ indexes, err := c.st.Search(ctx, cmd, store.ActionNone, nil, printIndex)
if err != nil {
warn(err.Error())
return 1, ""
@@ -513,7 +513,7 @@ func (c *CLI) cmdFullCommit(ctx context.Context) int {
// cmdSearchOnly runs a search without any action and returns the result.
func (c *CLI) cmdSearchOnly(ctx context.Context, term string) (int, string) {
- indexes, err := c.st.Search(ctx, term, store.ActionNone, nil)
+ indexes, err := c.st.Search(ctx, term, store.ActionNone, nil, printIndex)
if err != nil {
warn(err.Error())
return 1, ""
@@ -526,7 +526,7 @@ func (c *CLI) cmdSearchOnly(ctx context.Context, term string) (int, string) {
// cmdSearchAction runs a search with the given action and optional callback.
func (c *CLI) cmdSearchAction(ctx context.Context, term string, action store.Action, actionFn func(context.Context, *store.Index, *store.Data) error) (int, string) {
- indexes, err := c.st.Search(ctx, term, action, actionFn)
+ indexes, err := c.st.Search(ctx, term, action, actionFn, printIndex)
if err != nil {
warn(err.Error())
return 1, ""
@@ -537,6 +537,10 @@ func (c *CLI) cmdSearchAction(ctx context.Context, term string, action store.Act
return 0, ""
}
+func printIndex(idx *store.Index) {
+ fmt.Print(idx.String())
+}
+
// makeActionFn returns the appropriate callback function for actions that
// require external tools (paste, open, edit). For actions handled internally
// by the store (cat, export, pathexport), nil is returned.
diff --git a/internal/store/store.go b/internal/store/store.go
index 8d251b3..e1fb4e8 100644
--- a/internal/store/store.go
+++ b/internal/store/store.go
@@ -155,8 +155,16 @@ func (s *Store) processIndexFile(ctx context.Context, path, searchTerm string, r
// printed; for ActionExport/ActionPathExport the content is written to ExportDir.
// Actions requiring external tools (paste, open, edit) are delegated to the
// optional actionFn callback — pass nil if those actions are not needed.
+// The optional onMatch callback lets callers handle presentation concerns
+// (for example, printing idx.String()) outside the store layer.
// Returns the sorted list of matching indexes for the caller's use.
-func (s *Store) Search(ctx context.Context, searchTerm string, action Action, actionFn func(context.Context, *Index, *Data) error) ([]*Index, error) {
+func (s *Store) Search(
+ ctx context.Context,
+ searchTerm string,
+ action Action,
+ actionFn func(context.Context, *Index, *Data) error,
+ onMatch func(*Index),
+) ([]*Index, error) {
var indexes IndexSlice
if err := s.WalkIndexes(ctx, searchTerm, func(idx *Index) error {
indexes = append(indexes, idx)
@@ -168,7 +176,9 @@ func (s *Store) Search(ctx context.Context, searchTerm string, action Action, ac
sort.Sort(indexes)
for _, idx := range indexes {
- fmt.Print(idx.String())
+ if onMatch != nil {
+ onMatch(idx)
+ }
if err := s.applyAction(ctx, idx, action, actionFn); err != nil {
return indexes, err
}
diff --git a/internal/store/store_test.go b/internal/store/store_test.go
index 86b7d0d..bae61bb 100644
--- a/internal/store/store_test.go
+++ b/internal/store/store_test.go
@@ -375,7 +375,7 @@ func TestRemoveEntry(t *testing.T) {
// --- TestSearch --------------------------------------------------------------
// TestSearch adds two entries, then calls Search with ActionNone and verifies
-// both descriptions are returned sorted and printed to stdout.
+// both descriptions are returned sorted without store-layer stdout side effects.
func TestSearch(t *testing.T) {
ctx, store, cfg, _, _ := testSetup(t)
initGitRepo(t, cfg.DataDir)
@@ -386,7 +386,20 @@ func TestSearch(t *testing.T) {
}
}
- results, err := store.Search(ctx, "", ActionNone, nil)
+ r, w, err := os.Pipe()
+ if err != nil {
+ t.Fatalf("creating pipe: %v", err)
+ }
+ oldStdout := os.Stdout
+ os.Stdout = w
+
+ results, err := store.Search(ctx, "", ActionNone, nil, nil)
+
+ w.Close()
+ os.Stdout = oldStdout
+ var out strings.Builder
+ io.Copy(&out, r)
+
if err != nil {
t.Fatalf("Search: %v", err)
}
@@ -397,6 +410,9 @@ func TestSearch(t *testing.T) {
if results[0].Description != "apple/entry" || results[1].Description != "zebra/entry" {
t.Errorf("unexpected sort order: %v, %v", results[0].Description, results[1].Description)
}
+ if out.String() != "" {
+ t.Errorf("Search(ActionNone) wrote unexpected stdout: %q", out.String())
+ }
}
// --- TestSearchActionCat -----------------------------------------------------
@@ -419,7 +435,7 @@ func TestSearchActionCat(t *testing.T) {
}
os.Stdout = w
- results, err := store.Search(ctx, "note.txt", ActionCat, nil)
+ results, err := store.Search(ctx, "note.txt", ActionCat, nil, nil)
w.Close()
os.Stdout = oldStdout
@@ -459,7 +475,7 @@ func TestSearchActionCatBinarySkip(t *testing.T) {
oldStdout := os.Stdout
os.Stdout = w
- results, searchErr := store.Search(ctx, "photo.jpg", ActionCat, nil)
+ results, searchErr := store.Search(ctx, "photo.jpg", ActionCat, nil, nil)
w.Close()
os.Stdout = oldStdout
@@ -519,7 +535,7 @@ func TestSearchActionExport(t *testing.T) {
t.Fatalf("Add: %v", err)
}
- results, err := store.Search(ctx, "report.txt", ActionExport, nil)
+ results, err := store.Search(ctx, "report.txt", ActionExport, nil, nil)
if err != nil {
t.Fatalf("Search ActionExport: %v", err)
}
@@ -759,7 +775,7 @@ func TestSearchActionPathExport(t *testing.T) {
t.Fatalf("Add: %v", err)
}
- results, err := store.Search(ctx, "report.txt", ActionPathExport, nil)
+ results, err := store.Search(ctx, "report.txt", ActionPathExport, nil, nil)
if err != nil {
t.Fatalf("Search ActionPathExport: %v", err)
}
@@ -800,7 +816,7 @@ func TestSearchActionWithCallback(t *testing.T) {
}
// ActionPaste falls through to the default/actionFn branch of applyAction.
- results, err := store.Search(ctx, "cb/entry.txt", ActionPaste, actionFn)
+ results, err := store.Search(ctx, "cb/entry.txt", ActionPaste, actionFn, nil)
if err != nil {
t.Fatalf("Search with callback: %v", err)
}
@@ -828,7 +844,7 @@ func TestSearchActionNilCallback(t *testing.T) {
}
// nil actionFn: applyAction must return nil without calling anything.
- _, err := store.Search(ctx, "nil/cb.txt", ActionPaste, nil)
+ _, err := store.Search(ctx, "nil/cb.txt", ActionPaste, nil, nil)
if err != nil {
t.Fatalf("Search with nil callback: %v", err)
}